import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.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'; import '../../widgets/custom_breadcrumb_navigation.dart'; import '../../widgets/popup_userList_action.dart'; class TemplatesList extends StatefulWidget { const TemplatesList({super.key}); @override TemplatesListState createState() => TemplatesListState(); } class TemplatesListState extends State { // final GlobalKey forexListKey = // GlobalKey(); final ApiService apiService = ApiService(); Future>? futureTemplates; // late Future> futureTemplates; late Map userSingleData; List? apiCountryData; String? selectedUserId; String? orgId; Color? layoutColor; Color? bodyColor; List allTemplate = []; List filteredTemplates = []; TextEditingController searchController = TextEditingController(); int currentPage = 0; int itemsPerPage = 10; @override void initState() { super.initState(); _checkAuthAndLoadData(); // futureTemplates = fetchGetForex(); // // futureTemplates.then((users) { // setState(() { // allTemplate = users; // print("AlL tEMPLATESNIT - $allTemplate"); // }); // }); // // WidgetsBinding.instance.addPostFrameCallback((_) { // loadInitialData(); // }); // futurePlans = fetchPlans(); } 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; } if (!mounted) return; try { futureTemplates = fetchGetForex(); futureTemplates?.then((users) { setState(() { allTemplate = users; print("AlL tEMPLATESNIT - $allTemplate"); }); }); WidgetsBinding.instance.addPostFrameCallback((_) { loadInitialData(); }); } catch (e) { print("group : $e"); } } Future?> refreshData() { print("Calling Refresh Data"); futureTemplates = fetchGetForex(); return futureTemplates!.then((users) { setState(() { allTemplate = users; }); return users; }); } void loadInitialData() async { String? layoutString = await getLayoutColor(); String? bodyStringColor = await getBodyColor(); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; bodyColor = bodyStringColor != null ? Color(int.parse(bodyStringColor)) : Colors.white; }); } String formatTemplateName(String input) { return input .split('_') // split by underscore .map( (word) => word.isNotEmpty ? '${word[0].toUpperCase()}${word.substring(1)}' : '', ) .join(' '); } String getPlaceholderNames(String? raw) { if (raw == null || raw.isEmpty) return ''; try { final List decoded = json.decode(raw); final List values = decoded .map((e) => e['value'].toString().replaceAll('%', '')) .toList(); return values.join(', '); } catch (e) { return 'Invalid placeholder'; } } Future getToken() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('auth_token'); } Future> fetchGetForex() async { // return []; orgId = await getOrgId(); // final String apiUrlData = '$apiUrl/api/getForexPerdiemList'; final String apiUrlData = '$apiUrl/api/template?org_id=${orgId}'; final String? token = await getToken(); print("Fetch Users"); print("TOEKRWE: $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', 'app-signature': 'ts-traveltool-2025-signature-123456', }, ); if (response.statusCode == 200) { final data = json.decode(response.body); print("TemplateDATA--- $data"); return data['data']; // Returning raw JSON list } else if (response.statusCode == 403) { print("403-FORB"); await apiService.logout(context); return []; // throw Exception('Failed to load users'); } else { throw Exception('Failed to load users'); } } void handleDelete(userId) { print("handDel - $userId"); } Future createTemplateData( Map userData, String userId, String newStatus, ) async { final uri = Uri.parse('$apiUrl/api/users/update/$userId'); final String? token = await getToken(); if (token == null) { throw Exception('Token not found. Please log in.'); } // Use MultipartRequest (POST only) final request = http.MultipartRequest('POST', uri); request.headers['Authorization'] = 'Bearer $token'; request.headers['app-signature'] = 'ts-traveltool-2025-signature-123456'; // If updating, spoof the method Laravel-style request.fields['_method'] = 'PUT'; request.fields['user_id'] = userId; print("STatus 2 - $newStatus"); // Add all non-null and non-empty user data fields userData.forEach((key, value) { if (value != null && value.toString().trim().isNotEmpty) { request.fields[key] = value.toString(); } }); request.fields['is_active'] = newStatus; print("🚀 Sending request with fields: ${request.fields}"); try { final streamedResponse = await request.send(); final response = await http.Response.fromStream(streamedResponse); print("Response status: ${response.statusCode}"); print("Response body: ${response.body}"); if (response.statusCode == 200 || response.statusCode == 201) { print("✅ User Status submitted successfully! "); print("📨 Response: ${response.body}"); refreshUserList(); } else { print("❌ Submission failed. Status: ${response.statusCode}"); print("📨 Body: ${response.body}"); } } catch (e) { print("🔥 Error submitting user: $e"); } } void handleToggleUserStatus( String userId, String currentStatus, Map userData, ) async { print("Toggling user status - $userId (Current: $currentStatus)"); final String apiUrlData = '$apiUrl/api/users/update/$userId'; // API for updating user final String? token = await getToken(); if (token == null) { print("Error: Token not found"); return; } // Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1") String newStatus = (currentStatus == "1") ? "0" : "1"; print("STatus 1 - $newStatus"); createTemplateData(userData, userId, newStatus); // try { // final response = await http.put( // Uri.parse(apiUrlData), // headers: { // 'Authorization': 'Bearer $token', // 'Content-Type': 'application/json', // }, // body: jsonEncode({ // "is_active": newStatus // Set new status dynamically // }), // ); // // if (response.statusCode == 200) { // print("User status updated successfully to $newStatus!"); // refreshUserList(); // Refresh users list after update // } else { // print("Failed to update user status. Status: ${response.statusCode}"); // print("Error: ${response.body}"); // } // } catch (e) { // print("Error updating user status: $e"); // } } // Refresh user list after update void refreshUserList() { setState(() { futureTemplates = fetchGetForex(); // Re-fetch users after status update // Wait for futurePlans to be fetched and update allPlans }); } void filterTemplates(String query) { print("Filtering by query: $query"); final lowerQuery = query.toLowerCase(); setState(() { filteredTemplates = allTemplate.where((user) { // Match template_name final templateName = (user['template_name'] ?? '').toString().toLowerCase(); final matchesTemplate = templateName.contains(lowerQuery); // Match placeholder values final placeholderRaw = user['placeholder']; bool matchesPlaceholder = false; if (placeholderRaw != null && placeholderRaw is String) { try { final List decoded = json.decode(placeholderRaw); final List placeholderValues = decoded .map( (e) => e['value'] .toString() .replaceAll('%', '') .toLowerCase(), ) .toList(); matchesPlaceholder = placeholderValues.any( (value) => value.contains(lowerQuery), ); } catch (e) { // ignore invalid placeholder format } } return matchesTemplate || matchesPlaceholder; }).toList(); }); print("Filtered results: $filteredTemplates"); } @override Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, sizingInfo) { bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Scaffold( backgroundColor: Color(0xFFf5f5f5), // appBar: isDesktop ? null : const CustomAppBar(title: 'User Management'), // drawer: isDesktop ? null : CustomDrawer(isDesktop: false), 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: MediaQuery.of(context).size.height * 0, // 5% of screen height as vertical padding ) : EdgeInsets.all(0), child: Row( children: [ // if (isDesktop) CustomDrawer(isDesktop: true), // const Expanded(child: Center(child: Text("User Page Content"))), Expanded(child: buildGroupList(isDesktop)), ], ), ), ); }, ); } Widget buildGroupList(bool isDesktop) { return Container( margin: isDesktop ? const EdgeInsets.only(top: 10.0, bottom: 10.0) : null, padding: const EdgeInsets.all(1), decoration: BoxDecoration( color: isDesktop ? Colors.white : Color(0xFFFCFCFC), ), // decoration: BoxDecoration( // // color: Colors.amber, // // color: bodyColor, // color: Color(0xFFE1F5FE), // border: Border.all( // color: Colors.white, // // color: Color(0xFFF7F7FB), // width: 3.5)), child: buildUserTable(isDesktop), ); } Widget buildUserTable(bool isDesktop) { return Container( // margin: isDesktop // ? EdgeInsets.all(10.0) // : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0), // padding: const EdgeInsets.all(10), height: isDesktop ? MediaQuery.of(context).size.height * 0.98 : MediaQuery.of(context).size.height, child: Padding( padding: const EdgeInsets.all(10.0), child: Container( color: Colors.white, padding: const EdgeInsets.all(10.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Divider( // thickness: 0.2, // how "thick" the line is // color: Colors.grey, // optional // ), Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ // Text( // 'Templates', // style: GoogleFonts.poppins( // fontSize: isDesktop ? 16 : 14, // fontWeight: FontWeight.w600, // color: Colors.black, // ), // ), Container( child: BreadcrumbNavigation( isDesktop: isDesktop, breadcrumbItems: [ BreadcrumbItem( title: 'Org Settings', tooltip: 'Go To Organization Settings', onTap: (context) { context.go("/OrganizationSettings"); }, ), BreadcrumbItem(title: 'Templates'), ], ), ), ], ), if (isDesktop) SizedBox(width: MediaQuery.of(context).size.width * 0.13), // SizedBox(width: MediaQuery.of(context).size.width * 0.23), if (isDesktop) Container( width: MediaQuery.of(context).size.width * 0.2, height: 40, child: TextField( controller: searchController, onChanged: filterTemplates, decoration: InputDecoration( hintText: "Search ...", hintStyle: TextStyle( fontSize: 12, color: Color(0xFF9E9DBD), ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), size: 18, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.grey.shade200, width: 0.5, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.grey.shade300, width: 1, ), ), ), style: GoogleFonts.poppins(fontSize: 12), ), ), // SizedBox(width: 16), // Spacer(), // // ElevatedButton( // style: ElevatedButton.styleFrom( // backgroundColor: Color(0xFF114D8B), // foregroundColor: Colors.white, // disabledBackgroundColor: Color(0xFF114D8B), // disabledForegroundColor: Colors.white, // shape: RoundedRectangleBorder( // borderRadius: BorderRadius.circular(8), // side: // BorderSide(color: Color(0xFF114D8B), width: 2), // ), // padding: EdgeInsets.symmetric( // horizontal: 20, vertical: 12), // ), // onPressed: () async {}, // child: Row( // mainAxisSize: // MainAxisSize.min, // Ensures content fits nicely // children: [ // Text( // "Add Templates", // style: GoogleFonts.poppins( // fontSize: isDesktop ? 13 : 11, // ), // ), // SizedBox(width: 8), // spacing between icon and text // Icon( // Icons.add_circle_outline_rounded, // size: 15, // color: Colors.white, // ), // ], // ), // ), ], ), if (!isDesktop) SizedBox(height: 5), isDesktop ? SizedBox.shrink() : Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( width: MediaQuery.of(context).size.width * 0.8, height: 35, child: TextField( controller: searchController, onChanged: filterTemplates, decoration: InputDecoration( hintText: "Search ...", hintStyle: TextStyle( fontSize: 12, color: Color(0xFF9E9DBD), ), prefixIcon: Icon( Icons.search, color: Color(0xFF9E9DBD), size: 18, ), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.grey.shade200, width: 0.5, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide( color: Colors.grey.shade300, width: 1, ), ), ), style: GoogleFonts.poppins(fontSize: 12), ), ), // SizedBox(width: 16), ], ), const SizedBox(height: 10), FutureBuilder>( future: futureTemplates, builder: (context, snapshot) { final adjHgt = MediaQuery.of(context).size.height; if (futureTemplates == null) { return const Center(child: CircularProgressIndicator()); } if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError || !snapshot.hasData || snapshot.data!.isEmpty) { return Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ // const Icon(Icons.error_outline, // color: Colors.redAccent, size: 60), // const SizedBox(height: 16), // Text( // "Oops!", // style: GoogleFonts.poppins( // fontSize: 20, // fontWeight: FontWeight.bold, // color: Colors.redAccent), // ), SizedBox(height: adjHgt / 4), Text( "No Data Found ", textAlign: TextAlign.center, style: GoogleFonts.poppins( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.grey, ), ), const SizedBox(height: 20), // Text( // "Please Create Templates", // textAlign: TextAlign.center, // style: GoogleFonts.poppins( // fontSize: 16, // color: Colors.grey, // ), // ), // const SizedBox(height: 20), ], ), ), ); } List templates = filteredTemplates.isNotEmpty ? filteredTemplates : allTemplate; // users.sort((a, b) { // DateTime dateA = DateTime.parse(a['created_on']); // DateTime dateB = DateTime.parse(b['created_on']); // // return dateB // .compareTo(dateA); // Descending: newest first // }); templates.sort((a, b) { try { DateTime dateA = DateTime.parse( a['created_at'] ?? '2000-01-01', ); DateTime dateB = DateTime.parse( b['created_at'] ?? '2000-01-01', ); return dateB.compareTo(dateA); } catch (e) { return 0; // If parsing fails, consider them equal } }); List paginatedTemplates = templates .skip(currentPage * itemsPerPage) .take(itemsPerPage) .toList(); Widget table = LayoutBuilder( builder: (context, constraints) { double minWidth = isDesktop ? constraints.maxWidth : 1300; return ConstrainedBox( constraints: BoxConstraints(minWidth: minWidth), child: DataTable( dividerThickness: 0.5, columnSpacing: isDesktop ? 24.0 : 16.0, border: TableBorder( horizontalInside: BorderSide( width: 0.5, color: Colors.grey.shade200, ), ), columns: [ DataColumn( label: Text( 'Template Name', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600, ), ), ), // DataColumn( // label: Text( // 'Attributes', // style: GoogleFonts.poppins( // fontSize: 13, // fontWeight: FontWeight.w600), // )), DataColumn( label: Text( 'Actions', style: GoogleFonts.poppins( fontSize: 13, fontWeight: FontWeight.w600, ), ), ), ], rows: paginatedTemplates.map((forex) { String forexId = forex['template_id'] .toString(); // Get user ID bool isSelected = selectedUserId == forexId; return DataRow( cells: [ DataCell( Text( // "${forex['template_name'] ?? ''}", formatTemplateName( forex['template_name'] ?? '', ), // Text("{forex['template_name'] ?? ''}", style: TextStyle( fontSize: 13, fontFamily: "Inter", ), ), ), // DataCell(Text( // getPlaceholderNames(forex['placeholder']), // style: TextStyle( // fontSize: 13, // fontFamily: "Inter", // ))), DataCell( // UserActionsMenu( // user: forex, // getUserDetails: (id) => // apiService.getSingleUser(id), // ), GestureDetector( child: Image.asset( 'assets/images/IconsImg/edit.png', width: 20, height: 15, ), onTap: () async { // final userId = getUserId(user['user_id']); // final usersData = await getUserDetails(userId); // // context.go('/template'); final templateId = int.tryParse( forex['template_id'].toString(), ); if (templateId != null) { print("templateId -- $templateId"); final data = await apiService .getTemplateFind( context, templateId, ); print("ForexId -- $data"); context.go( '/template', extra: {'templateData': data}, ); } else { print("Invalid Forex ID"); } }, ), ), ], ); }).toList(), ), ); }, ); Widget buildMobileCardView(List paginatedUser) { return ListView.builder( itemCount: paginatedUser.length, itemBuilder: (context, index) { final forex = paginatedUser[index]; return Card( color: Colors.white, margin: EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 3, child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Status and Employee Code Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( // forex['template_name'] ?? 'N/A', formatTemplateName( forex['template_name'] ?? '', ), style: GoogleFonts.poppins( fontSize: 10, color: Colors.black87, fontWeight: FontWeight.w700, ), ), UserActionsMenu( user: forex, getUserDetails: (id) => apiService.getSingleUser( context, id, ), ), ], ), SizedBox(height: 2), // Trip Id and Trip Name // Row( // children: [ // Column( // crossAxisAlignment: // CrossAxisAlignment.start, // children: [ // Text( // getPlaceholderNames( // forex['placeholder']), // style: GoogleFonts.poppins( // fontSize: 12, // color: Colors.black87, // fontWeight: FontWeight.w500), // ), // ], // ), // ], // ), // Actions // Actions ], ), ), ); }, ); } return Expanded( child: Column( // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: isDesktop ? (searchController.text.isNotEmpty && filteredTemplates.isEmpty ? Center( child: Text( "No matches found", style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey, ), ), ) : SingleChildScrollView( scrollDirection: Axis.vertical, child: table, )) : (searchController.text.isNotEmpty && filteredTemplates.isEmpty ? Center( child: Text( "No matches found", style: GoogleFonts.poppins( fontSize: 14, color: Colors.grey, ), ), ) : buildMobileCardView( paginatedTemplates, )), ), // Expanded( // child: isDesktop // ? SingleChildScrollView( // scrollDirection: Axis.vertical, // child: table, // <-- your existing table // ) // : buildMobileCardView(paginatedUser), // ), PaginationControls( currentPage: currentPage, itemsPerPage: itemsPerPage, totalItems: templates.length, activeColor: layoutColor, // your theme color onPageChanged: (page) { setState(() { currentPage = page; }); }, onItemsPerPageChanged: (items) { setState(() { itemsPerPage = items; currentPage = 0; }); }, ), ], ), ); }, ), ], ), ), ), ); } }