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.
cloudsolutions-atoms/lib/modules/signup/screens/signup_screen.dart

714 lines
23 KiB
Dart

import 'dart:io';
import 'package:flutter/cupertino.dart';
3 months ago
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/context_extension.dart';
3 months ago
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
3 months ago
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
2 months ago
import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart';
3 months ago
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';
3 months ago
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';
3 months ago
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';
3 months ago
import 'package:test_sa/modules/signup/providers/signup_site_provider.dart';
import 'package:test_sa/modules/signup/providers/signup_department_provider.dart';
3 months ago
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';
import 'package:test_sa/new_views/swipe_module/dialoge/single_btn_dialog.dart';
import 'package:test_sa/views/widgets/dialogs/dialog.dart';
3 months ago
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>();
3 months ago
late AssetGroupProvider _assetGroupProvider;
3 months ago
late CompanyProvider _companyProvider;
late SignUpProvider _signUpProvider;
3 months ago
late SignupSiteProvider _siteProvider;
late SignupDepartmentProvider _departmentProvider;
3 months ago
// Form field controllers
final TextEditingController _employeeIdController = TextEditingController();
final TextEditingController _fullNameController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _mobileController = TextEditingController();
final TextEditingController _siteController = TextEditingController();
final TextEditingController _departmentController = TextEditingController();
3 months ago
final TextEditingController _extensionController = TextEditingController();
final TextEditingController _roleController = TextEditingController();
// Selected values
3 months ago
AssetGroup? _selectedAssetGroup;
3 months ago
Company? _selectedCompany;
3 months ago
SiteModel? _selectedSite;
DepartmentModel? _selectedDepartment;
EmployeeERPModel? _employeeERPData; // Store the ERP employee data
3 months ago
bool _isHMGGroup = false;
bool _employeeFetched = false;
bool _isSubmitting = false;
// Autovalidate mode - enables validation on user interaction after first submit attempt
AutovalidateMode _autovalidateMode = AutovalidateMode.disabled;
3 months ago
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
3 months ago
_assetGroupProvider.getData();
3 months ago
});
}
@override
void dispose() {
_employeeIdController.dispose();
_fullNameController.dispose();
_emailController.dispose();
_mobileController.dispose();
_siteController.dispose();
_departmentController.dispose();
3 months ago
_extensionController.dispose();
_roleController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
3 months ago
_assetGroupProvider = Provider.of<AssetGroupProvider>(context);
3 months ago
_companyProvider = Provider.of<CompanyProvider>(context);
_signUpProvider = Provider.of<SignUpProvider>(context);
3 months ago
_siteProvider = Provider.of<SignupSiteProvider>(context);
_departmentProvider = Provider.of<SignupDepartmentProvider>(context);
3 months ago
return Scaffold(
backgroundColor: AppColor.scaffoldBackground(context),
appBar: const DefaultAppBar(title: "Sign Up"),
body: Column(
children: [
2 months ago
SingleChildScrollView(
padding: EdgeInsets.all(16.toScreenWidth),
child: Form(
key: _formKey,
autovalidateMode: _autovalidateMode, // Set autovalidate mode
2 months ago
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 12,
children: [
_buildAssetGroupDropdown(),
// 16.height,
if (_selectedAssetGroup != null) ...[
_buildCompanyDropdown(),
// 16.height,
],
2 months ago
if (_selectedCompany != null) ..._buildDynamicFields(),
],
).toShadowContainer(context, borderRadius: 20),
),
2 months ago
).expanded,
if (_selectedCompany != null)
2 months ago
FooterActionButton.footerContainer(
context: context,
child: AppFilledButton(
label: "Submit",
maxWidth: true,
loading: _isSubmitting,
disableButton: _isSubmitting,
onPressed: _submitForm,
),
),
],
3 months ago
),
);
}
3 months ago
Widget _buildAssetGroupDropdown() {
return SingleItemDropDownMenu<AssetGroup, AssetGroupProvider>(
3 months ago
context: context,
title: "Group".addTranslation,
3 months ago
initialValue: _selectedAssetGroup,
2 months ago
showAsFullScreenDialog: true,
showAsBottomSheet: false,
3 months ago
onSelect: (value) {
setState(() {
3 months ago
_selectedAssetGroup = value;
_selectedCompany = null;
_clearForm();
3 months ago
});
3 months ago
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();
}
3 months ago
},
validator: (value) {
if (value == null) return "Group is required";
3 months ago
return null;
},
);
}
3 months ago
Widget _buildCompanyDropdown() {
return SingleItemDropDownMenu<Company, CompanyProvider>(
3 months ago
context: context,
3 months ago
title: "Company",
initialValue: _selectedCompany,
2 months ago
showAsFullScreenDialog: true,
showAsBottomSheet: false,
3 months ago
onSelect: (value) {
setState(() {
3 months ago
_selectedCompany = value;
_isHMGGroup = value?.companyName?.toUpperCase() == 'HMG';
3 months ago
_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);
}
3 months ago
},
validator: (value) {
3 months ago
if (value == null) return "Company is required";
3 months ago
return null;
},
);
}
List<Widget> _buildDynamicFields() {
if (_isHMGGroup) {
return [
_buildEmployeeIdField(),
2 months ago
// 16.height,
3 months ago
if (_employeeFetched) ...[
_buildReadOnlyField("Full Name", _fullNameController),
_buildReadOnlyField("Email Address", _emailController),
_buildReadOnlyField("Mobile Number", _mobileController),
2 months ago
// _buildReadOnlyField("Site", _siteController),
// _buildReadOnlyField("Department", _departmentController),
2 months ago
// 16.height,
3 months ago
_buildExtensionField(),
2 months ago
// 16.height,
2 months ago
_buildSiteDropdown(),
2 months ago
// 16.height,
2 months ago
_buildDepartmentDropdown(),
2 months ago
// 16.height,
3 months ago
_buildRoleField(),
],
];
} else {
return [
// Employee ID is auto-generated for Non-HMG, so not shown during signup
3 months ago
_buildFullNameField(),
2 months ago
// 16.height,
3 months ago
_buildEmailField(),
2 months ago
// 16.height,
3 months ago
_buildMobileField(),
2 months ago
// 16.height,
3 months ago
_buildExtensionField(),
2 months ago
// 16.height,
3 months ago
_buildSiteDropdown(),
2 months ago
// 16.height,
3 months ago
_buildDepartmentDropdown(),
2 months ago
// 16.height,
3 months ago
_buildRoleField(),
];
}
}
Widget _buildEmployeeIdField() {
3 months ago
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: AppTextFormField(
controller: _employeeIdController,
labelText: "Employee ID",
textInputType: TextInputType.number,
validator: (value) {
2 months ago
if (_employeeIdController.text.isEmpty) return "Employee ID is required";
3 months ago
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);
},
),
),
],
3 months ago
);
}
Widget _buildReadOnlyField(String label, TextEditingController controller) {
2 months ago
if (controller.text.isEmpty) {
return const SizedBox();
}
return Column(
children: [
16.height,
AppTextFormField(
controller: controller,
labelText: label,
enable: false,
backgroundColor: AppColor.disableFieldBackground(context),
),
],
3 months ago
);
}
Widget _buildFullNameField() {
return AppTextFormField(
controller: _fullNameController,
labelText: "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",
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",
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",
textInputType: TextInputType.number,
validator: (value) {
if (value != null && value.isNotEmpty && !Validator.isNumeric(value)) {
return "Extension must be numeric";
}
return null;
},
);
}
Widget _buildSiteDropdown() {
3 months ago
return SingleItemDropDownMenu<SiteModel, SignupSiteProvider>(
3 months ago
context: context,
title: "Site",
initialValue: _selectedSite,
showAsFullScreenDialog: true,
requestById: _selectedAssetGroup?.id,
3 months ago
onSelect: (value) {
setState(() {
_selectedSite = value;
});
// No need to fetch departments when site changes - they are independent
3 months ago
},
validator: (value) {
if (value == null) return "Site is required";
return null;
},
);
}
Widget _buildDepartmentDropdown() {
3 months ago
return SingleItemDropDownMenu<DepartmentModel, SignupDepartmentProvider>(
3 months ago
context: context,
title: "Department",
initialValue: _selectedDepartment,
showAsFullScreenDialog: true,
// showAsBottomSheet: true,
requestById: _selectedAssetGroup?.id,
3 months ago
onSelect: (value) {
setState(() {
_selectedDepartment = value;
});
},
validator: (value) {
if (value == null) return "Department is required";
return null;
},
);
}
Widget _buildRoleField() {
return AppTextFormField(
controller: _roleController,
labelText: "Role",
validator: (value) {
if (value == null || value.isEmpty) return "Role is required";
return null;
},
);
}
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;
// }
3 months ago
setState(() {
_employeeFetched = true;
_employeeERPData = employee; // Store the ERP data for later use
3 months ago
_fullNameController.text = employee.employeeName ?? '';
_emailController.text = employee.employeeEmailAddress ?? '';
_mobileController.text = employee.employeeMobileNumber ?? '';
//Todo need to map site and department from ERP data
2 months ago
_departmentController.text = '';
_siteController.text = employee.branchCode ?? '';
3 months ago
});
} else {
setState(() {
_employeeFetched = false;
_employeeERPData = null;
// _employeeIdController.clear();
});
// 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;
// });
// }
3 months ago
}
}
void _showDuplicateAccountDialog() {
final errorMessage = _signUpProvider.errorMessage ?? "Already you have an account on Atoms";
3 months ago
showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: AppColor.background(context),
title: "Account Exists".bodyText(context),
content: errorMessage.bodyText(context),
3 months ago
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
3 months ago
child: "OK".bodyText(context).custom(color: AppColor.primary10),
),
],
),
);
}
void _clearForm() {
_employeeIdController.clear();
3 months ago
_fullNameController.clear();
_emailController.clear();
_mobileController.clear();
_extensionController.clear();
_roleController.clear();
_selectedSite = null;
_selectedDepartment = null;
_employeeFetched = false;
_employeeERPData = null;
3 months ago
}
Future<void> _submitForm() async {
// Validate form fields
if (!_formKey.currentState!.validate()) {
// Enable autovalidation after first submit attempt
setState(() {
_autovalidateMode = AutovalidateMode.onUserInteraction;
});
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
2 months ago
if (_selectedSite == null) {
Fluttertoast.showToast(msg: "Site is required");
return;
}
//
// // Department is mandatory for HMG
2 months ago
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;
}
3 months ago
// Role is mandatory
if (_roleController.text.isEmpty) {
Fluttertoast.showToast(msg: "Role is required");
3 months ago
return;
}
// Validate Asset Group is selected
if (_selectedAssetGroup == null) {
Fluttertoast.showToast(msg: "Asset Group is required");
3 months ago
return;
}
// Validate Company is selected
if (_selectedCompany == null) {
Fluttertoast.showToast(msg: "Company is required");
3 months ago
return;
}
3 months ago
setState(() {
_isSubmitting = true;
});
if (mounted) {
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const AppLazyLoading(),
);
}
// Build request with conditional logic based on isHMGGroup
3 months ago
final request = SignUpRequestModel(
assetGroupId: _selectedAssetGroup?.id,
companyId: _selectedCompany?.id,
3 months ago
employeeId: _isHMGGroup ? _employeeIdController.text : null,
fullName: _fullNameController.text,
emailAddress: _emailController.text,
3 months ago
mobileNumber: _mobileController.text,
extensionNumber: _extensionController.text.isEmpty ? null : _extensionController.text,
// For HMG: send both selected IDs and ERP data
siteId: _selectedSite?.id,
2 months ago
// erpSite: _isHMGGroup ? _employeeERPData?.branchCode : null,
departmentId: _selectedDepartment?.id,
2 months ago
// erpDepartment: _isHMGGroup ? _employeeERPData?.branchName : null,
3 months ago
role: _roleController.text,
isFromERP: _isHMGGroup,
3 months ago
);
final success = await _signUpProvider.submitSignUpRequest(request);
if (mounted) {
Navigator.pop(context); // Close loading dialog
}
setState(() {
_isSubmitting = false;
});
if (success) {
if (mounted) {
_showSuccessDialog();
}
} else {
if (_signUpProvider.errorMessage != null) {
context.showSingleBtnDialog(_signUpProvider.errorMessage!);
}
3 months ago
}
}
void _showSuccessDialog() {
final successMessage = _signUpProvider.successMessage ?? 'Your signup request has been submitted';
context.showSingleBtnDialog(successMessage, title: 'Success', onTap: () {
Navigator.pop(context); // Close dialog
Navigator.pop(context);
});
// showDialog(
// context: context,
// builder: (context) => Dialog(
// backgroundColor: Colors.white,
// shape: const RoundedRectangleBorder(),
// insetPadding: const EdgeInsets.only(left: 21, right: 21),
// child: Padding(
// padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisSize: MainAxisSize.min,
// children: [
// Row(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Expanded(
// child: Text(
// "Success",
// style: const TextStyle(
// fontSize: 24,
// fontWeight: FontWeight.w600,
// color: Colors.black87,
// height: 35 / 24,
// letterSpacing: -0.96,
// ),
// ).paddingOnly(top: 16),
// ),
// IconButton(
// padding: EdgeInsets.zero,
// icon: const Icon(Icons.close),
// color: Colors.black87,
// constraints: const BoxConstraints(),
// onPressed: () {
// Navigator.pop(context); // Close dialog
// Navigator.pop(context); // Go back to login
// },
// )
// ],
// ),
// successMessage.bodyText(context),
// 28.height,
// AppFilledButton(
// label: "OK",
// onPressed: () {
// Navigator.pop(context); // Close dialog
// Navigator.pop(context); // Go back to login
// },
// textColor: Colors.white,
// ),
// ],
// ),
// ),
// ),
// );
3 months ago
}
}