improvements

export_module_ui_ux_enhancement_signup
WaseemAbbasi22 1 day ago
parent fb6dc22c05
commit 7186fb13c0

@ -4,12 +4,12 @@ class URLs {
static const String appReleaseBuildNumber = "40"; static const String appReleaseBuildNumber = "40";
// static const host1 = "https://atomsm.hmg.com"; // production url // static const host1 = "https://atomsm.hmg.com"; // production url
static const host1 = "https://atomsmdev.hmg.com"; // local DEV url // static const host1 = "https://atomsmdev.hmg.com"; // local DEV url
// static const host1 = "https://atomsmuat.hmg.com"; // local UAT url static const host1 = "https://atomsmuat.hmg.com"; // local UAT url
static final String _baseUrl = "$_host/mobile"; // host local UAT // static final String _baseUrl = "$_host/mobile"; // host local UAT
// static final String _baseUrl = "$_host/v1/mobile"; // new V1 apis // static final String _baseUrl = "$_host/v1/mobile"; // new V1 apis
// static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis static final String _baseUrl = "$_host/v2/mobile"; // new V2 apis
// static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM // static final String _baseUrl = "$_host/v3/mobile"; // v3 for production CM,PM,TM
// static final String _baseUrl = "$_host/v4/mobile"; // v4 for Demo module // static final String _baseUrl = "$_host/v4/mobile"; // v4 for Demo module
// static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation // static final String _baseUrl = "$_host/v5/mobile"; // v5 for data segregation

@ -439,7 +439,7 @@ class MyApp extends StatelessWidget {
CreateDemoRequestPage.id: (_) => CreateDemoRequestPage(), CreateDemoRequestPage.id: (_) => CreateDemoRequestPage(),
CleanerSearchFormView.routeName: (_) => const CleanerSearchFormView(), CleanerSearchFormView.routeName: (_) => const CleanerSearchFormView(),
CleanerSearchListView.routeName: (_) => const CleanerSearchListView(), CleanerSearchListView.routeName: (_) => const CleanerSearchListView(),
DemoRequestFormScreen.routeName: (_) => const DemoRequestFormScreen(), ApplicationDemoRequestFormScreen.routeName: (_) => const ApplicationDemoRequestFormScreen(),
}, },
), ),
), ),

