991 lines
33 KiB
Dart
991 lines
33 KiB
Dart
import 'dart:convert';
|
|
// import 'dart:io' as html;
|
|
import 'dart:typed_data'; // Import for Uint8List
|
|
import 'package:dropdown_search/dropdown_search.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:nhance_partner/core/routing/routes.dart';
|
|
import 'package:nhance_partner/data/utils/toastNotification.dart';
|
|
import 'package:nhance_partner/presentation/themes/indicators/text_field_theme.dart';
|
|
|
|
import '../../../../core/config/env.dart';
|
|
import '../../../../core/services/api_service.dart';
|
|
import '../../../../data/services/auth_service.dart';
|
|
import '../../../../data/utils/validators.dart';
|
|
import '../../../layouts/main_layout.dart';
|
|
import '../../../layouts/responsive_layout.dart';
|
|
import '../../../providers/manager_provider.dart';
|
|
|
|
import 'package:universal_html/html.dart' as html;
|
|
|
|
import '../../../themes/indicators/input_field_decoration.dart';
|
|
|
|
class Staff extends ConsumerStatefulWidget {
|
|
final String? id;
|
|
final void Function(String value) onSubmit;
|
|
const Staff({super.key, this.id, required this.onSubmit});
|
|
@override
|
|
ConsumerState<Staff> createState() => StaffState();
|
|
}
|
|
|
|
class StaffState extends ConsumerState<Staff> {
|
|
final _formKey = GlobalKey<FormState>();
|
|
late ApiService apiService;
|
|
|
|
final GlobalKey<DropdownSearchState<Map<String, dynamic>>> dropDownKey =
|
|
GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
|
|
|
final GlobalKey<DropdownSearchState<Map<String, dynamic>>>
|
|
dropDownKeyHandler = GlobalKey<DropdownSearchState<Map<String, dynamic>>>();
|
|
|
|
List<String> tabHeader = ['name', 'email', 'mobile', 'code'];
|
|
late String isActive = "1";
|
|
html.File? passportFile;
|
|
String? selectedFileNames;
|
|
String? passportFileUrlFromApi;
|
|
String? selectedId;
|
|
String? _token;
|
|
bool isLoading = false;
|
|
bool showHandler = false;
|
|
|
|
String? selectedRole;
|
|
List<Map<String, dynamic>> filteredRolesData = [];
|
|
List<Map<String, dynamic>> getRolesData = [];
|
|
|
|
String? selectedHandler;
|
|
List<dynamic>? selectedHandlerIds = [];
|
|
List<Map<String, dynamic>> filteredHandlersData = [];
|
|
List<Map<String, dynamic>> getHandlersData = [];
|
|
|
|
Map<String, TextEditingController> controllers = {};
|
|
|
|
dynamic userId;
|
|
dynamic managerId;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
apiService = ApiService();
|
|
for (String field in tabHeader) {
|
|
controllers[field] = TextEditingController();
|
|
}
|
|
|
|
_initializeToken();
|
|
updateData();
|
|
getRole();
|
|
|
|
Future.microtask(() {
|
|
managerId = ref.watch(managerIdProvider);
|
|
userId = ref.watch(userIdProvider);
|
|
if (managerId != null) {
|
|
print('hansles');
|
|
getHandlers(managerId);
|
|
}
|
|
});
|
|
}
|
|
|
|
Map<String, dynamic> dataDetails() {
|
|
final data = {
|
|
"name": controllers["name"]?.text,
|
|
"email": controllers["email"]?.text,
|
|
"mobile": controllers["mobile"]?.text,
|
|
// "emp_id": controllers["code"]?.text,
|
|
"is_active": isActive,
|
|
"role_id": selectedRole,
|
|
// "handler_id": selectedHandler,
|
|
"handler_id": selectedHandlerIds,
|
|
// Always send mapped manager id:
|
|
// 1) Secondary Manager -> mapped Manager ID
|
|
// 2) All other roles -> mapped Manager ID
|
|
"manager_id": managerId,
|
|
};
|
|
return data;
|
|
}
|
|
|
|
Future<void> _initializeToken() async {
|
|
_token = await AuthService.getToken();
|
|
print("APISERTOKEN - $_token");
|
|
}
|
|
|
|
void updateData() async {
|
|
if (widget.id != null && widget.id != 'Create') {
|
|
dynamic response = await apiService.findSingleStaffData(widget.id!);
|
|
final data = response['data'];
|
|
print("updateData - ${response['data']}");
|
|
|
|
if (data == null) return;
|
|
setState(() {
|
|
selectedId = widget.id;
|
|
controllers['name']?.text = data['name'] ?? '';
|
|
controllers['email']?.text = data['email'] ?? '';
|
|
controllers['mobile']?.text = data['mobile'] ?? '';
|
|
selectedRole = data['role_id'] ?? '';
|
|
// selectedHandler = data['handler_id'] ?? '';
|
|
if (data['handler_id'] != null &&
|
|
data['handler_id'].toString().isNotEmpty) {
|
|
try {
|
|
// Decode only if it's a valid JSON array string
|
|
selectedHandlerIds = jsonDecode(data['handler_id']);
|
|
} catch (e) {
|
|
// Fallback: handle if it's not JSON (e.g., already a list)
|
|
if (data['handler_id'] is List) {
|
|
selectedHandlerIds = (data['handler_id'] as List)
|
|
.map((e) => e.toString())
|
|
.toList();
|
|
} else {
|
|
selectedHandlerIds = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
// controllers['code']?.text = data['emp_id'] ?? '';
|
|
isActive = data["is_active"];
|
|
|
|
String? apiDocPath = data["certificate_file_name"];
|
|
if (apiDocPath != null && apiDocPath.isNotEmpty) {
|
|
selectedFileNames = apiDocPath.split('/').last;
|
|
passportFileUrlFromApi = apiDocPath;
|
|
passportFile = null;
|
|
} else {
|
|
selectedFileNames = null;
|
|
passportFile = null;
|
|
passportFileUrlFromApi = null;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> handleSave() async {
|
|
if (!_formKey.currentState!.validate()) return;
|
|
|
|
final email = controllers['email']!.text.trim();
|
|
final phone = controllers['mobile']!.text.trim();
|
|
|
|
// 🔴 Conditional validation: at least one required
|
|
if (email.isEmpty && phone.isEmpty) {
|
|
ToastHelper.showSuccessToast(
|
|
context,
|
|
'Please enter either Email or Phone Number',
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Prevent submit without mapped manager id.
|
|
if (managerId == null || managerId.toString().trim().isEmpty) {
|
|
print('Manager mapping missing. managerId: $managerId, userId: $userId');
|
|
ToastHelper.showSuccessToast(
|
|
context,
|
|
'Manager is missing. Please map a manager and try again.',
|
|
);
|
|
return;
|
|
}
|
|
|
|
setState(() {
|
|
if (_formKey.currentState!.validate()) {
|
|
dataDetails();
|
|
final dataSet = dataDetails();
|
|
print("dataSetAgent - $dataSet");
|
|
createUserData(dataSet);
|
|
} else {
|
|
// isDisable = false;
|
|
}
|
|
});
|
|
}
|
|
|
|
Future<void> createUserData(data) async {
|
|
final bool isUpdating = widget.id != null && widget.id != 'Create';
|
|
final String? id = isUpdating ? widget.id : null;
|
|
final String apiUrldata;
|
|
|
|
apiUrldata = isUpdating
|
|
? '${Env.apiUrl}staff/updateStaff'
|
|
: '${Env.apiUrl}staff/createStaff';
|
|
|
|
// final token = await getToken(); // Fetch token
|
|
|
|
if (_token == null) {
|
|
throw Exception('Token not found. Please log in.');
|
|
}
|
|
|
|
if (isUpdating) {
|
|
data['id'] = id; // Add plan_id for update
|
|
data['updated_by'] = userId;
|
|
} else {
|
|
data['created_by'] = userId;
|
|
}
|
|
|
|
print("data------- $data}");
|
|
|
|
try {
|
|
final response = await http.post(
|
|
Uri.parse(apiUrldata),
|
|
headers: {
|
|
'Authorization': 'Bearer $_token',
|
|
'Content-Type': 'application/json',
|
|
'app-signature': Env.App_Signature,
|
|
},
|
|
body: jsonEncode(data), // Convert map to JSON
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
print("Staff submitted successfully!");
|
|
print("Response: ${response.body}");
|
|
|
|
isUpdating
|
|
? ToastHelper.showSuccessToast(
|
|
context,
|
|
'Staff Updated Successfully',
|
|
)
|
|
: ToastHelper.showSuccessToast(
|
|
context,
|
|
'Staff Created Successfully',
|
|
);
|
|
|
|
Navigator.of(context).pop();
|
|
widget.onSubmit("success");
|
|
// context.go(AppRoutes.staffLst);
|
|
} else if (response.statusCode == 403) {
|
|
await apiService.clearLocalStorageAndRedirect();
|
|
} else {
|
|
final responseBody = jsonDecode(response.body);
|
|
dynamic msg = responseBody['data'];
|
|
|
|
print("Failed to submit plan. Status: ${response.statusCode}");
|
|
print("Error: ${response.body}");
|
|
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: Text("Staff User Creation Failed"),
|
|
content: Text(msg),
|
|
actions: [
|
|
TextButton(
|
|
child: Text("OK"),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
} catch (e) {
|
|
print(" Error submitting Staff: $e");
|
|
}
|
|
}
|
|
|
|
Future<void> getRole() async {
|
|
print('Handlers called');
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final response = await apiService.fetchMasterDropDown('staffRole');
|
|
|
|
if (response['status'] == 200) {
|
|
print('getRole - ${response['data']}');
|
|
setState(() {
|
|
getRolesData = List<Map<String, dynamic>>.from(response['data']);
|
|
print('API Data - $getRolesData');
|
|
|
|
filteredRolesData = List.from(getRolesData);
|
|
print('originalData - $filteredRolesData');
|
|
});
|
|
} else {
|
|
getRolesData = [];
|
|
filteredRolesData = [];
|
|
}
|
|
} catch (e) {
|
|
print('Exception occurred: $e');
|
|
} finally {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> getHandlers(managerId) async {
|
|
print('Handlers called');
|
|
setState(() {
|
|
isLoading = true;
|
|
});
|
|
|
|
try {
|
|
final response = await apiService.fetchHandlerNameDropDown(managerId);
|
|
|
|
if (response['code'] == 200 || response['status'] == 'success') {
|
|
print('getHandlers -');
|
|
print('getHandlers - ${response['data']}');
|
|
setState(() {
|
|
getHandlersData = List<Map<String, dynamic>>.from(response['data']);
|
|
print('API HandlersData - $getHandlersData');
|
|
|
|
filteredHandlersData = List.from(getHandlersData);
|
|
print('HandlersoriginalData - $filteredHandlersData');
|
|
});
|
|
} else {
|
|
getHandlersData = [];
|
|
filteredHandlersData = [];
|
|
}
|
|
} catch (e) {
|
|
print('Exception occurred: $e');
|
|
} finally {
|
|
setState(() {
|
|
isLoading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
for (var controller in controllers.values) {
|
|
controller.dispose();
|
|
}
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SelectionArea(
|
|
child:AlertDialog(
|
|
backgroundColor: Colors.white,
|
|
content: Container(
|
|
// color: Colors.yellow.shade50,
|
|
width: MediaQuery.of(context).size.width * 0.62,
|
|
height: MediaQuery.of(context).size.height * 0.65,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
// height: 30,
|
|
// color: Colors.red.shade50,
|
|
width: MediaQuery.of(context).size.width,
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
context.go(AppRoutes.staffLst);
|
|
},
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
// Tooltip(
|
|
// message: 'Back',
|
|
// child: IconButton(
|
|
// icon: const Icon(
|
|
// Icons.arrow_left_sharp,
|
|
// size: 25,
|
|
// color: Color(0xFF425B5B),
|
|
// ),
|
|
// onPressed: () {
|
|
// context.go(AppRoutes.staffLst);
|
|
// },
|
|
// splashRadius: 28,
|
|
// hoverColor: Colors.black12,
|
|
// padding: const EdgeInsets.all(8),
|
|
// constraints: const BoxConstraints(),
|
|
// ),
|
|
// ),
|
|
const SizedBox(width: 5), // spacing between icon and text
|
|
Text(
|
|
"Staff",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
Spacer(),
|
|
GestureDetector(
|
|
onTap: () => Navigator.pop(context),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(5.0),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFFF1F1F1),
|
|
borderRadius: BorderRadius.circular(5.0),
|
|
),
|
|
child: Tooltip(
|
|
message: 'Close',
|
|
child: const Icon(Icons.close, size: 18),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
// SizedBox(height: 10),
|
|
Expanded(
|
|
child: Container(
|
|
// color: Colors.green,
|
|
width: MediaQuery.of(context).size.width,
|
|
margin: EdgeInsets.all(8.0),
|
|
padding: EdgeInsets.symmetric(horizontal: 14.0, vertical: 20.0),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8.0),
|
|
color: Colors.white,
|
|
// color: Color(0xFFEDF6F5),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
Row(children: [Expanded(child: buildFormFields())]),
|
|
Spacer(),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
handleSave();
|
|
},
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 45.0,
|
|
vertical: 8,
|
|
),
|
|
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8.0),
|
|
color: Color(0xFF2E7D6E),
|
|
// color: Color(0xFF425B5B),
|
|
),
|
|
child: Text(
|
|
'Save',
|
|
style: GoogleFonts.poppins(
|
|
color: Colors.white,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 30),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
|
|
SizedBox(height: 10),
|
|
],
|
|
),
|
|
),
|
|
),);
|
|
}
|
|
|
|
Widget buildFormFields() {
|
|
return Form(
|
|
key: _formKey,
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
buildName(),
|
|
SizedBox(width: 25),
|
|
buildEmail(),
|
|
SizedBox(width: 25),
|
|
buildPhNumber(),
|
|
],
|
|
),
|
|
SizedBox(height: 20),
|
|
Row(
|
|
children: [
|
|
buildRole(context),
|
|
SizedBox(width: 25),
|
|
if (showHandler) ...[
|
|
Row(
|
|
children: [
|
|
buildHandler(context),
|
|
SizedBox(width: 25),
|
|
SizedBox.shrink(),
|
|
],
|
|
),
|
|
],
|
|
],
|
|
),
|
|
|
|
// Row(
|
|
// children: [
|
|
// Expanded(child: buildAddress()),
|
|
// SizedBox(width: 25),
|
|
// Expanded(child: SizedBox.shrink()),
|
|
// ],
|
|
// ),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildName() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("Full Name *", style: _textStyle),
|
|
SizedBox(height: 10),
|
|
ThemedFormField(
|
|
borderColor: Color(0xFFE2E8F0),
|
|
highlightColor: Color(0xFF50A398),
|
|
controller: controllers['name']!,
|
|
validator: (value) => Validators.requiredField(value, "name"),
|
|
txtwidth: MediaQuery.of(context).size.width * 0.18,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildEmail() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("Email *", style: _textStyle),
|
|
SizedBox(height: 10),
|
|
ThemedFormField(
|
|
controller: controllers['email']!,
|
|
borderColor: Color(0xFFE2E8F0),
|
|
highlightColor: Color(0xFF50A398),
|
|
|
|
// validator: (value) => Validators.email(value, "email"),
|
|
validator: (value) {
|
|
final phone = controllers['mobile']!.text.trim();
|
|
|
|
if (value!.isEmpty && phone.isNotEmpty) {
|
|
return null; // phone is given → email not required
|
|
}
|
|
|
|
return Validators.email(value, "email");
|
|
},
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9_@.]')),
|
|
],
|
|
txtwidth: MediaQuery.of(context).size.width * 0.18,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildPhNumber() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("Phone Number *", style: _textStyle),
|
|
SizedBox(height: 10),
|
|
ThemedFormField(
|
|
controller: controllers['mobile']!,
|
|
borderColor: Color(0xFFE2E8F0),
|
|
highlightColor: Color(0xFF50A398),
|
|
|
|
// validator: (value) => Validators.phone(value, "phNumber"),
|
|
validator: (value) {
|
|
final email = controllers['email']!.text.trim();
|
|
|
|
if (value!.isEmpty && email.isNotEmpty) {
|
|
return null; // email is given → phone not required
|
|
}
|
|
|
|
return Validators.phone(value, "phNumber");
|
|
},
|
|
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
|
|
],
|
|
txtwidth: MediaQuery.of(context).size.width * 0.18,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildId() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("Staff Id ", style: _textStyle),
|
|
SizedBox(height: 10),
|
|
ThemedFormField(
|
|
controller: controllers['code']!,
|
|
borderColor: Color(0xFFE2E8F0),
|
|
highlightColor: Color(0xFF50A398),
|
|
// validator: (value) => Validators.requiredField(value, "id"),
|
|
txtwidth: MediaQuery.of(context).size.width * 0.18,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// Widget buildAddress() {
|
|
// return Row(
|
|
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
// children: [
|
|
// Text("Role*", style: _textStyle),
|
|
// SizedBox(width: 10),
|
|
// ThemedFormField(
|
|
// controller: controllers['address']!,
|
|
//
|
|
// txtwidth: MediaQuery.of(context).size.width * 0.18,
|
|
// ),
|
|
// ],
|
|
// );
|
|
// }
|
|
|
|
Widget buildRole(BuildContext context) {
|
|
Map<String, dynamic>? selectedroleVal = filteredRolesData.firstWhere(
|
|
(item) => item['id'].toString() == selectedRole,
|
|
orElse: () => {},
|
|
);
|
|
|
|
if (selectedroleVal.isNotEmpty &&
|
|
selectedroleVal['role'] != null &&
|
|
selectedroleVal['role'] == 'Staff') {
|
|
showHandler = true;
|
|
} else {
|
|
selectedHandlerIds = [];
|
|
showHandler = false;
|
|
}
|
|
|
|
final isReadOnly = widget.id != null && widget.id != 'Create';
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("Role *", style: _textStyle),
|
|
SizedBox(height: 10),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
// color: Colors.white,
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
),
|
|
width: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.18,
|
|
// height: 40,
|
|
child: AbsorbPointer(
|
|
absorbing: isReadOnly,
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
key: dropDownKey,
|
|
selectedItem: selectedroleVal.isNotEmpty ? selectedroleVal : null,
|
|
items: (filter, infiniteScrollProps) {
|
|
return filteredRolesData;
|
|
},
|
|
|
|
itemAsString: (val) => val['role'].toString(), // what to show
|
|
compareFn: (item, selectedItem) =>
|
|
item['id'] == selectedItem['id'], // ✅ compare by id
|
|
validator: (val) {
|
|
if (val == null) {
|
|
return "Required"; // ✅ error message
|
|
}
|
|
return null;
|
|
},
|
|
decoratorProps: DropDownDecoratorProps(
|
|
decoration:
|
|
AppInputDecorations.dropdownDecoration(
|
|
label: "Select Role",
|
|
).copyWith(
|
|
filled: true,
|
|
fillColor:
|
|
Colors.white, // 👈 makes the dropdown input white
|
|
isDense: true,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(5),
|
|
borderSide: const BorderSide(
|
|
color: Color(0xFFE2E8F0),
|
|
width: 1,
|
|
),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(5),
|
|
borderSide: const BorderSide(
|
|
color: Color(0xFFE2E8F0),
|
|
width: 1,
|
|
),
|
|
),
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 6,
|
|
),
|
|
),
|
|
),
|
|
|
|
popupProps: PopupProps.menu(
|
|
fit: FlexFit.loose,
|
|
constraints: BoxConstraints(maxHeight: 250),
|
|
menuProps: MenuProps(
|
|
backgroundColor:
|
|
Colors.white, // 👈 sets dropdown background to white
|
|
),
|
|
showSearchBox: true,
|
|
|
|
searchFieldProps: TextFieldProps(
|
|
autofocus: true,
|
|
decoration: InputDecoration(
|
|
contentPadding: EdgeInsets.all(3),
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
hintText: "Select Role...",
|
|
hintStyle: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
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
|
|
),
|
|
enabled: widget.id == null,
|
|
),
|
|
),
|
|
// constraints: BoxConstraints(),
|
|
),
|
|
|
|
onChanged: widget.id != null && widget.id != 'Create'
|
|
? null
|
|
: (val) {
|
|
if (val != null) {
|
|
print("Selected Role : ${val['role']}");
|
|
print("Id: ${val['id']}");
|
|
selectedRole = val['id'];
|
|
print('selectedRole - ${val['role']}');
|
|
if (val['role'] == 'Staff') {
|
|
setState(() {
|
|
showHandler = true;
|
|
});
|
|
} else {
|
|
setState(() {
|
|
showHandler = false;
|
|
});
|
|
}
|
|
|
|
print('showHandler - $showHandler');
|
|
|
|
// controllers['agentId']?.text = val['agent_code'];
|
|
// agentId = agent['id'];
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildHandler(BuildContext context) {
|
|
Map<String, dynamic>? selectedHandlered = filteredHandlersData.firstWhere(
|
|
(item) => item['id'].toString() == selectedHandler,
|
|
orElse: () => {},
|
|
);
|
|
// final isReadOnly = widget.id != null;
|
|
final isReadOnly = false;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("Handler Name *", style: _textStyle),
|
|
SizedBox(height: 10),
|
|
Container(
|
|
decoration: BoxDecoration(
|
|
// color: Colors.white,
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
),
|
|
width: ResponsiveLayout.isMobile(context)
|
|
? null
|
|
: MediaQuery.of(context).size.width * 0.18,
|
|
// height: 40,
|
|
child: AbsorbPointer(
|
|
absorbing: isReadOnly,
|
|
// child: DropdownSearch<Map<String, dynamic>>(
|
|
child: DropdownSearch<Map<String, dynamic>>.multiSelection(
|
|
key: dropDownKeyHandler,
|
|
|
|
// selectedItem: selectedHandlered.isNotEmpty
|
|
// ? selectedHandlered
|
|
// : null,
|
|
selectedItems: filteredHandlersData
|
|
.where(
|
|
(item) => (selectedHandlerIds ?? []).contains(item['id']),
|
|
)
|
|
.toList(),
|
|
items: (filter, infiniteScrollProps) {
|
|
return filteredHandlersData;
|
|
},
|
|
|
|
itemAsString: (val) => val['name'].toString(), // what to show
|
|
compareFn: (item, selectedItem) =>
|
|
item['id'] == selectedItem['id'], // ✅ compare by id
|
|
// validator: (val) {
|
|
// if (val == null) {
|
|
// return "Required"; // ✅ error message
|
|
// }
|
|
// return null;
|
|
// },
|
|
validator: (val) {
|
|
if (val == null || val.isEmpty) {
|
|
return "Required";
|
|
}
|
|
return null;
|
|
},
|
|
decoratorProps: DropDownDecoratorProps(
|
|
decoration:
|
|
AppInputDecorations.dropdownDecoration(
|
|
label: "Select Handler",
|
|
).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: PopupPropsMultiSelection.menu(
|
|
fit: FlexFit.loose,
|
|
constraints: BoxConstraints(maxHeight: 250),
|
|
showSearchBox: true,
|
|
menuProps: MenuProps(backgroundColor: Colors.white),
|
|
|
|
searchFieldProps: TextFieldProps(
|
|
autofocus: true,
|
|
decoration: InputDecoration(
|
|
filled: true,
|
|
fillColor: Colors.white,
|
|
hintText: "Search Handler...",
|
|
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.5,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// checkBoxBuilder: (context, item, isSelected, isDisabled) {
|
|
// return Icon(
|
|
// isSelected
|
|
// ? Icons.check_box
|
|
// : Icons.check_box_outline_blank,
|
|
// color: isSelected ? Color(0xFFE26728) : Colors.red,
|
|
// );
|
|
// },
|
|
// validationBuilder: (context, selectedItems) {
|
|
// return Padding(
|
|
// padding: const EdgeInsets.symmetric(
|
|
// vertical: 8.0,
|
|
// horizontal: 8.0,
|
|
// ),
|
|
// child: Row(
|
|
// mainAxisAlignment: MainAxisAlignment.end,
|
|
// children: [
|
|
// ElevatedButton(
|
|
// style: ElevatedButton.styleFrom(
|
|
// backgroundColor: Color(0xFF425B5B),
|
|
// foregroundColor: Colors.white,
|
|
// shape: RoundedRectangleBorder(
|
|
// borderRadius: BorderRadius.circular(8),
|
|
// ),
|
|
// minimumSize: Size(70, 36),
|
|
// ),
|
|
// onPressed: () => Navigator.pop(context),
|
|
// child: const Text("OK"),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// );
|
|
// },
|
|
),
|
|
onChanged: (List<Map<String, dynamic>> selectedVals) {
|
|
selectedHandlerIds = selectedVals
|
|
.map((v) => v['id'].toString())
|
|
.toList();
|
|
print("Selected Handler IDs: $selectedHandlerIds");
|
|
},
|
|
// popupProps: PopupProps.menu(
|
|
// fit: FlexFit.loose,
|
|
// constraints: BoxConstraints(maxHeight: 250),
|
|
// menuProps: MenuProps(
|
|
// backgroundColor:
|
|
// Colors.white, // 👈 sets dropdown background to white
|
|
// ),
|
|
// showSearchBox: true,
|
|
// searchFieldProps: TextFieldProps(
|
|
// decoration: InputDecoration(
|
|
// filled: true,
|
|
// fillColor: Colors.white,
|
|
// hintText: "Search Handler...",
|
|
// enabledBorder: OutlineInputBorder(
|
|
// borderSide: BorderSide(
|
|
// color: Colors.white,
|
|
// ), // 👈 Normal border
|
|
// ),
|
|
// focusedBorder: OutlineInputBorder(
|
|
// borderSide: BorderSide(
|
|
// color: Colors.white,
|
|
// width: 1.5,
|
|
// ), // 👈 Focused border
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// // constraints: BoxConstraints(),
|
|
// ),
|
|
//
|
|
// onChanged: (val) {
|
|
// if (val != null) {
|
|
// print("Selected Handler : ${val['name']}");
|
|
// print("Id: ${val['id']}");
|
|
// selectedHandler = val['id'];
|
|
// // controllers['agentId']?.text = val['agent_code'];
|
|
// // agentId = agent['id'];
|
|
// }
|
|
// },
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
static final _textStyle = GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
);
|
|
}
|