diff --git a/lib/model/service_project/service_project_job_detail_model.dart b/lib/model/service_project/service_project_job_detail_model.dart index f340416..97a77fa 100644 --- a/lib/model/service_project/service_project_job_detail_model.dart +++ b/lib/model/service_project/service_project_job_detail_model.dart @@ -1,91 +1,95 @@ class JobDetailsResponse { - final bool success; - final String message; + final bool? success; + final String? message; final JobData? data; final dynamic errors; - final int statusCode; - final String timestamp; + final int? statusCode; + final String? timestamp; JobDetailsResponse({ - required this.success, - required this.message, + this.success, + this.message, this.data, this.errors, - required this.statusCode, - required this.timestamp, + this.statusCode, + this.timestamp, }); - factory JobDetailsResponse.fromJson(Map json) { + factory JobDetailsResponse.fromJson(Map? json) { + if (json == null) return JobDetailsResponse(); + return JobDetailsResponse( - success: json['success'] as bool, - message: json['message'] as String, + success: json['success'] as bool?, + message: json['message'] as String?, data: json['data'] != null ? JobData.fromJson(json['data']) : null, errors: json['errors'], - statusCode: json['statusCode'] as int, - timestamp: json['timestamp'] as String, + statusCode: json['statusCode'] as int?, + timestamp: json['timestamp'] as String?, ); } } class JobData { - final String id; - final String title; - final String description; - final String jobTicketUId; - final Project project; - final List assignees; - final Status status; - final String startDate; - final String dueDate; - final bool isActive; + final String? id; + final String? title; + final String? description; + final String? jobTicketUId; + final Project? project; + final List? assignees; + final Status? status; + final String? startDate; + final String? dueDate; + final bool? isActive; final dynamic taggingAction; final String? attendanceId; final int? nextTaggingAction; - final String createdAt; - final User createdBy; - final List tags; - final List updateLogs; + final String? createdAt; + final User? createdBy; + final List? tags; + final List? updateLogs; JobData({ - required this.id, - required this.title, - required this.description, - required this.jobTicketUId, - required this.project, - required this.assignees, - required this.status, - required this.startDate, - required this.dueDate, - required this.isActive, + this.id, + this.title, + this.description, + this.jobTicketUId, + this.project, + this.assignees, + this.status, + this.startDate, + this.dueDate, + this.isActive, this.taggingAction, this.attendanceId, this.nextTaggingAction, - required this.createdAt, - required this.createdBy, - required this.tags, - required this.updateLogs, + this.createdAt, + this.createdBy, + this.tags, + this.updateLogs, }); - factory JobData.fromJson(Map json) { + factory JobData.fromJson(Map? json) { + if (json == null) return JobData(); + return JobData( - id: json['id'] as String, - title: json['title'] as String, - description: json['description'] as String, - jobTicketUId: json['jobTicketUId'] as String, - project: Project.fromJson(json['project']), + id: json['id'] as String?, + title: json['title'] as String?, + description: json['description'] as String?, + jobTicketUId: json['jobTicketUId'] as String?, + project: json['project'] != null ? Project.fromJson(json['project']) : null, assignees: (json['assignees'] as List?) ?.map((e) => Assignee.fromJson(e)) .toList() ?? [], - status: Status.fromJson(json['status']), - startDate: json['startDate'] as String, - dueDate: json['dueDate'] as String, - isActive: json['isActive'] as bool, + status: json['status'] != null ? Status.fromJson(json['status']) : null, + startDate: json['startDate'] as String?, + dueDate: json['dueDate'] as String?, + isActive: json['isActive'] as bool?, taggingAction: json['taggingAction'], attendanceId: json['attendanceId'] as String?, nextTaggingAction: json['nextTaggingAction'] as int?, - createdAt: json['createdAt'] as String, - createdBy: User.fromJson(json['createdBy']), + createdAt: json['createdAt'] as String?, + createdBy: json['createdBy'] != null ? User.fromJson(json['createdBy']) : null, tags: (json['tags'] as List?) ?.map((e) => Tag.fromJson(e)) .toList() ?? @@ -99,120 +103,128 @@ class JobData { } class Project { - final String id; - final String name; - final String shortName; - final String assignedDate; - final String contactName; - final String contactPhone; - final String contactEmail; + final String? id; + final String? name; + final String? shortName; + final String? assignedDate; + final String? contactName; + final String? contactPhone; + final String? contactEmail; Project({ - required this.id, - required this.name, - required this.shortName, - required this.assignedDate, - required this.contactName, - required this.contactPhone, - required this.contactEmail, + this.id, + this.name, + this.shortName, + this.assignedDate, + this.contactName, + this.contactPhone, + this.contactEmail, }); - factory Project.fromJson(Map json) { + factory Project.fromJson(Map? json) { + if (json == null) return Project(); + return Project( - id: json['id'] as String, - name: json['name'] as String, - shortName: json['shortName'] as String, - assignedDate: json['assignedDate'] as String, - contactName: json['contactName'] as String, - contactPhone: json['contactPhone'] as String, - contactEmail: json['contactEmail'] as String, + id: json['id'] as String?, + name: json['name'] as String?, + shortName: json['shortName'] as String?, + assignedDate: json['assignedDate'] as String?, + contactName: json['contactName'] as String?, + contactPhone: json['contactPhone'] as String?, + contactEmail: json['contactEmail'] as String?, ); } } class Assignee { - final String id; - final String firstName; - final String lastName; + final String? id; + final String? firstName; + final String? lastName; final String? email; final String? photo; - final String jobRoleId; - final String jobRoleName; + final String? jobRoleId; + final String? jobRoleName; Assignee({ - required this.id, - required this.firstName, - required this.lastName, + this.id, + this.firstName, + this.lastName, this.email, this.photo, - required this.jobRoleId, - required this.jobRoleName, + this.jobRoleId, + this.jobRoleName, }); - factory Assignee.fromJson(Map json) { + factory Assignee.fromJson(Map? json) { + if (json == null) return Assignee(); + return Assignee( - id: json['id'] as String, - firstName: json['firstName'] as String, - lastName: json['lastName'] as String, + id: json['id'] as String?, + firstName: json['firstName'] as String?, + lastName: json['lastName'] as String?, email: json['email'] as String?, photo: json['photo'] as String?, - jobRoleId: json['jobRoleId'] as String, - jobRoleName: json['jobRoleName'] as String, + jobRoleId: json['jobRoleId'] as String?, + jobRoleName: json['jobRoleName'] as String?, ); } } class Status { - final String id; - final String name; - final String displayName; - final int level; + final String? id; + final String? name; + final String? displayName; + final int? level; Status({ - required this.id, - required this.name, - required this.displayName, - required this.level, + this.id, + this.name, + this.displayName, + this.level, }); - factory Status.fromJson(Map json) { + factory Status.fromJson(Map? json) { + if (json == null) return Status(); + return Status( - id: json['id'] as String, - name: json['name'] as String, - displayName: json['displayName'] as String, - level: json['level'] as int, + id: json['id'] as String?, + name: json['name'] as String?, + displayName: json['displayName'] as String?, + level: json['level'] as int?, ); } } class User { - final String id; - final String firstName; - final String lastName; + final String? id; + final String? firstName; + final String? lastName; final String? email; final String? photo; - final String jobRoleId; - final String jobRoleName; + final String? jobRoleId; + final String? jobRoleName; User({ - required this.id, - required this.firstName, - required this.lastName, + this.id, + this.firstName, + this.lastName, this.email, this.photo, - required this.jobRoleId, - required this.jobRoleName, + this.jobRoleId, + this.jobRoleName, }); - factory User.fromJson(Map json) { + factory User.fromJson(Map? json) { + if (json == null) return User(); + return User( - id: json['id'] as String, - firstName: json['firstName'] as String, - lastName: json['lastName'] as String, + id: json['id'] as String?, + firstName: json['firstName'] as String?, + lastName: json['lastName'] as String?, email: json['email'] as String?, photo: json['photo'] as String?, - jobRoleId: json['jobRoleId'] as String, - jobRoleName: json['jobRoleName'] as String, + jobRoleId: json['jobRoleId'] as String?, + jobRoleName: json['jobRoleName'] as String?, ); } } @@ -223,7 +235,9 @@ class Tag { Tag({this.id, this.name}); - factory Tag.fromJson(Map json) { + factory Tag.fromJson(Map? json) { + if (json == null) return Tag(); + return Tag( id: json['id'] as String?, name: json['name'] as String?, @@ -232,27 +246,29 @@ class Tag { } class UpdateLog { - final String id; + final String? id; final Status? status; - final Status nextStatus; - final String comment; - final User updatedBy; + final Status? nextStatus; + final String? comment; + final User? updatedBy; UpdateLog({ - required this.id, + this.id, this.status, - required this.nextStatus, - required this.comment, - required this.updatedBy, + this.nextStatus, + this.comment, + this.updatedBy, }); - factory UpdateLog.fromJson(Map json) { + factory UpdateLog.fromJson(Map? json) { + if (json == null) return UpdateLog(); + return UpdateLog( - id: json['id'] as String, + id: json['id'] as String?, status: json['status'] != null ? Status.fromJson(json['status']) : null, - nextStatus: Status.fromJson(json['nextStatus']), - comment: json['comment'] as String, - updatedBy: User.fromJson(json['updatedBy']), + nextStatus: json['nextStatus'] != null ? Status.fromJson(json['nextStatus']) : null, + comment: json['comment'] as String?, + updatedBy: json['updatedBy'] != null ? User.fromJson(json['updatedBy']) : null, ); } } diff --git a/lib/view/service_project/service_project_job_detail_screen.dart b/lib/view/service_project/service_project_job_detail_screen.dart index 8881b87..a323829 100644 --- a/lib/view/service_project/service_project_job_detail_screen.dart +++ b/lib/view/service_project/service_project_job_detail_screen.dart @@ -50,15 +50,16 @@ class _JobDetailsScreenState extends State with UIMixin { controller.fetchJobDetail(widget.jobId).then((_) { final job = controller.jobDetail.value?.data; if (job != null) { - _titleController.text = job.title; - _descriptionController.text = job.description; + _titleController.text = job.title ?? ''; + _descriptionController.text = job.description ?? ''; _startDateController.text = DateTimeUtils.convertUtcToLocal( - job.startDate, + job.startDate ?? DateTime.now().toIso8601String(), format: "yyyy-MM-dd"); - _dueDateController.text = - DateTimeUtils.convertUtcToLocal(job.dueDate, format: "yyyy-MM-dd"); - _selectedAssignees.value = job.assignees; - _selectedTags.value = job.tags; + _dueDateController.text = DateTimeUtils.convertUtcToLocal( + job.dueDate ?? '', + format: "yyyy-MM-dd"); + _selectedAssignees.value = job.assignees ?? []; + _selectedTags.value = job.tags ?? []; } }); } @@ -114,14 +115,14 @@ class _JobDetailsScreenState extends State with UIMixin { } final originalAssignees = job.assignees; - final assigneesPayload = originalAssignees.map((a) { + final assigneesPayload = originalAssignees?.map((a) { final isSelected = _selectedAssignees.any((s) => s.id == a.id); return {"employeeId": a.id, "isActive": isSelected}; }).toList(); for (var s in _selectedAssignees) { - if (!originalAssignees.any((a) => a.id == s.id)) { - assigneesPayload.add({"employeeId": s.id, "isActive": true}); + if (!(originalAssignees?.any((a) => a.id == s.id) ?? false)) { + assigneesPayload?.add({"employeeId": s.id, "isActive": true}); } } @@ -129,7 +130,7 @@ class _JobDetailsScreenState extends State with UIMixin { {"op": "replace", "path": "/assignees", "value": assigneesPayload}); final originalTags = job.tags; - final replaceTagsPayload = originalTags.map((t) { + final replaceTagsPayload = originalTags?.map((t) { final isSelected = _selectedTags.any((s) => s.id == t.id); return {"id": t.id, "name": t.name, "isActive": isSelected}; }).toList(); @@ -139,7 +140,7 @@ class _JobDetailsScreenState extends State with UIMixin { .map((t) => {"name": t.name, "isActive": true}) .toList(); - if (replaceTagsPayload.isNotEmpty) { + if ((replaceTagsPayload?.isNotEmpty ?? false)) { operations .add({"op": "replace", "path": "/tags", "value": replaceTagsPayload}); } @@ -157,7 +158,7 @@ class _JobDetailsScreenState extends State with UIMixin { } final success = await ApiService.editServiceProjectJobApi( - jobId: job.id, + jobId: job.id ?? "", operations: operations, ); @@ -207,7 +208,7 @@ class _JobDetailsScreenState extends State with UIMixin { ); await controller.updateJobAttendance( - jobId: job.id, + jobId: job.id ?? "", action: action == 0 ? 0 : 1, comment: comment, attachment: attachmentFile, @@ -352,14 +353,14 @@ class _JobDetailsScreenState extends State with UIMixin { onTap: () async { final initiallySelected = assignees.map((a) { return EmployeeModel( - id: a.id, - employeeId: a.id, - firstName: a.firstName, - lastName: a.lastName, + id: a.id ?? '', + employeeId: a.id ?? '', + firstName: a.firstName ?? '', + lastName: a.lastName ?? '', name: "${a.firstName} ${a.lastName}", - designation: a.jobRoleName, - jobRole: a.jobRoleName, - jobRoleID: a.jobRoleId, + designation: a.jobRoleName ?? '', + jobRole: a.jobRoleName ?? '', + jobRoleID: a.jobRoleId ?? '', email: a.email ?? '', phoneNumber: '', activity: 0, @@ -495,7 +496,7 @@ class _JobDetailsScreenState extends State with UIMixin { onPressed: () async { isAttendanceExpanded.value = !isAttendanceExpanded.value; - if (isAttendanceExpanded.value && job != null) { + if (isAttendanceExpanded.value ) { await controller .fetchJobAttendanceLog(job.attendanceId ?? ''); } @@ -774,11 +775,11 @@ class _JobDetailsScreenState extends State with UIMixin { titleIcon: Icons.label_outline, children: [_tagEditor()]), MySpacing.height(16), - if (job.updateLogs.isNotEmpty) + if ((job.updateLogs?.isNotEmpty ?? false)) _buildSectionCard( title: "Update Logs", titleIcon: Icons.history, - children: [JobTimeline(logs: job.updateLogs)]), + children: [JobTimeline(logs: job.updateLogs ?? [])]), MySpacing.height(80), ], ), @@ -806,12 +807,14 @@ class JobTimeline extends StatelessWidget { itemBuilder: (_, index) { final log = reversedLogs[index]; final statusName = log.status?.displayName ?? "Created"; - final nextStatusName = log.nextStatus.displayName; - final comment = log.comment; + final nextStatusName = log.nextStatus?.displayName ?? "N/A"; + final comment = log.comment ?? ''; final updatedBy = - "${log.updatedBy.firstName} ${log.updatedBy.lastName}"; + "${log.updatedBy?.firstName ?? ''} ${log.updatedBy?.lastName ?? ''}"; + final f = log.updatedBy?.firstName ?? ''; + final l = log.updatedBy?.lastName ?? ''; final initials = - "${log.updatedBy.firstName.isNotEmpty ? log.updatedBy.firstName[0] : ''}${log.updatedBy.lastName.isNotEmpty ? log.updatedBy.lastName[0] : ''}"; + "${f.isNotEmpty ? f[0] : ''}${l.isNotEmpty ? l[0] : ''}"; return TimelineTile( alignment: TimelineAlign.start,