Compare commits

..

No commits in common. 'cdc695360557087f5c91a1873ea5f8cf841a8457' and 'c0c165eba47c8f538396f1dd84416c3cdccc323b' have entirely different histories.

@ -61,7 +61,6 @@ class URLs {
static get signUpGetCompaniesByAssetGroup => "$_baseUrl/Company/GetCompaniesByAssetGroup";
static get signUpGetSiteList => "$_baseUrl/Customer/GetSiteList";
static get signUpGetDepartmentAutoComplete => "$_baseUrl/Department/GetDepartmentAutoCompleteByAssetGroup";
static get signUpGetDepartmentsBySitesAndAssetGroup => "$_baseUrl/Department/GetDepartmentsBySitesAndAssetGroup";
// Site Request APIs
static get getSiteRequestTypes => "$_baseUrl/Lookups/GetLookup?lookupEnum=4016"; // get site request types
@ -587,8 +586,7 @@ class URLs {
static get getDemoRequests => "$_baseUrl/DemoRequest/GetDemoRequests"; // get demo request history
// Application Demo Request APIs
// static get getApplicationDemoRoles => "$_baseUrl/Account/GetRolesByAssetGroupId?assetGroupId=6"; // get roles for application demo request
static get getApplicationDemoRoles => "$_baseUrl/RoleDemo/GetDemoRolesAutoComplete"; // get roles for application demo request
static get getApplicationDemoRoles => "$_baseUrl/Account/GetRolesByAssetGroupId?assetGroupId=6"; // get roles for application demo request
static get submitApplicationDemoRequest => "$_baseUrl/DemoUser/CreateDemoUser"; // post submit application demo request// get application demo request history
static get sendDemoUserOtp => "$_baseUrl/DemoUser/EmailVerification"; // post send OTP to user
static get verifyDemoUserOtp => "$_baseUrl/DemoUser/VerifyOtp"; // post verify OTP

@ -1,11 +1,11 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/modules/applicationDemo/models/application_demo_request_model.dart';
import 'package:test_sa/modules/applicationDemo/models/role_model.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
/// Provider for managing application demo request operations
class ApplicationDemoRequestProvider extends ChangeNotifier {
bool _loading = false;
bool _roleLoading = false;
@ -13,6 +13,7 @@ class ApplicationDemoRequestProvider extends ChangeNotifier {
String? _errorMessage;
String? _successMessage;
// Getters
bool get loading => _loading;
bool get roleLoading => _roleLoading;
@ -23,6 +24,7 @@ class ApplicationDemoRequestProvider extends ChangeNotifier {
List<ApplicationDemoRoleModel> get roles => _roles;
// Setter
set loading(bool value) {
_loading = value;
notifyListeners();
@ -33,30 +35,29 @@ class ApplicationDemoRequestProvider extends ChangeNotifier {
notifyListeners();
}
/// Get headers without authentication token for public APIs
Map<String, String> get _unauthenticatedHeaders => {
'Content-Type': 'application/json',
'X-Timezone-Offset': DateTime.now().timeZoneOffset.toString().split(".").first,
};
Future<int> fetchRoles({String? searchText}) async {
/// Fetches available roles from the server (without authentication)
Future<int> fetchRoles() async {
if (roleLoading) return -2;
roleLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiManager.instance.post(URLs.getApplicationDemoRoles, headers: _unauthenticatedHeaders, body: {'searchText': searchText ?? ''});
final response = await http.get(
Uri.parse(URLs.getApplicationDemoRoles),
headers: _unauthenticatedHeaders,
);
if (response.statusCode >= 200 && response.statusCode < 300) {
final Map<String, dynamic> responseData = jsonDecode(response.body);
List<dynamic> rolesList = [];
if (responseData.containsKey('data')) {
rolesList = responseData['data'] is List ? responseData['data'] : [];
} else if (responseData.containsKey('roles')) {
rolesList = responseData['roles'] is List ? responseData['roles'] : [];
}
_roles = rolesList.map((json) => ApplicationDemoRoleModel.fromJson(json)).toList();
final List<dynamic> responseData = jsonDecode(response.body);
_roles = responseData.map((json) => ApplicationDemoRoleModel.fromJson(json)).toList();
roleLoading = false;
notifyListeners();
@ -86,10 +87,10 @@ class ApplicationDemoRequestProvider extends ChangeNotifier {
try {
final requestBody = request.toJson();
final response = await ApiManager.instance.post(
URLs.submitApplicationDemoRequest,
final response = await http.post(
Uri.parse(URLs.submitApplicationDemoRequest),
headers: _unauthenticatedHeaders,
body: requestBody,
body: json.encode(requestBody),
);
if (response.statusCode >= 200 && response.statusCode < 300) {
@ -133,10 +134,10 @@ class ApplicationDemoRequestProvider extends ChangeNotifier {
'typeSend': typeSend,
};
final response = await ApiManager.instance.post(
URLs.sendDemoUserOtp,
final response = await http.post(
Uri.parse(URLs.sendDemoUserOtp),
headers: _unauthenticatedHeaders,
body: requestBody,
body: json.encode(requestBody),
);
if (response.statusCode >= 200 && response.statusCode < 300) {
@ -184,10 +185,10 @@ class ApplicationDemoRequestProvider extends ChangeNotifier {
requestBody['phoneNumber'] = phoneNumber;
}
final response = await ApiManager.instance.post(
URLs.verifyDemoUserOtp,
final response = await http.post(
Uri.parse(URLs.verifyDemoUserOtp),
headers: _unauthenticatedHeaders,
body: requestBody,
body: json.encode(requestBody),
);
if (response.statusCode >= 200 && response.statusCode < 300) {

@ -18,7 +18,7 @@ class DepartmentModel extends Base {
factory DepartmentModel.fromJson(Map<String, dynamic> json) {
return DepartmentModel(
id: json['id'],
departmentName: json['departmentName']??json['name'],
departmentName: json['departmentName'],
departmentCode: json['departmentCode'],
ntCode: json['ntCode'],
operationalHours: json['operationalHours'],

@ -6,9 +6,9 @@ class SignUpRequestModel {
final String? emailAddress;
final String? mobileNumber;
final String? extensionNumber;
final List<int>? siteIds;
final int? siteId;
final String? erpSite;
final List<int>? departmentIds;
final int? departmentId;
final String? erpDepartment;
final String? role;
final bool isFromERP;
@ -21,9 +21,9 @@ class SignUpRequestModel {
this.emailAddress,
this.mobileNumber,
this.extensionNumber,
this.siteIds,
this.siteId,
this.erpSite,
this.departmentIds,
this.departmentId,
this.erpDepartment,
this.role,
required this.isFromERP,
@ -32,15 +32,19 @@ class SignUpRequestModel {
Map<String, dynamic> toJson() {
return {
'assetGroupId': assetGroupId ?? 0,
'companyId': companyId ?? 0,
'employeeId': employeeId ?? '',
'fullName': fullName ?? '',
'emailAddress': emailAddress ?? '',
'mobileNumber': mobileNumber ?? '',
'extensionNumber': extensionNumber ?? '',
'siteIds': siteIds,
if(siteId!=null)
'siteId': siteId,
// 'siteId': siteId ?? 0,
'erpSite': erpSite ?? '',
'departmentIds': departmentIds,
if(departmentId!=null)
'departmentId': departmentId,
'erpDepartment': erpDepartment ?? '',
'role': role ?? '',
'isFromERP': isFromERP,

@ -2,7 +2,6 @@ import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:test_sa/controllers/api_routes/api_manager.dart';
import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/modules/signup/models/department_model.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
@ -29,9 +28,8 @@ class SignupDepartmentProvider extends LoadingListNotifier<DepartmentModel> {
'X-Timezone-Offset': DateTime.now().timeZoneOffset.toString().split(".").first,
};
final response = await ApiManager.instance.get(
'${URLs.signUpGetDepartmentAutoComplete}?assetGroupId=$id',
// Uri.parse('${URLs.signUpGetDepartmentAutoComplete}?assetGroupId=$id'),
final response = await http.get(
Uri.parse('${URLs.signUpGetDepartmentAutoComplete}?assetGroupId=$id'),
headers: headers,
);
@ -73,68 +71,4 @@ class SignupDepartmentProvider extends LoadingListNotifier<DepartmentModel> {
return -1;
}
}
/// Fetches departments based on selected sites and asset group
Future<int> getDepartmentsBySites({
required List<int> siteIds,
required int assetGroupId,
}) async {
log('Fetching departments for sites: $siteIds and asset group: $assetGroupId');
if (loading) return -2;
if (siteIds.isEmpty) {
log('Department fetch: Site IDs are empty, clearing items');
items = [];
notifyListeners();
return -1;
}
loading = true;
notifyListeners();
try {
final headers = {
'Content-Type': 'application/json',
'X-Timezone-Offset': DateTime.now().timeZoneOffset.toString().split(".").first,
};
final body = {
'siteIds': siteIds,
'assetGroupId': assetGroupId,
};
final response = await ApiManager.instance.post(
URLs.signUpGetDepartmentsBySitesAndAssetGroup,
headers: headers,
body: body,
);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
final data = json.decode(response.body);
if (data is Map && data['data'] != null && data['data'] is List) {
items = (data['data'] as List).map((item) => DepartmentModel.fromJson(item)).toList();
} else if (data is List) {
items = data.map((item) => DepartmentModel.fromJson(item)).toList();
} else {
items = [];
}
} else {
items = [];
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
log('Department by sites API error: $error');
loading = false;
stateCode = -1;
items = [];
notifyListeners();
return -1;
}
}
}

@ -27,7 +27,6 @@ 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/multiple_item_drop_down_menu.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';
@ -63,8 +62,8 @@ class _SignUpScreenState extends State<SignUpScreen> {
// Selected values
AssetGroup? _selectedAssetGroup;
Company? _selectedCompany;
List<SiteModel> _selectedSite=[];
List<DepartmentModel> _selectedDepartment=[];
SiteModel? _selectedSite;
DepartmentModel? _selectedDepartment;
EmployeeERPModel? _employeeERPData; // Store the ERP employee data
bool _isHMGGroup = false;
bool _employeeFetched = false;
@ -353,92 +352,43 @@ class _SignUpScreenState extends State<SignUpScreen> {
}
Widget _buildSiteDropdown() {
return MultipleItemDropDownMenu<SiteModel, SignupSiteProvider>(
return SingleItemDropDownMenu<SiteModel, SignupSiteProvider>(
context: context,
title: "Site",
initialValue: _selectedSite,
requestById: _selectedAssetGroup?.id,
// staticData: provider.roles,
// loading: provider.roleLoading,
showAsFullScreenDialog: true,
onSelect: (selectedSites) {
requestById: _selectedAssetGroup?.id,
onSelect: (value) {
setState(() {
_selectedSite = selectedSites ?? [];
// Clear departments when sites change
_selectedDepartment = [];
// _rolesValidationError = null; // Clear error when roles are selected
_selectedSite = value;
});
// Fetch departments based on selected sites
if (_selectedSite.isNotEmpty && _selectedAssetGroup?.id != null) {
final siteIds = _selectedSite.map((site) => site.id ?? 0).where((id) => id != 0).toList();
if (siteIds.isNotEmpty) {
_departmentProvider.getDepartmentsBySites(
siteIds: siteIds,
assetGroupId: _selectedAssetGroup!.id!,
);
}
} else {
// If no sites selected, clear departments
_departmentProvider.items = [];
_departmentProvider.notifyListeners();
}
// No need to fetch departments when site changes - they are independent
},
validator: (value) {
if (value == null) return "Site is required";
return null;
},
);
// return SingleItemDropDownMenu<SiteModel, SignupSiteProvider>(
// context: context,
// title: "Site",
// initialValue: _selectedSite,
// showAsFullScreenDialog: true,
// requestById: _selectedAssetGroup?.id,
// onSelect: (value) {
// setState(() {
// _selectedSite = value;
// });
// // No need to fetch departments when site changes - they are independent
// },
// validator: (value) {
// if (value == null) return "Site is required";
// return null;
// },
// );
}
Widget _buildDepartmentDropdown() {
return MultipleItemDropDownMenu<DepartmentModel, SignupDepartmentProvider>(
return SingleItemDropDownMenu<DepartmentModel, SignupDepartmentProvider>(
context: context,
title: "Department",
initialValue: _selectedDepartment,
requestById: _selectedAssetGroup?.id,
// staticData: provider.roles,
// loading: provider.roleLoading,
showAsFullScreenDialog: true,
onSelect: (selectedDepartments) {
// showAsBottomSheet: true,
requestById: _selectedAssetGroup?.id,
onSelect: (value) {
setState(() {
_selectedDepartment = selectedDepartments ?? [];
// _rolesValidationError = null; // Clear error when roles are selected
_selectedDepartment = value;
});
},
validator: (value) {
if (value == null) return "Department is required";
return null;
},
);
// return SingleItemDropDownMenu<DepartmentModel, SignupDepartmentProvider>(
// context: context,
// title: "Department",
// initialValue: _selectedDepartment,
// showAsFullScreenDialog: true,
// // showAsBottomSheet: true,
// requestById: _selectedAssetGroup?.id,
// onSelect: (value) {
// setState(() {
// _selectedDepartment = value;
// });
// },
// validator: (value) {
// if (value == null) return "Department is required";
// return null;
// },
// );
}
Widget _buildRoleField() {
@ -532,8 +482,8 @@ class _SignUpScreenState extends State<SignUpScreen> {
_mobileController.clear();
_extensionController.clear();
_roleController.clear();
_selectedSite = [];
_selectedDepartment = [];
_selectedSite = null;
_selectedDepartment = null;
_employeeFetched = false;
_employeeERPData = null;
}
@ -570,13 +520,13 @@ class _SignUpScreenState extends State<SignUpScreen> {
}
// Site is mandatory for HMG
if (_selectedSite.isEmpty) {
if (_selectedSite == null) {
Fluttertoast.showToast(msg: "Site is required");
return;
}
//
// // Department is mandatory for HMG
if (_selectedDepartment.isEmpty) {
if (_selectedDepartment == null) {
Fluttertoast.showToast(msg: "Department is required");
return;
}
@ -610,13 +560,13 @@ class _SignUpScreenState extends State<SignUpScreen> {
}
// Site is mandatory for Non-HMG
if (_selectedSite.isEmpty) {
if (_selectedSite == null) {
Fluttertoast.showToast(msg: "Site is required");
return;
}
// Department is mandatory for Non-HMG
if (_selectedDepartment.isEmpty) {
if (_selectedDepartment == null) {
Fluttertoast.showToast(msg: "Department is required");
return;
}
@ -670,11 +620,9 @@ class _SignUpScreenState extends State<SignUpScreen> {
mobileNumber: _mobileController.text,
extensionNumber: _extensionController.text.isEmpty ? null : _extensionController.text,
// For HMG: send both selected IDs and ERP data
// siteId: _selectedSite?.id,
siteIds: _selectedSite.map((site) => site.id??0).toList(),
siteId: _selectedSite?.id,
// erpSite: _isHMGGroup ? _employeeERPData?.branchCode : null,
departmentIds: _selectedDepartment.map((department) => department.id??0).toList(),
// departmentIds: _selectedDepartment?.id,
departmentId: _selectedDepartment?.id,
// erpDepartment: _isHMGGroup ? _employeeERPData?.branchName : null,
role: _roleController.text,
isFromERP: _isHMGGroup,

@ -9,7 +9,6 @@ class SiteRequestModel {
final String emailAddress;
final String mobileNumber;
final String? extensionNumber;
final String? returnReason;
final List<String> siteIds;
final List<String> siteNames;
final List<String>? oldSiteIds; // For Change Site request
@ -41,7 +40,6 @@ class SiteRequestModel {
this.oldDepartmentNames,
required this.role,
this.status,
this.returnReason,
this.requestDate,
});
@ -83,7 +81,6 @@ class SiteRequestModel {
mobileNumber: json['mobileNumber'] ?? '',
extensionNumber: json['extensionNo'] ?? json['extensionNumber'],
siteIds: sitesData['siteIds'],
returnReason: json['returnReason'],
siteNames: sitesData['siteNames'],
oldSiteIds: sitesData['oldSiteIds'],
oldSiteNames: sitesData['oldSiteNames'],
@ -106,7 +103,6 @@ class SiteRequestModel {
String? emailAddress,
String? mobileNumber,
String? extensionNumber,
String? returnReason,
List<String>? siteIds,
List<String>? siteNames,
List<String>? oldSiteIds,
@ -134,7 +130,6 @@ class SiteRequestModel {
departmentNames: departmentNames ?? this.departmentNames,
role: role ?? this.role,
status: status ?? this.status,
returnReason: returnReason ?? this.returnReason,
requestDate: requestDate ?? this.requestDate,
);
}
@ -175,29 +170,7 @@ class SiteRequestModel {
List<String>? oldDepartmentIds;
List<String>? oldDepartmentNames;
// Check if departments are nested within sites array
if (json['sites'] != null && json['sites'] is List) {
for (var site in json['sites']) {
if (site['departments'] != null && site['departments'] is List) {
for (var dept in site['departments']) {
departmentIds ??= [];
departmentNames ??= [];
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'] ?? '');
}
}
}
}
}
// Fallback: check if departments are at root level (for backward compatibility)
else if (json['departments'] != null &&
if (json['departments'] != null &&
json['departments'] is List &&
(json['departments'] as List).isNotEmpty) {
departmentIds = [];

@ -1,10 +1,7 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:provider/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/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/modules/site_request_module/models/site_request_model.dart';
@ -13,7 +10,6 @@ import 'package:test_sa/modules/site_request_module/screens/site_request_form_sc
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/app_style/app_text_styles.dart';
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
import 'package:test_sa/views/widgets/loaders/app_loading.dart';
import 'package:test_sa/views/widgets/loaders/no_data_found.dart';
import 'package:test_sa/views/widgets/item_views/info_date_widget.dart';
import 'package:test_sa/views/widgets/item_views/info_header_widget.dart';
@ -140,7 +136,7 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
itemCount: _provider.requestHistory.length + (_provider.isLoadingMore ? 1 : 0),
itemBuilder: (context, index) {
if (index == _provider.requestHistory.length) {
return const ALoading();
return const Center(child: CircularProgressIndicator());
}
return _buildRequestCard(_provider.requestHistory[index]);
@ -201,38 +197,17 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
}
Widget _buildUserInfo(SiteRequestModel request) {
log('return reason: ${request.returnReason}');
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoTextWidget(
label: "Full Name",
value: request.fullName,
showEmptyValue: true,
),
InfoTextWidget(
label: "Employee ID",
value: request.employeeId,
showEmptyValue: true,
),
InfoTextWidget(
label: "Email",
value: request.emailAddress,
showEmptyValue: true,
),
InfoTextWidget(label: "Full Name", value: request.fullName),
InfoTextWidget(label: "Employee ID", value: request.employeeId),
InfoTextWidget(label: "Email", value: request.emailAddress),
InfoTextWidget(label: "Mobile", value: request.mobileNumber, showEmptyValue: true),
InfoTextWidget(label: "Extension", value: request.extensionNumber, showEmptyValue: true),
InfoTextWidget(
label: "Role",
value: request.role,
showEmptyValue: true,
),
if( request.returnReason != null && request.returnReason!.isNotEmpty)
InfoTextWidget(
label: "Return Reason".addTranslation,
value: request.returnReason,
showEmptyValue: true,
),
if (request.extensionNumber != null && request.extensionNumber!.isNotEmpty) ...[
InfoTextWidget(label: "Extension", value: request.extensionNumber!, showEmptyValue: true),
],
InfoTextWidget(label: "Role", value: request.role),
],
);
}
@ -281,8 +256,13 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoTextLabelWidget(
label: "Old Departments:".addTranslation,
Text(
"Old Departments:",
style: TextStyle(
fontSize: 14,
color: AppColor.labelTextStyleColor(context),
fontWeight: FontWeight.w500,
),
),
8.height,
Wrap(
@ -304,17 +284,14 @@ class _SiteRequestHistoryScreenState extends State<SiteRequestHistoryScreen> {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InfoTextLabelWidget(
label: label,
Text(
label,
style: TextStyle(
fontSize: 14,
color: AppColor.labelTextStyleColor(context),
fontWeight: FontWeight.w500,
),
),
// Text(
// label,
// style: TextStyle(
// fontSize: 14,
// color: AppColor.labelTextStyleColor(context),
// fontWeight: FontWeight.w500,
// ),
// ),
8.height,
Wrap(
runSpacing: 4.toScreenHeight,

Loading…
Cancel
Save