import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:on_field_work/helpers/utils/mixins/ui_mixin.dart'; import 'package:on_field_work/helpers/widgets/my_spacing.dart'; import 'package:on_field_work/helpers/widgets/my_text.dart'; import 'package:on_field_work/helpers/widgets/my_refresh_indicator.dart'; import 'package:on_field_work/controller/infra_project/infra_project_screen_controller.dart'; import 'package:on_field_work/model/infra_project/infra_project_list.dart'; import 'package:on_field_work/helpers/widgets/custom_app_bar.dart'; import 'package:on_field_work/view/infraProject/infra_project_details_screen.dart'; class InfraProjectScreen extends StatefulWidget { const InfraProjectScreen({super.key}); @override State createState() => _InfraProjectScreenState(); } class _InfraProjectScreenState extends State with UIMixin { final TextEditingController searchController = TextEditingController(); final InfraProjectController controller = Get.put(InfraProjectController()); @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { controller.fetchProjects(); }); searchController.addListener(() { controller.updateSearch(searchController.text); }); } Future _refreshProjects() async { await controller.fetchProjects(); } // --------------------------------------------------------------------------- // PROJECT CARD // --------------------------------------------------------------------------- Widget _buildProjectCard(ProjectData project) { return Card( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)), margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), shadowColor: Colors.indigo.withOpacity(0.10), color: Colors.white, child: InkWell( borderRadius: BorderRadius.circular(6), onTap: () { Get.to(() => InfraProjectDetailsScreen(projectId: project.id!, projectName: project.name)); }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // TOP: Name + Status Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: MyText.titleMedium( project.name ?? "-", fontWeight: 700, maxLines: 2, overflow: TextOverflow.ellipsis, ), ), MySpacing.width(8), ], ), MySpacing.height(10), if (project.shortName != null) _buildDetailRow( Icons.badge_outlined, Colors.teal, "Short Name: ${project.shortName}", ), MySpacing.height(8), if (project.projectAddress != null) _buildDetailRow( Icons.location_on_outlined, Colors.orange, "Address: ${project.projectAddress}", ), MySpacing.height(8), if (project.contactPerson != null) _buildDetailRow( Icons.phone, Colors.green, "Contact: ${project.contactPerson}", ), MySpacing.height(12), if (project.teamSize != null) _buildDetailRow( Icons.group, Colors.indigo, "Team Size: ${project.teamSize}", ), ], ), ), ), ); } Widget _buildDetailRow(IconData icon, Color color, String value) { return Row( children: [ Icon(icon, size: 18, color: color), MySpacing.width(8), Expanded( child: MyText.bodySmall( value, color: Colors.grey[900], fontWeight: 500, fontSize: 13, overflow: TextOverflow.ellipsis, maxLines: 2, ), ), ], ); } // --------------------------------------------------------------------------- // EMPTY STATE // --------------------------------------------------------------------------- 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, ), ], ), ); } // --------------------------------------------------------------------------- // MAIN BUILD // --------------------------------------------------------------------------- @override Widget build(BuildContext context) { final Color appBarColor = contentTheme.primary; return Scaffold( backgroundColor: const Color(0xFFF5F5F5), appBar: CustomAppBar( title: "Infra Projects", projectName: 'All Infra Projects', backgroundColor: appBarColor, onBackPressed: () => Get.toNamed('/dashboard'), ), body: Stack( children: [ // GRADIENT BACKDROP Container( height: 80, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ appBarColor, appBarColor.withOpacity(0), ], ), ), ), SafeArea( bottom: true, child: Column( children: [ // SEARCH BAR Padding( padding: MySpacing.xy(8, 8), 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), ), ), ), ), ), // 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: 100), itemCount: projects.length, separatorBuilder: (_, __) => MySpacing.height(12), itemBuilder: (_, index) => _buildProjectCard(projects[index]), ), ); }), ), ], ), ), ], ), ); } }