improvements

export_module_ui_ux_enhancement_signup
WaseemAbbasi22 1 week ago
parent 61472df110
commit 8ad2e79b1e

@ -14,9 +14,11 @@ class SiteRequestModel {
final List<String>? oldSiteNames; // For Change Site request
final List<String>? departmentIds;
final List<String>? departmentNames;
final List<String>? oldDepartmentIds; // For Change Site request - old departments
final List<String>? oldDepartmentNames; // For Change Site request - old departments
final String role;
final String status; // Pending, Approved, Rejected
final DateTime requestDate;
final String ?status; // Pending, Approved, Rejected
final DateTime? requestDate;
SiteRequestModel({
this.id,
@ -32,9 +34,11 @@ class SiteRequestModel {
this.oldSiteNames,
this.departmentIds,
this.departmentNames,
this.oldDepartmentIds,
this.oldDepartmentNames,
required this.role,
required this.status,
required this.requestDate,
this.status,
this.requestDate,
});
/// Converts model to JSON for API submission
@ -54,8 +58,8 @@ class SiteRequestModel {
'departmentIds': departmentIds,
'departmentNames': departmentNames,
'role': role,
'status': status,
'requestDate': requestDate.toIso8601String(),
// 'status': status,
// 'requestDate': requestDate?.toIso8601String(),
};
}
@ -78,6 +82,8 @@ class SiteRequestModel {
oldSiteNames: sitesData['oldSiteNames'],
departmentIds: departmentsData['departmentIds'],
departmentNames: departmentsData['departmentNames'],
oldDepartmentIds: departmentsData['oldDepartmentIds'],
oldDepartmentNames: departmentsData['oldDepartmentNames'],
role: json['role'] ?? '',
status: _parseStatus(json),
requestDate: _parseDate(json),
@ -156,6 +162,8 @@ class SiteRequestModel {
static Map<String, List<String>?> _parseDepartmentsFromJson(Map<String, dynamic> json) {
List<String>? departmentIds;
List<String>? departmentNames;
List<String>? oldDepartmentIds;
List<String>? oldDepartmentNames;
if (json['departments'] != null &&
json['departments'] is List &&
@ -165,12 +173,22 @@ class SiteRequestModel {
for (var dept in json['departments']) {
departmentIds.add(dept['departmentId']?.toString() ?? '');
departmentNames.add(dept['departmentName'] ?? '');
// Check for old department data in Change Site requests
if (dept['oldDepartmentId'] != null && dept['oldDepartmentId'] != 0) {
oldDepartmentIds ??= [];
oldDepartmentNames ??= [];
oldDepartmentIds.add(dept['oldDepartmentId']?.toString() ?? '');
oldDepartmentNames.add(dept['oldDepartmentName'] ?? '');
}
}
}
return {
'departmentIds': departmentIds,
'departmentNames': departmentNames,
'oldDepartmentIds': oldDepartmentIds,
'oldDepartmentNames': oldDepartmentNames,
};
}

@ -42,7 +42,7 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
RequestTypeModel? _selectedRequestType;
List<Site> _selectedSites = [];
List<Site> _selectedOldSites = []; // For Change Site - old sites
List<Site> _selectedOldSites = []; // For Change Site - old sites
List<Department> _selectedDepartments = [];
// Map to track replacements: oldSiteId -> {newSite, departments}
@ -99,69 +99,71 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
return Scaffold(
backgroundColor: AppColor.scaffoldBackground(context),
appBar: const DefaultAppBar(title: "Site Request"),
body: _isLoadingData ?const ALoading().center:Column(
children: [
Expanded(
child: Form(
key: _formKey,
child: SingleChildScrollView(
padding: EdgeInsets.all(16.toScreenWidth),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildUserInfoCard(),
16.height,
_buildRequestTypeDropdown(),
16.height,
if (_selectedRequestType != null) ...[
if (_isChangeSiteRequest()) ...[
_buildOldSiteDropdown(),
16.height,
// Show replacement containers for each old site
if (_selectedOldSites.isNotEmpty) ...[
Text(
"Site Replacements",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColor.textStyleColor(context),
),
),
8.height,
..._buildSiteReplacementContainers(),
body: _isLoadingData
? const ALoading().center
: Column(
children: [
Expanded(
child: Form(
key: _formKey,
child: SingleChildScrollView(
padding: EdgeInsets.all(16.toScreenWidth),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildUserInfoCard(),
16.height,
_buildRequestTypeDropdown(),
16.height,
if (_selectedRequestType != null) ...[
if (_isChangeSiteRequest()) ...[
_buildOldSiteDropdown(),
16.height,
// Show replacement containers for each old site
if (_selectedOldSites.isNotEmpty) ...[
Text(
"Site Replacements",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColor.textStyleColor(context),
),
),
8.height,
..._buildSiteReplacementContainers(),
],
] else if (_isRemoveSiteRequest()) ...[
_buildRemoveSiteDropdown(),
] else ...[
_buildSiteMultiSelect(),
16.height,
_buildDepartmentMultiSelect(),
],
],
],
] else if (_isRemoveSiteRequest()) ...[
_buildRemoveSiteDropdown(),
] else ...[
_buildSiteMultiSelect(),
16.height,
_buildDepartmentMultiSelect(),
],
],
],
),
),
),
),
),
),
),
if (_selectedRequestType != null)
Container(
padding: EdgeInsets.all(16.toScreenWidth),
decoration: BoxDecoration(
color: AppColor.background(context),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, -2),
if (_selectedRequestType != null)
Container(
padding: EdgeInsets.all(16.toScreenWidth),
decoration: BoxDecoration(
color: AppColor.background(context),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, -2),
),
],
),
child: SafeArea(
child: _buildSubmitButton(),
),
),
],
),
child: SafeArea(
child: _buildSubmitButton(),
),
],
),
],
),
);
}
@ -242,11 +244,13 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
Widget _buildOldSiteDropdown() {
return MultipleItemDropDownMenu<SiteRequestSiteModel, NullableLoadingProvider>(
context: context,
title: "Old Site (Current Site)",
initialValue: _selectedOldSites.map((s) => SiteRequestSiteModel(
id: s.identifier ?? '',
name: s.name ?? '',
)).toList(),
title: "Current Sites",
initialValue: _selectedOldSites
.map((s) => SiteRequestSiteModel(
id: s.identifier ?? '',
name: s.name ?? '',
))
.toList(),
staticData: _provider.userCurrentSites,
showAsBottomSheet: true,
backgroundColor: context.isDark ? AppColor.neutral60 : Colors.white,
@ -288,10 +292,12 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
return MultipleItemDropDownMenu<SiteRequestSiteModel, NullableLoadingProvider>(
context: context,
title: "Site to Remove",
initialValue: _selectedSites.map((s) => SiteRequestSiteModel(
id: s.identifier ?? '',
name: s.name ?? '',
)).toList(),
initialValue: _selectedSites
.map((s) => SiteRequestSiteModel(
id: s.identifier ?? '',
name: s.name ?? '',
))
.toList(),
staticData: _provider.userCurrentSites,
showAsBottomSheet: true,
backgroundColor: context.isDark ? AppColor.neutral60 : Colors.white,
@ -384,20 +390,29 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
return Consumer<SiteProvider>(
builder: (context, siteProvider, child) {
// Filter out already selected new sites and the current old site
// Get ALL user's current site IDs to exclude from new site selection
final allUserCurrentSiteIds = _provider.userCurrentSites
.map((s) => s.identifier ?? '')
.toSet();
// Filter out already selected new sites and ALL user's current sites
final alreadySelectedNewSites = _siteReplacements.values
.where((r) => r.newSite != null)
.map((r) => r.newSite!.identifier)
.toSet();
final filteredSites = siteProvider.items.where((site) {
// Exclude the old site itself
if (site.identifier == oldSiteId) return false;
final siteId = site.identifier ?? '';
// Exclude ALL user's current sites (not just selected old sites)
if (allUserCurrentSiteIds.contains(siteId)) return false;
// Exclude already selected new sites (except current one)
if (replacement?.newSite?.identifier != site.identifier &&
alreadySelectedNewSites.contains(site.identifier)) {
if (replacement?.newSite?.identifier != siteId &&
alreadySelectedNewSites.contains(siteId)) {
return false;
}
return true;
}).toList();
@ -599,8 +614,8 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
departmentIds: departmentIds.isNotEmpty ? departmentIds : null,
departmentNames: departmentNames.isNotEmpty ? departmentNames : null,
role: _provider.getCurrentUserRole(_userProvider),
status: 'Pending',
requestDate: DateTime.now(),
// status: 'Pending',
// requestDate: DateTime.now(),
);
final success = await _provider.submitSiteRequest(request, _userProvider);
@ -647,8 +662,8 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
departmentIds: _selectedDepartments.isNotEmpty ? _selectedDepartments.map((d) => d.identifier ?? '').toList() : null,
departmentNames: _selectedDepartments.isNotEmpty ? _selectedDepartments.map((d) => d.name ?? '').toList() : null,
role: _provider.getCurrentUserRole(_userProvider),
status: 'Pending',
requestDate: DateTime.now(),
// status: 'Pending',
// requestDate: DateTime.now(),
);
final success = await _provider.submitSiteRequest(request, _userProvider);
@ -663,7 +678,9 @@ class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
// API will handle success/error toast messages
if (success && mounted) {
Navigator.pushReplacementNamed(context, SiteRequestHistoryScreen.routeName);
UserProvider userProvider = Provider.of<UserProvider>(context, listen: false);
await _provider.fetchSiteRequests(pageNumber: 1, pageSize: 10, userProvider: userProvider);
Navigator.pop(context);
}
}
}

@ -67,7 +67,13 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
}
Future<void> _refreshData() async {
setState(() {
_isLoadingData = true;
});
await _provider.refreshSiteRequests(userProvider: _userProvider);
if (mounted) {
setState(() => _isLoadingData = false);
}
}
@override
@ -149,21 +155,42 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
_buildRequestHeader(request),
InfoHeaderWidget("Site Request #${request.id ?? 'N/A'}"),
_buildUserInfo(request),
_buildSitesSection(request),
if (request.departmentNames != null && request.departmentNames!.isNotEmpty)
// Show old sites if present (for Change Site requests)
if (request.oldSiteNames != null && request.oldSiteNames!.isNotEmpty)
_buildOldSitesSection(request),
// Show new/current sites only if they are not empty and not all null
if (_hasValidSiteNames(request.siteNames))
_buildSitesSection(request),
// Show old departments if present (for Change Site requests)
if (request.oldDepartmentNames != null && request.oldDepartmentNames!.isNotEmpty)
_buildOldDepartmentsSection(request),
// Show new departments only if they are not empty and not all null
if (_hasValidDepartmentNames(request.departmentNames))
_buildDepartmentsSection(request),
],
).toShadowContainer(context).paddingOnly(bottom: 16);
}
// Helper method to check if site names list has valid (non-null, non-empty) values
bool _hasValidSiteNames(List<String> siteNames) {
return siteNames.isNotEmpty && siteNames.any((name) => name.isNotEmpty);
}
// Helper method to check if department names list has valid (non-null, non-empty) values
bool _hasValidDepartmentNames(List<String>? departmentNames) {
return departmentNames != null &&
departmentNames.isNotEmpty &&
departmentNames.any((name) => name.isNotEmpty);
}
Widget _buildRequestHeader(SiteRequestModel request) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusLabel(
label: request.status,
textColor: _getStatusTextColor(request.status),
backgroundColor: _getStatusColor(request.status),
textColor: _getStatusTextColor(request.status ?? ''),
backgroundColor: _getStatusColor(request.status ?? ''),
),
8.width,
StatusLabel(
@ -185,19 +212,70 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
InfoTextWidget(label: "Employee ID", value: request.employeeId),
InfoTextWidget(label: "Email", value: request.emailAddress),
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),
],
);
}
Widget _buildOldSitesSection(SiteRequestModel request) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Old Sites:",
style: TextStyle(
fontSize: 14,
color: AppColor.labelTextStyleColor(context),
fontWeight: FontWeight.w500,
),
),
8.height,
Wrap(
runSpacing: 4.toScreenHeight,
spacing: 4.toScreenWidth,
children: request.oldSiteNames!.map((site) => _buildChip(site, isOld: true)).toList(),
),
],
).paddingOnly(bottom: 8);
}
Widget _buildSitesSection(SiteRequestModel request) {
// Determine label based on whether old sites exist
final label = (request.oldSiteNames != null && request.oldSiteNames!.isNotEmpty)
? "New Sites:"
: "Sites:";
// Filter out null or empty site names
final validSiteNames = request.siteNames.where((site) => site.isNotEmpty).toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 14,
color: AppColor.labelTextStyleColor(context),
fontWeight: FontWeight.w500,
),
),
8.height,
Wrap(
runSpacing: 4.toScreenHeight,
spacing: 4.toScreenWidth,
children: validSiteNames.map((site) => _buildChip(site)).toList(),
),
],
).paddingOnly(bottom: 8);
}
Widget _buildOldDepartmentsSection(SiteRequestModel request) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Sites:",
"Old Departments:",
style: TextStyle(
fontSize: 14,
color: AppColor.labelTextStyleColor(context),
@ -208,18 +286,26 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
Wrap(
runSpacing: 4.toScreenHeight,
spacing: 4.toScreenWidth,
children: request.siteNames.map((site) => _buildChip(site)).toList(),
children: request.oldDepartmentNames!.map((dept) => _buildChip(dept, isOld: true)).toList(),
),
],
).paddingOnly(bottom: 8);
}
Widget _buildDepartmentsSection(SiteRequestModel request) {
// Determine label based on whether old departments exist
final label = (request.oldDepartmentNames != null && request.oldDepartmentNames!.isNotEmpty)
? "New Departments:"
: "Departments:";
// Filter out null or empty department names
final validDepartmentNames = request.departmentNames!.where((dept) => dept.isNotEmpty).toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Departments:",
label,
style: TextStyle(
fontSize: 14,
color: AppColor.labelTextStyleColor(context),
@ -230,20 +316,28 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
Wrap(
runSpacing: 4.toScreenHeight,
spacing: 4.toScreenWidth,
children: request.departmentNames!.map((dept) => _buildChip(dept)).toList(),
children: validDepartmentNames.map((dept) => _buildChip(dept)).toList(),
),
],
);
}
Widget _buildChip(String label) {
Widget _buildChip(String label, {bool isOld = false}) {
return Chip(
backgroundColor: AppColor.backgroundTabBarColor(context),
side: BorderSide(color: AppColor.border2Color(context)),
backgroundColor: isOld
? AppColor.redStatusColor.withValues(alpha: 0.1)
: AppColor.backgroundTabBarColor(context),
side: BorderSide(
color: isOld
? AppColor.redStatusColor.withValues(alpha: 0.3)
: AppColor.border2Color(context),
),
label: Text(label),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)),
labelStyle: AppTextStyles.tinyFont2.copyWith(
color: AppColor.labelTextStyleColor(context),
color: isOld
? AppColor.redStatusTextColor
: AppColor.labelTextStyleColor(context),
),
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
padding: EdgeInsets.symmetric(horizontal: 8.toScreenWidth, vertical: 4.toScreenHeight),

Loading…
Cancel
Save