import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_refresh_indicator.dart'; import 'package:marco/controller/service_project/service_project_screen_controller.dart'; import 'package:marco/model/service_project/service_projects_list_model.dart'; import 'package:marco/helpers/utils/date_time_utils.dart'; import 'package:marco/view/service_project/service_project_details_screen.dart'; import 'package:marco/helpers/widgets/custom_app_bar.dart'; class ServiceProjectScreen extends StatefulWidget { const ServiceProjectScreen({super.key}); @override State createState() => _ServiceProjectScreenState(); } class _ServiceProjectScreenState extends State with UIMixin { final TextEditingController searchController = TextEditingController(); final ServiceProjectController controller = Get.put(ServiceProjectController()); @override void initState() { super.initState(); // Fetch projects safely after first frame WidgetsBinding.instance.addPostFrameCallback((_) { controller.fetchProjects(); }); searchController.addListener(() { controller.updateSearch(searchController.text); }); } Future _refreshProjects() async { await controller.fetchProjects(); } Widget _buildProjectCard(ProjectItem project) { return Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), shadowColor: Colors.indigo.withOpacity(0.10), color: Colors.white, child: InkWell( borderRadius: BorderRadius.circular(14), onTap: () { // Navigate to ServiceProjectDetailsScreen Get.to(() => ServiceProjectDetailsScreen(projectId: project.id)); }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ /// Project Header Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.titleMedium( project.name, fontWeight: 700, ), MySpacing.height(4), ], ), ), if (project.status?.status.isNotEmpty ?? false) Container( decoration: BoxDecoration( color: Colors.indigo.withOpacity(0.08), borderRadius: BorderRadius.circular(6), ), padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4), child: MyText.labelSmall( project.status!.status, color: Colors.indigo[700], fontWeight: 600, ), ), ], ), MySpacing.height(10), /// Assigned Date _buildDetailRow( Icons.date_range_outlined, Colors.teal, "Assigned: ${DateTimeUtils.convertUtcToLocal(project.assignedDate.toIso8601String(), format: DateTimeUtils.defaultFormat)}", fontSize: 13, ), MySpacing.height(8), /// Client Info if (project.client != null) _buildDetailRow( Icons.account_circle_outlined, Colors.indigo, "Client: ${project.client!.name} (${project.client!.contactPerson})", fontSize: 13, ), MySpacing.height(8), /// Contact Info _buildDetailRow( Icons.phone, Colors.green, "Contact: ${project.contactName} (${project.contactPhone})", fontSize: 13, ), MySpacing.height(12), /// Services List if (project.services.isNotEmpty) Wrap( spacing: 6, runSpacing: 4, children: project.services .map((service) => _buildServiceChip(service.name)) .toList(), ), ], ), ), ), ); } Widget _buildServiceChip(String name) { return Container( decoration: BoxDecoration( color: Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(6), ), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), child: MyText.labelSmall( name, color: Colors.orange[800], fontWeight: 500, ), ); } Widget _buildDetailRow(IconData icon, Color iconColor, String value, {double fontSize = 12}) { return Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon(icon, size: 18, color: iconColor), MySpacing.width(8), Flexible( child: MyText.bodySmall( value, color: Colors.grey[900], fontWeight: 500, fontSize: fontSize, ), ), ], ); } Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.work_outline, size: 60, color: Colors.grey), MySpacing.height(18), MyText.titleMedium('No matching projects found.', fontWeight: 600, color: Colors.grey), MySpacing.height(10), MyText.bodySmall('Try adjusting your filters or refresh.', color: Colors.grey), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF5F5F5), appBar: CustomAppBar( title: "Service Projects", onBackPressed: () => Get.toNamed('/dashboard'), ), body: Column( children: [ /// Search bar and actions Padding( padding: MySpacing.xy(8, 8), child: Row( children: [ Expanded( child: SizedBox( height: 35, child: TextField( controller: searchController, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(horizontal: 12), prefixIcon: const Icon(Icons.search, size: 20, color: Colors.grey), suffixIcon: ValueListenableBuilder( valueListenable: searchController, builder: (context, value, _) { if (value.text.isEmpty) { return const SizedBox.shrink(); } return IconButton( icon: const Icon(Icons.clear, size: 20, color: Colors.grey), onPressed: () { searchController.clear(); controller.updateSearch(''); }, ); }, ), hintText: 'Search projects...', filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: Colors.grey.shade300), ), ), ), ), ), ], ), ), /// Project List Expanded( child: Obx(() { if (controller.isLoading.value) { return const Center(child: CircularProgressIndicator()); } final projects = controller.filteredProjects; return MyRefreshIndicator( onRefresh: _refreshProjects, backgroundColor: Colors.indigo, color: Colors.white, child: projects.isEmpty ? _buildEmptyState() : ListView.separated( physics: const AlwaysScrollableScrollPhysics(), padding: MySpacing.only( left: 8, right: 8, top: 4, bottom: 80), itemCount: projects.length, separatorBuilder: (_, __) => MySpacing.height(12), itemBuilder: (_, index) => _buildProjectCard(projects[index]), ), ); }), ), ], ), ); } }