diff --git a/lib/controller/service_project/add_service_project_job_controller.dart b/lib/controller/service_project/add_service_project_job_controller.dart index 3d07097..eb84fee 100644 --- a/lib/controller/service_project/add_service_project_job_controller.dart +++ b/lib/controller/service_project/add_service_project_job_controller.dart @@ -71,11 +71,11 @@ class AddServiceProjectJobController extends GetxController { title: titleCtrl.text.trim(), description: descCtrl.text.trim(), projectId: projectId, - branchId: selectedBranch.value?.id, + branchId: selectedBranch.value?.id, assignees: assigneeIds.map((id) => {"id": id}).toList(), startDate: startDate.value!, dueDate: dueDate.value!, - tags: enteredTags.map((tag) => {"name": tag}).toList(), + tags: enteredTags.map((tag) => {"name": tag.trim()}).toList(), ); isLoading.value = false; diff --git a/lib/model/service_project/add_service_project_job_bottom_sheet.dart b/lib/model/service_project/add_service_project_job_bottom_sheet.dart index 3b4d133..e02bdd1 100644 --- a/lib/model/service_project/add_service_project_job_bottom_sheet.dart +++ b/lib/model/service_project/add_service_project_job_bottom_sheet.dart @@ -204,11 +204,37 @@ class _AddServiceProjectJobBottomSheetState height: 48, child: TextFormField( controller: controller.tagCtrl, + textInputAction: TextInputAction.done, + onEditingComplete: () { + final raw = controller.tagCtrl.text.trim(); + if (raw.isEmpty) return; + + final parts = raw + .split(',') + .map((s) => s.trim()) + .where((s) => s.isNotEmpty); + + for (final p in parts) { + if (!controller.enteredTags.contains(p)) { + controller.enteredTags.add(p); + } + } + controller.tagCtrl.clear(); + }, onFieldSubmitted: (v) { - final value = v.trim(); - if (value.isNotEmpty && - !controller.enteredTags.contains(value)) { - controller.enteredTags.add(value); + // also handle normal submit + final raw = v.trim(); + if (raw.isEmpty) return; + + final parts = raw + .split(',') + .map((s) => s.trim()) + .where((s) => s.isNotEmpty); + + for (final p in parts) { + if (!controller.enteredTags.contains(p)) { + controller.enteredTags.add(p); + } } controller.tagCtrl.clear(); }, diff --git a/lib/view/service_project/jobs_tab.dart b/lib/view/service_project/jobs_tab.dart index 096db50..ed2d084 100644 --- a/lib/view/service_project/jobs_tab.dart +++ b/lib/view/service_project/jobs_tab.dart @@ -201,7 +201,7 @@ class _JobsTabState extends State { children: job.tags!.map((tag) { return Chip( label: Text( - tag.name, + tag.name.replaceAll('_', ' '), style: const TextStyle(fontSize: 12), ), @@ -318,7 +318,7 @@ class _JobsTabState extends State { final success = await ApiService.editServiceProjectJobApi( - jobId: job.id , + jobId: job.id, operations: operations, ); 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 977c323..ac6375c 100644 --- a/lib/view/service_project/service_project_job_detail_screen.dart +++ b/lib/view/service_project/service_project_job_detail_screen.dart @@ -39,6 +39,10 @@ class _JobDetailsScreenState extends State with UIMixin { final TextEditingController _dueDateController = TextEditingController(); final TextEditingController _tagTextController = TextEditingController(); + final RxList tags = [].obs; // For showing/editing tag chips + final TextEditingController tagController = + TextEditingController(); // For tag input + final RxList _selectedAssignees = [].obs; final RxList _selectedTags = [].obs; final RxBool isEditing = false.obs; @@ -47,21 +51,38 @@ class _JobDetailsScreenState extends State with UIMixin { @override void initState() { super.initState(); - controller = Get.put(ServiceProjectDetailsController()); - controller.fetchJobDetail(widget.jobId).then((_) { + controller = Get.find(); + + WidgetsBinding.instance.addPostFrameCallback((_) async { + await controller.fetchJobDetail(widget.jobId); + final job = controller.jobDetail.value?.data; if (job != null) { _titleController.text = job.title ?? ''; _descriptionController.text = job.description ?? ''; + _startDateController.text = DateTimeUtils.convertUtcToLocal( 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 ?? []; + + // ---------- TAG FIX ---------- + final tagList = job.tags ?? []; + + final cleanedTags = tagList + .map((t) => (t.name ?? "").replaceAll("_", " ").trim()) + .toList(); + + tags.assignAll(cleanedTags); + tagController.clear(); } + + setState(() {}); }); } @@ -165,11 +186,21 @@ class _JobDetailsScreenState extends State with UIMixin { if (success) { showAppSnackbar( - title: "Success", - message: "Job updated successfully", - type: SnackbarType.success); + title: "Success", + message: "Job updated successfully", + type: SnackbarType.success, + ); + + /// Refresh detail screen await controller.fetchJobDetail(widget.jobId); + + /// 🔥 Auto refresh job list UI (main Service Project Details screen) + if (Get.isRegistered()) { + await Get.find().refreshJobsAfterAdd(); + } + isEditing.value = false; + Navigator.pop(context); // optional if you want auto-close } else { showAppSnackbar( title: "Error",