Merge branch 'master' into majd_development

# Conflicts:
#	lib/controllers/providers/api/devices_provider.dart
#	lib/views/widgets/equipment/auto_complete_devices_field.dart
merge-requests/16/head
nextwo 3 years ago
commit 7e808e5bf4

@ -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<Device> 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<List<Device>> 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<List<Device>> 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();
}
}

@ -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<ServiceRequest> 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,
);
}
}

@ -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<String, dynamic> jsonObject = {};
// jsonObject["uid"] = user.id;

@ -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<Device> _searchableList = [];
List<Device> 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<Device>? _devices;
List<Device>? 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<int> 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<List<Device>> 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<Device> 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<List<Device>> 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<List<Device>> 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<Device> 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<List<Device>> getDevicesListBySN({required String? hospitalId, required String serialNumber}) {
if (hospitalId == null) return Future.value(const []);
return DevicesApiClient().getDevicesListBySN(hospitalId: hospitalId, serialNumber: serialNumber);
}
}

@ -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<ServiceRequest>? 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<ServiceRequest> _serviceRequestsPage = requestsListJson.map((request) => ServiceRequest.fromJson(request)).toList();
serviceRequests ??= [];
serviceRequests?.addAll(_serviceRequestsPage);
if (_serviceRequestsPage.length == pageItemNumber) {
List<ServiceRequest> 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<ServiceRequest> 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<int> 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<File> 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<int> createIssueReport({

@ -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;
}

@ -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<CreateRequestPage> {
class CreateRequestPageState extends State<CreateRequestPage> {
late double _height;
late UserProvider _userProvider;
late SettingProvider _settingProvider;
late ServiceRequestsProvider _serviceRequestsProvider;
ServiceRequest _serviceRequest = ServiceRequest();
List<File> _deviceImages = [];
bool _isLoading = false;
final ServiceRequest _serviceRequest = ServiceRequest();
Device? _device;
late Subtitle _subtitle;
Subtitle? _subtitle;
final List<File> _deviceImages = [];
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
late TextEditingController _controller;
@override
void initState() {
_controller = TextEditingController();
super.initState();
_controller = TextEditingController();
}
@override
@ -67,15 +61,13 @@ class _CreateRequestPageState extends State<CreateRequestPage> {
@override
Widget build(BuildContext context) {
_height = MediaQuery.of(context).size.height;
_userProvider = Provider.of<UserProvider>(context);
_settingProvider = Provider.of<SettingProvider>(context);
_subtitle = AppLocalization.of(context)?.subtitle;
_serviceRequestsProvider = Provider.of<ServiceRequestsProvider>(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<CreateRequestPage> {
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<CreateRequestPage> {
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<CreateRequestPage> {
),
12.height,
MultiImagesPicker(
label: _subtitle.deviceImages,
label: _subtitle?.deviceImages,
images: _deviceImages,
),
12.height,
@ -173,11 +165,11 @@ class _CreateRequestPageState extends State<CreateRequestPage> {
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<CreateRequestPage> {
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(),
],
),
),

@ -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<ServiceRequestsPage> with TickerProviderStateMixin {
class ServiceRequestsPageState extends State<ServiceRequestsPage> 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<ServiceRequestsProvider>(context);
_userProvider = Provider.of<UserProvider>(context);
_settingProvider = Provider.of<SettingProvider>(context);
Subtitle? subtitle = AppLocalization.of(context)?.subtitle;
if (_firstTime) {
@ -44,7 +44,6 @@ class _ServiceRequestsPageState extends State<ServiceRequestsPage> 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<ServiceRequestsPage> with TickerPr
hospitalId: UserApiClient().user?.hospital?.id,
);
},
requests: _serviceRequestsProvider.serviceRequests ?? [],
requests: ServiceRequestApiClient().serviceRequests,
),
),
],

@ -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';
@ -19,14 +15,11 @@ class AutoCompleteDeviceField extends StatefulWidget {
const AutoCompleteDeviceField({Key? key, required this.initialValue, this.onPick, this.hospitalId}) : super(key: key);
@override
_AutoCompleteDeviceFieldState createState() => _AutoCompleteDeviceFieldState();
AutoCompleteDeviceFieldState createState() => AutoCompleteDeviceFieldState();
}
class _AutoCompleteDeviceFieldState extends State<AutoCompleteDeviceField> {
SettingProvider? _settingProvider;
class AutoCompleteDeviceFieldState extends State<AutoCompleteDeviceField> {
DevicesProvider? _devicesProvider;
UserProvider? _userProvider;
TextEditingController? _controller;
@override
@ -40,56 +33,42 @@ class _AutoCompleteDeviceFieldState extends State<AutoCompleteDeviceField> {
_controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
_settingProvider = Provider.of<SettingProvider>(context);
_userProvider = Provider.of<UserProvider>(context);
_devicesProvider = Provider.of<DevicesProvider>(context);
//Subtitle _subtitle = AppLocalization.of(context).subtitle;
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 16
),
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color:AColors.black),
borderRadius: BorderRadius.circular(
AppStyle.borderRadius * AppStyle.getScaleFactor(context)
),
boxShadow: [
AppStyle.boxShadow
]
),
border: Border.all(color: AColors.black),
borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)),
boxShadow: const [AppStyle.boxShadow]),
child: TypeAheadField<Device>(
textFieldConfiguration: TextFieldConfiguration(
style: Theme.of(context).textTheme.headline6,
controller: _controller,
textAlign: TextAlign.center,
decoration: const InputDecoration(
border: InputBorder.none,
disabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
),
textInputAction: TextInputAction.search,
style: Theme.of(context).textTheme.titleLarge,
controller: _controller,
textAlign: TextAlign.center,
decoration: const InputDecoration(
border: InputBorder.none,
disabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
),
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(
title: Text(device.serialNumber??""),
subtitle: Text("${device.model??""}/${device.brand??""}"),
title: Text(device.serialNumber ?? ""),
subtitle: Text("${device.model ?? ""}/${device.brand ?? ""}"),
);
},
onSuggestionSelected: (device) {
_controller?.text = device.serialNumber??"";
widget.onPick!(device.id??"");
_controller?.text = device.serialNumber ?? "";
widget.onPick!(device.id ?? "");
},
),
);

@ -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,
),
],
),

