diff --git a/lib/api/devices_api_client.dart b/lib/api/devices_api_client.dart new file mode 100644 index 00000000..b77d50a5 --- /dev/null +++ b/lib/api/devices_api_client.dart @@ -0,0 +1,52 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:test_sa/models/device/device.dart'; + +import '../controllers/api_routes/urls.dart'; +import 'api_client.dart'; + +class DevicesApiClient { + static final DevicesApiClient _instance = DevicesApiClient._internal(); + final List devices = []; + + DevicesApiClient._internal(); + + factory DevicesApiClient() => _instance; + + /// Fetch devices by [hospitalId] and insert the result into [devices] list + Future getEquipment(String hospitalId) async { + final response = await ApiClient().getJsonForResponse(URLs.host1 + URLs.getEquipment, queryParameters: {'client': hospitalId}); + List equipmentListJson = json.decode(utf8.decode(response.bodyBytes)); + devices.clear(); + devices.addAll(equipmentListJson.map((device) => Device.fromJson(device)).toList()); + debugPrint("devices : ${devices.length}"); + } + + /// Returns a list of devices by [hospitalId] and [serialNumber] (or | and) [number] + Future> getDevicesList({required String hospitalId, String? serialNumber, String? number}) async { + final response = await ApiClient().getJsonForResponse( + URLs.host1 + URLs.getEquipment, + queryParameters: { + 'client': hospitalId, + if (serialNumber?.isEmpty == false) 'name': serialNumber, + if (number?.isEmpty == false) 'number': number, + }, + ); + List categoriesListJson = json.decode(utf8.decode(response.bodyBytes)); + return categoriesListJson.map((device) => Device.fromJson(device)).toList(); + } + + /// Returns a list of devices by [hospitalId] (and optionally) [serialNumber] + Future> getDevicesListBySN({required String hospitalId, required String serialNumber}) async { + final response = await ApiClient().getJsonForResponse( + URLs.host1 + URLs.getEquipment, + queryParameters: { + 'client': hospitalId, + if (serialNumber.isNotEmpty) 'serial_qr': serialNumber, + }, + ); + List categoriesListJson = json.decode(utf8.decode(response.bodyBytes)); + return categoriesListJson.map((device) => Device.fromJson(device)).toList(); + } +} diff --git a/lib/api/service_request_api_client.dart b/lib/api/service_request_api_client.dart new file mode 100644 index 00000000..ca5f5be5 --- /dev/null +++ b/lib/api/service_request_api_client.dart @@ -0,0 +1,41 @@ +import 'dart:convert'; + +import 'package:test_sa/api/api_client.dart'; +import 'package:test_sa/api/user_api_client.dart'; +import 'package:test_sa/models/service_request/service_request.dart'; + +import '../controllers/api_routes/urls.dart'; + +class ServiceRequestApiClient { + static final ServiceRequestApiClient _instance = ServiceRequestApiClient._internal(); + final List serviceRequests = []; + + ServiceRequestApiClient._internal(); + + factory ServiceRequestApiClient() => _instance; + + /// ### The result will be added to [serviceRequests] + Future createRequest(ServiceRequest serviceRequest) async { + final user = UserApiClient().user; + await ApiClient().postJsonForObject( + (json) { + // ServiceRequest.fromJson(json.decode(utf8.decode(response.bodyBytes))[0]) + serviceRequests.insert(0, ServiceRequest.fromJson(json[0])); + }, + '${URLs.host1}${URLs.createRequest}', + { + "uid": user?.id, + "token": user?.token ?? "", + "sn_id": serviceRequest.deviceId ?? "", + "date": (DateTime.now().millisecondsSinceEpoch).toString(), + "client": user?.hospital?.id ?? '', + "complaint": serviceRequest.maintenanceIssue, + "image": json.encode(serviceRequest.devicePhotos), + "priority": (serviceRequest.priority?.id).toString(), + "defect_types": (serviceRequest.defectType?.id).toString(), + "audio": serviceRequest.audio, + }, + isFormData: true, + ); + } +} diff --git a/lib/api/user_api_client.dart b/lib/api/user_api_client.dart index c568a261..370d7052 100644 --- a/lib/api/user_api_client.dart +++ b/lib/api/user_api_client.dart @@ -41,6 +41,7 @@ class UserApiClient { }, "${URLs.host1}${URLs.register}", await newUser.toRegisterJson(), //body + isFormData: true, ); } @@ -56,6 +57,7 @@ class UserApiClient { }, "${URLs.host1}${URLs.updateProfile}", updatedUser.toUpdateProfileJson(), //body + isFormData: true, ); // Map jsonObject = {}; // jsonObject["uid"] = user.id; diff --git a/lib/controllers/providers/api/devices_provider.dart b/lib/controllers/providers/api/devices_provider.dart index 5779ee64..14f88cae 100644 --- a/lib/controllers/providers/api/devices_provider.dart +++ b/lib/controllers/providers/api/devices_provider.dart @@ -1,16 +1,16 @@ -import 'dart:convert'; - -import 'package:flutter/cupertino.dart'; -import 'package:http/http.dart'; +import 'package:test_sa/api/devices_api_client.dart'; +import 'package:test_sa/controllers/providers/loading_notifier.dart'; import '../../../models/device/device.dart'; -import '../../../models/user.dart'; -import '../../api_routes/urls.dart'; -class DevicesProvider extends ChangeNotifier { +class DevicesProvider extends LoadingNotifier { + final List _searchableList = []; + + List get searchableList => _searchableList; + //reset provider data void reset() { - _devices = null; + DevicesApiClient().devices.clear(); _stateCode = null; } @@ -21,117 +21,43 @@ class DevicesProvider extends ChangeNotifier { int? get stateCode => _stateCode; - List? _devices; - - List? get devices => _devices; - - // when categories in-process _loading = true - // done _loading = true - // failed _loading = false - bool? _loading; - - bool? get isLoading => _loading; - - set isLoading(bool? isLoading) { - _loading = isLoading; - notifyListeners(); - } - - /// return -2 if request in progress - /// return -1 if error happen when sending request - /// return state code if request complete may be 200, 404 or 403 - /// for more details check http state manager - /// lib\controllers\http_status_manger\http_status_manger.dart - Future getEquipment({required String host, required User user, required String hospitalId}) async { - if (_loading == true) { - return -2; - } - _loading = true; - notifyListeners(); - Response response; - try { - response = await get( - Uri.parse("${host + URLs.getEquipment}?client=$hospitalId"), - headers: {"Content-Type": "application/json; charset=utf-8"}, + /// - Fetching Devices From The Server and Inserting them into [DevicesApiClient.devices] List. + /// - [_searchableList] will be filled after the request succeed. + /// + /// ### NOTE : if [hospitalId] is [NULL] nothing will happen + Future getEquipment({required String? hospitalId}) async { + if (hospitalId != null) { + _searchableList.clear(); + waitApiRequest( + () async { + await DevicesApiClient().getEquipment(hospitalId); + _searchableList.addAll(DevicesApiClient().devices); + }, + onSuccess: () { + /// TODO : this is temporary + _stateCode = 200; + }, + onError: (error) { + /// TODO : this is temporary + _stateCode = error.error?.errorCode; + }, ); - } catch (error) { - _loading = false; - _stateCode = -1; - notifyListeners(); - return -1; } - _stateCode = response.statusCode; - if (response.statusCode >= 200 && response.statusCode < 300) { - // client's request was successfully received - List equipmentListJson = json.decode(utf8.decode(response.bodyBytes)); - _devices = equipmentListJson.map((device) => Device.fromJson(device)).toList(); - } - _loading = false; - notifyListeners(); - return response.statusCode; } - /// return -2 if request in progress - /// return -1 if error happen when sending request - /// return state code if request complete may be 200, 404 or 403 - /// for more details check http state manager - /// lib\controllers\http_status_manger\http_status_manger.dart - Future> getDevicesList({ - required String host, - required User? user, - required String hospitalId, - String? serialNumber, - String? number, - }) async { - Response response; - try { - response = await get( - Uri.parse("$host${URLs.getEquipment}?client=$hospitalId" - "${serialNumber?.isEmpty == false ? "&name=$serialNumber" : ""}" - "${number?.isEmpty == false ? "&number=$number" : ""}"), - ); - List page = []; - if (response.statusCode >= 200 && response.statusCode < 300) { - // client's request was successfully received - List categoriesListJson = json.decode(utf8.decode(response.bodyBytes)); - page = categoriesListJson.map((device) => Device.fromJson(device)).toList(); - } - return page; - } catch (error) { - return []; - } + /// Returns a list of devices by [hospitalId] and [serialNumber] (or | and) [number] + /// + /// ### NOTE : if [hospitalId] is [NULL] empty list will be returned + Future> getDevicesList({required String? hospitalId, String? serialNumber, String? number}) { + if (hospitalId == null) return Future.value(const []); + return DevicesApiClient().getDevicesList(hospitalId: hospitalId, serialNumber: serialNumber, number: number); } - /// return -2 if request in progress - /// return -1 if error happen when sending request - /// return state code if request complete may be 200, 404 or 403 - /// for more details check http state manager - /// lib\controllers\http_status_manger\http_status_manger.dart - Future> getDevicesListBySN({ - required String host, - required User user, - required String hospitalId, - required String sn, - }) async { - Response response; - try { - response = await get( - Uri.parse("$host${URLs.getEquipment}?client=$hospitalId${sn.isEmpty ? "" : "&serial_qr=$sn"}"), - ); - - _stateCode = response.statusCode; - List page = []; - if (response.statusCode >= 200 && response.statusCode < 300) { - // client's request was successfully received - List categoriesListJson = json.decode(utf8.decode(response.bodyBytes)); - page = categoriesListJson.map((device) => Device.fromJson(device)).toList(); - } - return page; - } catch (error) { - _loading = false; - _stateCode = -1; - notifyListeners(); - return []; - } + /// Returns a list of devices by [hospitalId] (and optionally) [serialNumber] + /// + /// ### NOTE : if [hospitalId] is [NULL] empty list will be returned + Future> getDevicesListBySN({required String? hospitalId, required String serialNumber}) { + if (hospitalId == null) return Future.value(const []); + return DevicesApiClient().getDevicesListBySN(hospitalId: hospitalId, serialNumber: serialNumber); } } diff --git a/lib/controllers/providers/api/service_requests_provider.dart b/lib/controllers/providers/api/service_requests_provider.dart index 506e77bb..0fe16f5d 100644 --- a/lib/controllers/providers/api/service_requests_provider.dart +++ b/lib/controllers/providers/api/service_requests_provider.dart @@ -1,7 +1,11 @@ import 'dart:convert'; +import 'dart:io'; -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:http/http.dart'; +import 'package:test_sa/api/service_request_api_client.dart'; +import 'package:test_sa/controllers/providers/loading_notifier.dart'; import '../../../models/issue.dart'; import '../../../models/lookup.dart'; @@ -13,14 +17,15 @@ import '../../../models/timer_model.dart'; import '../../../models/user.dart'; import '../../api_routes/urls.dart'; import '../../http_status_manger/http_status_manger.dart'; +import '../../localization/localization.dart'; -class ServiceRequestsProvider extends ChangeNotifier { +class ServiceRequestsProvider extends LoadingNotifier { // number of items call in each request final pageItemNumber = 50; //reset provider data void reset() { - serviceRequests = null; + ServiceRequestApiClient().serviceRequests.clear(); nextPage = true; stateCode = null; } @@ -33,9 +38,6 @@ class ServiceRequestsProvider extends ChangeNotifier { // true if there is next page in product list and false if not bool? nextPage = true; - // list of user requests - List? serviceRequests; - // when requests in-process _loading = true // done _loading = true // failed _loading = false @@ -61,7 +63,7 @@ class ServiceRequestsProvider extends ChangeNotifier { try { response = await get( Uri.parse( - "$host${URLs.getServiceRequests}?uid=${user?.id}${hospitalId == null ? "" : "&client_nid=$hospitalId"}&token=${user?.token}&page=${(serviceRequests?.length ?? 0) ~/ pageItemNumber}${search?.toSearchString()}", + "$host${URLs.getServiceRequests}?uid=${user?.id}${hospitalId == null ? "" : "&client_nid=$hospitalId"}&token=${user?.token}&page=${(ServiceRequestApiClient().serviceRequests.length) ~/ pageItemNumber}${search?.toSearchString()}", ), headers: {"Content-Type": "application/json; charset=utf-8"}, ); @@ -69,10 +71,9 @@ class ServiceRequestsProvider extends ChangeNotifier { if (response.statusCode >= 200 && response.statusCode < 300) { // client's request was successfully received List requestsListJson = json.decode(utf8.decode(response.bodyBytes).replaceAll("\\", "")); - List _serviceRequestsPage = requestsListJson.map((request) => ServiceRequest.fromJson(request)).toList(); - serviceRequests ??= []; - serviceRequests?.addAll(_serviceRequestsPage); - if (_serviceRequestsPage.length == pageItemNumber) { + List serviceRequestsPage = requestsListJson.map((request) => ServiceRequest.fromJson(request)).toList(); + ServiceRequestApiClient().serviceRequests.addAll(serviceRequestsPage); + if (serviceRequestsPage.length == pageItemNumber) { nextPage = true; } else { nextPage = false; @@ -115,50 +116,33 @@ class ServiceRequestsProvider extends ChangeNotifier { List requests = jsonList.map((i) => ServiceRequest.fromJson(i)).toList(); return requests[0]; } else { - throw (HttpStatusManger.getStatusMessage(status: response.statusCode, subtitle: subtitle) ?? ""); + throw (HttpStatusManger.getStatusMessage(status: response.statusCode, subtitle: subtitle)); } } - Future createRequest({ - required String host, - required User user, - required ServiceRequest serviceRequest, - }) async { - var body = { - "uid": user.id, - "token": user.token ?? "", - "sn_id": serviceRequest.deviceId ?? "", - "date": (DateTime.now().millisecondsSinceEpoch).toString(), - "client": user.hospital?.id ?? '', - "complaint": serviceRequest.maintenanceIssue, - "image": json.encode(serviceRequest.devicePhotos), - "priority": (serviceRequest.priority?.id).toString(), - "defect_types": (serviceRequest.defectType?.id).toString(), - }; - body["audio"] = serviceRequest.audio; - Response response; - try { - response = await post( - Uri.parse(host + URLs.createRequest), - body: body, - ); - stateCode = response.statusCode; - if (response.statusCode >= 200 && response.statusCode < 300) { - final serviceRequests = this.serviceRequests; - if (serviceRequests != null) { - serviceRequests.insert( - 0, - ServiceRequest.fromJson( - json.decode(utf8.decode(response.bodyBytes))[0], - ), - ); - } - notifyListeners(); - } - return response.statusCode; - } catch (error) { - return -1; + /// ### Create service request and add the response to [ServiceRequestApiClient.serviceRequests] + Future createRequest(BuildContext context, {required ServiceRequest serviceRequest, required List deviceImages}) async { + final subtitle = AppLocalization.of(context)?.subtitle; + serviceRequest.devicePhotos = deviceImages.map((e) => base64Encode(e.readAsBytesSync())).toList(); + if (serviceRequest.audio != null) { + final file = File(serviceRequest.audio!); + serviceRequest.audio = base64Encode(file.readAsBytesSync()); } + waitApiRequest(() async { + await ServiceRequestApiClient().createRequest(serviceRequest); + }, onSuccess: () { + /// TODO : this is temporary + stateCode = 200; + + Fluttertoast.showToast(msg: subtitle?.requestCompleteSuccessfully ?? ''); + Navigator.of(context).pop(); + }, onError: (error) { + /// TODO : this is temporary + stateCode = error.error?.errorCode; + + String errorMessage = HttpStatusManger.getStatusMessage(status: error.error?.errorCode, subtitle: subtitle); + ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(errorMessage))); + }); } Future createIssueReport({ diff --git a/lib/controllers/validator/validator.dart b/lib/controllers/validator/validator.dart index 168ce897..a4beae1a 100644 --- a/lib/controllers/validator/validator.dart +++ b/lib/controllers/validator/validator.dart @@ -3,14 +3,14 @@ class Validator { Validator._(); // check if string not empty and has value - static bool hasValue(String string) { + static bool hasValue(String? string) { if (string == null || string.isEmpty) return false; return true; } // Return true if email is valid. Otherwise, return false static bool isEmail(String email) { - RegExp exp = new RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+"); + RegExp exp = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+"); if (exp.hasMatch(email)) return true; return false; } diff --git a/lib/views/pages/user/requests/create_request.dart b/lib/views/pages/user/requests/create_request.dart index d4917ef2..48d3f821 100644 --- a/lib/views/pages/user/requests/create_request.dart +++ b/lib/views/pages/user/requests/create_request.dart @@ -1,17 +1,12 @@ -import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:fluttertoast/fluttertoast.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import '../../../../api/user_api_client.dart'; -import '../../../../controllers/http_status_manger/http_status_manger.dart'; import '../../../../controllers/localization/localization.dart'; import '../../../../controllers/providers/api/service_requests_provider.dart'; -import '../../../../controllers/providers/settings/setting_provider.dart'; -import '../../../../controllers/providers/user_provider.dart'; import '../../../../controllers/validator/validator.dart'; import '../../../../extensions/int_extensions.dart'; import '../../../../extensions/widget_extensions.dart'; @@ -32,30 +27,29 @@ import '../../../widgets/status/service_request/service_request_priority_mune.da import '../../../widgets/titles/app_sub_title.dart'; class CreateRequestPage extends StatefulWidget { - static final String id = "/create-request"; + static const String id = "/create-request"; + + const CreateRequestPage({super.key}); @override - _CreateRequestPageState createState() => _CreateRequestPageState(); + CreateRequestPageState createState() => CreateRequestPageState(); } -class _CreateRequestPageState extends State { +class CreateRequestPageState extends State { late double _height; - late UserProvider _userProvider; - late SettingProvider _settingProvider; late ServiceRequestsProvider _serviceRequestsProvider; - ServiceRequest _serviceRequest = ServiceRequest(); - List _deviceImages = []; - bool _isLoading = false; + final ServiceRequest _serviceRequest = ServiceRequest(); Device? _device; - late Subtitle _subtitle; + Subtitle? _subtitle; + final List _deviceImages = []; final GlobalKey _formKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); late TextEditingController _controller; @override void initState() { - _controller = TextEditingController(); super.initState(); + _controller = TextEditingController(); } @override @@ -67,15 +61,13 @@ class _CreateRequestPageState extends State { @override Widget build(BuildContext context) { _height = MediaQuery.of(context).size.height; - _userProvider = Provider.of(context); - _settingProvider = Provider.of(context); + _subtitle = AppLocalization.of(context)?.subtitle; _serviceRequestsProvider = Provider.of(context); - _subtitle = AppLocalization.of(context)!.subtitle!; return Scaffold( key: _scaffoldKey, body: SafeArea( child: LoadingManager( - isLoading: _isLoading, + isLoading: _serviceRequestsProvider.loading, isFailedLoading: false, stateCode: 200, onRefresh: () async {}, @@ -86,22 +78,22 @@ class _CreateRequestPageState extends State { ListView( children: [ //AppNameBar(), - SizedBox( + const SizedBox( height: 16, ), Hero( tag: "logo", child: Image( height: _height / 6, - image: AssetImage("assets/images/logo.png"), + image: const AssetImage("assets/images/logo.png"), ), ), Center( child: Padding( padding: const EdgeInsets.all(8.0), child: Text( - _subtitle.newServiceRequest, - style: Theme.of(context).textTheme.headline5?.copyWith(color: AColors.cyan, fontWeight: FontWeight.w600), + _subtitle?.newServiceRequest ?? '', + style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: AColors.cyan, fontWeight: FontWeight.w600), ), ), ), @@ -110,23 +102,23 @@ class _CreateRequestPageState extends State { children: [ 12.height, UserApiClient().user?.hospital == null - ? SizedBox.shrink() + ? const SizedBox.shrink() : ATextFormField( enable: false, - initialValue: UserApiClient().user?.hospital?.name ?? _subtitle.noHospitalFound, - hintText: _subtitle.hospital, + initialValue: UserApiClient().user?.hospital?.name ?? _subtitle?.noHospitalFound, + hintText: _subtitle?.hospital, prefixIconData: FontAwesomeIcons.hospital, - style: Theme.of(context).textTheme.subtitle1, + style: Theme.of(context).textTheme.titleMedium, ), 12.height, UserApiClient().user?.department == null - ? SizedBox.shrink() + ? const SizedBox.shrink() : ATextFormField( enable: false, - initialValue: UserApiClient().user?.department?.name ?? _subtitle.noUniteFound, - hintText: _subtitle.unite, + initialValue: UserApiClient().user?.department?.name ?? _subtitle?.noUniteFound, + hintText: _subtitle?.unite, prefixIconData: FontAwesomeIcons.hospitalUser, - style: Theme.of(context).textTheme.subtitle1, + style: Theme.of(context).textTheme.titleMedium, ), 12.height, DeviceButton( @@ -164,7 +156,7 @@ class _CreateRequestPageState extends State { ), 12.height, MultiImagesPicker( - label: _subtitle.deviceImages, + label: _subtitle?.deviceImages, images: _deviceImages, ), 12.height, @@ -173,11 +165,11 @@ class _CreateRequestPageState extends State { ATextFormField( controller: _controller, initialValue: _serviceRequest.maintenanceIssue, - hintText: _subtitle.maintenanceIssue, + hintText: _subtitle?.maintenanceIssue, prefixIconData: FontAwesomeIcons.triangleExclamation, - style: Theme.of(context).textTheme.headline6, + style: Theme.of(context).textTheme.titleLarge, textInputType: TextInputType.multiline, - validator: (value) => Validator.hasValue(value!) ? '' : _subtitle.maintenanceIssueRequired, + validator: (value) => Validator.hasValue(value) ? null : _subtitle?.maintenanceIssueRequired, onSaved: (value) { _serviceRequest.maintenanceIssue = value; }, @@ -192,42 +184,19 @@ class _CreateRequestPageState extends State { Padding( padding: const EdgeInsets.all(20.0), child: AButton( - text: _subtitle.submit, + text: _subtitle?.submit ?? '', onPressed: () async { - if (!(_formKey.currentState?.validate() ?? false)) return; - _formKey.currentState?.save(); - _serviceRequest.deviceId = _device?.id ?? ""; - _isLoading = true; - setState(() {}); - _serviceRequest.devicePhotos = _deviceImages.map((e) => base64Encode(e.readAsBytesSync())).toList(); - if (_serviceRequest.audio != null) { - final file = File(_serviceRequest.audio!); - _serviceRequest.audio = base64Encode(file.readAsBytesSync()); - } - int status = await _serviceRequestsProvider.createRequest( - user: UserApiClient().user!, - host: _settingProvider.host ?? "", - serviceRequest: _serviceRequest, - ); - _isLoading = false; - setState(() {}); - if (status >= 200 && status < 300) { - Fluttertoast.showToast( - msg: _subtitle.requestCompleteSuccessfully, - ); - Navigator.of(context).pop(); - } else { - String errorMessage = HttpStatusManger.getStatusMessage(status: status, subtitle: _subtitle); - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text(errorMessage), - )); + if (_formKey.currentState?.validate() ?? false) { + _formKey.currentState?.save(); + _serviceRequest.deviceId = _device?.id ?? ""; + await _serviceRequestsProvider.createRequest(context, serviceRequest: _serviceRequest, deviceImages: _deviceImages); } }, ), ), ], ), - ABackButton(), + const ABackButton(), ], ), ), diff --git a/lib/views/pages/user/requests/requests_page.dart b/lib/views/pages/user/requests/requests_page.dart index b5423b21..ab0eba6b 100644 --- a/lib/views/pages/user/requests/requests_page.dart +++ b/lib/views/pages/user/requests/requests_page.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/api/service_request_api_client.dart'; import '../../../../api/user_api_client.dart'; import '../../../../controllers/localization/localization.dart'; import '../../../../controllers/providers/api/service_requests_provider.dart'; import '../../../../controllers/providers/settings/setting_provider.dart'; -import '../../../../controllers/providers/user_provider.dart'; import '../../../../models/service_request/service_request_search.dart'; import '../../../../models/subtitle.dart'; import '../../../app_style/colors.dart'; @@ -16,23 +16,23 @@ import '../../../widgets/requests/service_request_list.dart'; import '../../../widgets/search/service_request_search_bar.dart'; class ServiceRequestsPage extends StatefulWidget { - static final String id = "/service-requests"; + static const String id = "/service-requests"; + + const ServiceRequestsPage({super.key}); @override - _ServiceRequestsPageState createState() => _ServiceRequestsPageState(); + ServiceRequestsPageState createState() => ServiceRequestsPageState(); } -class _ServiceRequestsPageState extends State with TickerProviderStateMixin { +class ServiceRequestsPageState extends State with TickerProviderStateMixin { late ServiceRequestsProvider _serviceRequestsProvider; - late UserProvider _userProvider; late SettingProvider _settingProvider; - bool _expandedSearch = false; + final bool _expandedSearch = false; bool _firstTime = true; @override Widget build(BuildContext context) { _serviceRequestsProvider = Provider.of(context); - _userProvider = Provider.of(context); _settingProvider = Provider.of(context); Subtitle? subtitle = AppLocalization.of(context)?.subtitle; if (_firstTime) { @@ -44,7 +44,6 @@ class _ServiceRequestsPageState extends State with TickerPr body: SafeArea( child: LoadingManager( isLoading: _serviceRequestsProvider.isLoading, - isFailedLoading: _serviceRequestsProvider.serviceRequests == null, stateCode: _serviceRequestsProvider.stateCode, onRefresh: () async { _serviceRequestsProvider.reset(); @@ -116,7 +115,7 @@ class _ServiceRequestsPageState extends State with TickerPr hospitalId: UserApiClient().user?.hospital?.id, ); }, - requests: _serviceRequestsProvider.serviceRequests ?? [], + requests: ServiceRequestApiClient().serviceRequests, ), ), ], diff --git a/lib/views/widgets/equipment/auto_complete_devices_field.dart b/lib/views/widgets/equipment/auto_complete_devices_field.dart index eb408168..70631fab 100644 --- a/lib/views/widgets/equipment/auto_complete_devices_field.dart +++ b/lib/views/widgets/equipment/auto_complete_devices_field.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:provider/provider.dart'; -import 'package:test_sa/api/user_api_client.dart'; -import 'package:test_sa/controllers/providers/api/devices_provider.dart'; -import '../../../controllers/providers/settings/setting_provider.dart'; -import '../../../controllers/providers/user_provider.dart'; +import '../../../controllers/providers/api/devices_provider.dart'; import '../../../models/device/device.dart'; -import '../../../models/user.dart'; import '../../app_style/colors.dart'; import '../../app_style/sizing.dart'; @@ -22,9 +18,7 @@ class AutoCompleteDeviceField extends StatefulWidget { _AutoCompleteDeviceFieldState createState() => _AutoCompleteDeviceFieldState(); } -class _AutoCompleteDeviceFieldState extends State { - - SettingProvider? _settingProvider; +class AutoCompleteDeviceFieldState extends State { DevicesProvider? _devicesProvider; UserProvider? _userProvider; TextEditingController? _controller; @@ -42,8 +36,6 @@ class _AutoCompleteDeviceFieldState extends State { } @override Widget build(BuildContext context) { - _settingProvider = Provider.of(context); - _userProvider = Provider.of(context); _devicesProvider = Provider.of(context); //Subtitle _subtitle = AppLocalization.of(context).subtitle; return Container( @@ -74,12 +66,7 @@ class _AutoCompleteDeviceFieldState extends State { textInputAction: TextInputAction.search, ), suggestionsCallback: (value) async { - return await _devicesProvider!.getDevicesList( - host: _settingProvider?.host??"", - user: UserApiClient().user, - hospitalId: widget.hospitalId??"", - serialNumber: value, - ); + return await _devicesProvider!.getDevicesList(hospitalId: widget.hospitalId, serialNumber: value); }, itemBuilder: (context, device) { return ListTile( diff --git a/lib/views/widgets/equipment/device_button.dart b/lib/views/widgets/equipment/device_button.dart index 6c5c9f93..47371fc5 100644 --- a/lib/views/widgets/equipment/device_button.dart +++ b/lib/views/widgets/equipment/device_button.dart @@ -15,7 +15,7 @@ class DeviceButton extends StatelessWidget { @override Widget build(BuildContext context) { - Subtitle? _subtitle = AppLocalization.of(context)?.subtitle; + Subtitle? subtitle = AppLocalization.of(context)?.subtitle; return ElevatedButton( style: ElevatedButton.styleFrom( elevation: 0, @@ -33,8 +33,8 @@ class DeviceButton extends StatelessWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Text( - _subtitle?.pickDevice ?? "", - style: Theme.of(context).textTheme.subtitle1, + subtitle?.pickDevice ?? "", + style: Theme.of(context).textTheme.titleMedium, textScaleFactor: AppStyle.getScaleFactor(context), textDirection: TextDirection.rtl, textAlign: TextAlign.left, @@ -43,27 +43,27 @@ class DeviceButton extends StatelessWidget { ) : Expanded( child: ListTile( - contentPadding: EdgeInsets.all(0), + contentPadding: const EdgeInsets.all(0), title: Text( - "${_subtitle?.sn ?? ""} : ${device?.serialNumber ?? ""}", - style: Theme.of(context).textTheme.subtitle1, + "${subtitle?.sn ?? ""} : ${device?.serialNumber ?? ""}", + style: Theme.of(context).textTheme.titleMedium, ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Divider( - color: Theme.of(context).textTheme.subtitle1?.color, + color: Theme.of(context).textTheme.titleMedium?.color, ), Text( - "${_subtitle?.brand} : ${device?.brand}", - style: Theme.of(context).textTheme.subtitle2, + "${subtitle?.brand} : ${device?.brand}", + style: Theme.of(context).textTheme.titleSmall, ), Divider( - color: Theme.of(context).textTheme.subtitle1?.color, + color: Theme.of(context).textTheme.titleMedium?.color, ), Text( - "${_subtitle?.model} : ${device?.model}", - style: Theme.of(context).textTheme.subtitle2, + "${subtitle?.model} : ${device?.model}", + style: Theme.of(context).textTheme.titleSmall, ), ], ), diff --git a/lib/views/widgets/equipment/single_device_picker.dart b/lib/views/widgets/equipment/single_device_picker.dart index c927828c..d8ca8491 100644 --- a/lib/views/widgets/equipment/single_device_picker.dart +++ b/lib/views/widgets/equipment/single_device_picker.dart @@ -1,15 +1,13 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:provider/provider.dart'; +import 'package:test_sa/api/devices_api_client.dart'; import 'package:test_sa/api/user_api_client.dart'; import '../../../controllers/localization/localization.dart'; import '../../../controllers/providers/api/devices_provider.dart'; -import '../../../controllers/providers/settings/setting_provider.dart'; -import '../../../controllers/providers/user_provider.dart'; import '../../../models/device/device.dart'; import '../../../models/subtitle.dart'; -import '../../../models/user.dart'; import '../app_text_form_field.dart'; import '../loaders/loading_manager.dart'; import '../loaders/no_item_found.dart'; @@ -27,10 +25,7 @@ class SingleDevicePicker extends StatefulWidget { } class SingleDevicePickerState extends State { - DevicesProvider? _devicesProvider; - UserProvider? _userProvider; - SettingProvider? _settingProvider; - final List _searchableList = []; + late DevicesProvider _devicesProvider; Subtitle? _subtitle; _getDevice(String? result) async { @@ -41,8 +36,7 @@ class SingleDevicePickerState extends State { builder: (dialogContext) { return const Center(child: CircularProgressIndicator()); }); - List devices = - await _devicesProvider!.getDevicesListBySN(host: _settingProvider?.host ?? "", user: UserApiClient().user ?? User(), hospitalId: UserApiClient().user?.hospital?.id ?? "", sn: result); + List devices = await _devicesProvider.getDevicesListBySN(hospitalId: UserApiClient().user?.hospital?.id ?? "", serialNumber: result); Navigator.of(context).pop(); if (devices.isEmpty) { Fluttertoast.showToast(msg: _subtitle?.noDeviceFound ?? ""); @@ -58,28 +52,22 @@ class SingleDevicePickerState extends State { @override void dispose() { - _devicesProvider?.reset(); + _devicesProvider.reset(); super.dispose(); } @override Widget build(BuildContext context) { _devicesProvider = Provider.of(context); - _userProvider = Provider.of(context); - _settingProvider = Provider.of(context); - _subtitle = AppLocalization.of(context)?.subtitle; return Scaffold( resizeToAvoidBottomInset: false, body: LoadingManager( - isLoading: _devicesProvider?.isLoading, - stateCode: _devicesProvider?.stateCode, - isFailedLoading: _devicesProvider?.devices == null, + isLoading: _devicesProvider.loading, + stateCode: _devicesProvider.stateCode, onRefresh: () async { - _devicesProvider?.reset(); - _searchableList.clear(); - await _devicesProvider?.getEquipment(user: UserApiClient().user ?? User(), host: _settingProvider?.host ?? "", hospitalId: UserApiClient().user?.hospital?.id ?? ""); - _searchableList.addAll(_devicesProvider?.devices ?? []); + _devicesProvider.reset(); + await _devicesProvider.getEquipment(hospitalId: UserApiClient().user?.hospital?.id); }, child: Column( children: [ @@ -90,24 +78,22 @@ class SingleDevicePickerState extends State { children: [ ATextFormField( hintText: _subtitle?.searchBySn ?? "", - style: Theme.of(context).textTheme.subtitle1, + style: Theme.of(context).textTheme.titleMedium, suffixIcon: const Icon(Icons.search_rounded), onChange: (value) { - _searchableList.clear(); - _searchableList.addAll(_devicesProvider!.devices!.where((element) => element.serialNumber!.toLowerCase().contains(value.toLowerCase())).toList()); + _devicesProvider.searchableList.clear(); + _devicesProvider.searchableList.addAll(DevicesApiClient().devices.where((element) => element.serialNumber!.toLowerCase().contains(value.toLowerCase())).toList()); setState(() {}); }, ), - const SizedBox( - height: 8, - ), + const SizedBox(height: 8), ATextFormField( hintText: "Search by Number", - style: Theme.of(context).textTheme.subtitle1, + style: Theme.of(context).textTheme.titleMedium, suffixIcon: const Icon(Icons.search_rounded), onChange: (value) { - _searchableList.clear(); - _searchableList.addAll(_devicesProvider!.devices!.where((element) => element.number!.toLowerCase().contains(value.toLowerCase())).toList()); + _devicesProvider.searchableList.clear(); + _devicesProvider.searchableList.addAll(DevicesApiClient().devices.where((element) => element.number!.toLowerCase().contains(value.toLowerCase())).toList()); setState(() {}); }, ), @@ -115,17 +101,15 @@ class SingleDevicePickerState extends State { ), ), Expanded( - child: _searchableList.isEmpty - ? NoItemFound( - message: _subtitle?.noDeviceFound ?? "", - ) + child: _devicesProvider.searchableList.isEmpty + ? NoItemFound(message: _subtitle?.noDeviceFound ?? "") : ListView.builder( padding: EdgeInsets.zero, shrinkWrap: true, - itemCount: _searchableList.length, + itemCount: _devicesProvider.searchableList.length, itemBuilder: (listContext, itemIndex) { return DeviceItem( - device: _searchableList[itemIndex], + device: _devicesProvider.searchableList[itemIndex], onPressed: (device) { Navigator.of(context).pop(device); }, @@ -140,9 +124,7 @@ class SingleDevicePickerState extends State { heroTag: "some tag 2", child: const Icon(Icons.qr_code_scanner), onPressed: () async { - String result = await Navigator.of(context).push( - MaterialPageRoute(builder: (_) => ScanQr()), - ) as String; + String result = await Navigator.of(context).push(MaterialPageRoute(builder: (_) => const ScanQr())) as String; _getDevice(result); }, ), diff --git a/lib/views/widgets/pentry/auto_complete_fields/auto_complete_devices_field.dart b/lib/views/widgets/pentry/auto_complete_fields/auto_complete_devices_field.dart index 798d23d7..21c5dc50 100644 --- a/lib/views/widgets/pentry/auto_complete_fields/auto_complete_devices_field.dart +++ b/lib/views/widgets/pentry/auto_complete_fields/auto_complete_devices_field.dart @@ -2,13 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:provider/provider.dart'; -import '../../../../api/user_api_client.dart'; import '../../../../controllers/providers/api/devices_provider.dart'; -import '../../../../controllers/providers/settings/setting_provider.dart'; -import '../../../../controllers/providers/user_provider.dart'; import '../../../../models/device/device.dart'; import '../../../../models/lookup.dart'; -import '../../../../models/user.dart'; import '../../../app_style/colors.dart'; import '../../../app_style/sizing.dart'; @@ -24,9 +20,7 @@ class AutoCompleteDeviceNumberField extends StatefulWidget { } class _AutoCompleteDeviceNumberFieldState extends State { - late SettingProvider _settingProvider; late DevicesProvider _devicesProvider; - late UserProvider _userProvider; late TextEditingController _controller; @override @@ -43,10 +37,7 @@ class _AutoCompleteDeviceNumberFieldState extends State(context); - _userProvider = Provider.of(context); _devicesProvider = Provider.of(context); - //Subtitle _subtitle = AppLocalization.of(context).subtitle; return Container( padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( @@ -56,7 +47,7 @@ class _AutoCompleteDeviceNumberFieldState extends State( textFieldConfiguration: TextFieldConfiguration( - style: Theme.of(context).textTheme.headline6, + style: Theme.of(context).textTheme.titleLarge, controller: _controller, textAlign: TextAlign.center, decoration: const InputDecoration( @@ -68,12 +59,7 @@ class _AutoCompleteDeviceNumberFieldState extends State(context); - final userProvider = Provider.of(context); final menuProvider = Provider.of(context); return LoadingManager( isLoading: menuProvider.isLoading,