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'; 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(); 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: [ /// Header Row: Avatar | Name & Tags | Status Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.titleMedium( project.name, fontWeight: 800, ), MySpacing.height(2), Row( children: [ if (project.shortName.isNotEmpty) _buildTag(project.shortName), if (project.shortName.isNotEmpty) MySpacing.width(6), Icon(Icons.location_on, size: 15, color: Colors.deepOrange.shade400), MySpacing.width(2), Flexible( child: MyText.bodySmall( project.projectAddress, color: Colors.grey[700], overflow: TextOverflow.ellipsis, ), ), ], ), ], ), ), ], ), MySpacing.height(12), _buildDetailRow( Icons.date_range_outlined, Colors.teal, "${DateTimeUtils.convertUtcToLocal(project.startDate.toIso8601String(), format: DateTimeUtils.defaultFormat)} To " "${DateTimeUtils.convertUtcToLocal(project.endDate.toIso8601String(), format: DateTimeUtils.defaultFormat)}", fontSize: 13, ), MySpacing.height(12), /// Stats Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildStatColumn(Icons.people_alt_rounded, "Team", "${project.teamSize}", Colors.blue[700]), _buildStatColumn( Icons.check_circle, "Completed", "${project.completedWork.toStringAsFixed(1)}%", Colors.green[600]), _buildStatColumn( Icons.pending, "Planned", "${project.plannedWork.toStringAsFixed(1)}%", Colors.orange[800]), ], ), ], ), ), ), ); } // Helper to build colored tags Widget _buildTag(String label) { return Container( decoration: BoxDecoration( color: Colors.indigo.withOpacity(0.08), borderRadius: BorderRadius.circular(6), ), padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), child: MyText.labelSmall(label, color: Colors.indigo[700], fontWeight: 500), ); } // Helper for detail row with icon and text Widget _buildDetailRow(IconData icon, Color iconColor, String value, {double fontSize = 12}) { return Row( children: [ Icon(icon, size: 19, color: iconColor), MySpacing.width(8), Flexible( child: MyText.bodySmall( value, color: Colors.grey[900], fontWeight: 500, fontSize: fontSize, overflow: TextOverflow.ellipsis, ), ), ], ); } // Helper for stats column (icon + label + value) Widget _buildStatColumn( IconData icon, String label, String value, Color? color) { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon(icon, color: color, size: 19), SizedBox(height: 3), MyText.labelSmall(value, color: color, fontWeight: 700), MyText.bodySmall(label, color: Colors.grey[500], fontSize: 11), ], ); } 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 appBar: PreferredSize( preferredSize: const Size.fromHeight(72), child: AppBar( backgroundColor: const Color(0xFFF5F5F5), elevation: 0.5, automaticallyImplyLeading: false, titleSpacing: 0, title: Padding( padding: MySpacing.xy(16, 0), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.arrow_back_ios_new, color: Colors.black, size: 20), onPressed: () => Get.back(), ), MySpacing.width(8), MyText.titleLarge( 'Service Projects', fontWeight: 700, color: Colors.black, ), ], ), ), ), ), body: Column( children: [ /// SEARCH + FILTER BAR 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), ), ), ), ), ), MySpacing.width(8), Container( height: 35, width: 35, decoration: BoxDecoration( color: Colors.white, border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(5), ), child: IconButton( icon: const Icon(Icons.tune, size: 20, color: Colors.black87), onPressed: () { // TODO: Open filter bottom sheet }, ), ), MySpacing.width(10), Container( height: 35, width: 35, decoration: BoxDecoration( color: Colors.white, border: Border.all(color: Colors.grey.shade300), borderRadius: BorderRadius.circular(5), ), child: PopupMenuButton( padding: EdgeInsets.zero, icon: const Icon(Icons.more_vert, size: 20, color: Colors.black87), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5)), itemBuilder: (context) => [ const PopupMenuItem( enabled: false, height: 30, child: Text("Actions", style: TextStyle( fontWeight: FontWeight.bold, color: Colors.grey)), ), const PopupMenuItem( value: 1, child: Row( children: [ SizedBox(width: 10), Expanded(child: Text("Manage Projects")), Icon(Icons.chevron_right, size: 20, color: Colors.indigo), ], ), ), ], ), ), ], ), ), /// 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]), ), ); }), ), ], ), ); } }