938 lines
31 KiB
Dart
938 lines
31 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 SalesExecutive extends ConsumerStatefulWidget {
|
|
final String? id;
|
|
final void Function(String value) onSubmit;
|
|
const SalesExecutive({super.key, this.id, required this.onSubmit});
|
|
@override
|
|
ConsumerState<SalesExecutive> createState() => SalesExecutiveState();
|
|
}
|
|
|
|
class SalesExecutiveState extends ConsumerState<SalesExecutive> {
|
|
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', 'address'];
|
|
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 = [];
|
|
|
|
List<dynamic>? selectedHandlerIds = [];
|
|
String? selectedHandler;
|
|
List<Map<String, dynamic>> filteredHandlersData = [];
|
|
List<Map<String, dynamic>> getHandlersData = [];
|
|
|
|
Map<String, TextEditingController> controllers = {};
|
|
|
|
dynamic userId;
|
|
dynamic managerId;
|
|
|
|
Map<String, dynamic> dataDetails() {
|
|
final data = {
|
|
"name": controllers["name"]?.text,
|
|
"email": controllers["email"]?.text,
|
|
"mobile": controllers["mobile"]?.text,
|
|
"address": controllers["address"]?.text,
|
|
// "emp_id": controllers["code"]?.text,
|
|
"is_active": isActive,
|
|
// "handler_id": selectedHandler,
|
|
"manager_id": managerId,
|
|
};
|
|
return data;
|
|
}
|
|
|
|
@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);
|
|
}
|
|
});
|
|
}
|
|
|
|
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.findSingleSalesExecutiveData(
|
|
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'] ?? '';
|
|
controllers['address']?.text = data['address'] ?? '';
|
|
|
|
// controllers['code']?.text = data['emp_id'] ?? '';
|
|
isActive = data["is_active"];
|
|
});
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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}salesExecutive/updateExecutive'
|
|
: '${Env.apiUrl}salesExecutive/createExecutive';
|
|
|
|
// 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("SalesExecutive submitted successfully!");
|
|
print("Response: ${response.body}");
|
|
|
|
isUpdating
|
|
? ToastHelper.showSuccessToast(
|
|
context,
|
|
'SalesExecutive Updated Successfully',
|
|
)
|
|
: ToastHelper.showSuccessToast(
|
|
context,
|
|
'SalesExecutive Created Successfully',
|
|
);
|
|
Navigator.of(context).pop();
|
|
widget.onSubmit("success");
|
|
// context.go(AppRoutes.salesExecutiveLst);
|
|
} 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("SalesExecutive User Creation Failed"),
|
|
content: Text(msg),
|
|
actions: [
|
|
TextButton(
|
|
child: Text("OK"),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
} catch (e) {
|
|
print(" Error submitting SalesExecutive: $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.42,
|
|
height: MediaQuery.of(context).size.height * 0.55,
|
|
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.salesExecutiveLst);
|
|
// },
|
|
// splashRadius: 28,
|
|
// hoverColor: Colors.black12,
|
|
// padding: const EdgeInsets.all(8),
|
|
// constraints: const BoxConstraints(),
|
|
// ),
|
|
// ),
|
|
const SizedBox(width: 5), // spacing between icon and text
|
|
Text(
|
|
"Sales Executive",
|
|
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(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
Row(children: [buildName(), SizedBox(width: 25), buildEmail()]),
|
|
SizedBox(height: 20),
|
|
Row(children: [buildPhNumber(), SizedBox(width: 25), buildAddress()]),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildName() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("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 buildAddress() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("Address", style: _textStyle),
|
|
SizedBox(height: 10),
|
|
ThemedFormField(
|
|
controller: controllers['address']!,
|
|
borderColor: Color(0xFFE2E8F0),
|
|
highlightColor: Color(0xFF50A398),
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(RegExp(r'[ a-zA-Z0-9_#/.]')),
|
|
],
|
|
txtwidth: MediaQuery.of(context).size.width * 0.18,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildId() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("SalesExecutive Id ", style: _textStyle),
|
|
SizedBox(width: 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'] == 'SalesExecutive') {
|
|
showHandler = true;
|
|
} else {
|
|
selectedHandlerIds = [];
|
|
showHandler = false;
|
|
}
|
|
|
|
final isReadOnly = widget.id != null;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text("Role *", style: _textStyle),
|
|
SizedBox(width: 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: "Search 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
|
|
? null
|
|
: (val) {
|
|
if (val != null) {
|
|
print("Selected Role : ${val['role']}");
|
|
print("Id: ${val['id']}");
|
|
selectedRole = val['id'];
|
|
if (val['role'] == 'SalesExecutive') {
|
|
setState(() {
|
|
showHandler = true;
|
|
});
|
|
} else {
|
|
setState(() {
|
|
showHandler = false;
|
|
});
|
|
}
|
|
|
|
// 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(width: 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,
|
|
);
|
|
}
|