1047 lines
40 KiB
Dart
1047 lines
40 KiB
Dart
// import 'dart:io' as html;
|
|
// import 'dart:nativewrappers/_internal/vm/lib/typed_data_patch.dart';
|
|
import 'dart:convert';
|
|
import 'dart:typed_data'; // Import for Uint8List
|
|
import 'package:dropdown_search/dropdown_search.dart';
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:nhance_partner/data/utils/toastNotification.dart';
|
|
import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart';
|
|
import 'package:toastification/toastification.dart';
|
|
import 'package:universal_html/html.dart' as html;
|
|
|
|
import '../../../../core/config/env.dart';
|
|
import '../../../../core/services/api_service.dart';
|
|
import '../../../../data/services/auth_service.dart';
|
|
import '../../../../data/utils/validators.dart';
|
|
import '../../../providers/manager_provider.dart';
|
|
import '../../../themes/indicators/customizd_file_upload.dart';
|
|
import '../../../themes/indicators/input_field_decoration.dart';
|
|
import '../../../themes/indicators/month_field_theme.dart';
|
|
import '../../../themes/indicators/search_field_theme.dart';
|
|
// import '../../../themes/indicators/upload_doc_theme.dart';
|
|
|
|
/// function to open the modal
|
|
void showUploadIncentiveModal(BuildContext context) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => const UploadIncentiveModal(),
|
|
);
|
|
}
|
|
|
|
class UploadIncentiveModal extends ConsumerStatefulWidget {
|
|
const UploadIncentiveModal({super.key});
|
|
|
|
@override
|
|
ConsumerState<UploadIncentiveModal> createState() =>
|
|
UploadIncentiveModalState();
|
|
}
|
|
|
|
class UploadIncentiveModalState extends ConsumerState<UploadIncentiveModal> {
|
|
late ApiService apiService;
|
|
bool isLoading = false;
|
|
final _formKey = GlobalKey<FormState>();
|
|
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
|
|
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
|
|
|
List<Map<String, dynamic>> getAgentData = [];
|
|
List<Map<String, dynamic>> filteredData = [];
|
|
List<Map<String, dynamic>> getAgentIncentiveFileData = [];
|
|
List<Map<String, dynamic>> filteredIncentiveData = [];
|
|
Map<String, TextEditingController> controllers = {};
|
|
|
|
// html.File? passportFile;
|
|
List<PlatformFile> selectedFiles = [];
|
|
String? selectedFileNames;
|
|
|
|
List<String> tabHeader = ['name', 'agentId', 'date', 'search'];
|
|
String? _token;
|
|
dynamic userId;
|
|
dynamic managerId;
|
|
dynamic agentId;
|
|
Map<String, dynamic> dataDetails() {
|
|
final selectedDate = controllers["date"]?.text;
|
|
final data = {
|
|
"agent_id": agentId,
|
|
"incentive_month": selectedDate,
|
|
"created_by": userId,
|
|
"file_type": "incentive",
|
|
};
|
|
return data;
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
apiService = ApiService();
|
|
for (String field in tabHeader) {
|
|
controllers[field] = TextEditingController();
|
|
}
|
|
Future.microtask(() {
|
|
managerId = ref.watch(managerIdProvider);
|
|
userId = ref.watch(userIdProvider);
|
|
if (managerId != null) {
|
|
getAgentList(managerId);
|
|
}
|
|
});
|
|
|
|
_initializeToken();
|
|
// getAgentList();
|
|
}
|
|
|
|
Future<void> _initializeToken() async {
|
|
_token = await AuthService.getToken();
|
|
print("APISERTOKEN - $_token");
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
for (var controller in controllers.values) {
|
|
controller.dispose();
|
|
}
|
|
controllers['date']?.dispose();
|
|
controllers['code']?.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> refresh() async {
|
|
setState(() {
|
|
// reset file fields
|
|
selectedFiles = [];
|
|
|
|
// selectedFileNames = "";
|
|
selectedFileNames = null;
|
|
|
|
// getAgentData = [];
|
|
// filteredData = [];
|
|
getAgentIncentiveFileData = [];
|
|
filteredIncentiveData = [];
|
|
|
|
// clear all text controllers
|
|
for (var controller in controllers.values) {
|
|
controller.clear();
|
|
}
|
|
dropDownKey.currentState?.clear(); // 👈 clear selected agent
|
|
controllers['agentId']?.clear(); // clear text field value too
|
|
controllers['uploadFile']?.clear();
|
|
});
|
|
}
|
|
|
|
void filterData(String query) {
|
|
print("FilterDAta - $query");
|
|
setState(() {
|
|
filteredIncentiveData = getAgentIncentiveFileData.where((item) {
|
|
return (item['incentive_month'] ?? '-').toLowerCase().contains(
|
|
query.toLowerCase(),
|
|
);
|
|
}).toList();
|
|
});
|
|
}
|
|
|
|
Future<void> getAgentList(int id) async {
|
|
print('getClaimList called');
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final response = await apiService.fetchAgentUserList(managerId);
|
|
|
|
if (response['status'] == 'success') {
|
|
print('getAgentListData - ${response['data']}');
|
|
setState(() {
|
|
getAgentData = List<Map<String, dynamic>>.from(response['data']);
|
|
print('API Data - $getAgentData');
|
|
|
|
filteredData = List.from(getAgentData);
|
|
// print('originalData - $filteredData');
|
|
});
|
|
} else {
|
|
getAgentData = [];
|
|
filteredData = [];
|
|
}
|
|
} catch (e) {
|
|
print('Exception occurred: $e');
|
|
} finally {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> getAgentIncenctiveFileList(agentId) async {
|
|
print('getClaimList agentId - $agentId');
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final response = await apiService.fetchAgentIncentiveList(agentId, type: 'incentive');
|
|
|
|
if (response['status'] == 'success') {
|
|
print('AgentIncentive - ${response['data']}');
|
|
setState(() {
|
|
getAgentIncentiveFileData = List<Map<String, dynamic>>.from(
|
|
response['data'],
|
|
);
|
|
print('API AgentIncentive - $getAgentIncentiveFileData');
|
|
|
|
filteredIncentiveData = List.from(getAgentIncentiveFileData);
|
|
// print('originalData - $filteredIncentiveData');
|
|
});
|
|
} else {
|
|
getAgentIncentiveFileData = [];
|
|
}
|
|
} catch (e) {
|
|
print('Exception occurred: $e');
|
|
} finally {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> handleSave() async {
|
|
if (agentId == null) {
|
|
ToastHelper.showErrorToast(context, 'Select Partner Name');
|
|
} else {
|
|
if (!_formKey.currentState!.validate()) return;
|
|
print("Handl1");
|
|
final dataSet = dataDetails();
|
|
print("dataSetAgent - $dataSet");
|
|
print("Handl13f");
|
|
|
|
if (selectedFiles.isEmpty) {
|
|
print("❌ No file selected!");
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content: Text("Please select a file before submitting"),
|
|
),
|
|
);
|
|
return;
|
|
}
|
|
|
|
setState(() {
|
|
uploadIncentiveData(dataSet);
|
|
});
|
|
|
|
print("Handl2");
|
|
}
|
|
}
|
|
|
|
Future<void> uploadIncentiveData(Map<String, dynamic> dataSet) async {
|
|
final uri = Uri.parse('${Env.apiUrl}agent/uploadAgentIncentiveFile');
|
|
|
|
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'] = Env.App_Signature;
|
|
|
|
print("USerDAta - $dataSet");
|
|
|
|
// dataSet.forEach((key, value) {
|
|
// request.fields[key] = value.toString();
|
|
// print("✅ Encoded travel_details2: ${request.fields[key]}");
|
|
// });
|
|
|
|
dataSet.forEach((key, value) {
|
|
if (key != 'incentive_file_name') {
|
|
request.fields[key] = value.toString();
|
|
print("✅ Encoded $key: ${request.fields[key]}");
|
|
}
|
|
});
|
|
|
|
if (selectedFiles.isNotEmpty) {
|
|
for (final file in selectedFiles) {
|
|
try {
|
|
if (file.bytes != null) {
|
|
final multipartFile = http.MultipartFile.fromBytes(
|
|
'incentive_file_name',
|
|
file.bytes!,
|
|
filename: file.name,
|
|
);
|
|
request.files.add(multipartFile);
|
|
} else if (file.path != null) {
|
|
final multipartFile = await http.MultipartFile.fromPath(
|
|
'incentive_file_name',
|
|
file.path!,
|
|
filename: file.name,
|
|
);
|
|
request.files.add(multipartFile);
|
|
}
|
|
print("📎 File attached: ${file.name}");
|
|
} catch (e) {
|
|
print("❌ Failed to attach file ${file.name}: $e");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Attach file if selected
|
|
// if (passportFile != null) {
|
|
// try {
|
|
// final reader = html.FileReader();
|
|
// reader.readAsArrayBuffer(passportFile! as html.Blob);
|
|
// await reader.onLoad.first;
|
|
//
|
|
// final data = reader.result as Uint8List;
|
|
//
|
|
// final multipartFile = http.MultipartFile.fromBytes(
|
|
// 'incentive_file_name',
|
|
// data,
|
|
// filename: passportFile!.name,
|
|
// );
|
|
//
|
|
// request.files.add(multipartFile);
|
|
// print("📎 File attached: ${passportFile!.name}");
|
|
// } catch (e) {
|
|
// print("❌ Failed to read file: $e");
|
|
// }
|
|
// } else {
|
|
// print("⚠️ No passport file selected.");
|
|
// }
|
|
|
|
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}");
|
|
final responseBody = json.decode(response.body);
|
|
|
|
if (response.statusCode == 200 || response.statusCode == 201) {
|
|
// dispose();
|
|
print("✅ Partner submitted successfully!");
|
|
|
|
print("📨 Response: ${response.body}");
|
|
|
|
final data = dataDetails();
|
|
final agentId = data["agent_id"];
|
|
|
|
final status = responseBody['status'];
|
|
final message = responseBody['data'] ?? '';
|
|
|
|
if (status == 'success') {
|
|
ToastHelper.showSuccessToast(context, 'File uploaded successfully!');
|
|
getAgentIncenctiveFileList(agentId);
|
|
} else {
|
|
ToastHelper.showErrorToast(
|
|
context,
|
|
message.isNotEmpty ? message : 'Something went wrong!',
|
|
);
|
|
}
|
|
|
|
// context.go(AppRoutes.agentLst);
|
|
} else if (response.statusCode == 403) {
|
|
await apiService.clearLocalStorageAndRedirect();
|
|
} else {
|
|
print("❌ Submission failed. Status: ${response.statusCode}");
|
|
print("📨 Body: ${response.body}");
|
|
String errorMessage = 'There was a problem in creating user. Please try again.';
|
|
try {
|
|
if (responseBody['data'] is String &&
|
|
(responseBody['data'] as String).isNotEmpty) {
|
|
final parsedData = json.decode(responseBody['data']);
|
|
if (parsedData is Map<String, dynamic> &&
|
|
parsedData['message'] != null) {
|
|
errorMessage = parsedData['message'].toString();
|
|
}
|
|
} else if (responseBody['message'] != null) {
|
|
errorMessage = responseBody['message'].toString();
|
|
}
|
|
} catch (_) {
|
|
// keep fallback message
|
|
}
|
|
|
|
ToastHelper.showErrorToast(context, errorMessage);
|
|
}
|
|
} catch (e) {
|
|
print("🔥 Error submitting user: $e");
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final screenWidth = MediaQuery.of(context).size.width;
|
|
final screenHeight = MediaQuery.of(context).size.height;
|
|
final isCompact = screenWidth < 1000;
|
|
final dialogWidth = isCompact ? screenWidth * 0.95 : screenWidth * 0.55;
|
|
final dialogHeight = isCompact ? screenHeight * 0.82 : screenHeight * 0.7;
|
|
final fieldWidth = isCompact ? dialogWidth * 0.9 : screenWidth * 0.27;
|
|
|
|
return SelectionArea(
|
|
child:AlertDialog(
|
|
backgroundColor: Colors.white,
|
|
content: Container(
|
|
width: dialogWidth,
|
|
height: dialogHeight,
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text(
|
|
"Incentive Files",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
color: Color(0xFF50A398),
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
Spacer(),
|
|
Tooltip(
|
|
message: 'Refresh',
|
|
child: IconButton(
|
|
icon: const Icon(
|
|
Icons.refresh,
|
|
size: 18,
|
|
color: Color(0xFF2E7D6E),
|
|
// color: Color(0xFF425B5B),
|
|
),
|
|
onPressed: () {
|
|
refresh();
|
|
},
|
|
splashRadius: 28,
|
|
hoverColor: Colors.black12,
|
|
padding: const EdgeInsets.all(8),
|
|
constraints: const BoxConstraints(),
|
|
),
|
|
),
|
|
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),
|
|
isCompact
|
|
? Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text('Partner Name', style: _labelStyle),
|
|
SizedBox(height: 5),
|
|
Container(
|
|
width: fieldWidth,
|
|
height: 40,
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
key: dropDownKey,
|
|
selectedItem: null,
|
|
items: (filter, infiniteScrollProps) {
|
|
return filteredData; // pass the whole object
|
|
},
|
|
itemAsString: (agent) =>
|
|
agent['name'].toString(), // what to show
|
|
compareFn: (item, selectedItem) =>
|
|
item['id'] ==
|
|
selectedItem['id'], // ✅ compare by id
|
|
decoratorProps: DropDownDecoratorProps(
|
|
decoration:
|
|
AppInputDecorations.dropdownDecoration(
|
|
label: "Select Partner",
|
|
).copyWith(
|
|
hintStyle: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Color(0xFFE2E8F0),
|
|
),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Color(0xFFE2E8F0),
|
|
width: 1.5,
|
|
),
|
|
),
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 6,
|
|
),
|
|
),
|
|
),
|
|
popupProps: PopupProps.menu(
|
|
fit: FlexFit.loose,
|
|
menuProps:
|
|
MenuProps(backgroundColor: Colors.white),
|
|
showSearchBox: true,
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search partner...",
|
|
hintStyle: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Color(0xFFEDF6F5),
|
|
width: 1,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
itemBuilder:
|
|
(
|
|
context,
|
|
item,
|
|
isDisabled,
|
|
isSelected,
|
|
) {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 3,
|
|
),
|
|
child: Text(
|
|
item['name'].toString(),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
onChanged: (agent) {
|
|
if (agent != null) {
|
|
print("Selected Partner Name: ${agent['name']}");
|
|
print("Agent Code: ${agent['agent_code']}");
|
|
print("Agent Id: ${agent['id']}");
|
|
getAgentIncenctiveFileList(agent['id']);
|
|
controllers['agentId']?.text =
|
|
agent['agent_code'];
|
|
agentId = agent['id'];
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 10),
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text('Partner ID', style: _labelStyle),
|
|
SizedBox(height: 5),
|
|
ThemedFormField(
|
|
controller: controllers['agentId']!,
|
|
txtwidth: fieldWidth,
|
|
txtheight: 40,
|
|
borderColor: Color(0xFFE2E8F0),
|
|
highlightColor: Color(0xFF50A398),
|
|
readOnly: true,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
)
|
|
: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text('Partner Name', style: _labelStyle),
|
|
SizedBox(height: 5),
|
|
|
|
Container(
|
|
width: fieldWidth,
|
|
height: 40,
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
key: dropDownKey,
|
|
selectedItem: null,
|
|
items: (filter, infiniteScrollProps) {
|
|
return filteredData; // pass the whole object
|
|
},
|
|
// itemAsString: (agent) => agent['name'],
|
|
// items: (filter, infiniteScrollProps) {
|
|
// return getAgentData
|
|
// .map((agent) => agent['name'].toString())
|
|
// .toList();
|
|
// },
|
|
itemAsString: (agent) =>
|
|
agent['name'].toString(), // what to show
|
|
compareFn: (item, selectedItem) =>
|
|
item['id'] == selectedItem['id'], // ✅ compare by id
|
|
|
|
decoratorProps: DropDownDecoratorProps(
|
|
decoration:
|
|
AppInputDecorations.dropdownDecoration(
|
|
label: "Select Partner",
|
|
).copyWith(
|
|
hintStyle: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
filled: true,
|
|
fillColor: Colors
|
|
.white, // 👈 makes the dropdown input white
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Color(0xFFE2E8F0),
|
|
// color: Colors.white,
|
|
), // 👈 Normal border
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: Color(0xFFE2E8F0),
|
|
// color: Colors.white,
|
|
width: 1.5,
|
|
), // 👈 Focused border
|
|
),
|
|
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 6,
|
|
),
|
|
),
|
|
),
|
|
popupProps: PopupProps.menu(
|
|
fit: FlexFit.loose,
|
|
menuProps: MenuProps(backgroundColor: Colors.white),
|
|
showSearchBox: true,
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search partner...",
|
|
hintStyle: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(color: Colors.white),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
// color: Colors.blue,
|
|
color: Color(0xFFEDF6F5),
|
|
width: 1,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
// constraints: BoxConstraints(),
|
|
itemBuilder: (context, item, isDisabled, isSelected) {
|
|
return Container(
|
|
// color: isSelected ? Colors.blue.withOpacity(0.1) : null,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 3,
|
|
),
|
|
child: Text(
|
|
item['name'].toString(),
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
|
|
onChanged: (agent) {
|
|
if (agent != null) {
|
|
print("Selected Partner Name: ${agent['name']}");
|
|
print("Agent Code: ${agent['agent_code']}");
|
|
print("Agent Id: ${agent['id']}");
|
|
getAgentIncenctiveFileList(agent['id']);
|
|
controllers['agentId']?.text = agent['agent_code'];
|
|
agentId = agent['id'];
|
|
}
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(width: 10),
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text('Partner ID', style: _labelStyle),
|
|
SizedBox(height: 5),
|
|
ThemedFormField(
|
|
controller: controllers['agentId']!,
|
|
txtwidth: fieldWidth,
|
|
txtheight: 40,
|
|
borderColor: Color(0xFFE2E8F0),
|
|
highlightColor: Color(0xFF50A398),
|
|
// backgroundColor: Color(0xFFECECEC),
|
|
readOnly: true,
|
|
// backgroundColor: Color(0xFFCBCBCB),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 20),
|
|
Text("Upload Documents", style: _labelHeaderStyle),
|
|
|
|
const SizedBox(height: 15),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
|
children: [
|
|
Form(
|
|
key: _formKey,
|
|
child: Row(
|
|
children: [
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Month',
|
|
style: _labelStyle,
|
|
),
|
|
SizedBox(height: 5),
|
|
ThemedMonthField(
|
|
hintText: "Select Month",
|
|
txtwidth: MediaQuery.of(context).size.width * 0.27,
|
|
txtheight: 40,
|
|
validator: (value) =>
|
|
Validators.requiredField(value, "date"),
|
|
borderColor: Color(0xFFE2E8F0),
|
|
highlightColor: Color(0xFF50A398),
|
|
controller: controllers['date']!,
|
|
onDateSelected: (date) {
|
|
print("Picked Date: $date");
|
|
},
|
|
),
|
|
],
|
|
),
|
|
SizedBox(width: 15),
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Text('Upload Files', style: _labelStyle),
|
|
],
|
|
),
|
|
SizedBox(height: 5),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
ThemedUploadField(
|
|
key: ValueKey(selectedFileNames),
|
|
allowMultiple: true,
|
|
// hintText: "Upload Document",
|
|
txtheight: 40,
|
|
// validator: (value) => Validators.requiredField(
|
|
// value,
|
|
// "Upload Document",
|
|
// ),
|
|
// validator: (value) {
|
|
// if (passportFile == null) {
|
|
// return "Please upload a file";
|
|
// }
|
|
// return null;
|
|
// },
|
|
// backgroundColor: Color(0xFFECECEC),
|
|
backgroundColor: Colors.white,
|
|
borderColor: Color(0xFFE2E8F0),
|
|
allowedExtensions: null,
|
|
hintText:
|
|
(selectedFileNames == null ||
|
|
selectedFileNames!.isEmpty)
|
|
? "Upload Document"
|
|
: selectedFileNames,
|
|
txtwidth: isCompact
|
|
? fieldWidth * 0.71
|
|
: MediaQuery.of(context).size.width * 0.19,
|
|
onFilesSelected: (fileNames, files) {
|
|
print("Files picked: ${fileNames.join(', ')}");
|
|
// print("Size: ${file.size}");
|
|
// print(
|
|
// "Path: ${file.path}",
|
|
// ); // works on mobile/desktop
|
|
// print("Bytes: ${file.bytes}");
|
|
setState(() {
|
|
selectedFiles = files;
|
|
selectedFileNames = fileNames.join(', ');
|
|
});
|
|
},
|
|
),
|
|
SizedBox(width: 15),
|
|
Container(
|
|
width: isCompact
|
|
? fieldWidth * 0.25
|
|
: MediaQuery.of(context).size.width * 0.068,
|
|
child: ElevatedButton(
|
|
style: ElevatedButton.styleFrom(
|
|
// backgroundColor: Color(0xFF425B5B),
|
|
backgroundColor: Color(0xFF2E7D6E),
|
|
foregroundColor: Colors.white,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(
|
|
6,
|
|
), // 👈 reduce radius (default ~20)
|
|
),
|
|
),
|
|
|
|
onPressed: () {
|
|
handleSave();
|
|
// Navigator.pop(context);
|
|
},
|
|
child: Text(
|
|
"Submit",
|
|
style: GoogleFonts.poppins(fontSize: 12),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 15),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text('Files', style: _labelHeaderStyle),
|
|
ThemedSearchField(
|
|
hintText: 'Search',
|
|
backgroundColor: Colors.white,
|
|
// backgroundColor: Color(0xFFECECEC),
|
|
onChanged: filterData,
|
|
// onChanged: null,
|
|
controller: controllers['search']!,
|
|
txtwidth: MediaQuery.of(context).size.width * 0.15,
|
|
txtHeight: 30,
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 10),
|
|
|
|
Expanded(
|
|
child: isLoading
|
|
? const Center(child: CircularProgressIndicator())
|
|
: filteredIncentiveData.isEmpty
|
|
? const Center(child: Text("No files found"))
|
|
: LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final fileAreaWidth = constraints.maxWidth;
|
|
final crossAxisCount =
|
|
fileAreaWidth < 520 ? 1 : 2;
|
|
const spacing = 10.0;
|
|
final tileWidth = (fileAreaWidth -
|
|
spacing * (crossAxisCount - 1) -
|
|
16) /
|
|
crossAxisCount;
|
|
const targetTileHeight = 70.0;
|
|
final aspectRatio = (tileWidth / targetTileHeight)
|
|
.clamp(3.2, 12.0);
|
|
|
|
return GridView.builder(
|
|
padding: const EdgeInsets.all(8),
|
|
gridDelegate:
|
|
SliverGridDelegateWithFixedCrossAxisCount(
|
|
crossAxisCount: crossAxisCount,
|
|
crossAxisSpacing: spacing,
|
|
mainAxisSpacing: spacing,
|
|
childAspectRatio: aspectRatio,
|
|
),
|
|
itemCount: filteredIncentiveData.length,
|
|
itemBuilder: (context, index) {
|
|
final file = filteredIncentiveData[index];
|
|
return IncentiveFileRow(
|
|
fileName:
|
|
file['incentive_file_name'] ?? "Unknown",
|
|
date: file['incentive_month'] ?? "-",
|
|
onUpload: () {
|
|
print("Upload ${file['id']}");
|
|
final selectedId = file['id'];
|
|
final path =
|
|
'agent/downloadAgentIncentiveFile?id=$selectedId';
|
|
apiService.getPdfDownload(path, selectedId);
|
|
},
|
|
onDelete: () async {
|
|
print("Delete ${file['id']}");
|
|
final selectedId = file['id'];
|
|
final response = await apiService
|
|
.deleteAgentIncentiveFile(selectedId);
|
|
print("res - $response");
|
|
|
|
if (response['status'] == 'success') {
|
|
print("DELTED");
|
|
|
|
final data =
|
|
dataDetails(); // Map<String, dynamic>
|
|
final agentId = data["agent_id"];
|
|
|
|
getAgentIncenctiveFileList(agentId);
|
|
} else {
|
|
// Fluttertoast.showToast(
|
|
// msg: "Something went wrong",
|
|
// toastLength: Toast.LENGTH_SHORT,
|
|
// gravity: ToastGravity.BOTTOM,
|
|
// );
|
|
}
|
|
},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),);
|
|
}
|
|
}
|
|
|
|
final _labelStyle = GoogleFonts.poppins(
|
|
color: Colors.black,
|
|
fontWeight: FontWeight.w400,
|
|
fontSize: 12,
|
|
);
|
|
final _labelHeaderStyle = GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
);
|
|
|
|
class IncentiveFileRow extends StatelessWidget {
|
|
final String fileName;
|
|
final String date;
|
|
final VoidCallback? onUpload;
|
|
final VoidCallback? onDelete;
|
|
|
|
const IncentiveFileRow({
|
|
super.key,
|
|
required this.fileName,
|
|
required this.date,
|
|
this.onUpload,
|
|
this.onDelete,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: const Color(0xFFE3E3E3)),
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(6.0),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF4F6F8),
|
|
borderRadius: BorderRadius.circular(6.0),
|
|
border: Border.all(color: const Color(0xFFE3E3E3)),
|
|
),
|
|
child: const Icon(
|
|
Icons.file_present,
|
|
color: Color(0xFF838587),
|
|
size: 18,
|
|
),
|
|
),
|
|
const SizedBox(width: 10),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Text(
|
|
fileName,
|
|
style: GoogleFonts.poppins(
|
|
fontWeight: FontWeight.w400,
|
|
fontSize: 12,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
maxLines: 1,
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
date,
|
|
style: GoogleFonts.poppins(
|
|
color: const Color(0xFF6E6E6E),
|
|
fontSize: 10,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
onTap: onUpload,
|
|
borderRadius: BorderRadius.circular(20),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(6),
|
|
child: Icon(
|
|
Icons.file_download_outlined,
|
|
color: Color(0xFF6E6E6E),
|
|
size: 20,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Material(
|
|
color: Colors.transparent,
|
|
child: InkWell(
|
|
onTap: onDelete,
|
|
borderRadius: BorderRadius.circular(20),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(6),
|
|
child: Icon(
|
|
Icons.delete_outlined,
|
|
color: Color(0xFF6E6E6E),
|
|
size: 20,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|