non EB
This commit is contained in:
parent
f940e53501
commit
3b323b4ce2
@ -17,6 +17,10 @@ class NhanceSideBar extends StatefulWidget {
|
||||
class _NhanceSideBarState extends State<NhanceSideBar> {
|
||||
String? activeRoute;
|
||||
late ApiService apiService;
|
||||
OverlayEntry? _claimsOverlayEntry;
|
||||
bool _isHoveringClaimsItem = false;
|
||||
bool _isHoveringClaimsMenu = false;
|
||||
DateTime? _claimsMenuSuppressUntil;
|
||||
// bool isLoading = true; // Add a loading state
|
||||
// bool hideInactiveStatus = true;
|
||||
final tokenService = TokenStorageService();
|
||||
@ -109,17 +113,104 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
||||
setState(() {
|
||||
activeRoute = newRoute;
|
||||
});
|
||||
_closeClaimsMenu();
|
||||
// Re-verify the menu items if the route changes
|
||||
_buildSideMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void _navigate(String routeName) {
|
||||
_closeClaimsMenu();
|
||||
if (activeRoute == routeName) return;
|
||||
// Navigation should happen first, didChangeDependencies will handle the state
|
||||
Navigator.pushReplacementNamed(context, routeName);
|
||||
}
|
||||
|
||||
void _showClaimsSubMenu(GlobalKey key) {
|
||||
if (_claimsMenuSuppressUntil != null &&
|
||||
DateTime.now().isBefore(_claimsMenuSuppressUntil!)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final targetContext = key.currentContext;
|
||||
if (targetContext == null) return;
|
||||
|
||||
final box = targetContext.findRenderObject() as RenderBox?;
|
||||
if (box == null) return;
|
||||
|
||||
final offset = box.localToGlobal(Offset.zero);
|
||||
_claimsOverlayEntry?.remove();
|
||||
_claimsOverlayEntry = OverlayEntry(
|
||||
builder: (context) => Positioned(
|
||||
left: offset.dx + box.size.width + 6,
|
||||
top: offset.dy,
|
||||
child: MouseRegion(
|
||||
onEnter: (_) {
|
||||
_isHoveringClaimsMenu = true;
|
||||
},
|
||||
onExit: (_) {
|
||||
_isHoveringClaimsMenu = false;
|
||||
_scheduleCloseClaimsMenu();
|
||||
},
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Container(
|
||||
width: 120,
|
||||
padding: const EdgeInsets.symmetric(vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Colors.black26,
|
||||
blurRadius: 8,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildClaimsMenuItem('EB', 'ClaimsPolicies'),
|
||||
_buildClaimsMenuItem('Non-EB', 'nonEBClaimsList'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
Overlay.of(context).insert(_claimsOverlayEntry!);
|
||||
}
|
||||
|
||||
Widget _buildClaimsMenuItem(String label, String route) {
|
||||
return _ClaimsSubMenuItem(
|
||||
label: label,
|
||||
onTap: () {
|
||||
_claimsMenuSuppressUntil =
|
||||
DateTime.now().add(const Duration(milliseconds: 700));
|
||||
_isHoveringClaimsItem = false;
|
||||
_isHoveringClaimsMenu = false;
|
||||
_closeClaimsMenu();
|
||||
_navigate(route);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _scheduleCloseClaimsMenu() {
|
||||
Future.delayed(const Duration(milliseconds: 120), () {
|
||||
if (!_isHoveringClaimsItem && !_isHoveringClaimsMenu) {
|
||||
_closeClaimsMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _closeClaimsMenu() {
|
||||
_claimsOverlayEntry?.remove();
|
||||
_claimsOverlayEntry = null;
|
||||
_isHoveringClaimsMenu = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
@ -198,7 +289,13 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
||||
// ),
|
||||
|
||||
...sideMenuItems.map((item) {
|
||||
final isClaims = item['route'] == 'ClaimsPolicies';
|
||||
final isClaimsActive =
|
||||
activeRoute == 'ClaimsPolicies' || activeRoute == 'nonEBClaimsList';
|
||||
final itemKey = GlobalKey();
|
||||
|
||||
return _SideItem(
|
||||
key: itemKey,
|
||||
icon: SvgPicture.string(
|
||||
SvgService.getSvg(item['icon']),
|
||||
width: 35,
|
||||
@ -209,10 +306,22 @@ class _NhanceSideBarState extends State<NhanceSideBar> {
|
||||
),
|
||||
),
|
||||
label: item['label'],
|
||||
isActive: activeRoute == item['route'],
|
||||
onTap: () => _navigate(item['route']),
|
||||
isActive: isClaims ? isClaimsActive : activeRoute == item['route'],
|
||||
onTap: isClaims ? null : () => _navigate(item['route']),
|
||||
onHoverEnter: isClaims
|
||||
? () {
|
||||
_isHoveringClaimsItem = true;
|
||||
_showClaimsSubMenu(itemKey);
|
||||
}
|
||||
: null,
|
||||
onHoverExit: isClaims
|
||||
? () {
|
||||
_isHoveringClaimsItem = false;
|
||||
_scheduleCloseClaimsMenu();
|
||||
}
|
||||
: null,
|
||||
);
|
||||
}).toList(),
|
||||
}),
|
||||
|
||||
if (postModules.isNotEmpty && postModules.contains(5))
|
||||
_SideItem(
|
||||
@ -260,42 +369,96 @@ class _SideItem extends StatelessWidget {
|
||||
final String label;
|
||||
final bool isActive;
|
||||
final VoidCallback? onTap;
|
||||
final VoidCallback? onHoverEnter;
|
||||
final VoidCallback? onHoverExit;
|
||||
|
||||
const _SideItem({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.label,
|
||||
this.isActive = false,
|
||||
this.onTap,
|
||||
this.onHoverEnter,
|
||||
this.onHoverExit,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.symmetric(vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: isActive
|
||||
? const Color(0xFF065D61) // ✅ ACTIVE like your screenshot
|
||||
: Colors.transparent,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// 👇 SVG or Icon widget
|
||||
icon,
|
||||
// Icon(icon, color: Colors.white, size: 22),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
return MouseRegion(
|
||||
onEnter: (_) => onHoverEnter?.call(),
|
||||
onExit: (_) => onHoverExit?.call(),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.symmetric(vertical: 6),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: isActive
|
||||
? const Color(0xFF065D61) // ✅ ACTIVE like your screenshot
|
||||
: Colors.transparent,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
// 👇 SVG or Icon widget
|
||||
icon,
|
||||
// Icon(icon, color: Colors.white, size: 22),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ClaimsSubMenuItem extends StatefulWidget {
|
||||
final String label;
|
||||
final VoidCallback onTap;
|
||||
|
||||
const _ClaimsSubMenuItem({
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
});
|
||||
|
||||
@override
|
||||
State<_ClaimsSubMenuItem> createState() => _ClaimsSubMenuItemState();
|
||||
}
|
||||
|
||||
class _ClaimsSubMenuItemState extends State<_ClaimsSubMenuItem> {
|
||||
bool isHovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MouseRegion(
|
||||
onEnter: (_) => setState(() => isHovered = true),
|
||||
onExit: (_) => setState(() => isHovered = false),
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: widget.onTap,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
color: isHovered ? const Color(0xFFE5F6F6) : Colors.white,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
widget.label,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isHovered ? const Color(0xFF006F73) : Colors.black87,
|
||||
fontWeight: isHovered ? FontWeight.w600 : FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -12,6 +12,7 @@ import 'package:nhancepolicy/presentation/excelVerification.dart';
|
||||
import 'package:nhancepolicy/presentation/postFileUpload.dart';
|
||||
import 'package:nhancepolicy/presentation/cdList.dart';
|
||||
import 'package:nhancepolicy/presentation/claims.dart';
|
||||
import 'package:nhancepolicy/presentation/nonEBClaimsList.dart';
|
||||
import 'package:nhancepolicy/presentation/policies.dart';
|
||||
import 'package:nhancepolicy/service/session/session_service.dart';
|
||||
import 'package:nhancepolicy/service/token_storage_service.dart';
|
||||
@ -297,6 +298,7 @@ final Map<String, WidgetBuilder> appRoutes = {
|
||||
'ClaimsPolicies': (context) => ClaimsPolicies(
|
||||
empCode: '',
|
||||
),
|
||||
'nonEBClaimsList': (context) => const NonEBClaimsList(empCode: '',),
|
||||
'cdTransactionDetails': (context) => cdTransactionDetails(
|
||||
insurerName: '',
|
||||
cdMasterAccountNo: '',
|
||||
|
||||
@ -156,7 +156,7 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
|
||||
_postPreToken = tokenService.getCurrentToken();
|
||||
empClientId = await tokenService.readValue('empClientId');
|
||||
empClientBranchId = await tokenService.readValue('empClientBranchId');
|
||||
|
||||
logDebug('empClientId0000 $empClientId');
|
||||
getClaimsPoliciesDetails();
|
||||
|
||||
// ✅ If empCode passed → auto filter
|
||||
@ -188,7 +188,7 @@ class _ClaimsPolicieState extends State<ClaimsPolicies> {
|
||||
try {
|
||||
logDebug('10');
|
||||
final response =
|
||||
await apiService.getClaimPoliciesToApi(_postPreToken!, '');
|
||||
await apiService.getClaimPoliciesToApi(_postPreToken!, empClientId);
|
||||
if (response['status'] == 'success') {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
|
||||
@ -661,13 +661,11 @@ class _ClaimHistoryPopupState extends State<ClaimHistoryPopup>
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: List.generate(stepKeys.length, (index) {
|
||||
String stepTitleKey = stepKeys[index];
|
||||
Map<String, dynamic> stepData =
|
||||
stepMap[stepTitleKey];
|
||||
Map<String, dynamic> stepData = stepMap[stepTitleKey];
|
||||
Widget content = _getStepContentFromApi(stepData);
|
||||
return _buildStep(
|
||||
stepNumber: index + 1,
|
||||
title:
|
||||
_getStepTitleFromApi(stepTitleKey, stepData),
|
||||
title: _getStepTitleFromApi(stepTitleKey, stepData),
|
||||
content: content,
|
||||
isLast: index == stepKeys.length - 1,
|
||||
);
|
||||
|
||||
@ -131,9 +131,13 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
List<String> currentPageIds = [];
|
||||
|
||||
List<dynamic> get _paginatedData {
|
||||
final startIndex = (_currentPage - 1) * _rowsPerPage;
|
||||
final endIndex =
|
||||
(_currentPage * _rowsPerPage).clamp(0, filteredData.length);
|
||||
final total = filteredData.length;
|
||||
if (total == 0) return [];
|
||||
|
||||
final startIndex = ((_currentPage - 1) * _rowsPerPage).clamp(0, total);
|
||||
final endIndex = (_currentPage * _rowsPerPage).clamp(0, total);
|
||||
|
||||
if (startIndex >= endIndex) return [];
|
||||
return filteredData.sublist(startIndex, endIndex);
|
||||
}
|
||||
|
||||
@ -456,10 +460,12 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
|
||||
if (lowerQuery.isEmpty) {
|
||||
setState(() {
|
||||
_currentPage = 1;
|
||||
filteredData = List.from(originalData);
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
_currentPage = 1;
|
||||
filteredData = originalData.where((row) {
|
||||
final status = row['status']?.toString().toLowerCase().trim() ?? '';
|
||||
|
||||
@ -475,6 +481,11 @@ class _HrPolicyDetailsState extends State<hrPolicyDetails>
|
||||
|
||||
return row['name']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
row['emp_code']
|
||||
?.toString()
|
||||
.toLowerCase()
|
||||
.contains(lowerQuery) ==
|
||||
true ||
|
||||
row['uhid']?.toString().toLowerCase().contains(lowerQuery) ==
|
||||
true ||
|
||||
row['relationship']
|
||||
|
||||
777
lib/presentation/nonEBClaimsCreate.dart
Normal file
777
lib/presentation/nonEBClaimsCreate.dart
Normal file
@ -0,0 +1,777 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:nhancepolicy/logger.dart';
|
||||
import 'package:pdf/widgets.dart' as pw;
|
||||
|
||||
import '../config/environment.dart';
|
||||
import '../customAppBar/toastHelper.dart';
|
||||
import '../service/api_service.dart';
|
||||
import '../service/token_storage_service.dart';
|
||||
|
||||
class NonEBClaimsCreate extends StatefulWidget {
|
||||
final BuildContext parentContext;
|
||||
final VoidCallback onSuccess;
|
||||
|
||||
const NonEBClaimsCreate({
|
||||
Key? key,
|
||||
required this.parentContext,
|
||||
required this.onSuccess,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<NonEBClaimsCreate> createState() => _NonEBClaimsCreateState();
|
||||
}
|
||||
|
||||
class _NonEBClaimsCreateState extends State<NonEBClaimsCreate> {
|
||||
bool isLoading = false;
|
||||
dynamic empClientId;
|
||||
dynamic empClientBranchId;
|
||||
final tokenService = TokenStorageService();
|
||||
String? _postPreToken = '';
|
||||
List<Map<String, dynamic>> policyNumberList = [];
|
||||
int? policyNumberId;
|
||||
bool isSubmitting = false;
|
||||
Map<String, dynamic> getClaimPoliciesApi = {};
|
||||
int? selectedClientPolicyId;
|
||||
bool isPolicyValid = true;
|
||||
bool isNatureOfLossValid = true;
|
||||
bool isLossLocationValid = true;
|
||||
bool isLossDateValid = true;
|
||||
bool isLossDescriptionValid = true;
|
||||
|
||||
final TextEditingController natureOfLossController = TextEditingController();
|
||||
final TextEditingController lossLocationController = TextEditingController();
|
||||
final TextEditingController lossDescriptionController =
|
||||
TextEditingController();
|
||||
DateTime? lossDate;
|
||||
PlatformFile? selectedAssetFile;
|
||||
late ApiService apiService;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
apiService = ApiService(context);
|
||||
_loadToken();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
natureOfLossController.dispose();
|
||||
lossLocationController.dispose();
|
||||
lossDescriptionController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _loadToken() async {
|
||||
_postPreToken = tokenService.getCurrentToken();
|
||||
empClientId = await tokenService.readValue('empClientId');
|
||||
empClientBranchId = await tokenService.readValue('empClientBranchId');
|
||||
getClaimsPoliciesDetails();
|
||||
}
|
||||
|
||||
Future<void> getClaimsPoliciesDetails() async {
|
||||
logDebug('9');
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
try {
|
||||
logDebug('10');
|
||||
|
||||
final request = {
|
||||
"client_id": empClientId,
|
||||
"client_branch_id": empClientBranchId
|
||||
};
|
||||
|
||||
final response =
|
||||
await apiService.getNonEBClaimPoliciesToApi(_postPreToken!, request);
|
||||
if (response['status'] == 'success' || response['status'] == true) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
final data = response['data'];
|
||||
final rawList = data is List ? data : <dynamic>[];
|
||||
|
||||
policyNumberList = rawList.map<Map<String, dynamic>>((policy) {
|
||||
final map = Map<String, dynamic>.from(policy as Map);
|
||||
return {
|
||||
// Send this id as client_policy_id in create API payload.
|
||||
'id': int.tryParse(map['id'].toString()) ?? 0,
|
||||
// Show only policy number in dropdown.
|
||||
'label': (map['policy_no'] ?? '').toString(),
|
||||
};
|
||||
}).where((p) => p['id'] != 0).toList();
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
|
||||
// ToastHelper.showWarningToast(
|
||||
// context, 'Request failed with status: ${response.statusCode}');
|
||||
logDebug('Request failed with status: ${response['code']}');
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
// isLoading = false;
|
||||
});
|
||||
logDebug('Exception occurred: $e');
|
||||
} finally {
|
||||
setState(() {
|
||||
// _isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<void> sendFormDataToApi() async {
|
||||
setState(() {
|
||||
isPolicyValid = selectedClientPolicyId != null;
|
||||
isNatureOfLossValid = natureOfLossController.text.trim().isNotEmpty;
|
||||
isLossLocationValid = lossLocationController.text.trim().isNotEmpty;
|
||||
isLossDateValid = lossDate != null;
|
||||
isLossDescriptionValid = lossDescriptionController.text.trim().isNotEmpty;
|
||||
});
|
||||
|
||||
if (!isPolicyValid ||
|
||||
!isNatureOfLossValid ||
|
||||
!isLossLocationValid ||
|
||||
!isLossDateValid ||
|
||||
!isLossDescriptionValid) {
|
||||
ToastHelper.showErrorToast(
|
||||
context,
|
||||
'Please Fill Required Fields',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedAssetFile == null || selectedAssetFile!.bytes == null) {
|
||||
ToastHelper.showErrorToast(
|
||||
context,
|
||||
'Please upload one document',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => isSubmitting = true); // 🔥 start loader
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
logDebug('Check One');
|
||||
final fields = <String, dynamic>{
|
||||
'client_policy_id': selectedClientPolicyId,
|
||||
'nature_of_loss': natureOfLossController.text.trim(),
|
||||
'loss_location': lossLocationController.text.trim(),
|
||||
'loss_date': DateFormat('dd-MM-yyyy').format(lossDate!),
|
||||
'loss_description': lossDescriptionController.text.trim(),
|
||||
};
|
||||
|
||||
final request = http.MultipartRequest(
|
||||
'POST', Uri.parse('${Environment.apiUrlPost}api/v1/non-eb-claim/create'));
|
||||
request.headers['Authorization'] = 'Bearer $_postPreToken';
|
||||
request.headers['APP-SIGNATURE'] =
|
||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
|
||||
final stringFields =
|
||||
fields.map((key, value) => MapEntry(key, value?.toString() ?? ''));
|
||||
logDebug("📁 stringFields: ${stringFields}");
|
||||
request.fields.addAll(stringFields);
|
||||
|
||||
final pf = selectedAssetFile!;
|
||||
final ext = pf.extension?.toLowerCase() ?? '';
|
||||
Uint8List fileBytes = pf.bytes!;
|
||||
var fileName = pf.name;
|
||||
|
||||
if (['jpg', 'jpeg', 'png', 'heic'].contains(ext)) {
|
||||
final pdf = pw.Document();
|
||||
final image = pw.MemoryImage(fileBytes);
|
||||
pdf.addPage(
|
||||
pw.Page(
|
||||
build: (pw.Context context) => pw.Center(
|
||||
child: pw.Image(image, fit: pw.BoxFit.contain),
|
||||
),
|
||||
),
|
||||
);
|
||||
fileBytes = await pdf.save();
|
||||
fileName = pf.name.replaceAll(RegExp(r'\.\w+$'), '.pdf');
|
||||
}
|
||||
|
||||
request.files.add(http.MultipartFile.fromBytes(
|
||||
'asset_file',
|
||||
fileBytes,
|
||||
filename: fileName,
|
||||
));
|
||||
|
||||
logDebug("Payload being sent:");
|
||||
logDebug("File: ${pf.name}");
|
||||
|
||||
final response = await request.send();
|
||||
final responseBody = await response.stream.bytesToString();
|
||||
|
||||
final decoded = jsonDecode(responseBody);
|
||||
if (decoded['status'] == true) {
|
||||
resetFormOnServiceChange();
|
||||
Navigator.pop(context);
|
||||
widget.onSuccess();
|
||||
|
||||
ToastHelper.showSuccessToast(context, decoded['message']);
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
logDebug('Form data submitted successfully');
|
||||
} else {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
ToastHelper.showErrorToast(context, "Failed: ${decoded['message']}");
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
isLoading = false;
|
||||
});
|
||||
logDebug('Error submitting form data: $e');
|
||||
} finally {
|
||||
setState(() => isSubmitting = false); // 🔥 stop loader
|
||||
}
|
||||
}
|
||||
|
||||
void resetFormOnServiceChange() {
|
||||
selectedClientPolicyId = null;
|
||||
policyNumberId = null;
|
||||
natureOfLossController.clear();
|
||||
lossLocationController.clear();
|
||||
lossDescriptionController.clear();
|
||||
lossDate = null;
|
||||
selectedAssetFile = null;
|
||||
|
||||
isPolicyValid = true;
|
||||
isNatureOfLossValid = true;
|
||||
isLossLocationValid = true;
|
||||
isLossDateValid = true;
|
||||
isLossDescriptionValid = true;
|
||||
|
||||
}
|
||||
|
||||
Future<void> pickSingleAssetFile() async {
|
||||
final result = await FilePicker.platform.pickFiles(
|
||||
withData: true,
|
||||
allowMultiple: false,
|
||||
);
|
||||
|
||||
if (result != null && result.files.isNotEmpty) {
|
||||
setState(() {
|
||||
selectedAssetFile = result.files.first;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
resetFormOnServiceChange();
|
||||
return true;
|
||||
},
|
||||
child: Dialog(
|
||||
backgroundColor: Colors.white, // ✅ PURE WHITE popup
|
||||
insetPadding: const EdgeInsets.all(20),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width *
|
||||
0.75, // Desktop popup width
|
||||
// height: MediaQuery.of(context).size.height * 0.85,
|
||||
child: Stack(
|
||||
children: [
|
||||
/// MAIN CONTENT (YOUR EXISTING UI)
|
||||
SingleChildScrollView(
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(20),
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
children: [
|
||||
/// 🔹 HEADER ROW (Title + Close)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Raise Insurance Claim",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => {
|
||||
setState(() {
|
||||
resetFormOnServiceChange();
|
||||
}),
|
||||
Navigator.pop(context)
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Column(
|
||||
children: [
|
||||
_row([
|
||||
buildDropdownField(
|
||||
'Select Policy',
|
||||
(value) {
|
||||
setState(() {
|
||||
policyNumberId = value;
|
||||
selectedClientPolicyId = value;
|
||||
isPolicyValid = true;
|
||||
});
|
||||
},
|
||||
policyNumberList,
|
||||
'label',
|
||||
policyNumberId,
|
||||
required: true,
|
||||
isValid: isPolicyValid,
|
||||
),
|
||||
buildTextField(
|
||||
'Nature Of Loss',
|
||||
natureOfLossController,
|
||||
required: true,
|
||||
isValid: isNatureOfLossValid,
|
||||
),
|
||||
]),
|
||||
_row([
|
||||
buildTextField(
|
||||
'Loss Location',
|
||||
lossLocationController,
|
||||
required: true,
|
||||
isValid: isLossLocationValid,
|
||||
),
|
||||
buildDatePickerField(
|
||||
label: 'Loss Date',
|
||||
selectedDate: lossDate,
|
||||
allowFuture: false,
|
||||
onDateSelected: (d) =>
|
||||
setState(() => lossDate = d),
|
||||
required: true,
|
||||
isValid: isLossDateValid,
|
||||
),
|
||||
]),
|
||||
_row([
|
||||
buildTextAreaField(
|
||||
'Loss Description',
|
||||
lossDescriptionController,
|
||||
required: true,
|
||||
isValid: isLossDescriptionValid,
|
||||
),
|
||||
]),
|
||||
_row([
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
fieldLabel(
|
||||
'Upload Document',
|
||||
required: true,
|
||||
),
|
||||
Container(
|
||||
height: 42,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F1F1),
|
||||
borderRadius:
|
||||
BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
selectedAssetFile?.name ??
|
||||
'Choose one file',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: pickSingleAssetFile,
|
||||
child: const Text('Browse'),
|
||||
),
|
||||
if (selectedAssetFile != null)
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
selectedAssetFile = null;
|
||||
});
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (selectedAssetFile == null)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
child: Text(
|
||||
"Required",
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
)
|
||||
else
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
),
|
||||
]),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
width: 120,
|
||||
height: 42,
|
||||
child: ElevatedButton(
|
||||
onPressed: isSubmitting
|
||||
? null
|
||||
: sendFormDataToApi,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
const Color(0xFFE26728),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
child: isSubmitting
|
||||
? const SizedBox(
|
||||
height: 18,
|
||||
width: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: const Text(
|
||||
'Send',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
/// 🔥 LOADER OVERLAY
|
||||
if (isLoading)
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withOpacity(0.6),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Center(
|
||||
child: Image.asset(
|
||||
'assets/nhance-loader.gif',
|
||||
height: 60,
|
||||
width: 60,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)));
|
||||
}
|
||||
|
||||
/// ---------- HELPERS ----------
|
||||
|
||||
Widget _row(List<Widget> children) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 14),
|
||||
child: Row(
|
||||
children: children
|
||||
.map((e) => Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 12),
|
||||
child: e,
|
||||
)))
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget fieldLabel(String text, {bool required = false}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 6),
|
||||
child: RichText(
|
||||
text: TextSpan(
|
||||
text: text,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
children: required
|
||||
? const [
|
||||
TextSpan(
|
||||
text: ' *',
|
||||
style: TextStyle(color: Colors.red),
|
||||
)
|
||||
]
|
||||
: [],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget errorText(bool isValid) {
|
||||
return SizedBox(
|
||||
height: 16, // fixed height for alignment
|
||||
child: isValid
|
||||
? null
|
||||
: const Text(
|
||||
"Required",
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 11,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTextField(
|
||||
String label,
|
||||
TextEditingController controller, {
|
||||
bool required = false,
|
||||
bool isValid = true,
|
||||
TextInputType keyboardType = TextInputType.text,
|
||||
List<TextInputFormatter>? inputFormatters,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
fieldLabel(label, required: required),
|
||||
formBox(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
keyboardType: keyboardType,
|
||||
inputFormatters: inputFormatters,
|
||||
decoration: const InputDecoration(
|
||||
isDense: true,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
errorText(isValid),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTextAreaField(
|
||||
String label,
|
||||
TextEditingController controller, {
|
||||
bool required = false,
|
||||
bool isValid = true,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
fieldLabel(label, required: required),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F1F1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
maxLines: 3,
|
||||
decoration: const InputDecoration(
|
||||
isDense: true,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
errorText(isValid),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildDropdownField(
|
||||
String label,
|
||||
void Function(int?) onChanged,
|
||||
List<Map<String, dynamic>> itemsList,
|
||||
String displayField,
|
||||
int? selectedValue, {
|
||||
bool required = false,
|
||||
bool isValid = true,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
fieldLabel(label, required: required),
|
||||
formBox(
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<int>(
|
||||
isExpanded: true,
|
||||
value: selectedValue,
|
||||
hint: const Text('Select'),
|
||||
icon: const Icon(Icons.keyboard_arrow_down),
|
||||
|
||||
/// ✅ This controls selected value (closed state)
|
||||
selectedItemBuilder: (context) {
|
||||
return itemsList.map<Widget>((item) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
item[displayField] ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
|
||||
/// ✅ This controls dropdown list (open state)
|
||||
items: itemsList.map<DropdownMenuItem<int>>((item) {
|
||||
return DropdownMenuItem<int>(
|
||||
value: item['id'],
|
||||
child: Text(
|
||||
item[displayField] ?? '',
|
||||
style: const TextStyle(fontSize: 13),
|
||||
), // FULL TEXT here
|
||||
);
|
||||
}).toList(),
|
||||
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (required) errorText(isValid),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildDatePickerField({
|
||||
required String label,
|
||||
required DateTime? selectedDate,
|
||||
required bool allowFuture,
|
||||
required ValueChanged<DateTime?> onDateSelected,
|
||||
DateTime? minDate,
|
||||
DateTime? maxDate,
|
||||
bool required = false,
|
||||
bool isValid = true,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
fieldLabel(label, required: required),
|
||||
Container(
|
||||
height: 42,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F1F1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
// border: required && !isValid
|
||||
// ? Border.all(color: Colors.red)
|
||||
// : null,
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
final DateTime now = DateTime.now();
|
||||
final DateTime first = minDate ?? DateTime(1980);
|
||||
final DateTime last =
|
||||
allowFuture ? (maxDate ?? DateTime(2100)) : now;
|
||||
|
||||
final DateTime initialDate =
|
||||
selectedDate ?? (first.isAfter(now) ? first : now);
|
||||
|
||||
final picked = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: initialDate,
|
||||
firstDate: first,
|
||||
lastDate: last,
|
||||
initialEntryMode: DatePickerEntryMode.calendarOnly,
|
||||
);
|
||||
|
||||
if (picked != null) {
|
||||
onDateSelected(picked);
|
||||
}
|
||||
},
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
selectedDate != null
|
||||
? DateFormat('dd-MM-yyyy').format(selectedDate)
|
||||
: 'Select',
|
||||
style: const TextStyle(color: Colors.black),
|
||||
),
|
||||
// ✅ Show clear button only if date selected
|
||||
if (selectedDate != null)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
onDateSelected(null); // 🔥 Clear date
|
||||
},
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 18,
|
||||
color: Colors.grey,
|
||||
),
|
||||
)
|
||||
else
|
||||
const Icon(Icons.calendar_today, size: 18),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
errorText(isValid),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget formBox({required Widget child}) {
|
||||
return Container(
|
||||
height: 42,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF1F1F1), // 👈 light grey
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(child: child),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// ---------- STYLES ----------
|
||||
final _labelStyle = GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
);
|
||||
1642
lib/presentation/nonEBClaimsHistory.dart
Normal file
1642
lib/presentation/nonEBClaimsHistory.dart
Normal file
File diff suppressed because it is too large
Load Diff
1657
lib/presentation/nonEBClaimsList.dart
Normal file
1657
lib/presentation/nonEBClaimsList.dart
Normal file
File diff suppressed because it is too large
Load Diff
@ -719,6 +719,20 @@ class ApiService {
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
Future<Map<String, dynamic>> getNonEBStatusToApi(
|
||||
String token, empClientId) async {
|
||||
logDebug("getgetClaimPoliciesToApii1");
|
||||
final url = Uri.parse(
|
||||
'${Environment.apiUrlPost}api/v1/non-eb-claim/statuses');
|
||||
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $token' ?? '',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getClaimPoliciesFileDownload(
|
||||
id, String token) async {
|
||||
final url = Uri.parse('${Environment.apiUrlPost}hrFileDownload?id=$id');
|
||||
@ -755,6 +769,31 @@ class ApiService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getNonEBClaimsListDataToApi(
|
||||
String token, Map<String, dynamic> body) async {
|
||||
logDebug("getgetClaimPoliciesToApii1");
|
||||
final url = Uri.parse('${Environment.apiUrlPost}api/v1/non-eb-claim/list');
|
||||
|
||||
final headers = {
|
||||
'APP-SIGNATURE':
|
||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y',
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: headers,
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return jsonDecode(response.body);
|
||||
} else {
|
||||
throw Exception(
|
||||
'Failed to load claims list: ${response.statusCode} ${response.body}');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getEcardRequest(eCardParam, String token) async {
|
||||
final url = Uri.parse('${Environment.apiUrlPost}ecardRequest');
|
||||
final headers = {
|
||||
@ -768,6 +807,20 @@ class ApiService {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getNonEBClaimPoliciesToApi(
|
||||
String token, request) async {
|
||||
final url = Uri.parse('${Environment.apiUrlPost}api/v1/non-eb-claim/policies');
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $token' ?? '',
|
||||
'Content-Type': 'application/json', // Ensure content type is JSON
|
||||
};
|
||||
final jsonBody = jsonEncode(request);
|
||||
// Send formDataJson as the body
|
||||
final response =
|
||||
await _makePostRequestWithoutFormData(url, jsonBody, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getCdTransactionData(
|
||||
String clintID, String insurerId, String cd_ac_pk, String token) async {
|
||||
logDebug("getCashDepositDetailsToApi1");
|
||||
@ -819,6 +872,19 @@ class ApiService {
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getNonEBClaimsHistoryToApi(
|
||||
String ticket_type_id, String token) async {
|
||||
logDebug("getCashDepositDetailsToApi1");
|
||||
final url = Uri.parse(
|
||||
'${Environment.apiUrlPost}api/v1/non-eb-claim/history/${ticket_type_id}');
|
||||
|
||||
final headers = {
|
||||
'Authorization': 'Bearer $token' ?? '',
|
||||
};
|
||||
final response = await _makeGetRequest(url, headers);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getSampleFileDownload(String token) async {
|
||||
final url =
|
||||
Uri.parse('${Environment.apiUrl}downloadSampleExcel/enrollment');
|
||||
|
||||
107
lib/service/nonEBClaimsService.dart
Normal file
107
lib/service/nonEBClaimsService.dart
Normal file
@ -0,0 +1,107 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
import '../config/environment.dart';
|
||||
|
||||
class NonEBClaimsService {
|
||||
static const String _appSignature =
|
||||
'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y';
|
||||
|
||||
Map<String, String> _headers(String token, {bool json = true}) {
|
||||
return {
|
||||
'Authorization': 'Bearer $token',
|
||||
'APP-SIGNATURE': _appSignature,
|
||||
if (json) 'Content-Type': 'application/json',
|
||||
};
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> createClaim({
|
||||
required String token,
|
||||
required Map<String, String> fields,
|
||||
required PlatformFile assetFile,
|
||||
}) async {
|
||||
final request = http.MultipartRequest(
|
||||
'POST',
|
||||
Uri.parse('${Environment.apiUrlPost}non-eb-claim/create'),
|
||||
);
|
||||
|
||||
request.headers.addAll(_headers(token, json: false));
|
||||
request.fields.addAll(fields);
|
||||
|
||||
final Uint8List? bytes = assetFile.bytes;
|
||||
if (bytes == null) {
|
||||
throw Exception('Selected file has no bytes');
|
||||
}
|
||||
|
||||
request.files.add(
|
||||
http.MultipartFile.fromBytes(
|
||||
'asset_file',
|
||||
bytes,
|
||||
filename: assetFile.name,
|
||||
),
|
||||
);
|
||||
|
||||
final streamed = await request.send();
|
||||
final body = await streamed.stream.bytesToString();
|
||||
return jsonDecode(body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> listClaims({
|
||||
required String token,
|
||||
required Map<String, dynamic> body,
|
||||
}) async {
|
||||
final response = await http.post(
|
||||
Uri.parse('${Environment.apiUrlPost}v1/non-eb-claim/list'),
|
||||
headers: _headers(token),
|
||||
body: jsonEncode(body),
|
||||
);
|
||||
return jsonDecode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getHistory({
|
||||
required String token,
|
||||
required String claimId,
|
||||
}) async {
|
||||
final response = await http.get(
|
||||
Uri.parse('${Environment.apiUrlPost}non-eb-claim/history/$claimId'),
|
||||
headers: _headers(token, json: false),
|
||||
);
|
||||
return jsonDecode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> uploadRequiredDoc({
|
||||
required String token,
|
||||
required String claimId,
|
||||
required String documentName,
|
||||
required PlatformFile file,
|
||||
}) async {
|
||||
final request = http.MultipartRequest(
|
||||
'POST',
|
||||
Uri.parse(
|
||||
'${Environment.apiUrlPost}non-eb-claim/$claimId/upload-required-doc'),
|
||||
);
|
||||
|
||||
request.headers.addAll(_headers(token, json: false));
|
||||
request.fields['document_name'] = documentName;
|
||||
|
||||
final Uint8List? bytes = file.bytes;
|
||||
if (bytes == null) {
|
||||
throw Exception('Selected file has no bytes');
|
||||
}
|
||||
|
||||
request.files.add(
|
||||
http.MultipartFile.fromBytes(
|
||||
'file',
|
||||
bytes,
|
||||
filename: file.name,
|
||||
),
|
||||
);
|
||||
|
||||
final streamed = await request.send();
|
||||
final body = await streamed.stream.bytesToString();
|
||||
return jsonDecode(body) as Map<String, dynamic>;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user