@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import 'package:test_sa/controllers/validator/validator.dart'; import 'package:test_sa/controllers/validator/validator.dart';
import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/applicationDemo/enums/otp_method.dart'; import 'package:test_sa/modules/applicationDemo/enums/otp_method.dart';
import 'package:test_sa/modules/applicationDemo/models/application_demo_request_model.dart'; import 'package:test_sa/modules/applicationDemo/models/application_demo_request_model.dart';
@ -15,19 +16,20 @@ 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_filled_button.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/new_views/common_widgets/custom_app_bar.dart'; import 'package:test_sa/new_views/common_widgets/custom_app_bar.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.dart'; import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.dart';
import 'package:test_sa/providers/loading_list_notifier.dart'; import 'package:test_sa/providers/loading_list_notifier.dart';
class DemoRequestFormScreen extends StatefulWidget { class ApplicationDemoRequestFormScreen extends StatefulWidget {
static const String routeName = '/demo_request_form'; static const String routeName = '/demo_request_form';
const DemoRequestFormScreen({Key? key}) : super(key: key); const ApplicationDemoRequestFormScreen({Key? key}) : super(key: key);
@override @override
State<DemoRequestFormScreen> createState() => _DemoRequestFormScreenState(); State<ApplicationDemoRequestFormScreen> createState() => _ApplicationDemoRequestFormScreenState();
} }
class _DemoRequestFormScreenState extends State<DemoRequestFormScreen> { class _ApplicationDemoRequestFormScreenState extends State<ApplicationDemoRequestFormScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _fullNameController = TextEditingController(); final TextEditingController _fullNameController = TextEditingController();
final TextEditingController _emailController = TextEditingController(); final TextEditingController _emailController = TextEditingController();
@ -36,6 +38,8 @@ class _DemoRequestFormScreenState extends State<DemoRequestFormScreen> {
List<ApplicationDemoRoleModel> _selectedRoles = []; List<ApplicationDemoRoleModel> _selectedRoles = [];
bool _isSubmitting = false; bool _isSubmitting = false;
String? _rolesValidationError;
bool _formSubmitted = false; // Track if form has been submitted
@override @override
void initState() { void initState() {
@ -61,12 +65,11 @@ class _DemoRequestFormScreenState extends State<DemoRequestFormScreen> {
final provider = Provider.of<ApplicationDemoRequestProvider>(context); final provider = Provider.of<ApplicationDemoRequestProvider>(context);
return Scaffold( return Scaffold(
backgroundColor: AppColor.background(context), backgroundColor: AppColor.scaffoldBackground(context),
appBar: const CustomAppBar( appBar: const DefaultAppBar(title: 'Demo Request'),
title: 'Demo Request',
),
body: Form( body: Form(
key: _formKey, key: _formKey,
autovalidateMode: AutovalidateMode.disabled,
child: Column( child: Column(
children: [ children: [
Column( Column(
@ -74,53 +77,53 @@ class _DemoRequestFormScreenState extends State<DemoRequestFormScreen> {
children: [ children: [
AppTextFormField( AppTextFormField(
controller: _fullNameController, controller: _fullNameController,
labelText: 'Full Name', labelText: 'Full Name *',
hintText: 'Enter your full name',
textInputType: TextInputType.name, textInputType: TextInputType.name,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return 'Full Name is required'; if (!_formSubmitted && _fullNameController.text.isEmpty) return null;
if (_fullNameController.text.isEmpty) return 'Full Name is required';
return null; return null;
}, },
), ),
16.height, 16.height,
AppTextFormField( AppTextFormField(
controller: _emailController, controller: _emailController,
labelText: 'Email Address', labelText: 'Email Address *',
hintText: 'Enter your email address',
textInputType: TextInputType.emailAddress, textInputType: TextInputType.emailAddress,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return 'Email is required'; if (!_formSubmitted && _emailController.text.isEmpty) return null;
if (!Validator.isEmail(value)) return 'Invalid email format'; if (_emailController.text.isEmpty) return 'Email is required';
if (!Validator.isEmail(_emailController.text)) return 'Invalid email format';
return null; return null;
}, },
), ),
16.height, 16.height,
AppTextFormField( AppTextFormField(
controller: _mobileController, controller: _mobileController,
labelText: 'Mobile Number', labelText: 'Mobile Number *',
hintText: 'Enter your mobile number',
textInputType: TextInputType.phone, textInputType: TextInputType.phone,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return 'Mobile Number is required'; if (!_formSubmitted && _mobileController.text.isEmpty) return null;
if (!Validator.isPhoneNumber(value)) return 'Invalid mobile number'; if (_mobileController.text.isEmpty) return 'Mobile Number is required';
if (!Validator.isPhoneNumber(_mobileController.text)) return 'Invalid mobile number';
return null; return null;
}, },
), ),
16.height, 16.height,
AppTextFormField( AppTextFormField(
controller: _companyNameController, controller: _companyNameController,
labelText: 'Company Name', labelText: 'Company Name *',
hintText: 'Enter your company name',
textInputType: TextInputType.text, textInputType: TextInputType.text,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return 'Company Name is required'; if (!_formSubmitted && _companyNameController.text.isEmpty) return null;
if (_companyNameController.text.isEmpty) return 'Company Name is required';
return null; return null;
}, },
), ),
16.height, 16.height,
MultipleItemDropDownMenu<ApplicationDemoRoleModel, NullableLoadingProvider>( MultipleItemDropDownMenu<ApplicationDemoRoleModel, NullableLoadingProvider>(
context: context, context: context,
title: 'Select Roles', title: 'Select Roles *',
initialValue: _selectedRoles, initialValue: _selectedRoles,
staticData: provider.roles, staticData: provider.roles,
loading: provider.roleLoading, loading: provider.roleLoading,
@ -128,9 +131,20 @@ class _DemoRequestFormScreenState extends State<DemoRequestFormScreen> {
onSelect: (selectedRoles) { onSelect: (selectedRoles) {
setState(() { setState(() {
_selectedRoles = selectedRoles ?? []; _selectedRoles = selectedRoles ?? [];
_rolesValidationError = null; // Clear error when roles are selected
}); });
}, },
), ),
if (_rolesValidationError != null)
Padding(
padding: const EdgeInsets.only(top: 8, left: 16),
child: Text(
_rolesValidationError!,
style: AppTextStyles.tinyFont.copyWith(
color: AppColor.red30,
),
),
),
], ],
).paddingAll(16).expanded, ).paddingAll(16).expanded,
FooterActionButton.footerContainer( FooterActionButton.footerContainer(
@ -150,14 +164,24 @@ class _DemoRequestFormScreenState extends State<DemoRequestFormScreen> {
} }
Future<void> _submitRequest() async { Future<void> _submitRequest() async {
// Mark form as submitted
setState(() {
_formSubmitted = true;
});
// Validate form fields // Validate form fields
if (!_formKey.currentState!.validate()) { if (!_formKey.currentState!.validate()) {
return; return;
} }
// Validate roles selection
if (_selectedRoles.isEmpty) { if (_selectedRoles.isEmpty) {
context.showConfirmDialog('Please select at least one role'); setState(() {
_rolesValidationError = 'Please select at least one role';
});
return; return;
} }
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {

@ -142,8 +142,8 @@ class _SignUpScreenState extends State<SignUpScreen> {
context: context, context: context,
title: "Asset Group", title: "Asset Group",
initialValue: _selectedAssetGroup, initialValue: _selectedAssetGroup,
showAsFullScreenDialog: false, showAsFullScreenDialog: true,
showAsBottomSheet: true, showAsBottomSheet: false,
onSelect: (value) { onSelect: (value) {
setState(() { setState(() {
_selectedAssetGroup = value; _selectedAssetGroup = value;
@ -170,8 +170,8 @@ class _SignUpScreenState extends State<SignUpScreen> {
context: context, context: context,
title: "Company", title: "Company",
initialValue: _selectedCompany, initialValue: _selectedCompany,
showAsFullScreenDialog: false, showAsFullScreenDialog: true,
showAsBottomSheet: true, showAsBottomSheet: false,
onSelect: (value) { onSelect: (value) {
setState(() { setState(() {
_selectedCompany = value; _selectedCompany = value;
@ -204,14 +204,14 @@ class _SignUpScreenState extends State<SignUpScreen> {
_buildReadOnlyField("Full Name", _fullNameController), _buildReadOnlyField("Full Name", _fullNameController),
_buildReadOnlyField("Email Address", _emailController), _buildReadOnlyField("Email Address", _emailController),
_buildReadOnlyField("Mobile Number", _mobileController), _buildReadOnlyField("Mobile Number", _mobileController),
_buildReadOnlyField("Site", _siteController), // _buildReadOnlyField("Site", _siteController),
_buildReadOnlyField("Department", _departmentController), // _buildReadOnlyField("Department", _departmentController),
16.height, 16.height,
_buildExtensionField(), _buildExtensionField(),
// 16.height, 16.height,
// _buildSiteDropdown(), _buildSiteDropdown(),
// 16.height, 16.height,
// _buildDepartmentDropdown(), _buildDepartmentDropdown(),
16.height, 16.height,
_buildRoleField(), _buildRoleField(),
], ],
@ -244,11 +244,16 @@ class _SignUpScreenState extends State<SignUpScreen> {
child: AppTextFormField( child: AppTextFormField(
controller: _employeeIdController, controller: _employeeIdController,
labelText: "Employee ID", labelText: "Employee ID",
hintText: "Enter Employee ID",
textInputType: TextInputType.number, textInputType: TextInputType.number,
onChange: (value) {
// Clear validation errors on user interaction
// if (_formKey.currentState != null) {
// _formKey.currentState!.validate();
// }
},
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return "Employee ID is required"; if (_employeeIdController.text.isEmpty) return "Employee ID is required";
if (!Validator.isNumeric(value)) return "Employee ID must be numeric"; // if (!Validator.isNumeric(_employeeIdController.text)) return "Employee ID must be numeric";
return null; return null;
}, },
), ),
@ -300,7 +305,6 @@ class _SignUpScreenState extends State<SignUpScreen> {
return AppTextFormField( return AppTextFormField(
controller: _fullNameController, controller: _fullNameController,
labelText: "Full Name", labelText: "Full Name",
hintText: "Enter Full Name",
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return "Full Name is required"; if (value == null || value.isEmpty) return "Full Name is required";
return null; return null;
@ -312,7 +316,6 @@ class _SignUpScreenState extends State<SignUpScreen> {
return AppTextFormField( return AppTextFormField(
controller: _emailController, controller: _emailController,
labelText: "Email Address", labelText: "Email Address",
hintText: "Enter Email Address",
textInputType: TextInputType.emailAddress, textInputType: TextInputType.emailAddress,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return "Email is required"; if (value == null || value.isEmpty) return "Email is required";
@ -326,7 +329,6 @@ class _SignUpScreenState extends State<SignUpScreen> {
return AppTextFormField( return AppTextFormField(
controller: _mobileController, controller: _mobileController,
labelText: "Mobile Number", labelText: "Mobile Number",
hintText: "Enter Mobile Number",
textInputType: TextInputType.phone, textInputType: TextInputType.phone,
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return "Mobile Number is required"; if (value == null || value.isEmpty) return "Mobile Number is required";
@ -340,7 +342,6 @@ class _SignUpScreenState extends State<SignUpScreen> {
return AppTextFormField( return AppTextFormField(
controller: _extensionController, controller: _extensionController,
labelText: "Extension Number", labelText: "Extension Number",
hintText: "Enter Extension Number (Optional)",
textInputType: TextInputType.number, textInputType: TextInputType.number,
validator: (value) { validator: (value) {
if (value != null && value.isNotEmpty && !Validator.isNumeric(value)) { if (value != null && value.isNotEmpty && !Validator.isNumeric(value)) {
@ -395,7 +396,6 @@ class _SignUpScreenState extends State<SignUpScreen> {
return AppTextFormField( return AppTextFormField(
controller: _roleController, controller: _roleController,
labelText: "Role", labelText: "Role",
hintText: "Enter Role",
validator: (value) { validator: (value) {
if (value == null || value.isEmpty) return "Role is required"; if (value == null || value.isEmpty) return "Role is required";
return null; return null;
@ -527,16 +527,16 @@ class _SignUpScreenState extends State<SignUpScreen> {
} }
// Site is mandatory for HMG // Site is mandatory for HMG
// if (_selectedSite == null) { if (_selectedSite == null) {
// Fluttertoast.showToast(msg: "Site is required"); Fluttertoast.showToast(msg: "Site is required");
// return; return;
// } }
// //
// // Department is mandatory for HMG // // Department is mandatory for HMG
// if (_selectedDepartment == null) { if (_selectedDepartment == null) {
// Fluttertoast.showToast(msg: "Department is required"); Fluttertoast.showToast(msg: "Department is required");
// return; return;
// } }
} else { } else {
// Non-HMG specific validations // Non-HMG specific validations
@ -628,9 +628,9 @@ class _SignUpScreenState extends State<SignUpScreen> {
extensionNumber: _extensionController.text.isEmpty ? null : _extensionController.text, extensionNumber: _extensionController.text.isEmpty ? null : _extensionController.text,
// For HMG: send both selected IDs and ERP data // For HMG: send both selected IDs and ERP data
siteId: _selectedSite?.id, siteId: _selectedSite?.id,
erpSite: _isHMGGroup ? _employeeERPData?.branchCode : null, // erpSite: _isHMGGroup ? _employeeERPData?.branchCode : null,
departmentId: _selectedDepartment?.id, departmentId: _selectedDepartment?.id,
erpDepartment: _isHMGGroup ? _employeeERPData?.branchName : null, // erpDepartment: _isHMGGroup ? _employeeERPData?.branchName : null,
role: _roleController.text, role: _roleController.text,
isFromERP: _isHMGGroup, isFromERP: _isHMGGroup,
); );

@ -3,6 +3,7 @@
class SiteRequestModel { class SiteRequestModel {
final String? id; final String? id;
final String requestType; final String requestType;
final String? requestNumber;
final String employeeId; final String employeeId;
final String fullName; final String fullName;
final String emailAddress; final String emailAddress;
@ -23,6 +24,7 @@ class SiteRequestModel {
SiteRequestModel({ SiteRequestModel({
this.id, this.id,
required this.requestType, required this.requestType,
this.requestNumber,
required this.employeeId, required this.employeeId,
required this.fullName, required this.fullName,
required this.emailAddress, required this.emailAddress,
@ -46,6 +48,7 @@ class SiteRequestModel {
return { return {
'id': id, 'id': id,
'requestType': requestType, 'requestType': requestType,
'requestNumber': requestNumber,
'employeeId': employeeId, 'employeeId': employeeId,
'fullName': fullName, 'fullName': fullName,
'emailAddress': emailAddress, 'emailAddress': emailAddress,
@ -72,6 +75,7 @@ class SiteRequestModel {
id: json['id']?.toString() ?? json['requestNumber']?.toString() ?? '', id: json['id']?.toString() ?? json['requestNumber']?.toString() ?? '',
requestType: _parseRequestType(json), requestType: _parseRequestType(json),
employeeId: json['employeeId'] ?? '', employeeId: json['employeeId'] ?? '',
requestNumber: json['requestNumber'] ?? '',
fullName: json['userName'] ?? json['fullName'] ?? '', fullName: json['userName'] ?? json['fullName'] ?? '',
emailAddress: json['email'] ?? json['emailAddress'] ?? '', emailAddress: json['email'] ?? json['emailAddress'] ?? '',
mobileNumber: json['mobileNumber'] ?? '', mobileNumber: json['mobileNumber'] ?? '',
@ -112,6 +116,7 @@ class SiteRequestModel {
return SiteRequestModel( return SiteRequestModel(
id: id ?? this.id, id: id ?? this.id,
requestType: requestType ?? this.requestType, requestType: requestType ?? this.requestType,
requestNumber: requestNumber ?? this.requestNumber,
employeeId: employeeId ?? this.employeeId, employeeId: employeeId ?? this.employeeId,
fullName: fullName ?? this.fullName, fullName: fullName ?? this.fullName,
emailAddress: emailAddress ?? this.emailAddress, emailAddress: emailAddress ?? this.emailAddress,

@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/new_models/site.dart'; import 'package:test_sa/models/new_models/site.dart';
import 'package:test_sa/models/new_models/department.dart'; import 'package:test_sa/models/new_models/department.dart';
@ -95,7 +96,6 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
_provider = Provider.of<SiteRequestProvider>(context); _provider = Provider.of<SiteRequestProvider>(context);
_userProvider = Provider.of<UserProvider>(context); _userProvider = Provider.of<UserProvider>(context);
return Scaffold( return Scaffold(
backgroundColor: AppColor.scaffoldBackground(context), backgroundColor: AppColor.scaffoldBackground(context),
appBar: const DefaultAppBar(title: "Site Request"), appBar: const DefaultAppBar(title: "Site Request"),
@ -109,34 +109,23 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.all(16.toScreenWidth), padding: EdgeInsets.all(16.toScreenWidth),
child: Column( child: Column(
spacing: 12,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildUserInfoCard(), _buildUserInfoCard(),
16.height,
_buildRequestTypeDropdown(), _buildRequestTypeDropdown(),
16.height,
if (_selectedRequestType != null) ...[ if (_selectedRequestType != null) ...[
if (_isChangeSiteRequest()) ...[ if (_isChangeSiteRequest()) ...[
_buildOldSiteDropdown(), _buildOldSiteDropdown(),
16.height,
// Show replacement containers for each old site // Show replacement containers for each old site
if (_selectedOldSites.isNotEmpty) ...[ if (_selectedOldSites.isNotEmpty) ...[
Text( InfoHeader16Widget("Site Replacements"),
"Site Replacements",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColor.textStyleColor(context),
),
),
8.height,
..._buildSiteReplacementContainers(), ..._buildSiteReplacementContainers(),
], ],
] else if (_isRemoveSiteRequest()) ...[ ] else if (_isRemoveSiteRequest()) ...[
_buildRemoveSiteDropdown(), _buildRemoveSiteDropdown(),
] else ...[ ] else ...[
_buildSiteMultiSelect(), _buildSiteMultiSelect(),
16.height,
_buildDepartmentMultiSelect(), _buildDepartmentMultiSelect(),
], ],
], ],
@ -173,8 +162,7 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
title: "Type of Site Request", title: "Type of Site Request",
initialValue: _selectedRequestType, initialValue: _selectedRequestType,
staticData: _provider.requestTypes, staticData: _provider.requestTypes,
showAsFullScreenDialog: false, showAsFullScreenDialog: true,
showAsBottomSheet: true,
onSelect: (value) async { onSelect: (value) async {
setState(() { setState(() {
_selectedRequestType = value; _selectedRequestType = value;
@ -211,7 +199,7 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
InfoHeader16Widget("Requester Information"), InfoHeader16Widget("Requester Information"),
8.height, // 8.height,
InfoTextWidget( InfoTextWidget(
label: "Full Name", label: "Full Name",
value: _provider.getCurrentUserFullName(_userProvider), value: _provider.getCurrentUserFullName(_userProvider),
@ -299,7 +287,7 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
)) ))
.toList(), .toList(),
staticData: _provider.userCurrentSites, staticData: _provider.userCurrentSites,
showAsBottomSheet: true, showAsFullScreenDialog: true,
backgroundColor: context.isDark ? AppColor.neutral60 : Colors.white, backgroundColor: context.isDark ? AppColor.neutral60 : Colors.white,
showShadow: true, showShadow: true,
height: 56.toScreenHeight, height: 56.toScreenHeight,
@ -391,15 +379,10 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
return Consumer<SiteProvider>( return Consumer<SiteProvider>(
builder: (context, siteProvider, child) { builder: (context, siteProvider, child) {
// Get ALL user's current site IDs to exclude from new site selection // Get ALL user's current site IDs to exclude from new site selection
final allUserCurrentSiteIds = _provider.userCurrentSites final allUserCurrentSiteIds = _provider.userCurrentSites.map((s) => s.identifier ?? '').toSet();
.map((s) => s.identifier ?? '')
.toSet();
// Filter out already selected new sites and ALL user's current sites // Filter out already selected new sites and ALL user's current sites
final alreadySelectedNewSites = _siteReplacements.values final alreadySelectedNewSites = _siteReplacements.values.where((r) => r.newSite != null).map((r) => r.newSite!.identifier).toSet();
.where((r) => r.newSite != null)
.map((r) => r.newSite!.identifier)
.toSet();
final filteredSites = siteProvider.items.where((site) { final filteredSites = siteProvider.items.where((site) {
final siteId = site.identifier ?? ''; final siteId = site.identifier ?? '';
@ -408,83 +391,53 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
if (allUserCurrentSiteIds.contains(siteId)) return false; if (allUserCurrentSiteIds.contains(siteId)) return false;
// Exclude already selected new sites (except current one) // Exclude already selected new sites (except current one)
if (replacement?.newSite?.identifier != siteId && if (replacement?.newSite?.identifier != siteId && alreadySelectedNewSites.contains(siteId)) {
alreadySelectedNewSites.contains(siteId)) {
return false; return false;
} }
return true; return true;
}).toList(); }).toList();
return Container( return Column(
padding: EdgeInsets.all(16.toScreenWidth), mainAxisSize: MainAxisSize.min,
margin: EdgeInsets.only(bottom: 16.toScreenHeight), crossAxisAlignment: CrossAxisAlignment.start,
decoration: BoxDecoration( spacing: 12,
color: AppColor.background(context), children: [
borderRadius: BorderRadius.circular(8), Text(
border: Border.all( oldSite.name ?? 'Unknown Site',
color: AppColor.neutral40.withValues(alpha: 0.3), style: AppTextStyles.bodyText,
width: 1,
), ),
boxShadow: [ SingleItemDropDownMenu<Site, NullableLoadingProvider>(
BoxShadow( context: context,
color: Colors.black.withValues(alpha: 0.05), title: "New Site",
blurRadius: 8, initialValue: replacement?.newSite,
offset: const Offset(0, 2), staticData: filteredSites,
), showAsFullScreenDialog: true,
], backgroundColor: context.isDark ? AppColor.neutral60 : Colors.white,
), showShadow: true,
child: Column( height: 56.toScreenHeight,
crossAxisAlignment: CrossAxisAlignment.start, onSelect: (value) {
children: [ setState(() {
// Old site name display (simple text only) if (value != null) {
Text( // Remove old site ID from loaded set to allow fetching new departments
oldSite.name ?? 'Unknown Site', if (replacement?.newSite != null) {
style: TextStyle( final oldNewSiteId = int.tryParse(replacement!.newSite!.identifier ?? '0') ?? 0;
fontSize: 16, _loadedDepartmentSiteIds.remove(oldNewSiteId);
fontWeight: FontWeight.w600,
color: AppColor.textStyleColor(context),
),
),
16.height,
// New site single selection
SingleItemDropDownMenu<Site, NullableLoadingProvider>(
context: context,
title: "New Site",
initialValue: replacement?.newSite,
staticData: filteredSites,
showAsBottomSheet: true,
backgroundColor: context.isDark ? AppColor.neutral60 : Colors.white,
showShadow: true,
height: 56.toScreenHeight,
onSelect: (value) {
setState(() {
if (value != null) {
// Remove old site ID from loaded set to allow fetching new departments
if (replacement?.newSite != null) {
final oldNewSiteId = int.tryParse(replacement!.newSite!.identifier ?? '0') ?? 0;
_loadedDepartmentSiteIds.remove(oldNewSiteId);
}
_siteReplacements[oldSiteId] = SiteReplacement(
newSite: value,
departments: [], // Clear departments when new site changes
);
} }
});
},
),
16.height, _siteReplacements[oldSiteId] = SiteReplacement(
newSite: value,
// Department multi-select based on new site departments: [], // Clear departments when new site changes
if (replacement?.newSite != null) ...[ );
_buildDepartmentDropdownForSite(oldSiteId, replacement!.newSite!), }
], });
},
),
if (replacement?.newSite != null) ...[
_buildDepartmentDropdownForSite(oldSiteId, replacement!.newSite!),
], ],
), ],
); ).toShadowContainer(context);
}, },
); );
}).toList(); }).toList();
@ -515,7 +468,7 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
title: "Departments", title: "Departments",
initialValue: replacement?.departments ?? [], initialValue: replacement?.departments ?? [],
staticData: departmentProvider.items, staticData: departmentProvider.items,
showAsBottomSheet: true, showAsFullScreenDialog: true,
backgroundColor: context.isDark ? AppColor.neutral60 : Colors.white, backgroundColor: context.isDark ? AppColor.neutral60 : Colors.white,
showShadow: true, showShadow: true,
height: 56.toScreenHeight, height: 56.toScreenHeight,

@ -129,16 +129,14 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
return RefreshIndicator( return RefreshIndicator(
onRefresh: _refreshData, onRefresh: _refreshData,
child: ListView.builder( child: ListView.separated(
separatorBuilder: (context, index) => 12.height,
controller: _scrollController, controller: _scrollController,
padding: EdgeInsets.all(16.toScreenWidth), padding: EdgeInsets.all(16.toScreenWidth),
itemCount: _provider.requestHistory.length + (_provider.isLoadingMore ? 1 : 0), itemCount: _provider.requestHistory.length + (_provider.isLoadingMore ? 1 : 0),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == _provider.requestHistory.length) { if (index == _provider.requestHistory.length) {
return Padding( return const Center(child: CircularProgressIndicator());
padding: EdgeInsets.symmetric(vertical: 16.toScreenHeight),
child: const Center(child: CircularProgressIndicator()),
);
} }
return _buildRequestCard(_provider.requestHistory[index]); return _buildRequestCard(_provider.requestHistory[index]);
@ -153,22 +151,18 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_buildRequestHeader(request), _buildRequestHeader(request),
InfoHeaderWidget("Site Request #${request.id ?? 'N/A'}"), InfoHeaderWidget("Request No: ${request.requestNumber ?? '-'}"),
_buildUserInfo(request), _buildUserInfo(request),
// Show old sites if present (for Change Site requests) // Show old sites if present (for Change Site requests)
if (request.oldSiteNames != null && request.oldSiteNames!.isNotEmpty) if (request.oldSiteNames != null && request.oldSiteNames!.isNotEmpty) _buildOldSitesSection(request),
_buildOldSitesSection(request),
// Show new/current sites only if they are not empty and not all null // Show new/current sites only if they are not empty and not all null
if (_hasValidSiteNames(request.siteNames)) if (_hasValidSiteNames(request.siteNames)) _buildSitesSection(request),
_buildSitesSection(request),
// Show old departments if present (for Change Site requests) // Show old departments if present (for Change Site requests)
if (request.oldDepartmentNames != null && request.oldDepartmentNames!.isNotEmpty) if (request.oldDepartmentNames != null && request.oldDepartmentNames!.isNotEmpty) _buildOldDepartmentsSection(request),
_buildOldDepartmentsSection(request),
// Show new departments only if they are not empty and not all null // Show new departments only if they are not empty and not all null
if (_hasValidDepartmentNames(request.departmentNames)) if (_hasValidDepartmentNames(request.departmentNames)) _buildDepartmentsSection(request),
_buildDepartmentsSection(request),
], ],
).toShadowContainer(context).paddingOnly(bottom: 16); ).toShadowContainer(context);
} }
// Helper method to check if site names list has valid (non-null, non-empty) values // Helper method to check if site names list has valid (non-null, non-empty) values
@ -178,9 +172,7 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
// Helper method to check if department names list has valid (non-null, non-empty) values // Helper method to check if department names list has valid (non-null, non-empty) values
bool _hasValidDepartmentNames(List<String>? departmentNames) { bool _hasValidDepartmentNames(List<String>? departmentNames) {
return departmentNames != null && return departmentNames != null && departmentNames.isNotEmpty && departmentNames.any((name) => name.isNotEmpty);
departmentNames.isNotEmpty &&
departmentNames.any((name) => name.isNotEmpty);
} }
Widget _buildRequestHeader(SiteRequestModel request) { Widget _buildRequestHeader(SiteRequestModel request) {
@ -212,7 +204,9 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
InfoTextWidget(label: "Employee ID", value: request.employeeId), InfoTextWidget(label: "Employee ID", value: request.employeeId),
InfoTextWidget(label: "Email", value: request.emailAddress), InfoTextWidget(label: "Email", value: request.emailAddress),
InfoTextWidget(label: "Mobile", value: request.mobileNumber, showEmptyValue: true), InfoTextWidget(label: "Mobile", value: request.mobileNumber, showEmptyValue: true),
if (request.extensionNumber != null && request.extensionNumber!.isNotEmpty) InfoTextWidget(label: "Extension", value: request.extensionNumber!, showEmptyValue: true), if (request.extensionNumber != null && request.extensionNumber!.isNotEmpty) ...[
InfoTextWidget(label: "Extension", value: request.extensionNumber!, showEmptyValue: true),
],
InfoTextWidget(label: "Role", value: request.role), InfoTextWidget(label: "Role", value: request.role),
], ],
); );
@ -222,13 +216,8 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( InfoTextLabelWidget(
"Old Sites:", label: "Old Sites:",
style: TextStyle(
fontSize: 14,
color: AppColor.labelTextStyleColor(context),
fontWeight: FontWeight.w500,
),
), ),
8.height, 8.height,
Wrap( Wrap(
@ -242,9 +231,7 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
Widget _buildSitesSection(SiteRequestModel request) { Widget _buildSitesSection(SiteRequestModel request) {
// Determine label based on whether old sites exist // Determine label based on whether old sites exist
final label = (request.oldSiteNames != null && request.oldSiteNames!.isNotEmpty) final label = (request.oldSiteNames != null && request.oldSiteNames!.isNotEmpty) ? "New Sites:" : "Sites:";
? "New Sites:"
: "Sites:";
// Filter out null or empty site names // Filter out null or empty site names
final validSiteNames = request.siteNames.where((site) => site.isNotEmpty).toList(); final validSiteNames = request.siteNames.where((site) => site.isNotEmpty).toList();
@ -252,13 +239,8 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( InfoTextLabelWidget(
label, label: label,
style: TextStyle(
fontSize: 14,
color: AppColor.labelTextStyleColor(context),
fontWeight: FontWeight.w500,
),
), ),
8.height, 8.height,
Wrap( Wrap(
@ -267,7 +249,7 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
children: validSiteNames.map((site) => _buildChip(site)).toList(), children: validSiteNames.map((site) => _buildChip(site)).toList(),
), ),
], ],
).paddingOnly(bottom: 8); );
} }
Widget _buildOldDepartmentsSection(SiteRequestModel request) { Widget _buildOldDepartmentsSection(SiteRequestModel request) {
@ -294,9 +276,7 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
Widget _buildDepartmentsSection(SiteRequestModel request) { Widget _buildDepartmentsSection(SiteRequestModel request) {
// Determine label based on whether old departments exist // Determine label based on whether old departments exist
final label = (request.oldDepartmentNames != null && request.oldDepartmentNames!.isNotEmpty) final label = (request.oldDepartmentNames != null && request.oldDepartmentNames!.isNotEmpty) ? "New Departments:" : "Departments:";
? "New Departments:"
: "Departments:";
// Filter out null or empty department names // Filter out null or empty department names
final validDepartmentNames = request.departmentNames!.where((dept) => dept.isNotEmpty).toList(); final validDepartmentNames = request.departmentNames!.where((dept) => dept.isNotEmpty).toList();
@ -324,20 +304,14 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
Widget _buildChip(String label, {bool isOld = false}) { Widget _buildChip(String label, {bool isOld = false}) {
return Chip( return Chip(
backgroundColor: isOld backgroundColor: isOld ? AppColor.redStatusColor.withValues(alpha: 0.1) : AppColor.backgroundTabBarColor(context),
? AppColor.redStatusColor.withValues(alpha: 0.1)
: AppColor.backgroundTabBarColor(context),
side: BorderSide( side: BorderSide(
color: isOld color: isOld ? AppColor.redStatusColor.withValues(alpha: 0.3) : AppColor.border2Color(context),
? AppColor.redStatusColor.withValues(alpha: 0.3)
: AppColor.border2Color(context),
), ),
label: Text(label), label: Text(label),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
labelStyle: AppTextStyles.tinyFont2.copyWith( labelStyle: AppTextStyles.tinyFont2.copyWith(
color: isOld color: isOld ? AppColor.redStatusTextColor : AppColor.labelTextStyleColor(context),
? AppColor.redStatusTextColor
: AppColor.labelTextStyleColor(context),
), ),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: EdgeInsets.symmetric(horizontal: 8.toScreenWidth, vertical: 4.toScreenHeight), padding: EdgeInsets.symmetric(horizontal: 8.toScreenWidth, vertical: 4.toScreenHeight),

@ -239,7 +239,7 @@ class _LoginPageState extends State<LoginPage> {
}), }),
" or ".bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral50), " or ".bodyText(context).custom(color: context.isDark ? AppColor.primary50 : AppColor.neutral50),
"Request a Demo".bodyText(context).custom(color: AppColor.primary10, fontWeight: FontWeight.w600).onPress(() { "Request a Demo".bodyText(context).custom(color: AppColor.primary10, fontWeight: FontWeight.w600).onPress(() {
Navigator.pushNamed(context, DemoRequestFormScreen.routeName); Navigator.pushNamed(context, ApplicationDemoRequestFormScreen.routeName);
}), }),
], ],
), ),

@ -59,3 +59,7 @@ class AAlertDialog extends StatelessWidget {
); );
} }
} }

Loading…
Cancel
Save