|
|
|
@ -26,6 +26,9 @@ class _FleetEmployeesState extends State<FleetEmployees> {
|
|
|
|
final _mobileCtrl = TextEditingController();
|
|
|
|
final _mobileCtrl = TextEditingController();
|
|
|
|
final _altMobileCtrl = TextEditingController();
|
|
|
|
final _altMobileCtrl = TextEditingController();
|
|
|
|
final _locationCtrl = TextEditingController();
|
|
|
|
final _locationCtrl = TextEditingController();
|
|
|
|
|
|
|
|
final _experienceController = TextEditingController();
|
|
|
|
|
|
|
|
final _licenseController = TextEditingController();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final List<String> licenseNumbers = [
|
|
|
|
final List<String> licenseNumbers = [
|
|
|
|
"UP3220050012345",
|
|
|
|
"UP3220050012345",
|
|
|
|
@ -91,10 +94,7 @@ class _FleetEmployeesState extends State<FleetEmployees> {
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> _addDriver() async {
|
|
|
|
Future<void> _addDriver() async {
|
|
|
|
if (!(_formKey.currentState?.validate() ?? false)) return;
|
|
|
|
if (!(_formKey.currentState?.validate() ?? false)) return;
|
|
|
|
if (selectedLicense == null || selectedExperience == null) {
|
|
|
|
|
|
|
|
AppSettings.longFailedToast("Select License & Experience");
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var payload = {
|
|
|
|
var payload = {
|
|
|
|
"Name": _nameCtrl.text.trim(),
|
|
|
|
"Name": _nameCtrl.text.trim(),
|
|
|
|
@ -373,6 +373,7 @@ class _FleetEmployeesState extends State<FleetEmployees> {
|
|
|
|
controller: _nameCtrl,
|
|
|
|
controller: _nameCtrl,
|
|
|
|
validator: (v) => _required(v, field: "Driver Name"),
|
|
|
|
validator: (v) => _required(v, field: "Driver Name"),
|
|
|
|
textCapitalization: TextCapitalization.none,
|
|
|
|
textCapitalization: TextCapitalization.none,
|
|
|
|
|
|
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
|
|
inputFormatters: const [
|
|
|
|
inputFormatters: const [
|
|
|
|
FirstCharUppercaseFormatter(), // << live first-letter caps
|
|
|
|
FirstCharUppercaseFormatter(), // << live first-letter caps
|
|
|
|
],
|
|
|
|
],
|
|
|
|
@ -386,81 +387,105 @@ class _FleetEmployeesState extends State<FleetEmployees> {
|
|
|
|
),
|
|
|
|
),
|
|
|
|
_LabeledField(
|
|
|
|
_LabeledField(
|
|
|
|
label: "Driver License Number *",
|
|
|
|
label: "Driver License Number *",
|
|
|
|
child: DropdownButtonFormField<String>(
|
|
|
|
child: TextFormField(
|
|
|
|
value: selectedLicense,
|
|
|
|
controller: _licenseController, // create controller
|
|
|
|
dropdownColor: Colors.white,
|
|
|
|
|
|
|
|
items: licenseNumbers
|
|
|
|
|
|
|
|
.map((t) =>
|
|
|
|
|
|
|
|
DropdownMenuItem(value: t, child: Text(t)))
|
|
|
|
|
|
|
|
.toList(),
|
|
|
|
|
|
|
|
onChanged: (v) => setState(() => selectedLicense = v),
|
|
|
|
|
|
|
|
validator: (v) => v == null || v.isEmpty
|
|
|
|
|
|
|
|
? "Driver License required"
|
|
|
|
|
|
|
|
: null,
|
|
|
|
|
|
|
|
isExpanded: true,
|
|
|
|
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
|
|
|
|
hint: Text(
|
|
|
|
|
|
|
|
"Select License Number",
|
|
|
|
|
|
|
|
style: fontTextStyle(
|
|
|
|
|
|
|
|
14, const Color(0xFF939495), FontWeight.w400),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
icon: Image.asset('images/downarrow.png',
|
|
|
|
|
|
|
|
width: 16, height: 16),
|
|
|
|
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
border: OutlineInputBorder(),
|
|
|
|
border: OutlineInputBorder(),
|
|
|
|
isDense: false,
|
|
|
|
hintText: "Enter License Number",
|
|
|
|
contentPadding: EdgeInsets.symmetric(
|
|
|
|
|
|
|
|
horizontal: 12, vertical: 14),
|
|
|
|
contentPadding:
|
|
|
|
|
|
|
|
EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
|
|
|
|
|
|
style: fontTextStyle(
|
|
|
|
|
|
|
|
14, const Color(0xFF2A2A2A), FontWeight.w400),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validator: (value) {
|
|
|
|
|
|
|
|
if (value == null || value.trim().isEmpty) {
|
|
|
|
|
|
|
|
return "Driver License required";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
_LabeledField(
|
|
|
|
_LabeledField(
|
|
|
|
label: "Years of Experience *",
|
|
|
|
label: "Years of Experience *",
|
|
|
|
child: DropdownButtonFormField<String>(
|
|
|
|
child: TextFormField(
|
|
|
|
value: selectedExperience,
|
|
|
|
controller: _experienceController, // create controller
|
|
|
|
dropdownColor: Colors.white,
|
|
|
|
keyboardType: TextInputType.number,
|
|
|
|
items: yearOptions
|
|
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
|
|
.map((t) =>
|
|
|
|
|
|
|
|
DropdownMenuItem(value: t, child: Text(t)))
|
|
|
|
|
|
|
|
.toList(),
|
|
|
|
|
|
|
|
onChanged: (v) =>
|
|
|
|
|
|
|
|
setState(() => selectedExperience = v),
|
|
|
|
|
|
|
|
validator: (v) => v == null || v.isEmpty
|
|
|
|
|
|
|
|
? "Experience is required"
|
|
|
|
|
|
|
|
: null,
|
|
|
|
|
|
|
|
isExpanded: true,
|
|
|
|
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
|
|
|
|
hint: Text(
|
|
|
|
|
|
|
|
"Years",
|
|
|
|
|
|
|
|
style: fontTextStyle(
|
|
|
|
|
|
|
|
14, const Color(0xFF939495), FontWeight.w400),
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
icon: Image.asset('images/downarrow.png',
|
|
|
|
|
|
|
|
width: 16, height: 16),
|
|
|
|
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
border: OutlineInputBorder(),
|
|
|
|
border: OutlineInputBorder(),
|
|
|
|
isDense: false,
|
|
|
|
hintText: "Enter Years",
|
|
|
|
contentPadding: EdgeInsets.symmetric(
|
|
|
|
contentPadding:
|
|
|
|
horizontal: 12, vertical: 14),
|
|
|
|
EdgeInsets.symmetric(horizontal: 12, vertical: 14),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
style: fontTextStyle(
|
|
|
|
|
|
|
|
14, const Color(0xFF2A2A2A), FontWeight.w400),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validator: (value) {
|
|
|
|
|
|
|
|
if (value == null || value.trim().isEmpty) {
|
|
|
|
|
|
|
|
return "Experience is required";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
_LabeledField(
|
|
|
|
_LabeledField(
|
|
|
|
label: "Phone Number *",
|
|
|
|
label: "Phone Number *",
|
|
|
|
child: TextFormField(
|
|
|
|
child: TextFormField(
|
|
|
|
controller: _mobileCtrl,
|
|
|
|
controller: _mobileCtrl,
|
|
|
|
validator: (v) => _validatePhone(v),
|
|
|
|
|
|
|
|
keyboardType: TextInputType.phone,
|
|
|
|
keyboardType: TextInputType.phone,
|
|
|
|
|
|
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
|
|
inputFormatters: [
|
|
|
|
inputFormatters: [
|
|
|
|
FilteringTextInputFormatter.digitsOnly,
|
|
|
|
FilteringTextInputFormatter.digitsOnly,
|
|
|
|
LengthLimitingTextInputFormatter(10),
|
|
|
|
LengthLimitingTextInputFormatter(10),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Restrict first digit 6-9
|
|
|
|
|
|
|
|
TextInputFormatter.withFunction(
|
|
|
|
|
|
|
|
(oldValue, newValue) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (newValue.text.isEmpty) {
|
|
|
|
|
|
|
|
return newValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// First digit must be 6-9
|
|
|
|
|
|
|
|
if (!RegExp(r'^[6-9]').hasMatch(newValue.text)) {
|
|
|
|
|
|
|
|
return oldValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return newValue;
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validator: (value) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (value == null || value.isEmpty) {
|
|
|
|
|
|
|
|
return "Phone Number required";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!RegExp(r'^[6-9]').hasMatch(value)) {
|
|
|
|
|
|
|
|
return "Enter digits starting 6,7,8,9";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (value.length != 10) {
|
|
|
|
|
|
|
|
return "Enter valid 10 digit number";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
decoration: InputDecoration(
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: "Mobile Number",
|
|
|
|
hintText: "Mobile Number",
|
|
|
|
hintStyle: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
|
|
|
|
hintStyle: fontTextStyle(
|
|
|
|
border: OutlineInputBorder(),
|
|
|
|
14, const Color(0xFF939495), FontWeight.w400),
|
|
|
|
|
|
|
|
border: const OutlineInputBorder(),
|
|
|
|
isDense: true,
|
|
|
|
isDense: true,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
textInputAction: TextInputAction.next,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
_LabeledField(
|
|
|
|
_LabeledField(
|
|
|
|
@ -468,9 +493,27 @@ class _FleetEmployeesState extends State<FleetEmployees> {
|
|
|
|
child: TextFormField(
|
|
|
|
child: TextFormField(
|
|
|
|
controller: _altMobileCtrl,
|
|
|
|
controller: _altMobileCtrl,
|
|
|
|
keyboardType: TextInputType.phone,
|
|
|
|
keyboardType: TextInputType.phone,
|
|
|
|
|
|
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
|
|
inputFormatters: [
|
|
|
|
inputFormatters: [
|
|
|
|
FilteringTextInputFormatter.digitsOnly,
|
|
|
|
FilteringTextInputFormatter.digitsOnly,
|
|
|
|
LengthLimitingTextInputFormatter(10),
|
|
|
|
LengthLimitingTextInputFormatter(10),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Restrict first digit 6-9
|
|
|
|
|
|
|
|
TextInputFormatter.withFunction(
|
|
|
|
|
|
|
|
(oldValue, newValue) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (newValue.text.isEmpty) {
|
|
|
|
|
|
|
|
return newValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// First digit must be 6-9
|
|
|
|
|
|
|
|
if (!RegExp(r'^[6-9]').hasMatch(newValue.text)) {
|
|
|
|
|
|
|
|
return oldValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return newValue;
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
decoration: InputDecoration(
|
|
|
|
decoration: InputDecoration(
|
|
|
|
hintText: "Mobile Number",
|
|
|
|
hintText: "Mobile Number",
|
|
|
|
@ -486,6 +529,7 @@ class _FleetEmployeesState extends State<FleetEmployees> {
|
|
|
|
controller: _locationCtrl,
|
|
|
|
controller: _locationCtrl,
|
|
|
|
validator: (v) => _required(v, field: "Location"),
|
|
|
|
validator: (v) => _required(v, field: "Location"),
|
|
|
|
textCapitalization: TextCapitalization.none,
|
|
|
|
textCapitalization: TextCapitalization.none,
|
|
|
|
|
|
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
|
|
inputFormatters: const [
|
|
|
|
inputFormatters: const [
|
|
|
|
FirstCharUppercaseFormatter(), // << live first-letter caps
|
|
|
|
FirstCharUppercaseFormatter(), // << live first-letter caps
|
|
|
|
],
|
|
|
|
],
|
|
|
|
@ -511,6 +555,7 @@ class _FleetEmployeesState extends State<FleetEmployees> {
|
|
|
|
validator: (v) => v == null || v.isEmpty ? "Status is required" : null,
|
|
|
|
validator: (v) => v == null || v.isEmpty ? "Status is required" : null,
|
|
|
|
isExpanded: true,
|
|
|
|
isExpanded: true,
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
alignment: Alignment.centerLeft,
|
|
|
|
|
|
|
|
autovalidateMode: AutovalidateMode.onUserInteraction,
|
|
|
|
hint: Text(
|
|
|
|
hint: Text(
|
|
|
|
"Select status",
|
|
|
|
"Select status",
|
|
|
|
style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
|
|
|
|
style: fontTextStyle(14, const Color(0xFF939495), FontWeight.w400),
|
|
|
|
@ -525,12 +570,36 @@ class _FleetEmployeesState extends State<FleetEmployees> {
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
ElevatedButton(
|
|
|
|
ElevatedButton(
|
|
|
|
onPressed: _addDriver,
|
|
|
|
onPressed: () {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (_formKey.currentState!.validate()) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_addDriver();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reset form validation
|
|
|
|
|
|
|
|
_formKey.currentState!.reset();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ Clear TextFields
|
|
|
|
|
|
|
|
_mobileCtrl.clear();
|
|
|
|
|
|
|
|
_licenseController.clear(); // Driver License
|
|
|
|
|
|
|
|
_experienceController.clear();
|
|
|
|
|
|
|
|
_locationCtrl.clear(); // Location field
|
|
|
|
|
|
|
|
_nameCtrl.clear();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ Reset variables (if any)
|
|
|
|
|
|
|
|
selectedLicense = null;
|
|
|
|
|
|
|
|
selectedExperience = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setState(() {});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
backgroundColor: const Color(0xFF8270DB),
|
|
|
|
backgroundColor: const Color(0xFF8270DB),
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
foregroundColor: Colors.white,
|
|
|
|
minimumSize: const Size(343, 41), // Width & Height
|
|
|
|
minimumSize: const Size(343, 41),
|
|
|
|
padding: const EdgeInsets.fromLTRB(24, 12, 24, 12),),
|
|
|
|
padding: const EdgeInsets.fromLTRB(24, 12, 24, 12),
|
|
|
|
|
|
|
|
),
|
|
|
|
child: const Text("Add Driver"),
|
|
|
|
child: const Text("Add Driver"),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
],
|
|
|
|
|