diff --git a/lib/view/service_project/service_project_details_screen.dart b/lib/view/service_project/service_project_details_screen.dart new file mode 100644 index 0000000..491ea8f --- /dev/null +++ b/lib/view/service_project/service_project_details_screen.dart @@ -0,0 +1,124 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:marco/controller/project_controller.dart'; +import 'package:marco/helpers/widgets/my_spacing.dart'; +import 'package:marco/helpers/widgets/my_text.dart'; + +class ServiceProjectDetailsScreen extends StatefulWidget { + const ServiceProjectDetailsScreen({super.key}); + + @override + State createState() => + _ServiceProjectDetailsScreenState(); +} + +class _ServiceProjectDetailsScreenState + extends State with SingleTickerProviderStateMixin { + late TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xFFF5F5F5), + 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.offNamed('/dashboard'), + ), + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + MyText.titleLarge( + 'Service Projects', + fontWeight: 700, + color: Colors.black, + ), + MySpacing.height(2), + GetBuilder( + builder: (projectController) { + final projectName = + projectController.selectedProject?.name ?? + 'Select Project'; + return Row( + children: [ + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), + MySpacing.width(4), + Expanded( + child: MyText.bodySmall( + projectName, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ); + }, + ), + ], + ), + ), + ], + ), + ), + ), + ), + body: Column( + children: [ + // ---------------- TabBar ---------------- + Container( + color: Colors.white, + child: TabBar( + controller: _tabController, + labelColor: Colors.black, + unselectedLabelColor: Colors.grey, + indicatorColor: Colors.red, + tabs: const [ + Tab(text: "Profile"), + Tab(text: "Jobs"), + ], + ), + ), + + // ---------------- TabBarView ---------------- + Expanded( + child: TabBarView( + controller: _tabController, + children: const [ + // Add your tab content here later + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/view/service_project/service_project_screen.dart b/lib/view/service_project/service_project_screen.dart new file mode 100644 index 0000000..aa56f80 --- /dev/null +++ b/lib/view/service_project/service_project_screen.dart @@ -0,0 +1,428 @@ +import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.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/helpers/widgets/avatar.dart'; +import 'package:marco/controller/project_controller.dart'; + +class ServiceProjectScreen extends StatefulWidget { + const ServiceProjectScreen({super.key}); + + @override + State createState() => _ServiceProjectScreenState(); +} + +class _ServiceProjectScreenState extends State + with UIMixin { + final TextEditingController searchController = TextEditingController(); + final RxList> allProjects = >[].obs; + final RxList> filteredProjects = + >[].obs; + + @override + void initState() { + super.initState(); + _loadProjects(); + } + + void _loadProjects() { + final staticProjects = [ + { + "name": "Website Redesign", + "description": "Revamping the corporate website UI/UX", + "status": "In Progress", + "manager": "John Doe", + "email": "john@company.com", + "phone": "+91 9876543210", + "tags": ["UI", "Frontend", "High Priority"] + }, + { + "name": "Mobile App Development", + "description": "Cross-platform mobile app for customers", + "status": "Completed", + "manager": "Priya Sharma", + "email": "priya@company.com", + "phone": "+91 9812345678", + "tags": ["Flutter", "Backend"] + }, + { + "name": "Data Migration", + "description": "Migrating legacy data to AWS", + "status": "Pending", + "manager": "Arun Mehta", + "email": "arun@company.com", + "phone": "+91 9999988888", + "tags": ["Database", "Cloud"] + }, + ]; + allProjects.assignAll(staticProjects); + filteredProjects.assignAll(staticProjects); + } + + void _filterProjects(String query) { + if (query.isEmpty) { + filteredProjects.assignAll(allProjects); + } else { + filteredProjects.assignAll(allProjects + .where((p) => + p["name"].toLowerCase().contains(query.toLowerCase()) || + p["manager"].toLowerCase().contains(query.toLowerCase())) + .toList()); + } + } + + Future _refreshProjects() async { + await Future.delayed(const Duration(seconds: 1)); + } + + Widget _buildProjectCard(Map project) { + return Card( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), + elevation: 3, + shadowColor: Colors.grey.withOpacity(0.3), + color: Colors.white, + child: InkWell( + borderRadius: BorderRadius.circular(5), + onTap: () { + // TODO: Navigate to Project Details screen + }, + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Avatar( + firstName: project["name"].split(" ").first, + lastName: project["name"].split(" ").length > 1 + ? project["name"].split(" ").last + : "", + size: 40, + ), + MySpacing.width(12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.titleSmall(project["name"], + fontWeight: 600, overflow: TextOverflow.ellipsis), + MyText.bodySmall(project["description"], + color: Colors.grey[700], + overflow: TextOverflow.ellipsis), + MySpacing.height(6), + Row( + children: [ + Icon(Icons.person_outline, + size: 16, color: Colors.indigo), + MySpacing.width(4), + MyText.labelSmall(project["manager"], + color: Colors.indigo), + ], + ), + MySpacing.height(4), + Row( + children: [ + Icon(Icons.email_outlined, + size: 16, color: Colors.indigo), + MySpacing.width(4), + Expanded( + child: MyText.labelSmall( + project["email"], + color: Colors.indigo, + decoration: TextDecoration.underline, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + MySpacing.height(4), + Row( + children: [ + Icon(Icons.phone_outlined, + size: 16, color: Colors.indigo), + MySpacing.width(4), + Expanded( + child: MyText.labelSmall( + project["phone"], + color: Colors.indigo, + decoration: TextDecoration.underline, + overflow: TextOverflow.ellipsis, + ), + ), + MySpacing.width(8), + const FaIcon(FontAwesomeIcons.whatsapp, + color: Colors.green, size: 20), + ], + ), + MySpacing.height(6), + Wrap( + spacing: 6, + runSpacing: 2, + children: (project["tags"] as List) + .map((tag) => Chip( + label: Text(tag), + backgroundColor: Colors.indigo.shade50, + labelStyle: const TextStyle( + color: Colors.indigo, fontSize: 12), + visualDensity: VisualDensity.compact, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5), + ), + )) + .toList(), + ), + ], + ), + ), + Column( + children: [ + Container( + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: project["status"] == "Completed" + ? Colors.green.shade100 + : project["status"] == "In Progress" + ? Colors.orange.shade100 + : Colors.red.shade100, + borderRadius: BorderRadius.circular(12), + ), + child: MyText.labelSmall( + project["status"], + fontWeight: 600, + color: project["status"] == "Completed" + ? Colors.green + : project["status"] == "In Progress" + ? Colors.orange + : Colors.red, + ), + ), + const SizedBox(height: 10), + const Icon(Icons.arrow_forward_ios, + color: Colors.grey, size: 20), + ], + ), + ], + ), + ), + ), + ); + } + + 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), + + /// --- SAME APPBAR AS DETAILS SCREEN --- + 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.offNamed('/dashboard'), + ), + MySpacing.width(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + MyText.titleLarge( + 'Service Projects', + fontWeight: 700, + color: Colors.black, + ), + MySpacing.height(2), + GetBuilder( + builder: (projectController) { + final projectName = + projectController.selectedProject?.name ?? + 'All Projects'; + return Row( + children: [ + const Icon(Icons.work_outline, + size: 14, color: Colors.grey), + MySpacing.width(4), + Expanded( + child: MyText.bodySmall( + projectName, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + color: Colors.grey[700], + ), + ), + ], + ); + }, + ), + ], + ), + ), + ], + ), + ), + ), + ), + + body: Column( + children: [ + /// --- SEARCH + FILTER BAR --- + Padding( + padding: MySpacing.xy(8, 8), + child: Row( + children: [ + Expanded( + child: SizedBox( + height: 35, + child: TextField( + controller: searchController, + onChanged: _filterProjects, + 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(); + _filterProjects(''); + }, + ); + }, + ), + 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(() => MyRefreshIndicator( + onRefresh: _refreshProjects, + backgroundColor: Colors.indigo, + color: Colors.white, + child: filteredProjects.isEmpty + ? _buildEmptyState() + : ListView.separated( + physics: const AlwaysScrollableScrollPhysics(), + padding: MySpacing.only( + left: 8, right: 8, top: 4, bottom: 80), + itemCount: filteredProjects.length, + separatorBuilder: (_, __) => MySpacing.height(12), + itemBuilder: (_, index) => + _buildProjectCard(filteredProjects[index]), + ), + )), + ), + ], + ), + ); + } +}