refactor: improve null safety in employee details and enhance UI handling
This commit is contained in:
parent
dc4ea7979c
commit
474ecac53c
@ -123,8 +123,8 @@ class _EmployeeDetailBottomSheetState extends State<EmployeeDetailBottomSheet> {
|
|||||||
radius: 40,
|
radius: 40,
|
||||||
backgroundColor: Colors.blueGrey[200],
|
backgroundColor: Colors.blueGrey[200],
|
||||||
child: Avatar(
|
child: Avatar(
|
||||||
firstName: employee.firstName,
|
firstName: employee.firstName ?? '',
|
||||||
lastName: employee.lastName,
|
lastName: employee.lastName ?? '',
|
||||||
size: 60,
|
size: 60,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -172,7 +172,7 @@ class _EmployeeDetailBottomSheetState extends State<EmployeeDetailBottomSheet> {
|
|||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (context) => AssignProjectBottomSheet(
|
builder: (context) => AssignProjectBottomSheet(
|
||||||
employeeId: widget.employeeId,
|
employeeId: widget.employeeId,
|
||||||
jobRoleId: employee.jobRoleId,
|
jobRoleId: employee.jobRoleId ?? '',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,52 +1,51 @@
|
|||||||
class EmployeeDetailsModel {
|
class EmployeeDetailsModel {
|
||||||
final String id;
|
final String? id;
|
||||||
final String firstName;
|
final String? firstName;
|
||||||
final String lastName;
|
final String? lastName;
|
||||||
final String? middleName;
|
final String? middleName;
|
||||||
final String? email;
|
final String? email;
|
||||||
final String gender;
|
final String? gender;
|
||||||
final DateTime? birthDate;
|
final DateTime? birthDate;
|
||||||
final DateTime? joiningDate;
|
final DateTime? joiningDate;
|
||||||
final String? permanentAddress;
|
final String? permanentAddress;
|
||||||
final String? currentAddress;
|
final String? currentAddress;
|
||||||
final String phoneNumber;
|
final String? phoneNumber;
|
||||||
final String? emergencyPhoneNumber;
|
final String? emergencyPhoneNumber;
|
||||||
final String? emergencyContactPerson;
|
final String? emergencyContactPerson;
|
||||||
final bool isActive;
|
final bool? isActive;
|
||||||
final bool isRootUser;
|
final bool? isRootUser;
|
||||||
final bool isSystem;
|
final bool? isSystem;
|
||||||
final String jobRole;
|
final String? jobRole;
|
||||||
final String jobRoleId;
|
final String? jobRoleId;
|
||||||
final String? photo;
|
final String? photo;
|
||||||
final String? applicationUserId;
|
final String? applicationUserId;
|
||||||
final bool hasApplicationAccess;
|
final bool? hasApplicationAccess;
|
||||||
final String? organizationId;
|
final String? organizationId;
|
||||||
final String? aadharNumber;
|
final String? aadharNumber;
|
||||||
final String? panNumber;
|
final String? panNumber;
|
||||||
|
|
||||||
|
|
||||||
EmployeeDetailsModel({
|
EmployeeDetailsModel({
|
||||||
required this.id,
|
this.id,
|
||||||
required this.firstName,
|
this.firstName,
|
||||||
required this.lastName,
|
this.lastName,
|
||||||
this.middleName,
|
this.middleName,
|
||||||
this.email,
|
this.email,
|
||||||
required this.gender,
|
this.gender,
|
||||||
this.birthDate,
|
this.birthDate,
|
||||||
this.joiningDate,
|
this.joiningDate,
|
||||||
this.permanentAddress,
|
this.permanentAddress,
|
||||||
this.currentAddress,
|
this.currentAddress,
|
||||||
required this.phoneNumber,
|
this.phoneNumber,
|
||||||
this.emergencyPhoneNumber,
|
this.emergencyPhoneNumber,
|
||||||
this.emergencyContactPerson,
|
this.emergencyContactPerson,
|
||||||
required this.isActive,
|
this.isActive,
|
||||||
required this.isRootUser,
|
this.isRootUser,
|
||||||
required this.isSystem,
|
this.isSystem,
|
||||||
required this.jobRole,
|
this.jobRole,
|
||||||
required this.jobRoleId,
|
this.jobRoleId,
|
||||||
this.photo,
|
this.photo,
|
||||||
this.applicationUserId,
|
this.applicationUserId,
|
||||||
required this.hasApplicationAccess,
|
this.hasApplicationAccess,
|
||||||
this.organizationId,
|
this.organizationId,
|
||||||
this.aadharNumber,
|
this.aadharNumber,
|
||||||
this.panNumber,
|
this.panNumber,
|
||||||
@ -54,30 +53,30 @@ class EmployeeDetailsModel {
|
|||||||
|
|
||||||
factory EmployeeDetailsModel.fromJson(Map<String, dynamic> json) {
|
factory EmployeeDetailsModel.fromJson(Map<String, dynamic> json) {
|
||||||
return EmployeeDetailsModel(
|
return EmployeeDetailsModel(
|
||||||
id: json['id'],
|
id: json['id'] as String?,
|
||||||
firstName: json['firstName'],
|
firstName: json['firstName'] as String?,
|
||||||
lastName: json['lastName'],
|
lastName: json['lastName'] as String?,
|
||||||
middleName: json['middleName'],
|
middleName: json['middleName'] as String?,
|
||||||
email: json['email'],
|
email: json['email'] as String?,
|
||||||
gender: json['gender'],
|
gender: json['gender'] as String?,
|
||||||
birthDate: _parseDate(json['birthDate']),
|
birthDate: _parseDate(json['birthDate'] as String?),
|
||||||
joiningDate: _parseDate(json['joiningDate']),
|
joiningDate: _parseDate(json['joiningDate'] as String?),
|
||||||
permanentAddress: json['permanentAddress'],
|
permanentAddress: json['permanentAddress'] as String?,
|
||||||
currentAddress: json['currentAddress'],
|
currentAddress: json['currentAddress'] as String?,
|
||||||
phoneNumber: json['phoneNumber'],
|
phoneNumber: json['phoneNumber'] as String?,
|
||||||
emergencyPhoneNumber: json['emergencyPhoneNumber'],
|
emergencyPhoneNumber: json['emergencyPhoneNumber'] as String?,
|
||||||
emergencyContactPerson: json['emergencyContactPerson'],
|
emergencyContactPerson: json['emergencyContactPerson'] as String?,
|
||||||
isActive: json['isActive'],
|
isActive: json['isActive'] as bool?,
|
||||||
isRootUser: json['isRootUser'],
|
isRootUser: json['isRootUser'] as bool?,
|
||||||
isSystem: json['isSystem'],
|
isSystem: json['isSystem'] as bool?,
|
||||||
jobRole: json['jobRole'],
|
jobRole: json['jobRole'] as String?,
|
||||||
jobRoleId: json['jobRoleId'],
|
jobRoleId: json['jobRoleId'] as String?,
|
||||||
photo: json['photo'],
|
photo: json['photo'] as String?,
|
||||||
applicationUserId: json['applicationUserId'],
|
applicationUserId: json['applicationUserId'] as String?,
|
||||||
hasApplicationAccess: json['hasApplicationAccess'],
|
hasApplicationAccess: json['hasApplicationAccess'] as bool?,
|
||||||
organizationId: json['organizationId'],
|
organizationId: json['organizationId'] as String?,
|
||||||
aadharNumber: json['aadharNumber'],
|
aadharNumber: json['aadharNumber'] as String?,
|
||||||
panNumber: json['panNumber'],
|
panNumber: json['panNumber'] as String?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,4 +115,4 @@ class EmployeeDetailsModel {
|
|||||||
}
|
}
|
||||||
return DateTime.tryParse(dateStr);
|
return DateTime.tryParse(dateStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,9 +13,8 @@ import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
|||||||
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
import 'package:marco/helpers/widgets/my_custom_skeleton.dart';
|
||||||
import 'package:marco/view/employees/manage_reporting_bottom_sheet.dart';
|
import 'package:marco/view/employees/manage_reporting_bottom_sheet.dart';
|
||||||
import 'package:marco/model/employees/employee_model.dart';
|
import 'package:marco/model/employees/employee_model.dart';
|
||||||
import 'package:marco/view/employees/manage_reporting_bottom_sheet.dart';
|
|
||||||
import 'package:marco/model/employees/employee_model.dart';
|
|
||||||
import 'package:marco/view/employees/assign_employee_bottom_sheet.dart';
|
import 'package:marco/view/employees/assign_employee_bottom_sheet.dart';
|
||||||
|
import 'package:marco/model/employees/employee_details_model.dart';
|
||||||
|
|
||||||
class EmployeeDetailPage extends StatefulWidget {
|
class EmployeeDetailPage extends StatefulWidget {
|
||||||
final String employeeId;
|
final String employeeId;
|
||||||
@ -177,16 +176,19 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
return SkeletonLoaders.employeeDetailSkeletonLoader();
|
return SkeletonLoaders.employeeDetailSkeletonLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
final employee = controller.selectedEmployeeDetails.value;
|
final EmployeeDetailsModel? employee =
|
||||||
|
controller.selectedEmployeeDetails.value;
|
||||||
if (employee == null) {
|
if (employee == null) {
|
||||||
return Center(child: MyText("No employee details found."));
|
return Center(child: MyText("No employee details found."));
|
||||||
}
|
}
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: MyRefreshIndicator(
|
child: MyRefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
await controller.fetchEmployeeDetails(widget.employeeId);
|
await controller.fetchEmployeeDetails(widget.employeeId);
|
||||||
await controller.fetchReportingManagers(employee.id);
|
if (employee.id != null) {
|
||||||
|
await controller.fetchReportingManagers(employee.id!);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
@ -206,8 +208,8 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Avatar(
|
Avatar(
|
||||||
firstName: employee.firstName,
|
firstName: employee.firstName ?? "",
|
||||||
lastName: employee.lastName,
|
lastName: employee.lastName ?? "",
|
||||||
size: 35,
|
size: 35,
|
||||||
),
|
),
|
||||||
MySpacing.width(16),
|
MySpacing.width(16),
|
||||||
@ -216,7 +218,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
MyText.titleMedium(
|
MyText.titleMedium(
|
||||||
'${employee.firstName} ${employee.lastName}',
|
'${employee.firstName ?? ""} ${employee.lastName ?? ""}',
|
||||||
fontWeight: 700,
|
fontWeight: 700,
|
||||||
),
|
),
|
||||||
MySpacing.height(6),
|
MySpacing.height(6),
|
||||||
@ -231,8 +233,8 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
icon: Icon(Icons.edit,
|
icon: Icon(Icons.edit,
|
||||||
size: 24, color: contentTheme.primary),
|
size: 24, color: contentTheme.primary),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final result = await showModalBottomSheet<
|
final result =
|
||||||
Map<String, dynamic>>(
|
await showModalBottomSheet<Map<String, dynamic>>(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
@ -244,11 +246,11 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
'phone_number': employee.phoneNumber,
|
'phone_number': employee.phoneNumber,
|
||||||
'email': employee.email,
|
'email': employee.email,
|
||||||
'hasApplicationAccess':
|
'hasApplicationAccess':
|
||||||
employee.hasApplicationAccess,
|
employee.hasApplicationAccess ?? false,
|
||||||
'gender': employee.gender.toLowerCase(),
|
'gender': employee.gender?.toLowerCase() ?? '',
|
||||||
'job_role_id': employee.jobRoleId,
|
'job_role_id': employee.jobRoleId,
|
||||||
'joining_date': employee.joiningDate
|
'joining_date':
|
||||||
?.toIso8601String(),
|
employee.joiningDate?.toIso8601String(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -279,28 +281,29 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (_) => ManageReportingBottomSheet(
|
builder: (_) => ManageReportingBottomSheet(
|
||||||
initialEmployee: EmployeeModel(
|
initialEmployee: EmployeeModel(
|
||||||
id: employee.id,
|
id: employee.id ?? '',
|
||||||
employeeId: employee.id.toString(),
|
employeeId: employee.id ?? '',
|
||||||
firstName: employee.firstName ?? "",
|
firstName: employee.firstName ?? '',
|
||||||
lastName: employee.lastName ?? "",
|
lastName: employee.lastName ?? '',
|
||||||
name:
|
name:
|
||||||
"${employee.firstName} ${employee.lastName}",
|
"${employee.firstName ?? ''} ${employee.lastName ?? ''}",
|
||||||
email: employee.email ?? "",
|
email: employee.email ?? '',
|
||||||
jobRole: employee.jobRole ?? "",
|
jobRole: employee.jobRole ?? '',
|
||||||
jobRoleID: "0",
|
jobRoleID: "0",
|
||||||
designation: employee.jobRole ?? "",
|
designation: employee.jobRole ?? '',
|
||||||
phoneNumber: employee.phoneNumber ?? "",
|
phoneNumber: employee.phoneNumber ?? '',
|
||||||
activity: 0,
|
activity: 0,
|
||||||
action: 0,
|
action: 0,
|
||||||
),
|
),
|
||||||
hideMainSelector: true,
|
hideMainSelector: true,
|
||||||
hideLoggedUserFromSelection: true,
|
hideLoggedUserFromSelection: true,
|
||||||
loggedUserId:
|
loggedUserId: controller.selectedEmployeeDetails.value?.id,
|
||||||
controller.selectedEmployeeDetails.value?.id,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
await controller.fetchReportingManagers(employee.id);
|
if (employee.id != null) {
|
||||||
|
await controller.fetchReportingManagers(employee.id!);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
@ -353,14 +356,12 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
Text(
|
Text(
|
||||||
'Primary → ${_getManagerNames(primary)}',
|
'Primary → ${_getManagerNames(primary)}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14, fontWeight: FontWeight.w600),
|
||||||
fontWeight: FontWeight.w600),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Secondary → ${_getManagerNames(secondary)}',
|
'Secondary → ${_getManagerNames(secondary)}',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14, fontWeight: FontWeight.w600),
|
||||||
fontWeight: FontWeight.w600),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -383,16 +384,15 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
isActionable: true,
|
isActionable: true,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (employee.email != null &&
|
if (employee.email != null &&
|
||||||
employee.email.toString().trim().isNotEmpty) {
|
employee.email!.trim().isNotEmpty) {
|
||||||
LauncherUtils.launchEmail(employee.email!);
|
LauncherUtils.launchEmail(employee.email!);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (employee.email != null &&
|
if (employee.email != null &&
|
||||||
employee.email.toString().trim().isNotEmpty) {
|
employee.email!.trim().isNotEmpty) {
|
||||||
LauncherUtils.copyToClipboard(
|
LauncherUtils.copyToClipboard(
|
||||||
employee.email!,
|
employee.email!, typeLabel: 'Email');
|
||||||
typeLabel: 'Email');
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -402,16 +402,16 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
value: _getDisplayValue(employee.phoneNumber),
|
value: _getDisplayValue(employee.phoneNumber),
|
||||||
isActionable: true,
|
isActionable: true,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (employee.phoneNumber.trim().isNotEmpty) {
|
if (employee.phoneNumber != null &&
|
||||||
LauncherUtils.launchPhone(employee.phoneNumber);
|
employee.phoneNumber!.trim().isNotEmpty) {
|
||||||
|
LauncherUtils.launchPhone(employee.phoneNumber!);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (employee.phoneNumber.trim().isNotEmpty) {
|
if (employee.phoneNumber != null &&
|
||||||
|
employee.phoneNumber!.trim().isNotEmpty) {
|
||||||
LauncherUtils.copyToClipboard(
|
LauncherUtils.copyToClipboard(
|
||||||
employee.phoneNumber,
|
employee.phoneNumber!, typeLabel: 'Phone Number');
|
||||||
typeLabel: 'Phone Number',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -428,29 +428,22 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.person_outline,
|
icon: Icons.person_outline,
|
||||||
label: 'Contact Person',
|
label: 'Contact Person',
|
||||||
value:
|
value: _getDisplayValue(employee.emergencyContactPerson),
|
||||||
_getDisplayValue(employee.emergencyContactPerson),
|
|
||||||
),
|
),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
icon: Icons.phone_in_talk_outlined,
|
icon: Icons.phone_in_talk_outlined,
|
||||||
label: 'Emergency Phone',
|
label: 'Emergency Phone',
|
||||||
value:
|
value: _getDisplayValue(employee.emergencyPhoneNumber),
|
||||||
_getDisplayValue(employee.emergencyPhoneNumber),
|
|
||||||
isActionable: true,
|
isActionable: true,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (employee.emergencyPhoneNumber != null &&
|
if (employee.emergencyPhoneNumber != null &&
|
||||||
employee.emergencyPhoneNumber!
|
employee.emergencyPhoneNumber!.trim().isNotEmpty) {
|
||||||
.trim()
|
LauncherUtils.launchPhone(employee.emergencyPhoneNumber!);
|
||||||
.isNotEmpty) {
|
|
||||||
LauncherUtils.launchPhone(
|
|
||||||
employee.emergencyPhoneNumber!);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: () {
|
onLongPress: () {
|
||||||
if (employee.emergencyPhoneNumber != null &&
|
if (employee.emergencyPhoneNumber != null &&
|
||||||
employee.emergencyPhoneNumber!
|
employee.emergencyPhoneNumber!.trim().isNotEmpty) {
|
||||||
.trim()
|
|
||||||
.isNotEmpty) {
|
|
||||||
LauncherUtils.copyToClipboard(
|
LauncherUtils.copyToClipboard(
|
||||||
employee.emergencyPhoneNumber!,
|
employee.emergencyPhoneNumber!,
|
||||||
typeLabel: 'Emergency Phone');
|
typeLabel: 'Emergency Phone');
|
||||||
@ -513,7 +506,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
|
|
||||||
/// ------------------ FLOATING BUTTON ------------------
|
/// ------------------ FLOATING BUTTON ------------------
|
||||||
floatingActionButton: Obx(() {
|
floatingActionButton: Obx(() {
|
||||||
final employee = controller.selectedEmployeeDetails.value;
|
final EmployeeDetailsModel? employee = controller.selectedEmployeeDetails.value;
|
||||||
if (employee == null) return const SizedBox.shrink();
|
if (employee == null) return const SizedBox.shrink();
|
||||||
|
|
||||||
return FloatingActionButton.extended(
|
return FloatingActionButton.extended(
|
||||||
@ -524,7 +517,7 @@ class _EmployeeDetailPageState extends State<EmployeeDetailPage> with UIMixin {
|
|||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
builder: (context) => AssignProjectBottomSheet(
|
builder: (context) => AssignProjectBottomSheet(
|
||||||
employeeId: widget.employeeId,
|
employeeId: widget.employeeId,
|
||||||
jobRoleId: employee.jobRoleId,
|
jobRoleId: employee.jobRoleId ?? '',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user