Merge branch 'OH_Dev_Manish' of https://git.marcoaiot.com/admin/marco.pms.mobileapp into OH_Dev_Manish

This commit is contained in:
Vaibhav Surve 2025-11-17 15:16:01 +05:30
commit f506bd1bfc
2 changed files with 49 additions and 68 deletions

View File

@ -135,6 +135,7 @@ class ApiEndpoints {
static const String manageOrganizationHierarchy = static const String manageOrganizationHierarchy =
"/organization/hierarchy/manage"; "/organization/hierarchy/manage";
// Service Project Module API Endpoints // Service Project Module API Endpoints
static const String getServiceProjectsList = "/serviceproject/list"; static const String getServiceProjectsList = "/serviceproject/list";
static const String getServiceProjectDetail = "/serviceproject/details"; static const String getServiceProjectDetail = "/serviceproject/details";

View File

@ -51,18 +51,18 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
} }
String _getDisplayValue(dynamic value) { String _getDisplayValue(dynamic value) {
if (value == null || value.toString().trim().isEmpty || value == 'null') { if (value == null || value.toString().trim().isEmpty || value == "null") {
return 'NA'; return "NA";
} }
return value.toString(); return value.toString();
} }
String _formatDate(DateTime? date) { String _formatDate(DateTime? date) {
if (date == null || date == DateTime(1)) return 'NA'; if (date == null || date == DateTime(1)) return "NA";
try { try {
return DateFormat('d/M/yyyy').format(date); return DateFormat('d/M/yyyy').format(date);
} catch (_) { } catch (_) {
return 'NA'; return "NA";
} }
} }
@ -77,18 +77,15 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
child: InkWell( child: InkWell(
onTap: isActionable && value != 'NA' ? onTap : null, onTap: isActionable && value != "NA" ? onTap : null,
onLongPress: isActionable && value != 'NA' ? onLongPress : null, onLongPress: isActionable && value != "NA" ? onLongPress : null,
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Icon( child: Icon(icon, size: 20),
icon,
size: 20,
),
), ),
MySpacing.width(16), MySpacing.width(16),
Expanded( Expanded(
@ -103,23 +100,19 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
MySpacing.height(4), MySpacing.height(4),
MyText( MyText(
value, value,
color: isActionable && value != 'NA' color: isActionable && value != "NA"
? Colors.blueAccent ? Colors.blueAccent
: Colors.black87, : Colors.black87,
fontWeight: 500, fontWeight: 500,
decoration: isActionable && value != 'NA' decoration: isActionable && value != "NA"
? TextDecoration.underline ? TextDecoration.underline
: TextDecoration.none, : TextDecoration.none,
), ),
], ],
), ),
), ),
if (isActionable && value != 'NA') if (isActionable && value != "NA")
Icon( Icon(Icons.chevron_right, color: Colors.grey[400], size: 20),
Icons.chevron_right,
color: Colors.grey[400],
size: 20,
),
], ],
), ),
), ),
@ -142,10 +135,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
children: [ children: [
Row( Row(
children: [ children: [
Icon( Icon(titleIcon, size: 20),
titleIcon,
size: 20,
),
MySpacing.width(8), MySpacing.width(8),
MyText( MyText(
title, title,
@ -172,7 +162,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
backgroundColor: const Color(0xFFF1F1F1), backgroundColor: const Color(0xFFF1F1F1),
appBar: showAppBar appBar: showAppBar
? CustomAppBar( ? CustomAppBar(
title: 'Employee Details', title: "Employee Details",
onBackPressed: () { onBackPressed: () {
if (widget.fromProfile) { if (widget.fromProfile) {
Get.back(); Get.back();
@ -189,7 +179,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
final employee = controller.selectedEmployeeDetails.value; final employee = controller.selectedEmployeeDetails.value;
if (employee == null) { if (employee == null) {
return Center(child: MyText('No employee details found.')); return const Center(child: MyText("No employee details found."));
} }
return SafeArea( return SafeArea(
@ -204,7 +194,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// Header Section /// ------------------ HEADER CARD ------------------
Card( Card(
elevation: 2, elevation: 2,
shadowColor: Colors.black12, shadowColor: Colors.black12,
@ -257,8 +247,8 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
employee.hasApplicationAccess, employee.hasApplicationAccess,
'gender': employee.gender.toLowerCase(), 'gender': employee.gender.toLowerCase(),
'job_role_id': employee.jobRoleId, 'job_role_id': employee.jobRoleId,
'joining_date': 'joining_date': employee.joiningDate
employee.joiningDate?.toIso8601String(), ?.toIso8601String(),
}, },
), ),
); );
@ -273,8 +263,10 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
), ),
), ),
), ),
MySpacing.height(16), MySpacing.height(16),
/// ------------------ MANAGE REPORTING ------------------
_buildSectionCard( _buildSectionCard(
title: 'Manage Reporting', title: 'Manage Reporting',
titleIcon: Icons.people_outline, titleIcon: Icons.people_outline,
@ -308,7 +300,6 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
), ),
); );
// 🔄 Refresh reporting managers after editing
await controller.fetchReportingManagers(employee.id); await controller.fetchReportingManagers(employee.id);
}, },
child: Padding( child: Padding(
@ -355,41 +346,32 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
} }
return Padding( return Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.fromLTRB(8, 8, 8, 8),
top: 8.0, left: 8, right: 8, bottom: 8),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Padding( Text(
padding:
const EdgeInsets.symmetric(vertical: 4.0),
child: Text(
'Primary → ${_getManagerNames(primary)}', 'Primary → ${_getManagerNames(primary)}',
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600),
), ),
), Text(
),
Padding(
padding:
const EdgeInsets.symmetric(vertical: 4.0),
child: Text(
'Secondary → ${_getManagerNames(secondary)}', 'Secondary → ${_getManagerNames(secondary)}',
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600),
),
),
), ),
], ],
), ),
); );
}) }),
], ],
), ),
// Contact Information Section MySpacing.height(16),
/// ------------------ CONTACT INFO ------------------
_buildSectionCard( _buildSectionCard(
title: 'Contact Information', title: 'Contact Information',
titleIcon: Icons.contact_phone, titleIcon: Icons.contact_phone,
@ -408,7 +390,8 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
onLongPress: () { onLongPress: () {
if (employee.email != null && if (employee.email != null &&
employee.email.toString().trim().isNotEmpty) { employee.email.toString().trim().isNotEmpty) {
LauncherUtils.copyToClipboard(employee.email!, LauncherUtils.copyToClipboard(
employee.email!,
typeLabel: 'Email'); typeLabel: 'Email');
} }
}, },
@ -434,9 +417,10 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
), ),
], ],
), ),
MySpacing.height(16), MySpacing.height(16),
// Emergency Contact Section /// ------------------ EMERGENCY CONTACT ------------------
_buildSectionCard( _buildSectionCard(
title: 'Emergency Contact', title: 'Emergency Contact',
titleIcon: Icons.emergency, titleIcon: Icons.emergency,
@ -446,17 +430,16 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
label: 'Contact Person', label: 'Contact Person',
value: value:
_getDisplayValue(employee.emergencyContactPerson), _getDisplayValue(employee.emergencyContactPerson),
isActionable: false,
), ),
_buildDetailRow( _buildDetailRow(
icon: Icons.phone_in_talk_outlined, icon: Icons.phone_in_talk_outlined,
label: 'Emergency Phone', label: 'Emergency Phone',
value: _getDisplayValue(employee.emergencyPhoneNumber), value:
_getDisplayValue(employee.emergencyPhoneNumber),
isActionable: true, isActionable: true,
onTap: () { onTap: () {
if (employee.emergencyPhoneNumber != null && if (employee.emergencyPhoneNumber != null &&
employee.emergencyPhoneNumber employee.emergencyPhoneNumber!
.toString()
.trim() .trim()
.isNotEmpty) { .isNotEmpty) {
LauncherUtils.launchPhone( LauncherUtils.launchPhone(
@ -465,8 +448,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
}, },
onLongPress: () { onLongPress: () {
if (employee.emergencyPhoneNumber != null && if (employee.emergencyPhoneNumber != null &&
employee.emergencyPhoneNumber employee.emergencyPhoneNumber!
.toString()
.trim() .trim()
.isNotEmpty) { .isNotEmpty) {
LauncherUtils.copyToClipboard( LauncherUtils.copyToClipboard(
@ -477,9 +459,10 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
), ),
], ],
), ),
MySpacing.height(16), MySpacing.height(16),
// Personal Information Section /// ------------------ PERSONAL INFO ------------------
_buildSectionCard( _buildSectionCard(
title: 'Personal Information', title: 'Personal Information',
titleIcon: Icons.person, titleIcon: Icons.person,
@ -488,25 +471,23 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
icon: Icons.wc_outlined, icon: Icons.wc_outlined,
label: 'Gender', label: 'Gender',
value: _getDisplayValue(employee.gender), value: _getDisplayValue(employee.gender),
isActionable: false,
), ),
_buildDetailRow( _buildDetailRow(
icon: Icons.cake_outlined, icon: Icons.cake_outlined,
label: 'Birth Date', label: 'Birth Date',
value: _formatDate(employee.birthDate), value: _formatDate(employee.birthDate),
isActionable: false,
), ),
_buildDetailRow( _buildDetailRow(
icon: Icons.work_outline, icon: Icons.work_outline,
label: 'Joining Date', label: 'Joining Date',
value: _formatDate(employee.joiningDate), value: _formatDate(employee.joiningDate),
isActionable: false,
), ),
], ],
), ),
MySpacing.height(16), MySpacing.height(16),
// Address Information Section /// ------------------ ADDRESS INFO ------------------
_buildSectionCard( _buildSectionCard(
title: 'Address Information', title: 'Address Information',
titleIcon: Icons.location_on, titleIcon: Icons.location_on,
@ -515,13 +496,11 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
icon: Icons.home_outlined, icon: Icons.home_outlined,
label: 'Current Address', label: 'Current Address',
value: _getDisplayValue(employee.currentAddress), value: _getDisplayValue(employee.currentAddress),
isActionable: false,
), ),
_buildDetailRow( _buildDetailRow(
icon: Icons.home_work_outlined, icon: Icons.home_work_outlined,
label: 'Permanent Address', label: 'Permanent Address',
value: _getDisplayValue(employee.permanentAddress), value: _getDisplayValue(employee.permanentAddress),
isActionable: false,
), ),
], ],
), ),
@ -532,7 +511,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
); );
}), }),
// Floating Assign to Project FAB /// ------------------ FLOATING BUTTON ------------------
floatingActionButton: Obx(() { floatingActionButton: Obx(() {
final employee = controller.selectedEmployeeDetails.value; final employee = controller.selectedEmployeeDetails.value;
if (employee == null) return const SizedBox.shrink(); if (employee == null) return const SizedBox.shrink();
@ -550,17 +529,18 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
); );
}, },
backgroundColor: contentTheme.primary, backgroundColor: contentTheme.primary,
icon: const Icon(Icons.add),
label: MyText( label: MyText(
'Assign to Project', 'Assign to Project',
fontSize: 14, fontSize: 14,
fontWeight: 500, fontWeight: 500,
), ),
icon: const Icon(Icons.add),
); );
}), }),
); );
} }
/// ------------------ UTIL ------------------
String _getManagerNames(List<EmployeeModel> managers) { String _getManagerNames(List<EmployeeModel> managers) {
if (managers.isEmpty) return ''; if (managers.isEmpty) return '';
return managers return managers