You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
662 lines
20 KiB
Dart
662 lines
20 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:fluttertoast/fluttertoast.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:test_sa/controllers/validator/validator.dart';
|
|
import 'package:test_sa/extensions/int_extensions.dart';
|
|
import 'package:test_sa/extensions/text_extensions.dart';
|
|
import 'package:test_sa/modules/signup/models/company_model.dart';
|
|
import 'package:test_sa/modules/signup/models/group_model.dart';
|
|
import 'package:test_sa/modules/signup/models/signup_request_model.dart';
|
|
import 'package:test_sa/modules/signup/models/site_model.dart';
|
|
import 'package:test_sa/modules/signup/models/department_model.dart';
|
|
import 'package:test_sa/modules/signup/models/employee_erp_model.dart';
|
|
import 'package:test_sa/modules/signup/providers/company_provider.dart';
|
|
import 'package:test_sa/modules/signup/providers/group_provider.dart';
|
|
import 'package:test_sa/modules/signup/providers/signup_provider.dart';
|
|
import 'package:test_sa/modules/signup/providers/signup_site_provider.dart';
|
|
import 'package:test_sa/modules/signup/providers/signup_department_provider.dart';
|
|
import 'package:test_sa/new_views/app_style/app_color.dart';
|
|
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
|
|
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
|
|
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
|
|
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
|
|
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
|
|
|
|
class SignUpScreen extends StatefulWidget {
|
|
static const String routeName = "/signup_screen";
|
|
|
|
const SignUpScreen({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
State<SignUpScreen> createState() => _SignUpScreenState();
|
|
}
|
|
|
|
class _SignUpScreenState extends State<SignUpScreen> {
|
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
|
|
|
late AssetGroupProvider _assetGroupProvider;
|
|
late CompanyProvider _companyProvider;
|
|
late SignUpProvider _signUpProvider;
|
|
late SignupSiteProvider _siteProvider;
|
|
late SignupDepartmentProvider _departmentProvider;
|
|
|
|
// Form field controllers
|
|
final TextEditingController _employeeIdController = TextEditingController();
|
|
final TextEditingController _fullNameController = TextEditingController();
|
|
final TextEditingController _emailController = TextEditingController();
|
|
final TextEditingController _mobileController = TextEditingController();
|
|
final TextEditingController _extensionController = TextEditingController();
|
|
final TextEditingController _roleController = TextEditingController();
|
|
|
|
// Selected values
|
|
AssetGroup? _selectedAssetGroup;
|
|
Company? _selectedCompany;
|
|
SiteModel? _selectedSite;
|
|
DepartmentModel? _selectedDepartment;
|
|
EmployeeERPModel? _employeeERPData; // Store the ERP employee data
|
|
bool _isHMGGroup = false;
|
|
bool _employeeFetched = false;
|
|
bool _isSubmitting = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_assetGroupProvider.getData();
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_employeeIdController.dispose();
|
|
_fullNameController.dispose();
|
|
_emailController.dispose();
|
|
_mobileController.dispose();
|
|
_extensionController.dispose();
|
|
_roleController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
_assetGroupProvider = Provider.of<AssetGroupProvider>(context);
|
|
_companyProvider = Provider.of<CompanyProvider>(context);
|
|
_signUpProvider = Provider.of<SignUpProvider>(context);
|
|
_siteProvider = Provider.of<SignupSiteProvider>(context);
|
|
_departmentProvider = Provider.of<SignupDepartmentProvider>(context);
|
|
|
|
return Scaffold(
|
|
backgroundColor: AppColor.scaffoldBackground(context),
|
|
appBar: const DefaultAppBar(title: "Sign Up"),
|
|
body: Column(
|
|
children: [
|
|
Expanded(
|
|
child: Form(
|
|
key: _formKey,
|
|
child: SingleChildScrollView(
|
|
padding: EdgeInsets.all(16.toScreenWidth),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
_buildAssetGroupDropdown(),
|
|
16.height,
|
|
if (_selectedAssetGroup != null) ...[
|
|
_buildCompanyDropdown(),
|
|
16.height,
|
|
],
|
|
if (_selectedCompany != null) ..._buildDynamicFields(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (_selectedCompany != null)
|
|
Container(
|
|
padding: EdgeInsets.all(16.toScreenWidth),
|
|
decoration: BoxDecoration(
|
|
color: AppColor.background(context),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, -2),
|
|
),
|
|
],
|
|
),
|
|
child: SafeArea(
|
|
child: _buildSubmitButton(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildAssetGroupDropdown() {
|
|
return SingleItemDropDownMenu<AssetGroup, AssetGroupProvider>(
|
|
context: context,
|
|
title: "Asset Group",
|
|
initialValue: _selectedAssetGroup,
|
|
showAsFullScreenDialog: false,
|
|
showAsBottomSheet: true,
|
|
onSelect: (value) {
|
|
setState(() {
|
|
_selectedAssetGroup = value;
|
|
_selectedCompany = null;
|
|
_clearForm();
|
|
});
|
|
if (value?.id != null) {
|
|
_companyProvider.getData(id: value!.id);
|
|
// Clear sites and departments when asset group changes
|
|
_signUpProvider.reset();
|
|
// Clear sites and departments when asset group changes
|
|
_signUpProvider.reset();
|
|
}
|
|
},
|
|
validator: (value) {
|
|
if (value == null) return "Asset Group is required";
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildCompanyDropdown() {
|
|
return SingleItemDropDownMenu<Company, CompanyProvider>(
|
|
context: context,
|
|
title: "Company",
|
|
initialValue: _selectedCompany,
|
|
showAsFullScreenDialog: false,
|
|
showAsBottomSheet: true,
|
|
onSelect: (value) {
|
|
setState(() {
|
|
_selectedCompany = value;
|
|
_isHMGGroup = value?.companyName?.toUpperCase() == 'HMG';
|
|
_employeeFetched = false;
|
|
_clearForm();
|
|
});
|
|
|
|
// Reset and fetch site and department data when company is selected
|
|
if (_selectedAssetGroup?.id != null) {
|
|
_siteProvider.reset();
|
|
_departmentProvider.reset();
|
|
_siteProvider.getData(id: _selectedAssetGroup!.id);
|
|
_departmentProvider.getData(id: _selectedAssetGroup!.id);
|
|
}
|
|
},
|
|
validator: (value) {
|
|
if (value == null) return "Company is required";
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
List<Widget> _buildDynamicFields() {
|
|
if (_isHMGGroup) {
|
|
return [
|
|
_buildEmployeeIdField(),
|
|
16.height,
|
|
if (_employeeFetched) ...[
|
|
_buildReadOnlyField("Full Name", _fullNameController),
|
|
16.height,
|
|
_buildReadOnlyField("Email Address", _emailController),
|
|
16.height,
|
|
_buildReadOnlyField("Mobile Number", _mobileController),
|
|
16.height,
|
|
_buildExtensionField(),
|
|
16.height,
|
|
_buildSiteDropdown(),
|
|
16.height,
|
|
_buildDepartmentDropdown(),
|
|
16.height,
|
|
_buildRoleField(),
|
|
],
|
|
];
|
|
} else {
|
|
return [
|
|
// Employee ID is auto-generated for Non-HMG, so not shown during signup
|
|
_buildFullNameField(),
|
|
16.height,
|
|
_buildEmailField(),
|
|
16.height,
|
|
_buildMobileField(),
|
|
16.height,
|
|
_buildExtensionField(),
|
|
16.height,
|
|
_buildSiteDropdown(),
|
|
16.height,
|
|
_buildDepartmentDropdown(),
|
|
16.height,
|
|
_buildRoleField(),
|
|
];
|
|
}
|
|
}
|
|
|
|
Widget _buildEmployeeIdField() {
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Expanded(
|
|
child: AppTextFormField(
|
|
controller: _employeeIdController,
|
|
labelText: "Employee ID",
|
|
hintText: "Enter Employee ID",
|
|
textInputType: TextInputType.number,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return "Employee ID is required";
|
|
if (!Validator.isNumeric(value)) return "Employee ID must be numeric";
|
|
return null;
|
|
},
|
|
),
|
|
),
|
|
12.width,
|
|
Container(
|
|
margin: EdgeInsets.only(top: 8.toScreenHeight),
|
|
child: AppFilledButton(
|
|
label: "Search",
|
|
height: 48,
|
|
width: 100,
|
|
loading: _signUpProvider.loading,
|
|
disableButton: _signUpProvider.loading,
|
|
onPressed: () {
|
|
if (_employeeIdController.text.isEmpty) {
|
|
Fluttertoast.showToast(msg: "Please enter Employee ID");
|
|
return;
|
|
}
|
|
if (!Validator.isNumeric(_employeeIdController.text)) {
|
|
Fluttertoast.showToast(msg: "Employee ID must be numeric");
|
|
return;
|
|
}
|
|
_fetchEmployeeDetails(_employeeIdController.text);
|
|
},
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildReadOnlyField(String label, TextEditingController controller) {
|
|
return AppTextFormField(
|
|
controller: controller,
|
|
labelText: label,
|
|
enable: false,
|
|
backgroundColor: AppColor.disableFieldBackground(context),
|
|
);
|
|
}
|
|
|
|
Widget _buildFullNameField() {
|
|
return AppTextFormField(
|
|
controller: _fullNameController,
|
|
labelText: "Full Name",
|
|
hintText: "Enter Full Name",
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return "Full Name is required";
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildEmailField() {
|
|
return AppTextFormField(
|
|
controller: _emailController,
|
|
labelText: "Email Address",
|
|
hintText: "Enter Email Address",
|
|
textInputType: TextInputType.emailAddress,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return "Email is required";
|
|
if (!Validator.isEmail(value)) return "Invalid email format";
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildMobileField() {
|
|
return AppTextFormField(
|
|
controller: _mobileController,
|
|
labelText: "Mobile Number",
|
|
hintText: "Enter Mobile Number",
|
|
textInputType: TextInputType.phone,
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return "Mobile Number is required";
|
|
if (!Validator.isPhoneNumber(value)) return "Invalid mobile number";
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildExtensionField() {
|
|
return AppTextFormField(
|
|
controller: _extensionController,
|
|
labelText: "Extension Number",
|
|
hintText: "Enter Extension Number (Optional)",
|
|
textInputType: TextInputType.number,
|
|
validator: (value) {
|
|
if (value != null && value.isNotEmpty && !Validator.isNumeric(value)) {
|
|
return "Extension must be numeric";
|
|
}
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildSiteDropdown() {
|
|
return SingleItemDropDownMenu<SiteModel, SignupSiteProvider>(
|
|
context: context,
|
|
title: "Site",
|
|
initialValue: _selectedSite,
|
|
showAsFullScreenDialog: false,
|
|
showAsBottomSheet: true,
|
|
requestById: _selectedAssetGroup?.id,
|
|
onSelect: (value) {
|
|
setState(() {
|
|
_selectedSite = value;
|
|
});
|
|
// No need to fetch departments when site changes - they are independent
|
|
},
|
|
validator: (value) {
|
|
if (value == null) return "Site is required";
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildDepartmentDropdown() {
|
|
return SingleItemDropDownMenu<DepartmentModel, SignupDepartmentProvider>(
|
|
context: context,
|
|
title: "Department",
|
|
initialValue: _selectedDepartment,
|
|
showAsFullScreenDialog: false,
|
|
showAsBottomSheet: true,
|
|
requestById: _selectedAssetGroup?.id,
|
|
onSelect: (value) {
|
|
setState(() {
|
|
_selectedDepartment = value;
|
|
});
|
|
},
|
|
validator: (value) {
|
|
if (value == null) return "Department is required";
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildRoleField() {
|
|
return AppTextFormField(
|
|
controller: _roleController,
|
|
labelText: "Role",
|
|
hintText: "Enter Role",
|
|
validator: (value) {
|
|
if (value == null || value.isEmpty) return "Role is required";
|
|
return null;
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildSubmitButton() {
|
|
return AppFilledButton(
|
|
label: "Submit",
|
|
maxWidth: true,
|
|
loading: _isSubmitting,
|
|
disableButton: _isSubmitting,
|
|
onPressed: _submitForm,
|
|
);
|
|
}
|
|
|
|
Future<void> _fetchEmployeeDetails(String employeeId) async {
|
|
final employee = await _signUpProvider.getEmployeeFromERP(employeeId);
|
|
|
|
if (employee != null) {
|
|
// Check if the error message indicates duplicate account
|
|
if (_signUpProvider.errorMessage != null &&
|
|
_signUpProvider.errorMessage!.toLowerCase().contains('already')) {
|
|
// Show duplicate account popup
|
|
_showDuplicateAccountDialog();
|
|
setState(() {
|
|
_employeeFetched = false;
|
|
_employeeERPData = null;
|
|
_employeeIdController.clear();
|
|
});
|
|
return;
|
|
}
|
|
|
|
setState(() {
|
|
_employeeFetched = true;
|
|
_employeeERPData = employee; // Store the ERP data for later use
|
|
_fullNameController.text = employee.employeeName ?? '';
|
|
_emailController.text = employee.employeeEmailAddress ?? '';
|
|
_mobileController.text = employee.employeeMobileNumber ?? '';
|
|
});
|
|
} else {
|
|
// Check if it's a duplicate account error
|
|
if (_signUpProvider.errorMessage != null &&
|
|
(_signUpProvider.errorMessage!.toLowerCase().contains('already') ||
|
|
_signUpProvider.errorMessage!.toLowerCase().contains('exists') ||
|
|
_signUpProvider.errorMessage!.toLowerCase().contains('account'))) {
|
|
_showDuplicateAccountDialog();
|
|
setState(() {
|
|
_employeeFetched = false;
|
|
_employeeERPData = null;
|
|
_employeeIdController.clear();
|
|
});
|
|
} else {
|
|
setState(() {
|
|
_employeeFetched = false;
|
|
_employeeERPData = null;
|
|
});
|
|
Fluttertoast.showToast(
|
|
msg: _signUpProvider.errorMessage ?? "Failed to fetch employee details",
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _showDuplicateAccountDialog() {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
backgroundColor: AppColor.background(context),
|
|
title: "Account Exists".bodyText(context),
|
|
content: "Already you have an account on Atoms".bodyText(context),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(context);
|
|
},
|
|
child: "OK".bodyText(context).custom(color: AppColor.primary10),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _clearForm() {
|
|
_employeeIdController.clear();
|
|
_fullNameController.clear();
|
|
_emailController.clear();
|
|
_mobileController.clear();
|
|
_extensionController.clear();
|
|
_roleController.clear();
|
|
_selectedSite = null;
|
|
_selectedDepartment = null;
|
|
_employeeFetched = false;
|
|
_employeeERPData = null;
|
|
}
|
|
|
|
Future<void> _submitForm() async {
|
|
// Validate form fields
|
|
if (!_formKey.currentState!.validate()) {
|
|
Fluttertoast.showToast(msg: "Please fill all required fields");
|
|
return;
|
|
}
|
|
|
|
// HMG Group specific validations
|
|
if (_isHMGGroup) {
|
|
// Employee ID is mandatory for HMG
|
|
if (_employeeIdController.text.isEmpty) {
|
|
Fluttertoast.showToast(msg: "Employee ID is required");
|
|
return;
|
|
}
|
|
|
|
// Employee must be fetched from ERP
|
|
if (!_employeeFetched) {
|
|
Fluttertoast.showToast(msg: "Please search and fetch employee details from ERP");
|
|
return;
|
|
}
|
|
|
|
// Validate Employee ID is numeric
|
|
if (!Validator.isNumeric(_employeeIdController.text)) {
|
|
Fluttertoast.showToast(msg: "Employee ID must be numeric");
|
|
return;
|
|
}
|
|
|
|
// Site is mandatory for HMG
|
|
if (_selectedSite == null) {
|
|
Fluttertoast.showToast(msg: "Site is required");
|
|
return;
|
|
}
|
|
|
|
// Department is mandatory for HMG
|
|
if (_selectedDepartment == null) {
|
|
Fluttertoast.showToast(msg: "Department is required");
|
|
return;
|
|
}
|
|
} else {
|
|
// Non-HMG specific validations
|
|
|
|
// Full Name is mandatory
|
|
if (_fullNameController.text.isEmpty) {
|
|
Fluttertoast.showToast(msg: "Full Name is required");
|
|
return;
|
|
}
|
|
|
|
// Email is mandatory and must be valid format
|
|
if (_emailController.text.isEmpty) {
|
|
Fluttertoast.showToast(msg: "Email Address is required");
|
|
return;
|
|
}
|
|
if (!Validator.isEmail(_emailController.text)) {
|
|
Fluttertoast.showToast(msg: "Please enter a valid email address");
|
|
return;
|
|
}
|
|
|
|
// Mobile Number is mandatory and must be numeric
|
|
if (_mobileController.text.isEmpty) {
|
|
Fluttertoast.showToast(msg: "Mobile Number is required");
|
|
return;
|
|
}
|
|
if (!Validator.isPhoneNumber(_mobileController.text)) {
|
|
Fluttertoast.showToast(msg: "Please enter a valid mobile number");
|
|
return;
|
|
}
|
|
|
|
// Site is mandatory for Non-HMG
|
|
if (_selectedSite == null) {
|
|
Fluttertoast.showToast(msg: "Site is required");
|
|
return;
|
|
}
|
|
|
|
// Department is mandatory for Non-HMG
|
|
if (_selectedDepartment == null) {
|
|
Fluttertoast.showToast(msg: "Department is required");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Common validations for both HMG and Non-HMG
|
|
|
|
// Extension Number validation (optional but must be numeric if provided)
|
|
if (_extensionController.text.isNotEmpty && !Validator.isNumeric(_extensionController.text)) {
|
|
Fluttertoast.showToast(msg: "Extension Number must be numeric");
|
|
return;
|
|
}
|
|
|
|
// Role is mandatory
|
|
if (_roleController.text.isEmpty) {
|
|
Fluttertoast.showToast(msg: "Role is required");
|
|
return;
|
|
}
|
|
|
|
// Validate Asset Group is selected
|
|
if (_selectedAssetGroup == null) {
|
|
Fluttertoast.showToast(msg: "Asset Group is required");
|
|
return;
|
|
}
|
|
|
|
// Validate Company is selected
|
|
if (_selectedCompany == null) {
|
|
Fluttertoast.showToast(msg: "Company is required");
|
|
return;
|
|
}
|
|
|
|
setState(() {
|
|
_isSubmitting = true;
|
|
});
|
|
|
|
if (mounted) {
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => const AppLazyLoading(),
|
|
);
|
|
}
|
|
|
|
// Build request with conditional logic based on isHMGGroup
|
|
final request = SignUpRequestModel(
|
|
assetGroupId: _selectedAssetGroup?.id,
|
|
companyId: _selectedCompany?.id,
|
|
employeeId: _isHMGGroup ? _employeeIdController.text : null,
|
|
fullName: _fullNameController.text,
|
|
emailAddress: _emailController.text,
|
|
mobileNumber: _mobileController.text,
|
|
extensionNumber: _extensionController.text.isEmpty ? null : _extensionController.text,
|
|
// For HMG: send both selected IDs and ERP data
|
|
siteId: _selectedSite?.id,
|
|
erpSite: _isHMGGroup ? _employeeERPData?.branchCode : null,
|
|
departmentId: _selectedDepartment?.id,
|
|
erpDepartment: _isHMGGroup ? _employeeERPData?.branchName : null,
|
|
role: _roleController.text,
|
|
isFromERP: _isHMGGroup,
|
|
);
|
|
|
|
final success = await _signUpProvider.submitSignUpRequest(request);
|
|
|
|
if (mounted) {
|
|
Navigator.pop(context); // Close loading dialog
|
|
}
|
|
|
|
setState(() {
|
|
_isSubmitting = false;
|
|
});
|
|
|
|
if (success) {
|
|
if (mounted) {
|
|
_showSuccessDialog();
|
|
}
|
|
} else {
|
|
Fluttertoast.showToast(
|
|
msg: _signUpProvider.errorMessage ?? "Failed to submit signup request",
|
|
);
|
|
}
|
|
}
|
|
|
|
void _showSuccessDialog() {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => AlertDialog(
|
|
backgroundColor: AppColor.background(context),
|
|
title: "Success".bodyText(context),
|
|
content: "Your signup request has been submitted successfully. It will be reviewed by the Human Resources Admin."
|
|
.bodyText(context),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () {
|
|
Navigator.pop(context); // Close dialog
|
|
Navigator.pop(context); // Go back to login
|
|
},
|
|
child: "OK".bodyText(context).custom(color: AppColor.primary10),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|