Commit a5f65ab1 authored by Sai Srinivas's avatar Sai Srinivas Committed by Sai Srinivas
Browse files

03-09-2025 HRM accessible pages and attendance/ leave approval/rejection

parent 332a8e91
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/screens/hrm/OrganizationStructureScreen.dart';
import 'package:generp/screens/hrm/RewardListScreen.dart';
import 'package:generp/screens/hrm/Attendancelist.dart';
import 'package:provider/provider.dart';
import '../../Utils/app_colors.dart';
import 'AttendanceRequestDetail.dart';
import 'LeaveApplicationScreen.dart';
import 'TourExpensesListScreen.dart';
import 'attendancelist.dart';
import 'RewardListScreen.dart';
import 'OrganizationStructureScreen.dart';
import '../../Notifiers/hrmProvider/hrmAccessiblePagesProvider.dart';
import 'oggchart.dart';
class HrmdashboardScreen extends StatefulWidget {
const HrmdashboardScreen({super.key});
......@@ -16,17 +20,30 @@ class HrmdashboardScreen extends StatefulWidget {
}
class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
final allowedPages = [
"Team Leave Request Approval",
"Team Attendance Approval",
"Leave Request List",
"Tour Bill List",
"Rewards List",
"Attendance Request List",
];
@override
void initState() {
super.initState();
Future.microtask(() =>
Provider.of<HrmAccessiblePagesProvider>(context, listen: false)
.fetchAccessiblePages(context));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
backgroundColor: const Color(0xFFCEEDFF),
// elevation: 2.0,
title: SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
title: Row(
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
......@@ -36,28 +53,23 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
),
const SizedBox(width: 10),
InkResponse(
onTap: () => Navigator.pop(context, true),
child: Text(
Text(
"HRM",
style: TextStyle(
fontSize: 18,
height: 1.1,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
color: AppColors.semi_black,
),
),
),
],
),
),
),
backgroundColor: Color(0xffF6F6F8),
backgroundColor: const Color(0xffF6F6F8),
body: SingleChildScrollView(
child: Column(
children: [
/// Background elements
/// Background
Stack(
children: [
Container(
......@@ -74,7 +86,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
colors: [
Color(0xFFCEEDFF),
Color(0xFFf9f9fb),
Color(0xffF6F6F8)
Color(0xffF6F6F8),
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
......@@ -92,25 +104,21 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
),
/// Content
Column(
children: [
/// Top Section with Gradient
/// Top Illustration & Button
Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 60, bottom: 30),
child: Column(
children: [
/// Illustration
SvgPicture.asset(
"assets/images/capa.svg",
height: 146,
width: 400,
fit: BoxFit.contain,
),
const SizedBox(height: 32),
/// Organization Structure Button
Container(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 8),
......@@ -127,7 +135,7 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OrganizationStructureScreen(),
builder: (context) => OrgChartt(),
),
);
},
......@@ -153,82 +161,56 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
],
),
),
/// Bottom Grid Section
// Bottom Grid Section
/// Grid Section
LayoutBuilder(
builder: (context, constraints) {
final itemWidth = 180.0; // Fixed desired width for each item
final availableWidth = constraints.maxWidth;
final crossAxisCount = (availableWidth / itemWidth).floor().clamp(2, 4);
return Padding(
padding: const EdgeInsets.all(14),
child: GridView.count(
crossAxisCount: crossAxisCount,
child: Consumer<HrmAccessiblePagesProvider>(
builder: (context, provider, child) {
if (provider.isLoading) {
return const Center(
child: CircularProgressIndicator());
}
if (provider.errorMessage != null) {
return Center(
child: Text(provider.errorMessage!));
}
final pages = (provider.response?.pagesAccessible ?? [])
.where((page) =>
allowedPages.contains(page.pageName))
.toList();
return GridView.builder(
itemCount: pages.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: (constraints.maxWidth / 180).floor().clamp(2, 4),
crossAxisSpacing: 8.5,
mainAxisSpacing: 16,
childAspectRatio: 1.7,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
_buildTile(
label: "Attendance List",
subtitle: "Real-time request",
assetIcon: "assets/svg/hrm/attendanceList.svg",
txtColor: const Color(0xff1487C9),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const Attendancelist(),
),
);
},
),
_buildTile(
label: "Leave Application",
subtitle: "Apply & Track",
assetIcon: "assets/svg/hrm/leaveApplication.svg",
txtColor: const Color(0xff1487C9),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LeaveApplicationListScreen(),
),
);
},
),
_buildTile(
label: "Rewards List",
subtitle: "Track earned rewards",
assetIcon: "assets/svg/hrm/rewardList.svg",
itemBuilder: (context, index) {
final page = pages[index];
return _buildTile(
label: page.pageName ?? "",
subtitle: _getSubtitle(page.pageName ?? ""),
assetIcon: _getIcon(page.pageName ?? ""),
txtColor: const Color(0xff1487C9),
onTap: () {
Navigator.push(
onTap: () => _handleNavigation(
context,
MaterialPageRoute(
builder: (context) => const RewardListScreen(),
page.pageName ?? "",
page.mode ?? "",
),
);
},
),
_buildTile(
label: "Tour Expenses",
subtitle: "Submit and manage claims",
assetIcon: "assets/svg/hrm/tourExp.svg",
txtColor: const Color(0xff1487C9),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const TourExpensesListScreen(),
),
);
},
),
],
),
);
},
),
......@@ -236,91 +218,86 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
],
),
],
),
),
);
}
/// Reusable Tile Widget (Row style)
/// Reusable Tile Widget (Row style) - Updated to match design
/// Card builder
Widget _buildTile({
required String label,
required String subtitle,
required String assetIcon, // SVG/PNG asset
required String assetIcon,
required Color txtColor,
VoidCallback? onTap,
}) {
return LayoutBuilder(
builder: (context, constraints) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(14),
child: Container(
padding: EdgeInsets.symmetric(
vertical: 5,
horizontal: 15,
),
margin: EdgeInsets.symmetric(
vertical: 7,
horizontal: 5,
vertical: constraints.maxHeight * 0.05,
horizontal: constraints.maxWidth * 0.05,
),
margin: const EdgeInsets.symmetric(vertical: 7, horizontal: 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
/// Left side text
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
Flexible(
child: Text(
label,
style: TextStyle(
fontSize: 14,
color: AppColors.app_blue,
fontFamily: "JakartaMedium",
),
softWrap: true,
overflow: TextOverflow.visible,
),
SizedBox(height: 4),
Text(
),
const SizedBox(height: 4),
Flexible(
child: Text(
subtitle,
style: TextStyle(
fontSize: 12,
color: AppColors.grey_semi,
fontFamily: "JakartaMedium",
),
softWrap: true,
overflow: TextOverflow.visible,
),
),
],
),
),
SizedBox(width: 10),
/// Right side icon (SVG/PNG)
Expanded(
flex: 1,
child: Container(
height: 42,
width: 42,
height: constraints.maxHeight * 0.5, // Responsive size
width: constraints.maxHeight * 0.5, // Responsive size
decoration: BoxDecoration(
shape: BoxShape.circle,
color: const Color(0xFFEDF8FF), // icon bg
color: const Color(0xFFEDF8FF),
),
child: Center(
child: SvgPicture.asset(
height: 25,
width: 25,
assetIcon,
fit: BoxFit.contain,
height: constraints.maxHeight * 0.3, // Responsive size
width: constraints.maxHeight * 0.3, // Responsive size
),
),
),
......@@ -329,5 +306,114 @@ class _HrmdashboardScreenState extends State<HrmdashboardScreen> {
),
),
);
},
);
}
/// Mapping subtitles
String _getSubtitle(String pageName) {
switch (pageName) {
case "Attendance Request List":
return "Real-time request";
case "Leave Request List":
return "Apply & Track";
case "Rewards List":
return "Track earned rewards";
case "Tour Bill List":
return "Submit and manage claims";
case "Team Leave Request Approval":
return "";
case "Team Attendance Approval":
return "";
default:
return "";
}
}
/// Mapping icons
String _getIcon(String pageName) {
switch (pageName) {
case "Attendance Request List":
return "assets/svg/hrm/attendanceList.svg";
case "Leave Request List":
return "assets/svg/hrm/leaveApplication.svg";
case "Rewards List":
return "assets/svg/hrm/rewardList.svg";
case "Tour Bill List":
return "assets/svg/hrm/tourExp.svg";
case "Team Leave Request Approval":
return "assets/svg/hrm/leaveApplication.svg";
case "Team Attendance Approval":
return "assets/svg/hrm/attendanceList.svg";
default:
return "assets/svg/hrm/groupIc.svg";
}
}
/// Navigation mapping
void _handleNavigation(
BuildContext context,
String pageName,
String mode,
) {
switch (pageName) {
case "Attendance Request List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AttendanceListScreen(mode: mode),
),
);
break;
case "Leave Request List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LeaveApplicationListScreen(
mode: mode,
),
),
);
break;
case "Rewards List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => RewardListScreen(),
),
);
break;
case "Tour Bill List":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TourExpensesListScreen(),
),
);
break;
case "Team Leave Request Approval":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LeaveApplicationListScreen(
mode: mode,
),
),
);
break;
case "Team Attendance Approval":
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AttendanceListScreen(mode: mode),
),
);
break;
}
}
}
......@@ -12,7 +12,8 @@ import '../finance/FileViewer.dart';
/// Screen for leave application details
class LeaveApplicationDetailScreen extends StatefulWidget {
final String leaveRequestId;
const LeaveApplicationDetailScreen({super.key, required this.leaveRequestId});
final String mode;
const LeaveApplicationDetailScreen({super.key, required this.leaveRequestId, required this.mode});
@override
State<LeaveApplicationDetailScreen> createState() => _LeaveApplicationDetailScreenState();
......@@ -209,12 +210,301 @@ class _LeaveApplicationDetailScreenState extends State<LeaveApplicationDetailScr
);
},
),
bottomNavigationBar: widget.mode == "teamleader"
? Container(
decoration: const BoxDecoration(color: Colors.white),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
height: 80,
child: Row(
children: [
/// Reject Button
Expanded(
child: InkWell(
onTap: () {
showRemarkSheet(
context: context,
actionType: "Reject",
onSubmit: (remark) {
provider.leaveRequestRejectApprove(
context,
mode: widget.mode,
type: "Rejected",
remarks: remark,
id: provider.response!.requestDetails!.id!,
);
},
);
},
child: Container(
alignment: Alignment.center,
height: 45,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: const Color(0xFFFFFFFF),
border: Border.all(
color: const Color(0xFFE0E0E0),
width: 0.5,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset("assets/svg/finance/level_reject_ic.svg"),
const SizedBox(width: 6),
const Text(
"Reject",
style: TextStyle(
color: Colors.black87,
fontSize: 14,
fontFamily: "JakartaMedium",
),
),
],
),
),
),
),
const SizedBox(width: 10),
/// Approve Button
Expanded(
child: InkWell(
onTap: () {
showRemarkSheet(
context: context,
actionType: "Approve",
onSubmit: (remark) {
provider.leaveRequestRejectApprove(
context,
mode: widget.mode,
type: "Approved",
remarks: remark,
id: provider.response!.requestDetails!.id!,
);
},
);
},
child: Container(
alignment: Alignment.center,
height: 45,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: const Color(0xFFFFFFFF),
border: Border.all(
color: const Color(0xFFE0E0E0),
width: 0.5,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset("assets/svg/finance/level_approve_ic.svg"),
const SizedBox(width: 6),
const Text(
"Approve",
style: TextStyle(
color: Colors.black87,
fontSize: 14,
fontFamily: "JakartaMedium",
),
),
],
),
),
),
),
],
),
)
: const SizedBox.shrink(),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
);
},
),
);
}
Future<void> showRemarkSheet({
required BuildContext context,
required String actionType, // "Approved" or "Rejected"
required Function(String remark) onSubmit,
}) {
final remarkController = TextEditingController();
String? remarkError;
return showModalBottomSheet(
useSafeArea: true,
isDismissible: true,
isScrollControlled: true,
showDragHandle: true,
backgroundColor: Colors.white,
enableDrag: true,
context: context,
builder: (context) {
return StatefulBuilder(
builder: (context, setState) {
void updateState(VoidCallback fn) {
setState(fn);
}
bool validateFields() {
String? newRemarkError =
remarkController.text.trim().isEmpty ? "Remark required" : null;
if (remarkError != newRemarkError) {
updateState(() {
remarkError = newRemarkError;
});
}
return newRemarkError == null;
}
Widget errorText(String? msg) => msg == null
? const SizedBox()
: Padding(
padding: const EdgeInsets.only(top: 4, left: 4),
child: Text(
msg,
style: const TextStyle(
color: Colors.red,
fontSize: 12,
fontFamily: "JakartaMedium",
),
),
);
return SafeArea(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"$actionType Attendance Request",
style: const TextStyle(
fontSize: 16,
color: Colors.black87,
fontFamily: "JakartaMedium",
),
),
const SizedBox(height: 16),
Text(
"Remark",
style: const TextStyle(
fontSize: 14,
color: Colors.black87,
fontFamily: "JakartaMedium",
),
),
const SizedBox(height: 6),
TextField(
controller: remarkController,
maxLines: 3,
onChanged: (val) {
if (remarkError != null && val.isNotEmpty) {
updateState(() => remarkError = null);
}
},
decoration: InputDecoration(
hintText: "Enter your remark here...",
filled: true,
fillColor: Colors.grey.shade100,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
contentPadding: const EdgeInsets.symmetric(
vertical: 12,
horizontal: 12,
),
),
),
errorText(remarkError),
const SizedBox(height: 20),
Row(
children: [
Expanded(
child: InkResponse(
onTap: () => Navigator.pop(context),
child: Container(
height: 45,
decoration: BoxDecoration(
color: Colors.red.shade100,
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child: Text(
"Cancel",
style: TextStyle(
color: Colors.red,
fontFamily: "JakartaMedium",
),
),
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: InkResponse(
onTap: () async {
if (validateFields()) {
final remark = remarkController.text.trim();
// Call provider
await onSubmit(remark);
// SnackBar here
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Request submitted successfully"),
backgroundColor: Colors.green,
behavior: SnackBarBehavior.floating,
),
);
}
},
child: Container(
height: 45,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child: Text(
"Submit",
style: TextStyle(
color: Colors.white,
fontFamily: "JakartaMedium",
),
),
),
),
),
),
],
),
],
),
),
),
);
},
);
},
);
}
/// Reusable Row Widget for details
Widget _buildDetailTile(String label, String? value) {
return Padding(
......
......@@ -11,7 +11,8 @@ import 'AddLeaveRequestScreen.dart';
import 'LeaveApplicationDetailScreen.dart';
class LeaveApplicationListScreen extends StatefulWidget {
const LeaveApplicationListScreen({super.key});
final mode;
const LeaveApplicationListScreen({super.key, required this.mode});
@override
State<LeaveApplicationListScreen> createState() => _LeaveApplicationListScreenState();
......@@ -34,7 +35,7 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
create: (_) {
final provider = LeaveApplicationListProvider();
Future.microtask(() {
provider.fetchLeaveApplications(context);
provider.fetchLeaveApplications(context, widget.mode);
});
return provider;
},
......@@ -59,6 +60,7 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
provider.setDateRangeFilter("Custom", customRange: dateRange);
provider.fetchLeaveApplications(
context,
widget.mode,
dateRange: "Custom",
customRange: dateRange,
);
......@@ -138,10 +140,11 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
MaterialPageRoute(
builder: (context) => LeaveApplicationDetailScreen(
leaveRequestId: item.id.toString(),
mode: widget.mode,
),
),
).then((_) {
provider.fetchLeaveApplications(context);
provider.fetchLeaveApplications(context,widget.mode);
});
},
......@@ -267,7 +270,7 @@ class _LeaveApplicationListScreenState extends State<LeaveApplicationListScreen>
),
),
).then((_) {
provider.fetchLeaveApplications(context);
provider.fetchLeaveApplications(context, widget.mode);
});
// show add bill screen here
......
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:generp/Utils/app_colors.dart';
import 'package:generp/screens/commom/accountsListDetails.dart';
import 'package:graphview/GraphView.dart';
import 'package:provider/provider.dart';
import '../../Models/hrmmodels/ogresponse.dart';
import '../../Notifiers/hrmprovider/orgprovider.dart';
import '../../Utils/commonServices.dart';
import '../../Utils/commonWidgets.dart';
class OrgChartt extends StatefulWidget {
const OrgChartt({super.key});
@override
State<OrgChartt> createState() => _OrgCharttState();
}
class _OrgCharttState extends State<OrgChartt> {
Map _source = {ConnectivityResult.none: false};
final MyConnectivity _connectivity = MyConnectivity.instance;
final Graph graph = Graph();
final Map<String, Node> nodeMap = {};
String connection = 'Offline';
bool _isGraphReady = false;
final Map<String, GlobalKey> nodeKeys = {};
final TransformationController _transformationController =
TransformationController();
final builder =
BuchheimWalkerConfiguration()
..levelSeparation = 50
..siblingSeparation = 20
..subtreeSeparation = 30
..orientation = BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM;
@override
void initState() {
super.initState();
_connectivity.initialise();
_connectivity.myStream.listen((source) {
setState(() => _source = source);
});
_transformationController.value = Matrix4.identity()..scale(0.5);
WidgetsBinding.instance.addPostFrameCallback((_) {
Provider.of<Orgprovider>(context, listen: false).ogChart(context).then((
_,
) {
setState(() {
_isGraphReady = true;
});
});
_centerOnRoot("130");
});
}
void _centerOnRoot(String rootId) {
if (!nodeKeys.containsKey(rootId)) return;
final key = nodeKeys[rootId]!;
final context = key.currentContext;
if (context == null) return;
final renderBox = context.findRenderObject() as RenderBox;
final rootPosition = renderBox.localToGlobal(Offset.zero);
final screenSize = MediaQuery.of(context).size;
final rootSize = renderBox.size;
final screenCenter = Offset(screenSize.width / 2, screenSize.height / 2);
final rootCenter =
rootPosition + Offset(rootSize.width / 2, rootSize.height / 2);
final dx = screenCenter.dx - rootCenter.dx;
final dy = screenCenter.dy - rootCenter.dy;
_transformationController.value = Matrix4.identity()..translate(dx, dy);
}
@override
Widget build(BuildContext context) {
switch (_source.keys.toList()[0]) {
case ConnectivityResult.mobile:
case ConnectivityResult.wifi:
connection = 'Online';
break;
case ConnectivityResult.none:
default:
connection = 'Offline';
}
return Platform.isAndroid
? WillPopScope(
onWillPop: () => onBackPressed(context),
child: SafeArea(top: false, bottom: true, child: _scaffold(context)),
)
: _scaffold(context);
}
Widget _scaffold(BuildContext context) {
return Consumer<Orgprovider>(
builder: (context, provider, child) {
graph.nodes.clear();
graph.edges.clear();
nodeMap.clear();
if (provider.rootID != null && provider.employees.isNotEmpty) {
_buildGraph(provider);
if (kDebugMode) {
print(
'Graph built with ${graph.nodes.length} nodes and ${graph.edges.length} edges',
);
}
}
return Scaffold(
backgroundColor: Color(0xFFF6F6F8),
appBar: appbar2New(
context,
"Organisation Chart",
null,
SizedBox.shrink(),
0xFFFFFFF,
),
body:
provider.rootID == null && provider.employees.isEmpty
? Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(AppColors.app_blue),
),
)
: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (graph.nodes.isNotEmpty && _isGraphReady)
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.8,
child: InteractiveViewer(
boundaryMargin: const EdgeInsets.all(
double.infinity,
),
panEnabled: true,
scaleEnabled: true,
constrained: false,
minScale: 0.1,
scaleFactor: 1,
transformationController:
_transformationController,
maxScale: 5,
panAxis: PanAxis.free,
// scaleEnabled: true,
// panAxis: PanAxis.free
child: GraphView(
graph: graph,
algorithm: BuchheimWalkerAlgorithm(
builder,
TreeEdgeRenderer(builder),
),
paint:
Paint()
..color = AppColors.grey_thick
..strokeWidth = 1
..style = PaintingStyle.stroke,
builder: (Node node) {
final key = GlobalKey();
nodeKeys[node.key!.value] = key;
final employee = _getEmployeeById(
node.key!.value,
provider,
);
if (employee == null) {
if (kDebugMode) {
print(
'No employee found for node ID: ${node.key!.value}',
);
}
return Container(
width: 50,
height: 82,
color: Colors.transparent,
child: const Center(
child: Text('Invalid Node'),
),
);
}
return InkResponse(
onTap: () {
// Navigator.push(
// context,
// MaterialPageRoute(
// builder:
// (context) =>
// employeeDetails(
// accountID: employee.id,
// ),
// ),
// );
},
child: Container(
key: key,
width: 200,
padding: const EdgeInsets.all(8.0),
// Reduced padding
child: Column(
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: 100,
height: 100,
child: ClipRRect(
borderRadius:
BorderRadius.circular(50),
child: CachedNetworkImage(
cacheKey:
employee.profile ?? '',
fit: BoxFit.cover,
imageUrl:
employee.profile ?? '',
useOldImageOnUrlChange: false,
placeholder:
(context, url) =>
CircularProgressIndicator.adaptive(),
errorWidget:
(context, url, error) =>
Icon(Icons.error),
),
),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: Text(
employee.name ?? 'Unknown',
maxLines: 2,
textAlign: TextAlign.center,
overflow:
TextOverflow.ellipsis,
style: TextStyle(
color:
AppColors.semi_black,
fontFamily:
"JakartaMedium",
fontSize: 25,
),
),
),
],
),
SizedBox(height: 10),
Row(
children: [
Expanded(
child: Text(
employee.title ?? '',
maxLines: 2,
textAlign: TextAlign.center,
overflow:
TextOverflow.ellipsis,
style: TextStyle(
color:
AppColors.grey_thick,
fontSize: 18,
), // Reduced font size
),
),
],
),
],
),
),
);
},
),
),
),
),
],
),
),
);
},
);
}
void _buildGraph(Orgprovider provider) {
final rootNode = Node.Id(provider.rootID!);
nodeMap[provider.rootID!] = rootNode;
graph.addNode(rootNode);
_addChildren(provider.employees, rootNode, provider);
}
void _addChildren(
List<Children> children,
Node parentNode,
Orgprovider provider,
) {
for (var child in children) {
if (child.id == null || child.id!.isEmpty) {
if (kDebugMode) {
print('Skipping invalid child with null or empty ID');
}
continue;
}
if (nodeMap.containsKey(child.id)) {
if (kDebugMode) {
print('Skipping duplicate node ID: ${child.id}');
}
continue;
}
final childNode = Node.Id(child.id!);
nodeMap[child.id!] = childNode;
graph.addNode(childNode);
graph.addEdge(
parentNode,
childNode,
paint: Paint()..color = AppColors.grey_thick,
);
if (child.children != null && child.children!.isNotEmpty) {
_addChildren(child.children!, childNode, provider);
}
}
}
Children? _getEmployeeById(String id, Orgprovider provider) {
if (id == provider.rootID) {
return Children(
id: provider.rootID,
name: provider.rootName,
title: provider.rootTitle,
profile: provider.rootProfile,
);
}
return _findEmployeeInList(id, provider.employees);
}
Children? _findEmployeeInList(String id, List<Children> employees) {
for (var employee in employees) {
if (employee.id == id) {
return employee;
}
if (employee.children != null && employee.children!.isNotEmpty) {
final found = _findEmployeeInList(id, employee.children!);
if (found != null) {
return found;
}
}
}
return null;
}
}
......@@ -57,6 +57,7 @@ export 'package:generp/Notifiers/crmProvider/followUpUpdateProvider.dart';
export 'package:generp/Notifiers/crmProvider/appointmentCalendarProvider.dart';
export 'package:generp/Notifiers/crmProvider/addNewLeadsandProspectsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/hrmAccessiblePagesProvider.dart';
export 'package:generp/Notifiers/hrmProvider/attendanceListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/AttendanceDetailsProvider.dart';
export 'package:generp/Notifiers/hrmProvider/tourExpensesProvider.dart';
......@@ -65,3 +66,5 @@ export 'package:generp/Notifiers/hrmProvider/rewardListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/LeaveApplicationListProvider.dart';
export 'package:generp/Notifiers/hrmProvider/LeaveApplicationDetailsProvider.dart';
export 'package:generp/Notifiers/hrmprovider/orgprovider.dart';
......@@ -102,6 +102,7 @@ import '../Models/financeModels/paymentRequisitionPaymentsReceiptsListResponse.d
import '../Models/generatorComplaintResponse.dart';
import '../Models/hrmModels/attendanceRequestDetailsResponse.dart';
import '../Models/hrmModels/hrmAccessiblePagesResponse.dart';
import '../Models/hrmmodels/ogresponse.dart';
import '../Models/loadGeneratorDetailsResponse.dart';
import '../Models/financeModels/financeDashboardPagesResponse.dart';
import '../Models/ordersModels/AddOrderPaymentSelectAccountResponse.dart';
......@@ -1943,6 +1944,34 @@ class ApiCalling {
return null;
}
}
static Future<CommonResponse?> editProcessedRequestAmountAPI(
empId,
session,
process_payment_id,
approval_amount,
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'process_payment_id': process_payment_id.toString(),
'approval_amount': approval_amount.toString(),
};
print("Process EDIT:${data}");
var res = await post(data, paymentRequesitionEditProcessedPaymentUrl, {});
if (res != null) {
return CommonResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
///common Module
static Future<commonAccessiblePagesResponse?> commonDashboardPagesAPI(
......@@ -4912,6 +4941,30 @@ class ApiCalling {
///hrm modules
///
///
static Future<ogresponse?> ogChartAPI(
empId,
session,
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
};
final res = await post(data, ogcharturl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return ogresponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<hrmAccessiblePagesResponse?> hrmAccessiblePagesAPI(
empId,
session,
......@@ -4942,11 +4995,13 @@ class ApiCalling {
type,
from,
to,
mode
) async {
try {
Map<String, String> data = {
'emp_id': (empId).toString(),
'session_id': (session).toString(),
'mode': (mode),
'type': (type),
'from': (from),
'to': (to),
......@@ -4966,6 +5021,7 @@ class ApiCalling {
}
}
static Future<attendanceRequestDetailsResponse?> attendanceRequestDetailAPI(
empId,
session,
......@@ -4992,6 +5048,38 @@ class ApiCalling {
}
}
static Future<CommonResponse?> attendanceRequestRejectAPI(
session,
empId,
mode,
type,
remarks,
id,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'mode': (mode).toString(),
'type': (type).toString(),
'remarks': (remarks).toString(),
'id': (id).toString(),
};
final res = await post(data, AttendanceRequestRejectUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return CommonResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
static Future<CommonResponse?> addAttendanceRequestAPI({
required String sessionId,
required String empId,
......@@ -5264,12 +5352,14 @@ class ApiCalling {
// Leave Application api
// Leave Application api
static Future<leaveApplicationLIstResponse?> leaveApplicationListAPI(
session,
empId,
dateFrom,
dateTo
dateTo,
mode
) async {
try {
Map<String, String> data = {
......@@ -5277,6 +5367,7 @@ class ApiCalling {
'emp_id': (empId).toString(),
'requested_date_from': (dateFrom),
'requested_date_to': (dateTo),
'mode': (mode),
};
final res = await post(data, LeaveApplicationListUrl, {});
if (res != null) {
......@@ -5355,6 +5446,38 @@ class ApiCalling {
}
}
static Future<CommonResponse?> leaveRequestRejectApproveAPI(
session,
empId,
mode,
type,
remarks,
id,
) async {
try {
Map<String, String> data = {
'session_id': (session).toString(),
'emp_id': (empId).toString(),
'mode': (mode).toString(),
'type': (type).toString(),
'remarks': (remarks).toString(),
'id': (id).toString(),
};
final res = await post(data, AttendanceRequestRejectUrl, {});
if (res != null) {
print(data);
debugPrint(res.body);
return CommonResponse.fromJson(jsonDecode(res.body));
} else {
debugPrint("Null Response");
return null;
}
} catch (e) {
debugPrint('hello bev=bug $e ');
return null;
}
}
// static Future<CommonResponse?> TpcIssueListApprovalAPI(
// empId,
......
......@@ -72,6 +72,7 @@ const paymentRequesitionPaymentsListUrl = "${baseUrl_test}payment_requistion_pay
const paymentRequesitionPaymentsDetailsUrl = "${baseUrl_test}payment_requisition_payment_details";
const paymentRequesitionPaymentsReceiptsListUrl = "${baseUrl_test}payment_receipts_list";
const paymentRequesitionPaymentsReceiptsDetailsUrl = "${baseUrl_test}payment_receipt_details";
const paymentRequesitionEditProcessedPaymentUrl = "${baseUrl_test}edit_processes_payment";
///common Module
......@@ -177,6 +178,8 @@ const crmNearbyOpenLeadsUrl = "${baseUrl_test}nearby_crm_open_leads";
const crmDashboardFollowUpUrl = "${baseUrl_test}crm_dashboard_followup_list";
const crmDashboardQuotationsUrl = "${baseUrl_test}crm_dashboard_quotations_list";
const ogcharturl = "${baseUrl_test}organisation_structures";
///HRM
//Attendance
......@@ -184,6 +187,8 @@ const HrmAccessiblePagesUrl ="${baseUrl_test}hrm_accessible_pages";
const AttendanceRequestListUrl ="${baseUrl_test}attendance_request_list";
const AttendanceRequestDetailsUrl ="${baseUrl_test}attendance_request_details";
const AddAttendanceRequestUrl ="${baseUrl_test}add_attendance_request";
const AttendanceRequestRejectUrl ="${baseUrl_test}attendance_approve_reject";
const AttendanceRequestAproveUrl ="${baseUrl_test}attendance_approve_reject";
// reward list
const RewardListUrl ="${baseUrl_test}hrm_emp_self_rewards";
// Tour Expenses hrm_emp_self_rewards
......@@ -195,6 +200,7 @@ const AddTourExpensesUrl ="${baseUrl_test}add_tour_bill";
const LeaveApplicationListUrl ="${baseUrl_test}leave_request_list";
const LeaveApplicationDetailsUrl ="${baseUrl_test}leave_request_details";
const LeaveRequestAdditionUrl ="${baseUrl_test}add_leave_request";
const LeaveRequestRejectAprroveUrl ="${baseUrl_test}leaves_approve_reject";
......
......@@ -1016,6 +1016,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
graphview:
dependency: "direct main"
description:
name: graphview
sha256: bdba183583b23c30c71edea09ad5f0beef612572d3e39e855467a925bd08392f
url: "https://pub.dev"
source: hosted
version: "1.2.0"
html:
dependency: transitive
description:
......
......@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.99+107
version: 1.0.101+109
environment:
sdk: ^3.7.2
......@@ -89,6 +89,7 @@ dependencies:
pinput: ^5.0.1
build_runner: ^2.4.0
build_web_compilers: ^4.0.4
graphview: ^1.2.0
file_picker: ^8.0.0
dev_dependencies:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment