done tags UX updates

This commit is contained in:
Manish 2025-11-24 10:16:41 +05:30
parent a85bea54f8
commit 4daa412a87
4 changed files with 71 additions and 14 deletions

View File

@ -71,11 +71,11 @@ class AddServiceProjectJobController extends GetxController {
title: titleCtrl.text.trim(), title: titleCtrl.text.trim(),
description: descCtrl.text.trim(), description: descCtrl.text.trim(),
projectId: projectId, projectId: projectId,
branchId: selectedBranch.value?.id, branchId: selectedBranch.value?.id,
assignees: assigneeIds.map((id) => {"id": id}).toList(), assignees: assigneeIds.map((id) => {"id": id}).toList(),
startDate: startDate.value!, startDate: startDate.value!,
dueDate: dueDate.value!, dueDate: dueDate.value!,
tags: enteredTags.map((tag) => {"name": tag}).toList(), tags: enteredTags.map((tag) => {"name": tag.trim()}).toList(),
); );
isLoading.value = false; isLoading.value = false;

View File

@ -204,11 +204,37 @@ class _AddServiceProjectJobBottomSheetState
height: 48, height: 48,
child: TextFormField( child: TextFormField(
controller: controller.tagCtrl, 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) { onFieldSubmitted: (v) {
final value = v.trim(); // also handle normal submit
if (value.isNotEmpty && final raw = v.trim();
!controller.enteredTags.contains(value)) { if (raw.isEmpty) return;
controller.enteredTags.add(value);
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(); controller.tagCtrl.clear();
}, },

View File

@ -201,7 +201,7 @@ class _JobsTabState extends State<JobsTab> {
children: job.tags!.map((tag) { children: job.tags!.map((tag) {
return Chip( return Chip(
label: Text( label: Text(
tag.name, tag.name.replaceAll('_', ' '),
style: style:
const TextStyle(fontSize: 12), const TextStyle(fontSize: 12),
), ),
@ -318,7 +318,7 @@ class _JobsTabState extends State<JobsTab> {
final success = final success =
await ApiService.editServiceProjectJobApi( await ApiService.editServiceProjectJobApi(
jobId: job.id , jobId: job.id,
operations: operations, operations: operations,
); );

View File

@ -39,6 +39,10 @@ class _JobDetailsScreenState extends State<JobDetailsScreen> with UIMixin {
final TextEditingController _dueDateController = TextEditingController(); final TextEditingController _dueDateController = TextEditingController();
final TextEditingController _tagTextController = TextEditingController(); final TextEditingController _tagTextController = TextEditingController();
final RxList<String> tags = <String>[].obs; // For showing/editing tag chips
final TextEditingController tagController =
TextEditingController(); // For tag input
final RxList<Assignee> _selectedAssignees = <Assignee>[].obs; final RxList<Assignee> _selectedAssignees = <Assignee>[].obs;
final RxList<Tag> _selectedTags = <Tag>[].obs; final RxList<Tag> _selectedTags = <Tag>[].obs;
final RxBool isEditing = false.obs; final RxBool isEditing = false.obs;
@ -47,21 +51,38 @@ class _JobDetailsScreenState extends State<JobDetailsScreen> with UIMixin {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
controller = Get.put(ServiceProjectDetailsController()); controller = Get.find<ServiceProjectDetailsController>();
controller.fetchJobDetail(widget.jobId).then((_) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
await controller.fetchJobDetail(widget.jobId);
final job = controller.jobDetail.value?.data; final job = controller.jobDetail.value?.data;
if (job != null) { if (job != null) {
_titleController.text = job.title ?? ''; _titleController.text = job.title ?? '';
_descriptionController.text = job.description ?? ''; _descriptionController.text = job.description ?? '';
_startDateController.text = DateTimeUtils.convertUtcToLocal( _startDateController.text = DateTimeUtils.convertUtcToLocal(
job.startDate ?? DateTime.now().toIso8601String(), job.startDate ?? DateTime.now().toIso8601String(),
format: "yyyy-MM-dd"); format: "yyyy-MM-dd");
_dueDateController.text = DateTimeUtils.convertUtcToLocal( _dueDateController.text = DateTimeUtils.convertUtcToLocal(
job.dueDate ?? '', job.dueDate ?? '',
format: "yyyy-MM-dd"); format: "yyyy-MM-dd");
_selectedAssignees.value = job.assignees ?? []; _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<JobDetailsScreen> with UIMixin {
if (success) { if (success) {
showAppSnackbar( showAppSnackbar(
title: "Success", title: "Success",
message: "Job updated successfully", message: "Job updated successfully",
type: SnackbarType.success); type: SnackbarType.success,
);
/// Refresh detail screen
await controller.fetchJobDetail(widget.jobId); await controller.fetchJobDetail(widget.jobId);
/// 🔥 Auto refresh job list UI (main Service Project Details screen)
if (Get.isRegistered<ServiceProjectDetailsController>()) {
await Get.find<ServiceProjectDetailsController>().refreshJobsAfterAdd();
}
isEditing.value = false; isEditing.value = false;
Navigator.pop(context); // optional if you want auto-close
} else { } else {
showAppSnackbar( showAppSnackbar(
title: "Error", title: "Error",