site implementation inprogress
parent
c6166d1e75
commit
d9eb1ae835
@ -0,0 +1,16 @@
|
||||
import 'package:test_sa/models/base.dart';
|
||||
|
||||
class SiteRequestSiteModel extends Base {
|
||||
SiteRequestSiteModel({
|
||||
required String id,
|
||||
required String name,
|
||||
}) : super(identifier: id, name: name);
|
||||
}
|
||||
|
||||
class SiteRequestDepartmentModel extends Base {
|
||||
SiteRequestDepartmentModel({
|
||||
required String id,
|
||||
required String name,
|
||||
}) : super(identifier: id, name: name);
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
class MockUserModel {
|
||||
final String employeeId;
|
||||
final String fullName;
|
||||
final String emailAddress;
|
||||
final String mobileNumber;
|
||||
final String extensionNumber;
|
||||
final String role;
|
||||
final List<String> assignedSites;
|
||||
|
||||
MockUserModel({
|
||||
required this.employeeId,
|
||||
required this.fullName,
|
||||
required this.emailAddress,
|
||||
required this.mobileNumber,
|
||||
required this.extensionNumber,
|
||||
required this.role,
|
||||
required this.assignedSites,
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import 'package:test_sa/models/base.dart';
|
||||
|
||||
class RequestTypeModel extends Base {
|
||||
RequestTypeModel({
|
||||
required String id,
|
||||
required String name,
|
||||
}) : super(identifier: id, name: name);
|
||||
}
|
||||
|
||||
@ -0,0 +1,106 @@
|
||||
class SiteRequestModel {
|
||||
final String? id;
|
||||
final String requestType; // Add Site, Change Site, Remove Site
|
||||
final String employeeId;
|
||||
final String fullName;
|
||||
final String emailAddress;
|
||||
final String mobileNumber;
|
||||
final String? extensionNumber;
|
||||
final List<String> siteIds;
|
||||
final List<String> siteNames;
|
||||
final List<String>? departmentIds;
|
||||
final List<String>? departmentNames;
|
||||
final String role;
|
||||
final String status; // Pending, Approved, Rejected
|
||||
final DateTime requestDate;
|
||||
|
||||
SiteRequestModel({
|
||||
this.id,
|
||||
required this.requestType,
|
||||
required this.employeeId,
|
||||
required this.fullName,
|
||||
required this.emailAddress,
|
||||
required this.mobileNumber,
|
||||
this.extensionNumber,
|
||||
required this.siteIds,
|
||||
required this.siteNames,
|
||||
this.departmentIds,
|
||||
this.departmentNames,
|
||||
required this.role,
|
||||
required this.status,
|
||||
required this.requestDate,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'requestType': requestType,
|
||||
'employeeId': employeeId,
|
||||
'fullName': fullName,
|
||||
'emailAddress': emailAddress,
|
||||
'mobileNumber': mobileNumber,
|
||||
'extensionNumber': extensionNumber,
|
||||
'siteIds': siteIds,
|
||||
'siteNames': siteNames,
|
||||
'departmentIds': departmentIds,
|
||||
'departmentNames': departmentNames,
|
||||
'role': role,
|
||||
'status': status,
|
||||
'requestDate': requestDate.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
factory SiteRequestModel.fromJson(Map<String, dynamic> json) {
|
||||
return SiteRequestModel(
|
||||
id: json['id'],
|
||||
requestType: json['requestType'] ?? '',
|
||||
employeeId: json['employeeId'] ?? '',
|
||||
fullName: json['fullName'] ?? '',
|
||||
emailAddress: json['emailAddress'] ?? '',
|
||||
mobileNumber: json['mobileNumber'] ?? '',
|
||||
extensionNumber: json['extensionNumber'],
|
||||
siteIds: List<String>.from(json['siteIds'] ?? []),
|
||||
siteNames: List<String>.from(json['siteNames'] ?? []),
|
||||
departmentIds: json['departmentIds'] != null ? List<String>.from(json['departmentIds']) : null,
|
||||
departmentNames: json['departmentNames'] != null ? List<String>.from(json['departmentNames']) : null,
|
||||
role: json['role'] ?? '',
|
||||
status: json['status'] ?? 'Pending',
|
||||
requestDate: json['requestDate'] != null ? DateTime.parse(json['requestDate']) : DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
SiteRequestModel copyWith({
|
||||
String? id,
|
||||
String? requestType,
|
||||
String? employeeId,
|
||||
String? fullName,
|
||||
String? emailAddress,
|
||||
String? mobileNumber,
|
||||
String? extensionNumber,
|
||||
List<String>? siteIds,
|
||||
List<String>? siteNames,
|
||||
List<String>? departmentIds,
|
||||
List<String>? departmentNames,
|
||||
String? role,
|
||||
String? status,
|
||||
DateTime? requestDate,
|
||||
}) {
|
||||
return SiteRequestModel(
|
||||
id: id ?? this.id,
|
||||
requestType: requestType ?? this.requestType,
|
||||
employeeId: employeeId ?? this.employeeId,
|
||||
fullName: fullName ?? this.fullName,
|
||||
emailAddress: emailAddress ?? this.emailAddress,
|
||||
mobileNumber: mobileNumber ?? this.mobileNumber,
|
||||
extensionNumber: extensionNumber ?? this.extensionNumber,
|
||||
siteIds: siteIds ?? this.siteIds,
|
||||
siteNames: siteNames ?? this.siteNames,
|
||||
departmentIds: departmentIds ?? this.departmentIds,
|
||||
departmentNames: departmentNames ?? this.departmentNames,
|
||||
role: role ?? this.role,
|
||||
status: status ?? this.status,
|
||||
requestDate: requestDate ?? this.requestDate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,193 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:test_sa/modules/site_request_module/models/dropdown_models.dart';
|
||||
import 'package:test_sa/modules/site_request_module/models/mock_user_model.dart';
|
||||
import 'package:test_sa/modules/site_request_module/models/request_type_model.dart';
|
||||
import 'package:test_sa/modules/site_request_module/models/site_request_model.dart';
|
||||
|
||||
class SiteRequestProvider extends ChangeNotifier {
|
||||
bool _loading = false;
|
||||
bool get loading => _loading;
|
||||
|
||||
set loading(bool value) {
|
||||
_loading = value;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
String? _errorMessage;
|
||||
String? get errorMessage => _errorMessage;
|
||||
|
||||
// Mock user data
|
||||
late MockUserModel _currentUser;
|
||||
MockUserModel get currentUser => _currentUser;
|
||||
|
||||
// Request types as Base models
|
||||
final List<RequestTypeModel> _requestTypes = [
|
||||
RequestTypeModel(id: 'add_site', name: 'Add Site'),
|
||||
RequestTypeModel(id: 'change_site', name: 'Change Site'),
|
||||
RequestTypeModel(id: 'remove_site', name: 'Remove Site'),
|
||||
];
|
||||
|
||||
List<RequestTypeModel> get requestTypes => _requestTypes;
|
||||
|
||||
// Mock sites as Base models
|
||||
final List<SiteRequestSiteModel> _allSites = [
|
||||
SiteRequestSiteModel(id: 'site_1', name: 'Site A'),
|
||||
SiteRequestSiteModel(id: 'site_2', name: 'Site B'),
|
||||
SiteRequestSiteModel(id: 'site_3', name: 'Site C'),
|
||||
SiteRequestSiteModel(id: 'site_4', name: 'Site D'),
|
||||
SiteRequestSiteModel(id: 'site_5', name: 'Site E'),
|
||||
];
|
||||
|
||||
List<SiteRequestSiteModel> get allSites => _allSites;
|
||||
|
||||
// Mock departments as Base models
|
||||
final List<SiteRequestDepartmentModel> _allDepartments = [
|
||||
SiteRequestDepartmentModel(id: 'dept_1', name: 'HR'),
|
||||
SiteRequestDepartmentModel(id: 'dept_2', name: 'IT'),
|
||||
SiteRequestDepartmentModel(id: 'dept_3', name: 'Finance'),
|
||||
SiteRequestDepartmentModel(id: 'dept_4', name: 'Operations'),
|
||||
SiteRequestDepartmentModel(id: 'dept_5', name: 'Administration'),
|
||||
];
|
||||
|
||||
List<SiteRequestDepartmentModel> get allDepartments => _allDepartments;
|
||||
|
||||
// History of submitted requests
|
||||
final List<SiteRequestModel> _requestHistory = [];
|
||||
List<SiteRequestModel> get requestHistory => _requestHistory;
|
||||
|
||||
SiteRequestProvider() {
|
||||
_initializeMockUser();
|
||||
}
|
||||
|
||||
void _initializeMockUser() {
|
||||
_currentUser = MockUserModel(
|
||||
employeeId: 'EMP001',
|
||||
fullName: 'John Smith',
|
||||
emailAddress: 'john.smith@test.com',
|
||||
mobileNumber: '+966500000000',
|
||||
extensionNumber: '1234',
|
||||
role: 'Engineer',
|
||||
assignedSites: ['Site A', 'Site B'],
|
||||
);
|
||||
}
|
||||
|
||||
// Get available sites based on request type
|
||||
List<SiteRequestSiteModel> getAvailableSites(String requestType) {
|
||||
if (requestType == 'Remove Site') {
|
||||
// Only return currently assigned sites
|
||||
return _allSites.where((site) {
|
||||
return _currentUser.assignedSites.contains(site.name ?? '');
|
||||
}).toList();
|
||||
}
|
||||
// For Add Site and Change Site, return all sites
|
||||
return _allSites;
|
||||
}
|
||||
|
||||
// Check if user has assigned sites for removal
|
||||
bool hasAssignedSites() {
|
||||
return _currentUser.assignedSites.isNotEmpty;
|
||||
}
|
||||
|
||||
// Submit site request (local only, no API)
|
||||
Future<bool> submitSiteRequest(SiteRequestModel request) async {
|
||||
loading = true;
|
||||
_errorMessage = null;
|
||||
notifyListeners();
|
||||
|
||||
// Simulate network delay
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
|
||||
try {
|
||||
// Add request to history with a generated ID
|
||||
final requestWithId = request.copyWith(
|
||||
id: 'REQ_${DateTime.now().millisecondsSinceEpoch}',
|
||||
status: 'Pending',
|
||||
requestDate: DateTime.now(),
|
||||
);
|
||||
|
||||
_requestHistory.insert(0, requestWithId);
|
||||
|
||||
loading = false;
|
||||
notifyListeners();
|
||||
return true;
|
||||
} catch (error) {
|
||||
_errorMessage = 'Failed to submit request';
|
||||
loading = false;
|
||||
notifyListeners();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get request by ID
|
||||
SiteRequestModel? getRequestById(String id) {
|
||||
try {
|
||||
return _requestHistory.firstWhere((request) => request.id == id);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset provider state
|
||||
void reset() {
|
||||
_loading = false;
|
||||
_errorMessage = null;
|
||||
_requestHistory.clear();
|
||||
_initializeMockUser();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Add mock history data for testing
|
||||
void addMockHistoryData() {
|
||||
final mockRequests = [
|
||||
SiteRequestModel(
|
||||
id: 'REQ_001',
|
||||
requestType: 'Add Site',
|
||||
employeeId: _currentUser.employeeId,
|
||||
fullName: _currentUser.fullName,
|
||||
emailAddress: _currentUser.emailAddress,
|
||||
mobileNumber: _currentUser.mobileNumber,
|
||||
extensionNumber: _currentUser.extensionNumber,
|
||||
siteIds: ['site_3', 'site_4'],
|
||||
siteNames: ['Site C', 'Site D'],
|
||||
departmentIds: ['dept_2'],
|
||||
departmentNames: ['IT'],
|
||||
role: _currentUser.role,
|
||||
status: 'Approved',
|
||||
requestDate: DateTime.now().subtract(const Duration(days: 5)),
|
||||
),
|
||||
SiteRequestModel(
|
||||
id: 'REQ_002',
|
||||
requestType: 'Change Site',
|
||||
employeeId: _currentUser.employeeId,
|
||||
fullName: _currentUser.fullName,
|
||||
emailAddress: _currentUser.emailAddress,
|
||||
mobileNumber: _currentUser.mobileNumber,
|
||||
extensionNumber: _currentUser.extensionNumber,
|
||||
siteIds: ['site_1'],
|
||||
siteNames: ['Site A'],
|
||||
departmentIds: ['dept_1', 'dept_3'],
|
||||
departmentNames: ['HR', 'Finance'],
|
||||
role: _currentUser.role,
|
||||
status: 'Pending',
|
||||
requestDate: DateTime.now().subtract(const Duration(days: 2)),
|
||||
),
|
||||
SiteRequestModel(
|
||||
id: 'REQ_003',
|
||||
requestType: 'Remove Site',
|
||||
employeeId: _currentUser.employeeId,
|
||||
fullName: _currentUser.fullName,
|
||||
emailAddress: _currentUser.emailAddress,
|
||||
mobileNumber: _currentUser.mobileNumber,
|
||||
extensionNumber: _currentUser.extensionNumber,
|
||||
siteIds: ['site_2'],
|
||||
siteNames: ['Site B'],
|
||||
role: _currentUser.role,
|
||||
status: 'Rejected',
|
||||
requestDate: DateTime.now().subtract(const Duration(days: 10)),
|
||||
),
|
||||
];
|
||||
|
||||
_requestHistory.addAll(mockRequests);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,278 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:test_sa/extensions/int_extensions.dart';
|
||||
import 'package:test_sa/extensions/text_extensions.dart';
|
||||
import 'package:test_sa/modules/site_request_module/models/dropdown_models.dart';
|
||||
import 'package:test_sa/modules/site_request_module/models/request_type_model.dart';
|
||||
import 'package:test_sa/modules/site_request_module/models/site_request_model.dart';
|
||||
import 'package:test_sa/modules/site_request_module/providers/site_request_provider.dart';
|
||||
import 'package:test_sa/modules/site_request_module/screens/site_request_history_screen.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';
|
||||
import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.dart';
|
||||
import 'package:test_sa/providers/loading_list_notifier.dart';
|
||||
|
||||
class SiteRequestFormScreen extends StatefulWidget {
|
||||
static const String routeName = "/site_request_form";
|
||||
|
||||
const SiteRequestFormScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SiteRequestFormScreen> createState() => _SiteRequestFormScreenState();
|
||||
}
|
||||
|
||||
class _SiteRequestFormScreenState extends State<SiteRequestFormScreen> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
|
||||
late SiteRequestProvider _provider;
|
||||
|
||||
RequestTypeModel? _selectedRequestType;
|
||||
List<SiteRequestSiteModel> _selectedSites = [];
|
||||
List<SiteRequestDepartmentModel> _selectedDepartments = [];
|
||||
|
||||
bool _isSubmitting = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_provider = Provider.of<SiteRequestProvider>(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.scaffoldBackground(context),
|
||||
appBar: const DefaultAppBar(title: "Site Request"),
|
||||
body: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: SingleChildScrollView(
|
||||
padding: EdgeInsets.all(16.toScreenWidth),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildRequestTypeDropdown(),
|
||||
16.height,
|
||||
_buildReadOnlyField("Full Name", _provider.currentUser.fullName),
|
||||
16.height,
|
||||
_buildReadOnlyField("Email Address", _provider.currentUser.emailAddress),
|
||||
16.height,
|
||||
_buildReadOnlyField("Mobile Number", _provider.currentUser.mobileNumber),
|
||||
16.height,
|
||||
_buildReadOnlyField("Extension Number", _provider.currentUser.extensionNumber),
|
||||
16.height,
|
||||
if (_selectedRequestType != null) ...[
|
||||
_buildSiteMultiSelect(),
|
||||
16.height,
|
||||
_buildDepartmentMultiSelect(),
|
||||
16.height,
|
||||
_buildReadOnlyField("Role", _provider.currentUser.role),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_selectedRequestType != 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 _buildRequestTypeDropdown() {
|
||||
return SingleItemDropDownMenu<RequestTypeModel, NullableLoadingProvider>(
|
||||
context: context,
|
||||
title: "Type of Site Request",
|
||||
initialValue: _selectedRequestType,
|
||||
staticData: _provider.requestTypes,
|
||||
showAsFullScreenDialog: false,
|
||||
showAsBottomSheet: true,
|
||||
onSelect: (value) {
|
||||
setState(() {
|
||||
_selectedRequestType = value;
|
||||
_selectedSites.clear();
|
||||
_selectedDepartments.clear();
|
||||
});
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null) return "Please select request type";
|
||||
return null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildReadOnlyField(String label, String value) {
|
||||
return AppTextFormField(
|
||||
initialValue: value,
|
||||
labelText: label,
|
||||
enable: false,
|
||||
backgroundColor: AppColor.disableFieldBackground(context),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSiteMultiSelect() {
|
||||
final availableSites = _provider.getAvailableSites(_selectedRequestType!.name!);
|
||||
|
||||
// Check if user has assigned sites for Remove Site request
|
||||
if (_selectedRequestType!.name == 'Remove Site' && !_provider.hasAssignedSites()) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: EdgeInsets.all(16.toScreenWidth),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.disableFieldBackground(context),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
border: Border.all(
|
||||
color: AppColor.border2Color(context),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
"Site *".bodyText(context).custom(
|
||||
color: AppColor.floatingTextStyleColor(context),
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12,
|
||||
),
|
||||
4.height,
|
||||
"No assigned sites available for removal".bodyText(context).custom(
|
||||
color: AppColor.labelTextStyleColor(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return MultipleItemDropDownMenu<SiteRequestSiteModel, NullableLoadingProvider>(
|
||||
context: context,
|
||||
title: "Site",
|
||||
initialValue: _selectedSites,
|
||||
staticData: availableSites,
|
||||
showAsFullScreenDialog: true,
|
||||
showAsBottomSheet: false,
|
||||
onSelect: (value) {
|
||||
setState(() {
|
||||
_selectedSites = value ?? [];
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDepartmentMultiSelect() {
|
||||
return MultipleItemDropDownMenu<SiteRequestDepartmentModel, NullableLoadingProvider>(
|
||||
context: context,
|
||||
title: "Department (Optional)",
|
||||
initialValue: _selectedDepartments,
|
||||
staticData: _provider.allDepartments,
|
||||
showAsFullScreenDialog: true,
|
||||
showAsBottomSheet: false,
|
||||
onSelect: (value) {
|
||||
setState(() {
|
||||
_selectedDepartments = value ?? [];
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubmitButton() {
|
||||
// Disable button if Remove Site is selected and no assigned sites
|
||||
final isDisabled = _selectedRequestType?.name == 'Remove Site' && !_provider.hasAssignedSites();
|
||||
|
||||
return AppFilledButton(
|
||||
label: "Submit",
|
||||
maxWidth: true,
|
||||
loading: _isSubmitting,
|
||||
disableButton: _isSubmitting || isDisabled,
|
||||
onPressed: isDisabled ? null : _submitForm,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _submitForm() async {
|
||||
if (!_formKey.currentState!.validate()) {
|
||||
Fluttertoast.showToast(msg: "Please fill all required fields");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectedRequestType == null) {
|
||||
Fluttertoast.showToast(msg: "Please select request type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectedSites.isEmpty) {
|
||||
Fluttertoast.showToast(msg: "Please select at least one site");
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isSubmitting = true;
|
||||
});
|
||||
|
||||
if (mounted) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (context) => const AppLazyLoading(),
|
||||
);
|
||||
}
|
||||
|
||||
final request = SiteRequestModel(
|
||||
requestType: _selectedRequestType!.name ?? '',
|
||||
employeeId: _provider.currentUser.employeeId,
|
||||
fullName: _provider.currentUser.fullName,
|
||||
emailAddress: _provider.currentUser.emailAddress,
|
||||
mobileNumber: _provider.currentUser.mobileNumber,
|
||||
extensionNumber: _provider.currentUser.extensionNumber,
|
||||
siteIds: _selectedSites.map((s) => s.identifier ?? '').toList(),
|
||||
siteNames: _selectedSites.map((s) => s.name ?? '').toList(),
|
||||
departmentIds: _selectedDepartments.isNotEmpty ? _selectedDepartments.map((d) => d.identifier ?? '').toList() : null,
|
||||
departmentNames: _selectedDepartments.isNotEmpty ? _selectedDepartments.map((d) => d.name ?? '').toList() : null,
|
||||
role: _provider.currentUser.role,
|
||||
status: 'Pending',
|
||||
requestDate: DateTime.now(),
|
||||
);
|
||||
|
||||
final success = await _provider.submitSiteRequest(request);
|
||||
|
||||
if (mounted) {
|
||||
Navigator.pop(context); // Close loading dialog
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isSubmitting = false;
|
||||
});
|
||||
|
||||
if (success) {
|
||||
Fluttertoast.showToast(msg: "Site request submitted successfully");
|
||||
if (mounted) {
|
||||
Navigator.pushReplacementNamed(context, SiteRequestHistoryScreen.routeName);
|
||||
}
|
||||
} else {
|
||||
Fluttertoast.showToast(
|
||||
msg: _provider.errorMessage ?? "Failed to submit site request",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,199 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:test_sa/extensions/int_extensions.dart';
|
||||
import 'package:test_sa/extensions/text_extensions.dart';
|
||||
import 'package:test_sa/modules/site_request_module/models/site_request_model.dart';
|
||||
import 'package:test_sa/modules/site_request_module/providers/site_request_provider.dart';
|
||||
import 'package:test_sa/new_views/app_style/app_color.dart';
|
||||
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
|
||||
|
||||
class SiteRequestHistoryScreen extends StatefulWidget {
|
||||
static const String routeName = "/site_request_history";
|
||||
|
||||
const SiteRequestHistoryScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SiteRequestHistoryScreen> createState() => _SiteRequestHistoryScreenState();
|
||||
}
|
||||
|
||||
class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
|
||||
late SiteRequestProvider _provider;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Add mock history data for demonstration purposes
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (_provider.requestHistory.isEmpty) {
|
||||
_provider.addMockHistoryData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_provider = Provider.of<SiteRequestProvider>(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: AppColor.scaffoldBackground(context),
|
||||
appBar: const DefaultAppBar(title: "Site Request History"),
|
||||
body: _provider.requestHistory.isEmpty
|
||||
? _buildEmptyState()
|
||||
: ListView.builder(
|
||||
padding: EdgeInsets.all(16.toScreenWidth),
|
||||
itemCount: _provider.requestHistory.length,
|
||||
itemBuilder: (context, index) {
|
||||
final request = _provider.requestHistory[index];
|
||||
return _buildRequestCard(request);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmptyState() {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.inbox_outlined,
|
||||
size: 80,
|
||||
color: AppColor.icon2Color(context).withOpacity(0.5),
|
||||
),
|
||||
16.height,
|
||||
"No site requests found".heading5(context).custom(
|
||||
color: AppColor.labelTextStyleColor(context),
|
||||
),
|
||||
8.height,
|
||||
"Your submitted requests will appear here".bodyText(context).custom(
|
||||
color: AppColor.lightTextColor(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildRequestCard(SiteRequestModel request) {
|
||||
return Card(
|
||||
margin: EdgeInsets.only(bottom: 16.toScreenHeight),
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
color: AppColor.background(context),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(16.toScreenWidth),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: request.requestType.heading6(context).custom(
|
||||
color: AppColor.labelTextStyleColor(context),
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
_buildStatusBadge(request.status),
|
||||
],
|
||||
),
|
||||
12.height,
|
||||
_buildInfoRow("Request ID", request.id ?? "N/A"),
|
||||
8.height,
|
||||
_buildInfoRow("Employee ID", request.employeeId),
|
||||
8.height,
|
||||
_buildInfoRow("Full Name", request.fullName),
|
||||
8.height,
|
||||
_buildInfoRow("Email", request.emailAddress),
|
||||
8.height,
|
||||
_buildInfoRow("Mobile", request.mobileNumber),
|
||||
8.height,
|
||||
if (request.extensionNumber != null && request.extensionNumber!.isNotEmpty)
|
||||
_buildInfoRow("Extension", request.extensionNumber!),
|
||||
8.height,
|
||||
_buildInfoRow("Role", request.role),
|
||||
8.height,
|
||||
_buildInfoRow(
|
||||
"Site(s)",
|
||||
request.siteNames.join(", "),
|
||||
),
|
||||
if (request.departmentNames != null && request.departmentNames!.isNotEmpty) ...[
|
||||
8.height,
|
||||
_buildInfoRow(
|
||||
"Department(s)",
|
||||
request.departmentNames!.join(", "),
|
||||
),
|
||||
],
|
||||
8.height,
|
||||
_buildInfoRow(
|
||||
"Request Date",
|
||||
DateFormat('dd MMM yyyy, hh:mm a').format(request.requestDate),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoRow(String label, String value) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 120.toScreenWidth,
|
||||
child: "$label:".bodyText(context).custom(
|
||||
color: AppColor.lightTextColor(context),
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: value.bodyText(context).custom(
|
||||
color: AppColor.labelTextStyleColor(context),
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatusBadge(String status) {
|
||||
Color backgroundColor;
|
||||
Color textColor;
|
||||
|
||||
switch (status.toLowerCase()) {
|
||||
case 'approved':
|
||||
backgroundColor = Colors.green.withOpacity(0.1);
|
||||
textColor = Colors.green;
|
||||
break;
|
||||
case 'rejected':
|
||||
backgroundColor = Colors.red.withOpacity(0.1);
|
||||
textColor = Colors.red;
|
||||
break;
|
||||
case 'pending':
|
||||
default:
|
||||
backgroundColor = Colors.orange.withOpacity(0.1);
|
||||
textColor = Colors.orange;
|
||||
break;
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.toScreenWidth,
|
||||
vertical: 6.toScreenHeight,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: status.bodyText(context).custom(
|
||||
color: textColor,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue