FIX_Minor Issues Fixes and Excel api changes in polict report and Audit History Modal
This commit is contained in:
parent
4dd43e1ed1
commit
a52b8d6c04
@ -1971,4 +1971,117 @@ class ApiService {
|
|||||||
final response = await _makeGetRequest(url, headers);
|
final response = await _makeGetRequest(url, headers);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Future<void> generatePolicyExcel(managerId,fromDate,toDate) async {
|
||||||
|
|
||||||
|
final url = Uri.parse('${Env.apiUrl}reports/policy-excel?manager_id=$managerId&from_date=${fromDate ?? ''}&to_date=${toDate ?? ''}',);
|
||||||
|
|
||||||
|
print('getPdfDownload policy-excel - $url');
|
||||||
|
await _initializeToken();
|
||||||
|
|
||||||
|
if (_token == null) throw Exception('Token not found. Please log in.');
|
||||||
|
|
||||||
|
final headers = {
|
||||||
|
'Authorization': 'Bearer $_token',
|
||||||
|
// 'App-Signature': Env.App_Signature,
|
||||||
|
'app-signature': Env.App_Signature,
|
||||||
|
};
|
||||||
|
|
||||||
|
final response = await _makeGethttpRequest(url, headers);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final contentType = response.headers['content-type'] ?? '';
|
||||||
|
if (contentType.contains('application/json')) {
|
||||||
|
final jsonResponse = jsonDecode(response.body);
|
||||||
|
if (jsonResponse['code'] == 404 || jsonResponse['code'] == 500) {
|
||||||
|
ToastHelper.show('File Not Found', Colors.red);
|
||||||
|
} else {
|
||||||
|
ToastHelper.show('No File Found', Colors.red);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
final blob = html.Blob([response.bodyBytes]);
|
||||||
|
final blobUrl = html.Url.createObjectUrlFromBlob(blob);
|
||||||
|
print('getPdfDownload $blobUrl');
|
||||||
|
final contentDisp = response.headers['content-disposition'];
|
||||||
|
print('getPdfDownloadcontentDisp $contentDisp');
|
||||||
|
|
||||||
|
String fileName = extractFileName(contentDisp, "policy_excel");
|
||||||
|
print('Resolved fileName: $fileName');
|
||||||
|
final anchor = html.AnchorElement(href: blobUrl)
|
||||||
|
..setAttribute('download', fileName)
|
||||||
|
..click();
|
||||||
|
|
||||||
|
html.Url.revokeObjectUrl(blobUrl);
|
||||||
|
}
|
||||||
|
} else if (response.statusCode == 404) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('File not found.'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: const Text('OK'),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (response.statusCode == 403) {
|
||||||
|
await clearLocalStorageAndRedirect();
|
||||||
|
} else if (response.statusCode == 500) {
|
||||||
|
ToastHelper.show('No File Found', Colors.red);
|
||||||
|
} else if (response.statusCode == 302) {
|
||||||
|
print('getPdfDownload 3-PP-302');
|
||||||
|
ToastHelper.show('No File Found', Colors.red);
|
||||||
|
} else {
|
||||||
|
ToastHelper.show('No File Found', Colors.red);
|
||||||
|
throw Exception('Failed to download file');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> fetchAuditHistory(id) async {
|
||||||
|
|
||||||
|
print( "fetchAuditHistory - $id " );
|
||||||
|
|
||||||
|
if (_token == null) {
|
||||||
|
await _initializeToken();
|
||||||
|
}
|
||||||
|
// final url = Uri.parse('${Env.apiUrl}audit/history?table_name=partner_policy&pk=80',);
|
||||||
|
final url = Uri.parse('${Env.apiUrl}audit/history?table_name=partner_policy&pk=${id ?? ''}',);
|
||||||
|
|
||||||
|
final headers = {
|
||||||
|
'Authorization': 'Bearer $_token' ?? '',
|
||||||
|
'app-signature': Env.App_Signature,
|
||||||
|
};
|
||||||
|
|
||||||
|
final response = await _makeGetRequest(url, headers);
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Future<Map<String, dynamic>> fetchPolicyExcel(managerId,fromDate,toDate) async {
|
||||||
|
//
|
||||||
|
// print( "fetchPolicyExcel - mangerID- $managerId - Dated - $fromDate - $toDate " );
|
||||||
|
//
|
||||||
|
// if (_token == null) {
|
||||||
|
// await _initializeToken();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final String query;
|
||||||
|
//
|
||||||
|
// query = 'manager_id=$managerId';
|
||||||
|
//
|
||||||
|
// final url = Uri.parse('${Env.apiUrl}reports/policy-excel?$query&from_date=${fromDate ?? ''}&to_date=${toDate ?? ''}',);
|
||||||
|
//
|
||||||
|
// final headers = {
|
||||||
|
// 'Authorization': 'Bearer $_token' ?? '',
|
||||||
|
// 'app-signature': Env.App_Signature,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// final response = await _makeGetRequest(url, headers);
|
||||||
|
//
|
||||||
|
// return response;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -227,7 +227,8 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
|||||||
// late Timer timer;
|
// late Timer timer;
|
||||||
Timer? timer;
|
Timer? timer;
|
||||||
int selectedSeconds = 30;
|
int selectedSeconds = 30;
|
||||||
bool isActionable = false;
|
bool isActionable = false; // current UI state
|
||||||
|
bool userPreference = false; // memory of what the user chose
|
||||||
bool _listened = false;
|
bool _listened = false;
|
||||||
|
|
||||||
//---------------------- Assign Staff Ends -------------------------------- //
|
//---------------------- Assign Staff Ends -------------------------------- //
|
||||||
@ -311,11 +312,25 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
|||||||
getInsuranceType();
|
getInsuranceType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Future<void> loadPreferences() async {
|
||||||
|
// final prefs = await SharedPreferences.getInstance();
|
||||||
|
//
|
||||||
|
// setState(() {
|
||||||
|
// isActionable = prefs.getBool('isActionable') ?? false;
|
||||||
|
// selectedSeconds = prefs.getInt('selectedSeconds') ?? 30;
|
||||||
|
//
|
||||||
|
// _showFilterRow = true;
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// startTimer(); // restart timer using stored value
|
||||||
|
// }
|
||||||
Future<void> loadPreferences() async {
|
Future<void> loadPreferences() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
isActionable = prefs.getBool('isActionable') ?? false;
|
bool savedValue = prefs.getBool('isActionable') ?? false;
|
||||||
|
isActionable = savedValue;
|
||||||
|
userPreference = savedValue; // Initialize the backup
|
||||||
selectedSeconds = prefs.getInt('selectedSeconds') ?? 30;
|
selectedSeconds = prefs.getInt('selectedSeconds') ?? 30;
|
||||||
|
|
||||||
_showFilterRow = true;
|
_showFilterRow = true;
|
||||||
@ -1719,8 +1734,10 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
|||||||
value: !isActionable,
|
value: !isActionable,
|
||||||
onChanged: (v) async {
|
onChanged: (v) async {
|
||||||
bool newValue = !v;
|
bool newValue = !v;
|
||||||
setState(() => isActionable = newValue);
|
setState(() {
|
||||||
|
isActionable = newValue;
|
||||||
|
userPreference = newValue; // Save this as the new "normal"
|
||||||
|
});
|
||||||
final prefs =
|
final prefs =
|
||||||
await SharedPreferences.getInstance();
|
await SharedPreferences.getInstance();
|
||||||
await prefs.setBool(
|
await prefs.setBool(
|
||||||
@ -1780,27 +1797,41 @@ class EnquiryStaffState extends ConsumerState<EnquiryListStaffInline> {
|
|||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
MouseRegion(
|
MouseRegion(
|
||||||
onEnter: (_) {
|
onEnter: (_) {
|
||||||
setState(() {
|
// setState(() {
|
||||||
isActionable = true;
|
// isActionable = true;
|
||||||
});
|
// });
|
||||||
},
|
},
|
||||||
onExit: (_) {
|
onExit: (_) {
|
||||||
setState(() {
|
// setState(() {
|
||||||
// isActionable = false;
|
// // isActionable = false;
|
||||||
isActionable = _searchStaffController.text.isNotEmpty;
|
// isActionable = _searchStaffController.text.isNotEmpty;
|
||||||
});
|
// });
|
||||||
},
|
},
|
||||||
child: ThemedSearchField(
|
child: ThemedSearchField(
|
||||||
hintText: 'Search',
|
hintText: 'Search',
|
||||||
backgroundColor: Color(0xFFFFFFFF),
|
backgroundColor: Color(0xFFFFFFFF),
|
||||||
txtHeight: 30,
|
txtHeight: 30,
|
||||||
// backgroundColor: Colors.white,
|
|
||||||
onChanged: filterData,
|
|
||||||
|
|
||||||
controller: _searchStaffController,
|
controller: _searchStaffController,
|
||||||
txtwidth: ResponsiveLayout.isMobile(context)
|
txtwidth: ResponsiveLayout.isMobile(context)
|
||||||
? MediaQuery.of(context).size.width * 0.7
|
? MediaQuery.of(context).size.width * 0.7
|
||||||
: MediaQuery.of(context).size.width * 0.13,
|
: MediaQuery.of(context).size.width * 0.13,
|
||||||
|
|
||||||
|
// Combine the logic into one function block
|
||||||
|
onChanged: (value) {
|
||||||
|
// 1. Call your existing filter function
|
||||||
|
filterData(value);
|
||||||
|
|
||||||
|
// 2. Perform the logic to toggle the switch UI
|
||||||
|
setState(() {
|
||||||
|
if (value.isNotEmpty) {
|
||||||
|
// CASE 1: Searching -> Force switch to "OFF" (isActionable = true)
|
||||||
|
isActionable = true;
|
||||||
|
} else {
|
||||||
|
// CASE 2: Search cleared -> Restore the status from memory
|
||||||
|
isActionable = userPreference;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
|
|||||||
@ -39,6 +39,8 @@ class policylistState extends ConsumerState<policylist> {
|
|||||||
List<Map<String, dynamic>> getStaffData = [];
|
List<Map<String, dynamic>> getStaffData = [];
|
||||||
List<Map<String, dynamic>> originalData = [];
|
List<Map<String, dynamic>> originalData = [];
|
||||||
List<Map<String, dynamic>> filteredData = [];
|
List<Map<String, dynamic>> filteredData = [];
|
||||||
|
List<Map<String, dynamic>> excelResponseData = [];
|
||||||
|
List<Map<String, dynamic>> auditHistoryResponseData = [];
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
dynamic roleId;
|
dynamic roleId;
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
@ -292,6 +294,34 @@ class policylistState extends ConsumerState<policylist> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> fetchPolicyExcelReport() async {
|
||||||
|
|
||||||
|
final String fromDate = controllers['startDate']!.text; // "11-01-2025"
|
||||||
|
final String toDate = controllers['endDate']!.text; // "20-01-2025"
|
||||||
|
final response = await apiService.generatePolicyExcel(managerId,fromDate,toDate);
|
||||||
|
// Map your controllers and IDs to the API parameters
|
||||||
|
// final String fromDate = controllers['startDate']!.text; // "11-01-2025"
|
||||||
|
// final String toDate = controllers['endDate']!.text; // "20-01-2025"
|
||||||
|
// print("fetchPolicyExcelReport function called here.");
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// // Assuming you have a generic _makeGetRequest method
|
||||||
|
// final response = await apiService.fetchPolicyExcel(managerId,fromDate,toDate);
|
||||||
|
//
|
||||||
|
// if (response != null && response['status'] == 200) {
|
||||||
|
// setState(() {
|
||||||
|
// // Store the list of data in your variable
|
||||||
|
// excelResponseData = response['data'];
|
||||||
|
// });
|
||||||
|
// print("Data fetched. Ready to export.");
|
||||||
|
// }
|
||||||
|
// } catch (e) {
|
||||||
|
// debugPrint("Export Error: $e");
|
||||||
|
// print("Failed to fetch report data.");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MainLayout(
|
return MainLayout(
|
||||||
@ -465,35 +495,63 @@ class policylistState extends ConsumerState<policylist> {
|
|||||||
ResponsiveLayout.isMobile(context)
|
ResponsiveLayout.isMobile(context)
|
||||||
? Spacer()
|
? Spacer()
|
||||||
: SizedBox(width: 10),
|
: SizedBox(width: 10),
|
||||||
ExportBtn(
|
// ExportBtn(
|
||||||
sheetName: "Policy",
|
// sheetName: "Policy",
|
||||||
fileName: "Policy_list",
|
// fileName: "Policy_list",
|
||||||
data: filteredData,
|
// data: filteredData,
|
||||||
txt: !ResponsiveLayout.isMobile(context) ? true : false,
|
// txt: !ResponsiveLayout.isMobile(context) ? true : false,
|
||||||
displayHeaders: [
|
// displayHeaders: [
|
||||||
// "Received Date & Time",
|
// // "Received Date & Time",
|
||||||
"Assigned To",
|
// "Assigned To",
|
||||||
"Partner",
|
// "Partner",
|
||||||
|
//
|
||||||
"Insurer",
|
// "Insurer",
|
||||||
"Vehicle.No.",
|
// "Vehicle.No.",
|
||||||
"Insured Name",
|
// "Insured Name",
|
||||||
"Premium",
|
// "Premium",
|
||||||
"Payment Mode",
|
// "Payment Mode",
|
||||||
"Policy Number",
|
// "Policy Number",
|
||||||
],
|
// ],
|
||||||
keys: [
|
// keys: [
|
||||||
// "updated_on",
|
// // "updated_on",
|
||||||
"assigned_to_name",
|
// "assigned_to_name",
|
||||||
"agent_name",
|
// "agent_name",
|
||||||
"insurer_short_name",
|
// "insurer_short_name",
|
||||||
"reg_no",
|
// "reg_no",
|
||||||
"insured_name",
|
// "insured_name",
|
||||||
"premium_amount",
|
// "premium_amount",
|
||||||
"payment_mode_value",
|
// "payment_mode_value",
|
||||||
"policy_number",
|
// "policy_number",
|
||||||
],
|
// ],
|
||||||
|
// ),
|
||||||
|
// SizedBox(width: 10),
|
||||||
|
Tooltip(
|
||||||
|
message: 'Export Excel',
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () async {
|
||||||
|
await fetchPolicyExcelReport();
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(4.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFF2E7D6E),
|
||||||
|
// color: const Color(0xFF425B5B),
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
"assets/miscellaneous/export.png",
|
||||||
|
height: 15,
|
||||||
|
width: 15,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -763,6 +821,33 @@ class policylistState extends ConsumerState<policylist> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(width: 10),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
showAuditHistoryModal(context,item['id']);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.all(5.4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFF2E7D6E),
|
||||||
|
// color: Color(0xFF425B5B),
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Tooltip(
|
||||||
|
message: 'Audit History Information',
|
||||||
|
child: Icon(
|
||||||
|
Icons.file_open_outlined,
|
||||||
|
color: Colors.white,
|
||||||
|
size: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -1039,3 +1124,163 @@ class policylistState extends ConsumerState<policylist> {
|
|||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void showAuditHistoryModal(BuildContext context, dynamic policyId) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AuditHistoryModal(policyId: policyId), // Pass ID here
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuditHistoryModal extends ConsumerStatefulWidget {
|
||||||
|
final dynamic policyId; // Add this
|
||||||
|
const AuditHistoryModal({super.key, required this.policyId}); // Update constructor
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<AuditHistoryModal> createState() => AuditHistoryModalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuditHistoryModalState extends ConsumerState<AuditHistoryModal> {
|
||||||
|
late ApiService apiService;
|
||||||
|
bool isLoading = true; // Start with loading true
|
||||||
|
List<Map<String, dynamic>> getAuditHistoryData = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
apiService = ApiService();
|
||||||
|
|
||||||
|
// Fetch the audit history as soon as the modal opens
|
||||||
|
fetchAuditHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> fetchAuditHistory() async {
|
||||||
|
setState(() => isLoading = true);
|
||||||
|
try {
|
||||||
|
// Use widget.policyId here!
|
||||||
|
final response = await apiService.fetchAuditHistory(widget.policyId.toString());
|
||||||
|
|
||||||
|
if (response != null && response['status'] == "success") {
|
||||||
|
setState(() {
|
||||||
|
getAuditHistoryData = List<Map<String, dynamic>>.from(response['data']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint("Audit History Error: $e");
|
||||||
|
} finally {
|
||||||
|
if (mounted) setState(() => isLoading = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
content: Container(
|
||||||
|
width: MediaQuery.of(context).size.width * 0.55,
|
||||||
|
height: MediaQuery.of(context).size.height * 0.7,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Audit History",
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Color(0xFF50A398),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Spacer(),
|
||||||
|
Tooltip(
|
||||||
|
message: 'Close',
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 18,
|
||||||
|
// color: Color(0xFF425B5B),
|
||||||
|
color: Color(0xFF2E7D6E),
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
splashRadius: 28,
|
||||||
|
hoverColor: Colors.black12,
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
constraints: const BoxConstraints(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Expanded(
|
||||||
|
child: isLoading
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: getAuditHistoryData.isEmpty
|
||||||
|
? const Center(child: Text("No Audit History Data Found"))
|
||||||
|
: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
child: Theme(
|
||||||
|
data: Theme.of(context).copyWith(
|
||||||
|
dividerColor: Colors.grey.shade200,
|
||||||
|
),
|
||||||
|
child: DataTable(
|
||||||
|
headingRowColor: WidgetStateProperty.all(Color(0xFFF8FAFC)),
|
||||||
|
columnSpacing: 20,
|
||||||
|
columns: [
|
||||||
|
DataColumn(label: Text("Column Name", style: _headerStyle())),
|
||||||
|
DataColumn(label: Text("Message", style: _headerStyle())),
|
||||||
|
DataColumn(label: Text("Changed On", style: _headerStyle())),
|
||||||
|
DataColumn(label: Text("Changed By", style: _headerStyle())),
|
||||||
|
],
|
||||||
|
// Inside your build method's DataTable
|
||||||
|
rows: getAuditHistoryData.map((data) {
|
||||||
|
return DataRow(cells: [
|
||||||
|
DataCell(Text(data['column_name'] ?? "-", style: _cellStyle())),
|
||||||
|
DataCell(
|
||||||
|
SizedBox(
|
||||||
|
width: 300, // Give message more width
|
||||||
|
child: Text(
|
||||||
|
data['message'] ?? "-",
|
||||||
|
style: _cellStyle(),
|
||||||
|
softWrap: true, // Ensure text wraps to next line
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
DataCell(Text(
|
||||||
|
formatIndianDate(data['changed_on'] ?? ""),
|
||||||
|
style: _cellStyle(),
|
||||||
|
)),
|
||||||
|
DataCell(Text(data['changed_by'] ?? "-", style: _cellStyle())),
|
||||||
|
]);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper styles to keep code clean
|
||||||
|
TextStyle _headerStyle() => GoogleFonts.poppins(
|
||||||
|
fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF475569));
|
||||||
|
|
||||||
|
TextStyle _cellStyle() => GoogleFonts.poppins(
|
||||||
|
fontSize: 11, color: Color(0xFF1E293B));
|
||||||
|
|
||||||
|
String formatIndianDate(String dateStr) {
|
||||||
|
try {
|
||||||
|
DateTime dateTime = DateTime.parse(dateStr);
|
||||||
|
// Format: Day-Month-Year Hour:Minute AM/PM
|
||||||
|
return DateFormat('dd-MM-yyyy hh:mm a').format(dateTime);
|
||||||
|
} catch (e) {
|
||||||
|
return dateStr; // Fallback to original string if parsing fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user