From 5bbda6f8fd027915e107db61182f6a9f2120b8cb Mon Sep 17 00:00:00 2001 From: venba-Inspriron-3558 Date: Fri, 18 Jul 2025 15:07:42 +0530 Subject: [PATCH] colors and a4 amentmend policy --- .../dashboard/status_dashboard_web.dart | 883 ++++++++++++++++++ lib/Screens/organization/orgSetup.dart | 4 +- lib/Screens/policy/policyCriteria.dart | 209 ++++- lib/routes/custom_appBar.dart | 12 +- lib/routes/custom_drawer.dart | 2 + lib/routes/organizationSetting.dart | 14 + lib/utils/auth_utils.dart | 10 + 7 files changed, 1113 insertions(+), 21 deletions(-) create mode 100644 lib/Screens/dashboard/status_dashboard_web.dart diff --git a/lib/Screens/dashboard/status_dashboard_web.dart b/lib/Screens/dashboard/status_dashboard_web.dart new file mode 100644 index 0000000..61aa6ae --- /dev/null +++ b/lib/Screens/dashboard/status_dashboard_web.dart @@ -0,0 +1,883 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:html' as html; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:frontend/routes/custom_router.dart'; +import 'package:go_router/go_router.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:http/http.dart' as http; +import 'package:responsive_builder/responsive_builder.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../config/apiUrl.dart'; +import '../../routes/custom_appBar.dart'; +import '../../routes/custom_drawer.dart'; +import '../../services/apiService.dart'; +import '../../utils/auth_utils.dart'; +import '../../utils/pagination.dart'; + +class StatusDashboard extends StatefulWidget { + const StatusDashboard({super.key}); + + @override + StatusDashboardState createState() => StatusDashboardState(); +} + +class StatusDashboardState extends State { + Map? apiData; + String? organizationId; + late bool _dialogShown = false; + bool loader = false; + + late StreamSubscription _popStateListener; + String selectedView = 'Today'; + String AccessKeyFlag = "Both"; + Map? currentData; + List typeData = []; + List statusData = []; + List flightData = []; + List accommodationData = []; + List forexData = []; + + final categoryIcons = { + "flight": Icons.flight, + "accommodation": Icons.hotel_outlined, + "forex": Icons.attach_money, + }; + + List statusLabels = [ + "Pending Approval", + "Partially Approved", + "Approved", + "Rejected", + "Cancelled" + ]; + + // Extract counts for "Both" section + List getStatusValues(Map serviceData) { + final both = serviceData['Both'] ?? {}; + return statusLabels.map((label) => (both[label] ?? 0) as int).toList(); + } + + @override + void initState() { + super.initState(); + _checkAuthAndLoadData(); + + checkbackbutton(); + + // loadDashboardData(); + } + + void checkbackbutton() async { + // Push a dummy state so back button triggers popstate instead of navigating + html.window.history.pushState(null, 'home', html.window.location.href); + + _popStateListener = html.window.onPopState.listen((event) { + if (!_dialogShown && mounted) { + _showBackConfirmationDialog(); + } + + // Re-push to prevent leaving + html.window.history.pushState(null, 'home', html.window.location.href); + }); + } + + @override + void dispose() { + _popStateListener.cancel(); // ✅ Remove the browser popstate listener + super.dispose(); + } + + void _showBackConfirmationDialog() { + if (!mounted) return; + + _dialogShown = true; + + showDialog( + context: context, + builder: + (context) => AlertDialog( + title: Text("Confirm"), + content: Text("Do you want to logout?"), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); // Close dialog + _dialogShown = false; + }, + child: Text("Cancel"), + ), + TextButton( + onPressed: () async { + Navigator.pop(context); // Close dialog + _dialogShown = false; + await _logoutAndRedirect(context); + }, + child: Text("Logout"), + ), + ], + ), + ); + } + + Future _logoutAndRedirect(BuildContext context) async { + print("logue 0"); + + // Example: clear session or shared preferences + final prefs = await SharedPreferences.getInstance(); + await prefs.clear(); + + context.go("/"); + + print("logue 1"); + } + + void _checkAuthAndLoadData() async { + final String? token = await getToken(); // Your async function to get token + + if (token == null || token.isEmpty) { + // Token doesn't exist → redirect to login + context.go( + "/", + ); // or use: router.go("/") if you're using `GoRouter` directly + return; + } + + WidgetsBinding.instance.addPostFrameCallback((_) { + checkbackbutton(); + }); + // If token exists, load the dashboard data + loadDashboardData(); + } + + Future loadDashboardData() async { + try { + final data = await fetchStatusDashboard(); + setState(() { + apiData = data; + }); + } catch (e) { + print("Error loading dashboard: $e"); + } + } + + Future getToken() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('auth_token'); + } + + Future getOrgId() async { + final prefs = await SharedPreferences.getInstance(); + final String? userDataString = prefs.getString('user_data'); + + if (userDataString != null) { + try { + final Map userData = jsonDecode(userDataString); + return userData["org_id"]?.toString(); + } catch (e) { + return null; + } + } + return null; + } + + Future> fetchStatusDashboard() async { + organizationId = await getOrgId(); + + final String apiUrlData = + '$apiUrl/api/plans/statusDashboard?org_id=$organizationId'; + final String? token = await getToken(); + + print("Fetch StatusDashboard -- 2KN Here : $token"); + + if (token == null) { + throw Exception('Token not found. Please log in.'); + } + + final response = await http.get( + Uri.parse(apiUrlData), + headers: { + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + }, + ); + print("called api : $apiUrlData"); + if (response.statusCode == 200) { + final apiData = json.decode(response.body); + print("Fetch StatusDashboard -- Reponse Here : $apiData"); + return apiData; // Returning raw JSON list + } else { + throw Exception('Failed to load users'); + } + } + + Color getStatusColor(String status) { + if (status == "Domestic") return Colors.white; + if (status == "International") return Colors.white; + if (status == "Partially Approved") return Colors.yellow.shade100; + if (status == "Pending Approval") return Colors.greenAccent.shade100; + if (status == "Approved") return Colors.green.shade100; + if (status == "Completed") return Colors.green.shade500; + if (status == "Rejected") return Colors.red.shade100; + if (status == "Cancelled") return Colors.red.shade200; + return Colors.grey.shade100; + } + + IconData getStatusIcon(String title) { + switch (title) { + case "Domestic": + return Icons.directions_bus_filled_outlined; + case "International": + return Icons.airplanemode_active; + case "Partially Approved": + return Icons.pending; + case "Approved": + return Icons.check_circle_outline; + case "Completed": + return Icons.done_all; + case "Rejected": + return Icons.cancel_outlined; + case "Cancelled": + return Icons.cancel_schedule_send_outlined; + + default: + return Icons.info_outline; + } + } + + @override + Widget build(BuildContext context) { + final typeBasedCount = List>.from( + (apiData?['data']?['typeBasedCount']) ?? [], + ); + + print("apiData - => $apiData"); + print("typeBasedCount => $typeBasedCount"); + + final statusBasedCount = List>.from( + (apiData?['data']?['statusBasedCount']) ?? [], + ); + + final statusBasedTodayCount = List>.from( + (apiData?['data']?['statusBasedTodayCount']) ?? [], + ); + + final statusBasedWeeklyCount = List>.from( + (apiData?['data']?['statusBasedWeeklyCount']) ?? [], + ); + + final typeBasedTodayCount = List>.from( + (apiData?['data']?['typeBasedTodayCount']) ?? [], + ); + + final typeBasedWeeklyCount = List>.from( + (apiData?['data']?['typeBasedWeeklyCount']) ?? [], + ); + + final typeTodayBasedCount = List>.from( + (apiData?['data']?['typeTodayBasedCount']) ?? [], + ); + + final typeWeeklyBasedCount = List>.from( + (apiData?['data']?['typeWeeklyBasedCount']) ?? [], + ); + + final statusTodayBasedCount = List>.from( + (apiData?['data']?['statusTodayBasedCount']) ?? [], + ); + + final statusWeeklyBasedCount = List>.from( + (apiData?['data']?['statusWeeklyBasedCount']) ?? [], + ); + + final List todayList = List>.from( + apiData?['data']?['todayTripCounts'] ?? [], + ); + + final List weekList = List>.from( + apiData?['data']?['weekTripCounts'] ?? [], + ); + + // Merge all maps in the list into one map + Map todayBasedCount = {}; + Map weekBasedCount = {}; + + for (final item in todayList) { + todayBasedCount.addAll(item); + } + + for (final item in weekList) { + weekBasedCount.addAll(item); + } + + setState(() { + // currentData = selectedView == "Today" ? todayBasedCount : weekBasedCount; + typeData = selectedView == "Today" + ? typeBasedTodayCount + : typeBasedWeeklyCount ; + statusData = selectedView == "Today" + ? statusBasedTodayCount + : statusBasedWeeklyCount ; + + flightData = selectedView == "Today" + ? getStatusValues(todayBasedCount['flight'] ?? {}) + : getStatusValues(weekBasedCount['flight'] ?? {}); + accommodationData = selectedView == "Today" + ? getStatusValues(todayBasedCount['acomodation'] ?? {}) + : getStatusValues(weekBasedCount['acomodation'] ?? {}); + forexData = selectedView == "Today" + ? getStatusValues(todayBasedCount['forex'] ?? {}) + : getStatusValues(weekBasedCount['forex'] ?? {}); + }); + + + print("statusBasedCount - => $statusBasedCount"); + print("Current Data - => $currentData"); + // 👇 Local function to create the card widget + Widget buildInfoCard(String title, int count, double width) { + print('title $title'); + final color = getStatusColor(title); + final icon = getStatusIcon(title); + return Card( + elevation: 3, + color: color, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + child: Container( + width: width, + padding: const EdgeInsets.all(12), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + icon, + size: 32, + color: + (title == 'Domestic' || title == 'International') + ? Colors.green + : Colors.black54, + ), // 👈 Add Icon here + const SizedBox(height: 8), + Text( + title, + textAlign: TextAlign.center, + style: TextStyle( + fontWeight: FontWeight.w600, + fontSize: + (title == 'Domestic' || title == 'International') + ? 16 + : 14, + color: Colors.black87, + ), + ), + const SizedBox(height: 8), + Text( + count.toString(), + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + // color: Colors.blue, + ), + ), + ], + ), + ), + ); + } + + void _handleBackButton() { + final location = GoRouterState.of(context).uri.toString(); + + print("location - $location"); + if (location.contains('/StatusDashboard')) { + // Do nothing or show "Press again to exit" toast + print("Blocked back on dashboard"); + } else { + print("dashboard .."); + } + } + String capitalize(String s) => s[0].toUpperCase() + s.substring(1); + + Widget toggleChip(String label) { + bool isSelected = selectedView == label; + + return GestureDetector( + onTap: () { + setState(() { + selectedView = label; + }); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20), + decoration: BoxDecoration( + color: isSelected ? const Color(0xFF003A78) : Colors.transparent, + borderRadius: BorderRadius.circular(20), + ), + alignment: Alignment.center, + height: 36, + child: Text( + label, + style: GoogleFonts.poppins( + fontSize: 10, + color: isSelected ? Colors.white : Colors.black, + fontWeight: FontWeight.w700, + ), + ), + ), + ); + } + + Widget buildTopCard({ + // required IconData icon, + required Color color, + required String label, + required int count, + required Color bgColor, + }) { + return Container( + padding: const EdgeInsets.all(1), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(1), + ), + child: Column( + children: [ + Tooltip( + message: label , + child: Image.asset( + label == 'Domestic' + ? 'assets/images/IconsImg/Domestic_new.png' + : 'assets/images/IconsImg/International_new.png', + // width: 65, + // height: label == 'Domestic' ? 37 : 40, + ), + ), + // Icon(icon, color: color, size: 40), + const SizedBox(height: 10), + Text( + count.toString(), + style: GoogleFonts.poppins( + fontSize: 28, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 5), + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), + decoration: BoxDecoration( + color: bgColor, + borderRadius: BorderRadius.circular(20), + ), + child: Text( + label, + style: GoogleFonts.poppins( + fontSize: 10, + color: color, + // color: const Color(0xFFEAF3FB), + fontWeight: FontWeight.w700, + ), + ), + ), + ], + ), + ); + } + + Widget statusCard(String label, int count) { + return Column( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), + decoration: BoxDecoration( + color: const Color(0xFFEAF3FB), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + children: [ + Text( + count.toString(), + style: GoogleFonts.poppins( + fontSize: 18, + color: Colors.black87, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 5), + Text( + label, + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black87, + ), + ), + ], + ), + ), + ], + ); + } + + Widget buildCategoryCard(String title, IconData icon, List statusLabels,List values) { + + final statusIcons = [ + Icons.calendar_today, + Icons.assignment, + Icons.verified, + Icons.block, + Icons.cancel, + ]; + + final statusColors = [ + Color(0xFFFBFFCA), + Color(0xFFFFF1CD),// Colors.yellow.shade100, // Color(0xFFCAE77B), + Color(0xFFDAFFE8),// Colors.green.shade100, // Color(0xFF72D480), + Color(0xFFFFD6D3),// Colors.red.shade100, // Color(0xFFF88C8C), + Color(0xFFFFA8A8), // Colors.red.shade200, // Color(0xFFE94B4B), + ]; + + final statusIconColors = [ + Color(0xFFB1BC1A), + Color(0xFFCC9300), + Color(0xFF509269), + Color(0xFFD25E55), + Color(0xFFAE0303), + ]; + + return Container( + padding: const EdgeInsets.all(16), + // decoration: BoxDecoration( + // color: Colors.white, + // borderRadius: BorderRadius.circular(12), + // ), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.blueGrey, + blurRadius: 5, + offset: Offset(0, 2), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: const BoxDecoration( + // color: Colors.blue, + color: Color(0xFF004A8E), + shape: BoxShape.circle, + ), + child: Icon( + icon, + color: Colors.white, + size: 20, + ), + ), + const SizedBox(width: 10), + Text(title, style: GoogleFonts.poppins( + color: Colors.black87, + fontWeight: FontWeight.bold + ),), + ], + ), + const SizedBox(height: 12), + ...List.generate(values.length, (index) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + // Icon(statusIcons[index], size: 15, color: statusIconColors[index]), + Container( + width: 28, + height: 28, + decoration: BoxDecoration( + color: statusColors[index], // background color + borderRadius: BorderRadius.circular(4), // square with slight rounding + ), + alignment: Alignment.center, + child: Icon( + statusIcons[index], + size: 15, + color: statusIconColors[index], // icon color + ), + ), + const SizedBox(width: 6), + Text(statusLabels[index], style: GoogleFonts.poppins( + color: Colors.black87, + ),), + ], + ), + Text(values[index].toString(), style: GoogleFonts.poppins( + fontSize: 10, + color: Colors.black87, + fontWeight: FontWeight.bold, + ),), + ], + ), + ); + }), + ], + ), + ); + } + + // Widget buildCategoryCard(String title, IconData icon,statusLabels,statusvalue) { + // + // + // return Container( + // padding: const EdgeInsets.all(16), + // // decoration: BoxDecoration( + // // color: Colors.white, + // // borderRadius: BorderRadius.circular(12), + // // ), + // + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // children: [ + // Row( + // children: [ + // Container( + // padding: const EdgeInsets.all(8), + // decoration: const BoxDecoration( + // color: Colors.blue, + // shape: BoxShape.circle, + // ), + // child: Icon( + // icon, + // color: Colors.white, + // size: 20, + // ), + // ), + // const SizedBox(width: 10), + // Text(title, style: GoogleFonts.poppins( + // color: Colors.black87, + // fontWeight: FontWeight.bold + // ),), + // ], + // ), + // const SizedBox(height: 12), + // ...List.generate(statusvalue.length, (index) { + // return Padding( + // padding: const EdgeInsets.symmetric(vertical: 10), + // child: Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // children: [ + // Row( + // children: [ + // // Icon(statusIcons[index], size: 20, color: statusColors[index]), + // Container( + // padding: const EdgeInsets.all(8), + // decoration: BoxDecoration( + // color: statusColors[index], // Background color behind the icon + // borderRadius: BorderRadius.circular(10), // Rounded square + // boxShadow: [ + // BoxShadow( + // color: Colors.grey, // Shadow color + // spreadRadius: 1, + // blurRadius: 5, + // offset: const Offset(0, 2), // Shadow position + // ), + // ], + // ), + // child: Icon( + // statusIcons[index], + // color: statusIconColors[index], + // size: 20, + // // color: statusColors[index], + // ), + // ), + // const SizedBox(width: 6), + // Text(statusLabels[index]), + // ], + // ), + // + // ], + // ), + // ); + // }), + // ], + // ), + // ); + // } + + return ResponsiveBuilder( + builder: (context, sizingInfo) { + bool isDesktop = + sizingInfo.deviceScreenType == DeviceScreenType.desktop; + + return PopScope( + canPop: false, // Allow back navigation only if not login screen + onPopInvokedWithResult: (didPop, result) { + if (didPop) return; + _handleBackButton(); // Show exit confirmation dialog + }, + child: Scaffold( + backgroundColor: const Color(0xFFf5f5f5), + appBar: CustomAppBar(isDesktop: isDesktop), + drawer: CustomDrawer(isDesktop: false), + body: Padding( + padding: + isDesktop + ? EdgeInsets.symmetric( + horizontal: + MediaQuery.of(context).size.width * + 0.1, // 30% of screen width as horizontal padding + vertical: 10, // 5% of screen height as vertical padding + ) + : EdgeInsets.all(0), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + // child: ConstrainedBox( + // constraints: BoxConstraints( + // minHeight: constraints.maxHeight, + // ), + // ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // First Row: Toggle Buttons Row + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + height: 36, + decoration: BoxDecoration( + color: const Color(0xFFEAEAEA), + borderRadius: BorderRadius.circular(20), + ), + child: Row( + children: [ + toggleChip("Today"), + toggleChip("This Week"), + ], + ), + ), + ], + ), + const SizedBox(height: 20), + + // Second Row: Domestic & International and Status + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + // LEFT SIDE + Expanded( + flex: 2, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.blueGrey, + blurRadius: 5, + offset: Offset(0, 2), + ), + ], + ), + padding: const EdgeInsets.all(15), + child: Row( + children: [ + ...typeData.map((item) { + return Expanded( + child: Column( + children: [ + buildTopCard( + // icon: item['value'] == "Domestic" + // ? Icons.home + // : Icons.travel_explore, + color: item['value'] == "Domestic" + ? Color(0xFF0DB04B) + : Color(0xFF004A8E), + label: item['value'], + count: item['count'], + bgColor: item['value'] == "Domestic" + ? const Color(0xFFD6FBE4) + : const Color(0xFFD9E8FF), + ), + ], + ), + ); + }).toList(), + ], + ), + ), + ), + const SizedBox(width: 20), + // RIGHT SIDE + Expanded( + flex: 3, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.blueGrey, + blurRadius: 5, + offset: Offset(0, 2), + ), + ], + ), + padding: const EdgeInsets.all(15), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Status", + style: GoogleFonts.poppins( + fontSize: 19, + color: Colors.black87, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 23), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ...statusData.map((item) => statusCard(item['value'], item['count'])).toList(), + ], + ), + ], + ), + ), + ), + ], + ), + const SizedBox(height: 20), + + // Third Row: Flight, Accommodation, Forex + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: buildCategoryCard("Flight", Icons.flight,statusLabels,flightData)), + const SizedBox(width: 10), + Expanded(child: buildCategoryCard("Accomodation", Icons.hotel_outlined,statusLabels,accommodationData)), + const SizedBox(width: 10), + Expanded(child: buildCategoryCard("Forex", Icons.attach_money,statusLabels,forexData)), + ], + ), + ], + ), + ); + }, + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/Screens/organization/orgSetup.dart b/lib/Screens/organization/orgSetup.dart index 5f4a590..4eb929d 100644 --- a/lib/Screens/organization/orgSetup.dart +++ b/lib/Screens/organization/orgSetup.dart @@ -116,7 +116,7 @@ class _OrgSetUpState extends State { "0x${layoutColor?.toARGB32().toRadixString(16).padLeft(8, '0').toUpperCase()}", "secondary_color": "0x${layoutSecondaryColor?.toARGB32().toRadixString(16).padLeft(8, '0').toUpperCase()}", - "tertiary_color": + "ternary_color": "0x${layoutTertiaryColor?.toARGB32().toRadixString(16).padLeft(8, '0').toUpperCase()}", "color": "0x${bodyColor?.toARGB32().toRadixString(16).padLeft(8, '0').toUpperCase()}", @@ -430,6 +430,8 @@ class _OrgSetUpState extends State { // Save back await prefs.setString('org_data', jsonEncode(orgData)); await prefs.setString('layout_color', orgData['layout_color']); + await prefs.setString('secondary_color', orgData['secondary_color']); + await prefs.setString('ternary_color', orgData['ternary_color']); print("✅ Updated org_data saved."); } diff --git a/lib/Screens/policy/policyCriteria.dart b/lib/Screens/policy/policyCriteria.dart index 6a50244..92531c6 100644 --- a/lib/Screens/policy/policyCriteria.dart +++ b/lib/Screens/policy/policyCriteria.dart @@ -73,6 +73,7 @@ class PolicyCriteriaState extends State { Map FirstAmendApproverAction = {}; Map SecondAmendApproverAction = {}; Map ThirdAmendApproverAction = {}; + Map FourthAmendApproverAction = {}; Map SelectedParallelAmendProcess = {}; Map policyDetailsIdMap = {}; // new Map policyIdMap = {}; // new @@ -166,7 +167,8 @@ class PolicyCriteriaState extends State { FirstAmendApproverAction.putIfAbsent(id, () => "None"); SecondAmendApproverAction.putIfAbsent(id, () => "None"); ThirdAmendApproverAction.putIfAbsent(id, () => "None"); - SelectedParallelAmendProcess.putIfAbsent(id, () => "3"); + FourthAmendApproverAction.putIfAbsent(id, () => "None"); + SelectedParallelAmendProcess.putIfAbsent(id, () => "4"); // Step 5: Add default policy entry policyData?.add({ @@ -186,6 +188,7 @@ class PolicyCriteriaState extends State { "a1_amendment_action": FirstAmendApproverAction[id], "a2_amendment_action": SecondAmendApproverAction[id], "a3_amendment_action": ThirdAmendApproverAction[id], + "a4_amendment_action": FourthAmendApproverAction[id], "amendment_parallel_process_from": SelectedParallelAmendProcess[id], "created_by": widget.userId, }); @@ -231,8 +234,10 @@ class PolicyCriteriaState extends State { item['a2_amendment_action']?.toString(); ThirdAmendApproverAction[serviceId] = item['a3_amendment_action']?.toString(); + FourthAmendApproverAction[serviceId] = + item['a4_amendment_action']?.toString(); SelectedParallelAmendProcess[serviceId] = - item['amendment_parallel_process_from']?.toString() ?? "3"; + item['amendment_parallel_process_from']?.toString() ?? "4"; if (item['policy_details_id'] != null) { policyDetailsIdMap[serviceId] = item['policy_details_id'].toString(); @@ -264,6 +269,7 @@ class PolicyCriteriaState extends State { (item) => item["service_id"] == serviceId, ); + print(FirstApproverAction); Map data = { "service_id": serviceId, "cost": costController[serviceId]?.text, @@ -282,6 +288,7 @@ class PolicyCriteriaState extends State { "a1_amendment_action": FirstAmendApproverAction[serviceId], "a2_amendment_action": SecondAmendApproverAction[serviceId], "a3_amendment_action": ThirdAmendApproverAction[serviceId], + "a4_amendment_action": FourthAmendApproverAction[serviceId], "amendment_parallel_process_from": SelectedParallelAmendProcess[serviceId], "created_by": widget.userId, @@ -303,6 +310,7 @@ class PolicyCriteriaState extends State { final aa1 = FirstAmendApproverAction[serviceId]; final aa2 = SecondAmendApproverAction[serviceId]; final aa3 = ThirdAmendApproverAction[serviceId]; + final aa4 = FourthAmendApproverAction[serviceId]; // bool hasValue = cost.trim().isNotEmpty; // bool hasValue = cost.trim().isNotEmpty || travelClass!.isNotEmpty; bool hasValue = @@ -313,15 +321,15 @@ class PolicyCriteriaState extends State { [a1, a2, a3].where((a) => a != null).length > 0 && [a1, a2, a3].where((a) => a == null).length > 0; - bool allExceptionalActionsNull = ae1 == null && ae2 == null && ae3 == null; + bool allExceptionalActionsNull = ae1 == null && ae2 == null && ae3 == null && ae4 == null; bool someExceptionalActionsMissing = - [ae1, ae2, ae3].where((a) => a != null).length > 0 && - [ae1, ae2, ae3].where((a) => a == null).length > 0; + [ae1, ae2, ae3, ae4].where((a) => a != null).length > 0 && + [ae1, ae2, ae3, ae4].where((a) => a == null).length > 0; - bool allAmendActionsNull = aa1 == null && aa2 == null && aa3 == null; + bool allAmendActionsNull = aa1 == null && aa2 == null && aa3 == null && aa4 == null; bool someAmendActionsMissing = - [aa1, aa2, aa3].where((a) => a != null).length > 0 && - [aa1, aa2, aa3].where((a) => a == null).length > 0; + [aa1, aa2, aa3, aa4].where((a) => a != null).length > 0 && + [aa1, aa2, aa3, aa4].where((a) => a == null).length > 0; if (someActionsMissing) { validationErrors[serviceId] = "All 3 approver actions must be selected."; @@ -329,12 +337,12 @@ class PolicyCriteriaState extends State { } if (someExceptionalActionsMissing) { validationErrors[serviceId] = - "All 3 exceptional approver actions must be selected."; + "All 4 exceptional approver actions must be selected."; return; } if (someAmendActionsMissing) { validationErrors[serviceId] = - "All 3 amendment approver actions must be selected."; + "All 4 amendment approver actions must be selected."; return; } @@ -460,6 +468,8 @@ class PolicyCriteriaState extends State { setState(() { // ServiceId = widget.selectedTabNotifier.value ?? "1"; ServiceId = widget.selectedServiceDBValue; + print("ServiceId"); + print(ServiceId); // Initialize controllers and variables if not present costController.putIfAbsent(ServiceId!, () => TextEditingController()); classAction.putIfAbsent((widget.selectedServiceDBValue), () => "1"); @@ -479,7 +489,8 @@ class PolicyCriteriaState extends State { FirstAmendApproverAction.putIfAbsent(ServiceId!, () => "None"); SecondAmendApproverAction.putIfAbsent(ServiceId!, () => "None"); ThirdAmendApproverAction.putIfAbsent(ServiceId!, () => "None"); - SelectedParallelAmendProcess.putIfAbsent(ServiceId!, () => "3"); + FourthAmendApproverAction.putIfAbsent(ServiceId!, () => "None"); + SelectedParallelAmendProcess.putIfAbsent(ServiceId!, () => "4"); }); widget.onPolicyDataChanged(policyData); @@ -1223,7 +1234,8 @@ class PolicyCriteriaState extends State { child: Column( children: [ Container( - padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1368,7 +1380,8 @@ class PolicyCriteriaState extends State { ), ), Container( - padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1517,7 +1530,8 @@ class PolicyCriteriaState extends State { ), ), Container( - padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1667,7 +1681,8 @@ class PolicyCriteriaState extends State { ), ), Container( - padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -1911,7 +1926,8 @@ class PolicyCriteriaState extends State { child: Column( children: [ Container( - padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -2056,7 +2072,8 @@ class PolicyCriteriaState extends State { ), ), Container( - padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -2205,7 +2222,8 @@ class PolicyCriteriaState extends State { ), ), Container( - padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -2354,6 +2372,161 @@ class PolicyCriteriaState extends State { ], ), ), + Container( + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.symmetric(horizontal: 4.0, vertical: 4.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "A4", + style: GoogleFonts.poppins( + fontSize: 13, + ), + ), + Spacer(), + CustomTextFieldUserWrapper( + width: + MediaQuery.of( + context, + ).size.width * + 0.12, + isFocused: false, + isDesktop: widget.isDesktop, + child: SizedBox( + height: 35, + child: DropdownSearch( + selectedItem: + FourthAmendApproverAction[ServiceId], + // enabled: !isViewMode, + popupProps: PopupProps.menu( + // showSearchBox: true, + fit: + FlexFit + .loose, // Allows flexible height + constraints: BoxConstraints( + maxHeight: 250, + ), + itemBuilder: + ( + context, + item, + isSelected, + ) => Padding( + padding: + const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Smaller text size here + color: + Colors + .black, // You can customize this + ), + ), + ), + ), + items: [ + "Approval", + "Notification", + "None", + ], + dropdownDecoratorProps: + DropDownDecoratorProps( + dropdownSearchDecoration: + InputDecoration( + border: + InputBorder.none, + contentPadding: + EdgeInsets.symmetric( + horizontal: 1, + vertical: 5, + ), + ), + ), + dropdownBuilder: + (context, selectedItem) => + // Center-align selected item + Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins( + fontSize: 12, + color: Color(0xFF114D8B), + ), + ), + + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + FourthAmendApproverAction[ServiceId!] = + newValue; + + // print("selectedUserType - $selectedUserType"); + + // if (selectedCountry!.isNotEmpty) { + // errorMessages.remove("country_code"); + // } + }); + }, + ), + ), + ), + Spacer(), + GestureDetector( + onTap: () { + setState(() { + SelectedParallelAmendProcess[ServiceId!] = + "4"; + }); + print( + "SelectedParallelAmendProcess - $SelectedParallelAmendProcess[ServiceId]", + ); + }, + child: Container( + height: 25, + width: 25, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3") || + (SelectedParallelAmendProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, + width: 2, + ), + ), + child: Icon( + Icons.check_circle, + size: 20, + color: + ((SelectedParallelAmendProcess[ServiceId] == + "1") || + (SelectedParallelAmendProcess[ServiceId] == + "2") || + (SelectedParallelAmendProcess[ServiceId] == + "3") || + (SelectedParallelAmendProcess[ServiceId] == + "4")) + ? Colors.green + : Colors.grey, + // color: Colors.grey, + ), + ), + ), + ], + ), + ), ], ), ), diff --git a/lib/routes/custom_appBar.dart b/lib/routes/custom_appBar.dart index 214b426..e0eeb70 100644 --- a/lib/routes/custom_appBar.dart +++ b/lib/routes/custom_appBar.dart @@ -173,6 +173,8 @@ class _CustomAppBarState extends State { // Save to SharedPreferences await prefs.setString('layout_color', selectedOrg?['layout_color']); + await prefs.setString('secondary_color', selectedOrg?['secondary_color']); + await prefs.setString('ternary_color', selectedOrg?['ternary_color']); await prefs.setString('body_color', selectedOrg?['color']); await prefs.setString('body_color', selectedOrg?['plan_action']); @@ -222,6 +224,8 @@ class _CustomAppBarState extends State { // Save to SharedPreferences await prefs.setString('layout_color', selectedOrg?['layout_color']); + await prefs.setString('secondary_color', selectedOrg?['secondary_color']); + await prefs.setString('ternary_color', selectedOrg?['ternary_color']); await prefs.setString('body_color', selectedOrg?['color']); await prefs.setString('body_color', selectedOrg?['plan_action']); @@ -473,7 +477,10 @@ class _CustomAppBarState extends State { child: Row( children: [ // if (userData?["role"] != "User") - Builder( + ExcludeFocus( + // Focus( + // canRequestFocus: false, + child:Builder( builder: (context) => PopupMenuButton( color: Colors.white, @@ -632,6 +639,7 @@ class _CustomAppBarState extends State { ), ), ), + ), const SizedBox(width: 8), ], ), @@ -645,7 +653,7 @@ class _CustomAppBarState extends State { color: layoutColor, // Set the color of the bottom border ), ), - ), + ), ); } diff --git a/lib/routes/custom_drawer.dart b/lib/routes/custom_drawer.dart index 245d721..3985fc6 100644 --- a/lib/routes/custom_drawer.dart +++ b/lib/routes/custom_drawer.dart @@ -128,6 +128,8 @@ class _CustomDrawerState extends State { // Save to SharedPreferences await prefs.setString('layout_color', selectedOrg?['layout_color']); + await prefs.setString('secondary_color', selectedOrg?['secondary_color']); + await prefs.setString('ternary_color', selectedOrg?['ternary_color']); await prefs.setString('body_color', selectedOrg?['color']); await prefs.setString('body_color', selectedOrg?['plan_action']); diff --git a/lib/routes/organizationSetting.dart b/lib/routes/organizationSetting.dart index 0d24272..c7bdc26 100644 --- a/lib/routes/organizationSetting.dart +++ b/lib/routes/organizationSetting.dart @@ -19,6 +19,8 @@ class OrganizationSetting extends StatefulWidget { class OrganizationSettingState extends State { final ApiService apiService = ApiService(); Color? layoutColor; + Color? secondColor; + Color? thridColor; String? roleUser = ""; @@ -29,6 +31,9 @@ class OrganizationSettingState extends State { void loadInitialData() async { String? layoutString = await getLayoutColor(); + String? secondString = await getSecondaryColor(); + String? thridString = await getTernaryColor(); + // String? bodyStringColor = await getBodyColor(); roleUser = await getRoleUser(); setState(() { @@ -37,6 +42,15 @@ class OrganizationSettingState extends State { ? Color(int.parse(layoutString)) : Colors.redAccent; + secondColor = + layoutString != null + ? Color(int.parse(secondString!)) + : Colors.orange; + + thridColor = + layoutString != null + ? Color(int.parse(thridString!)) + : Colors.orangeAccent; // bodyColor = // bodyStringColor != null // ? Color(int.parse(bodyStringColor)) diff --git a/lib/utils/auth_utils.dart b/lib/utils/auth_utils.dart index 83ba0e6..d085727 100644 --- a/lib/utils/auth_utils.dart +++ b/lib/utils/auth_utils.dart @@ -17,6 +17,16 @@ Future getLayoutColor() async { return prefs.getString("layout_color"); } +Future getSecondaryColor() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString("secondary_color"); +} + +Future getTernaryColor() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString("ternary_color"); +} + Future getBodyColor() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString("body_color");