From cddc2990fbeffa9562e5e8c92ba9113604db73bf Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Fri, 14 Nov 2025 15:35:41 +0530 Subject: [PATCH] created new emploee selector bottomsheet --- ...ice_project_details_screen_controller.dart | 6 +- .../service_project_details_screen.dart | 149 ++++++++++++++++++ 2 files changed, 153 insertions(+), 2 deletions(-) diff --git a/lib/controller/service_project/service_project_details_screen_controller.dart b/lib/controller/service_project/service_project_details_screen_controller.dart index 6a3f1d6..b09a150 100644 --- a/lib/controller/service_project/service_project_details_screen_controller.dart +++ b/lib/controller/service_project/service_project_details_screen_controller.dart @@ -52,12 +52,14 @@ class ServiceProjectDetailsController extends GetxController { errorMessage.value = ''; try { - final result = await ApiService.getServiceProjectDetailApi(projectId.value); + final result = + await ApiService.getServiceProjectDetailApi(projectId.value); if (result != null && result.data != null) { projectDetail.value = result.data!; } else { - errorMessage.value = result?.message ?? "Failed to fetch project details"; + errorMessage.value = + result?.message ?? "Failed to fetch project details"; } } catch (e) { errorMessage.value = "Error: $e"; diff --git a/lib/view/service_project/service_project_details_screen.dart b/lib/view/service_project/service_project_details_screen.dart index 6e1dc36..33f6311 100644 --- a/lib/view/service_project/service_project_details_screen.dart +++ b/lib/view/service_project/service_project_details_screen.dart @@ -315,6 +315,155 @@ class _ServiceProjectDetailsScreenState ); } + Widget _buildJobsTab() { + return Obx(() { + if (controller.isJobLoading.value && controller.jobList.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + + if (controller.jobErrorMessage.value.isNotEmpty && + controller.jobList.isEmpty) { + return Center( + child: MyText.bodyMedium(controller.jobErrorMessage.value)); + } + + if (controller.jobList.isEmpty) { + return Center(child: MyText.bodyMedium("No jobs found")); + } + + return ListView.separated( + controller: _jobScrollController, + padding: const EdgeInsets.fromLTRB(12, 12, 12, 80), + itemCount: controller.jobList.length + 1, + separatorBuilder: (_, __) => const SizedBox(height: 12), + itemBuilder: (context, index) { + if (index == controller.jobList.length) { + return controller.hasMoreJobs.value + ? const Padding( + padding: EdgeInsets.symmetric(vertical: 16), + child: Center(child: CircularProgressIndicator()), + ) + : const SizedBox.shrink(); + } + + final job = controller.jobList[index]; + return Card( + elevation: 3, + shadowColor: Colors.black26, + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Job Title + MyText.titleMedium(job.title, fontWeight: 700), + MySpacing.height(6), + + // Job Description + MyText.bodySmall( + job.description.isNotEmpty + ? job.description + : "No description provided", + color: Colors.grey[700], + ), + + // Tags + if (job.tags != null && job.tags!.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 2), + child: Wrap( + spacing: 2, + runSpacing: 4, + children: job.tags!.map((tag) { + return Chip( + label: Text( + tag.name, + style: const TextStyle(fontSize: 12), + ), + backgroundColor: Colors.grey[200], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + ); + }).toList(), + ), + ), + + MySpacing.height(8), + + // Assignees & Status + Row( + children: [ + if (job.assignees != null && job.assignees!.isNotEmpty) + ...job.assignees!.map((assignee) { + return Padding( + padding: const EdgeInsets.only(right: 6), + child: CircleAvatar( + radius: 12, + backgroundImage: assignee.photo.isNotEmpty + ? NetworkImage(assignee.photo) + : null, + child: assignee.photo.isEmpty + ? Text(assignee.firstName[0]) + : null, + ), + ); + }).toList(), + ], + ), + + MySpacing.height(8), + + // Date Row with Status Chip + Row( + children: [ + // Dates (same as existing) + const Icon(Icons.calendar_today_outlined, + size: 14, color: Colors.grey), + MySpacing.width(4), + Text( + "${DateTimeUtils.convertUtcToLocal(job.startDate, format: 'dd MMM yyyy')} to " + "${DateTimeUtils.convertUtcToLocal(job.dueDate, format: 'dd MMM yyyy')}", + style: + const TextStyle(fontSize: 12, color: Colors.grey), + ), + + const Spacer(), + + // Status Chip + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: job.status.name.toLowerCase() == 'completed' + ? Colors.green[100] + : Colors.orange[100], + borderRadius: BorderRadius.circular(5), + ), + child: Text( + job.status.displayName, + style: TextStyle( + fontSize: 12, + color: job.status.name.toLowerCase() == 'completed' + ? Colors.green[800] + : Colors.orange[800], + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ], + ), + ), + ); + }, + ); + }); + } + Widget _buildJobsTab() { return Obx(() { if (controller.isJobLoading.value && controller.jobList.isEmpty) {