@ -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<SingleDevicePicker> {
DevicesProvider? _devicesProvider;
UserProvider? _userProvider;
SettingProvider? _settingProvider;
final List<Device> _searchableList = [];
late DevicesProvider _devicesProvider;
Subtitle? _subtitle;
_getDevice(String? result) async {
@ -41,8 +36,7 @@ class SingleDevicePickerState extends State<SingleDevicePicker> {
builder: (dialogContext) {
return const Center(child: CircularProgressIndicator());
});
List<Device> devices =
await _devicesProvider!.getDevicesListBySN(host: _settingProvider?.host ?? "", user: UserApiClient().user ?? User(), hospitalId: UserApiClient().user?.hospital?.id ?? "", sn: result);
List<Device> 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<SingleDevicePicker> {
@override
void dispose() {
_devicesProvider?.reset();
_devicesProvider.reset();
super.dispose();
}
@override
Widget build(BuildContext context) {
_devicesProvider = Provider.of<DevicesProvider>(context);
_userProvider = Provider.of<UserProvider>(context);
_settingProvider = Provider.of<SettingProvider>(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<SingleDevicePicker> {
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<SingleDevicePicker> {
),
),
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<SingleDevicePicker> {
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);
},
),

@ -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<AutoCompleteDeviceNumberField> {
late SettingProvider _settingProvider;
late DevicesProvider _devicesProvider;
late UserProvider _userProvider;
late TextEditingController _controller;
@override
@ -43,10 +37,7 @@ class _AutoCompleteDeviceNumberFieldState extends State<AutoCompleteDeviceNumber
@override
Widget build(BuildContext context) {
_settingProvider = Provider.of<SettingProvider>(context);
_userProvider = Provider.of<UserProvider>(context);
_devicesProvider = Provider.of<DevicesProvider>(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<AutoCompleteDeviceNumber
boxShadow: const [AppStyle.boxShadow]),
child: TypeAheadField<Device>(
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<AutoCompleteDeviceNumber
textInputAction: TextInputAction.search,
),
suggestionsCallback: (vale) async {
return await _devicesProvider.getDevicesList(
host: _settingProvider.host ?? "",
user: UserApiClient().user ?? User(),
hospitalId: widget.hospitalId ?? "",
number: vale,
);
return await _devicesProvider.getDevicesList(hospitalId: widget.hospitalId, number: vale);
},
itemBuilder: (context, device) {
return ListTile(

@ -4,7 +4,6 @@ import 'package:provider/provider.dart';
import '../../../../api/user_api_client.dart';
import '../../../../controllers/providers/api/status_drop_down/report/service_report_priority_provider.dart';
import '../../../../controllers/providers/settings/setting_provider.dart';
import '../../../../controllers/providers/user_provider.dart';
import '../../../../models/lookup.dart';
import '../../loaders/loading_manager.dart';
import '../single_status_menu.dart';
@ -22,7 +21,6 @@ class ServiceRequestPriorityMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
final settingProvider = Provider.of<SettingProvider>(context);
final userProvider = Provider.of<UserProvider>(context);
final menuProvider = Provider.of<ServiceRequestPriorityProvider>(context);
return LoadingManager(
isLoading: menuProvider.isLoading,

Loading…
Cancel
Save