diff --git a/assets/images/doc.png b/assets/images/doc.png new file mode 100644 index 00000000..e7418db8 Binary files /dev/null and b/assets/images/doc.png differ diff --git a/assets/images/excel.png b/assets/images/excel.png new file mode 100644 index 00000000..470e1be5 Binary files /dev/null and b/assets/images/excel.png differ diff --git a/assets/images/pdf.png b/assets/images/pdf.png new file mode 100644 index 00000000..3102284f Binary files /dev/null and b/assets/images/pdf.png differ diff --git a/assets/subtitles/ar_subtitle.json b/assets/subtitles/ar_subtitle.json index b9a51fd7..e419c327 100644 --- a/assets/subtitles/ar_subtitle.json +++ b/assets/subtitles/ar_subtitle.json @@ -201,5 +201,6 @@ "floor": "طابق", "department": "قسم", "room": "غرفه", - "actions": "اجراءات" + "actions": "اجراءات", + "deviceFiles": "ملفات الجهاز" } \ No newline at end of file diff --git a/assets/subtitles/en_subtitle.json b/assets/subtitles/en_subtitle.json index fc57c444..33ff94b5 100644 --- a/assets/subtitles/en_subtitle.json +++ b/assets/subtitles/en_subtitle.json @@ -201,5 +201,6 @@ "floor": "Floor", "department": "Department", "room": "Room", - "actions": "Actions" + "actions": "Actions", + "deviceFiles": "Asset Files" } \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 86b0405c..4d661d08 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -36,6 +36,10 @@ This app requires permission to access photo gallery to allow user to attach images NSSpeechRecognitionUsageDescription This app requires permission for Speech Recognition for speech to text functionality. + LSSupportsOpeningDocumentsInPlace + + UISupportsDocumentBrowser + UIBackgroundModes fetch diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 0dec4fe9..626f9639 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -51,6 +51,7 @@ class URLs { static get updateRegularVisits => "$_baseUrl/Visit/UpdateVisits"; // get static get getSingleServiceRequest => "$_baseUrl/return/call/information"; // get + static get getSuppliersAutoComplete => "$_baseUrl/Supplier/GetSuppliersAutoComplete"; static get getNotifications => "$_baseUrl/return/user/notification"; // get static get getRecentNotifications => "$_baseUrl/return/user/recent/notification"; // get static get createRequest => "$_baseUrl/CallRequest/AddCallRequest"; // get diff --git a/lib/controllers/providers/api/device_transfer_provider.dart b/lib/controllers/providers/api/device_transfer_provider.dart index cdd685bd..289fd413 100644 --- a/lib/controllers/providers/api/device_transfer_provider.dart +++ b/lib/controllers/providers/api/device_transfer_provider.dart @@ -74,10 +74,7 @@ class DeviceTransferProvider extends ChangeNotifier { body["pageSize"] = pageItemNumber; body.addAll(deviceTransferSearch.toMap()); - response = await ApiManager.instance.post( - URLs.getDeviceTransfer, - body: body, - ); + response = await ApiManager.instance.post(URLs.getDeviceTransfer, body: body); stateCode = response.statusCode; if (stateCode >= 200 && stateCode < 300) { @@ -87,8 +84,7 @@ class DeviceTransferProvider extends ChangeNotifier { items ??= []; items.addAll(itemsPage.toSet().toList()); sortMostRecent(items, deviceTransferSearch.mostRecent); - - print(listJson); + notifyListeners(); if (itemsPage.length == pageItemNumber) { nextPage = true; } else { diff --git a/lib/controllers/providers/api/gas_refill_provider.dart b/lib/controllers/providers/api/gas_refill_provider.dart index 445be633..6d19dac9 100644 --- a/lib/controllers/providers/api/gas_refill_provider.dart +++ b/lib/controllers/providers/api/gas_refill_provider.dart @@ -77,6 +77,7 @@ class GasRefillProvider extends ChangeNotifier { } items.addAll(itemsPage); sortMostRecent(items, mostRecent); + notifyListeners(); if (itemsPage.length == pageItemNumber) { nextPage = true; } else { diff --git a/lib/controllers/providers/api/regular_visits_provider.dart b/lib/controllers/providers/api/regular_visits_provider.dart index 25e330f7..66883477 100644 --- a/lib/controllers/providers/api/regular_visits_provider.dart +++ b/lib/controllers/providers/api/regular_visits_provider.dart @@ -13,7 +13,7 @@ import 'package:test_sa/models/visits/visits_search.dart'; class RegularVisitsProvider extends ChangeNotifier { // number of items call in each request - final pageItemNumber = 20; + final pageItemNumber = 12; //reset provider data void reset() { @@ -52,7 +52,7 @@ class RegularVisitsProvider extends ChangeNotifier { }) async { if (isLoading == true) return -2; isLoading = true; - if (visits == null) notifyListeners(); + if (visits?.isEmpty ?? true) notifyListeners(); Response response; //userId = 397.toString(); // testing id to view data @@ -77,11 +77,12 @@ class RegularVisitsProvider extends ChangeNotifier { // client's request was successfully received try { List requestsListJson = json.decode(response.body)["data"]; - List _visits = requestsListJson.map((request) => Visit.fromJson(request)).toList(); - visits ??= []; - sortMostRecent(_visits); - visits.addAll(_visits); - if (_visits.length == pageItemNumber) { + List visits = requestsListJson.map((request) => Visit.fromJson(request)).toList(); + this.visits ??= []; + this.visits.addAll(visits); + sortMostRecent(this.visits); + notifyListeners(); + if (visits.length == pageItemNumber) { nextPage = true; } else { nextPage = false; diff --git a/lib/controllers/providers/api/service_requests_provider.dart b/lib/controllers/providers/api/service_requests_provider.dart index ea03c7ee..54dbee7d 100644 --- a/lib/controllers/providers/api/service_requests_provider.dart +++ b/lib/controllers/providers/api/service_requests_provider.dart @@ -57,7 +57,7 @@ class ServiceRequestsProvider extends ChangeNotifier { Future getRequests({@required int hospitalId}) async { if (isLoading == true) return -2; isLoading = true; - if (serviceRequests == null) notifyListeners(); + if (serviceRequests?.isEmpty ?? false) notifyListeners(); Response response; try { Map body = {}; @@ -76,8 +76,9 @@ class ServiceRequestsProvider extends ChangeNotifier { List requestsListJson = json.decode(response.body)["data"]; List serviceRequestsPage = requestsListJson.map((request) => ServiceRequest.fromJson(request)).toList(); serviceRequests ??= []; - sortMostRecent(serviceRequestsPage); serviceRequests.addAll(serviceRequestsPage); + sortMostRecent(serviceRequests); + notifyListeners(); if (serviceRequestsPage.length == pageItemNumber) { nextPage = true; } else { diff --git a/lib/controllers/providers/api/status_drop_down/report/vendor_provider.dart b/lib/controllers/providers/api/status_drop_down/report/vendor_provider.dart new file mode 100644 index 00000000..4271e001 --- /dev/null +++ b/lib/controllers/providers/api/status_drop_down/report/vendor_provider.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart'; +import 'package:test_sa/controllers/api_routes/api_manager.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/models/service_request/search_work_order.dart'; + +class VendorProvider extends ChangeNotifier { + void reset() { + _vendors = null; + _loading = null; + _stateCode = null; + } + + int _stateCode; + int get stateCode => _stateCode; + + List _vendors; + List get vendors => _vendors; + + bool _loading; + bool get isLoading => _loading; + set isLoading(bool isLoading) { + _loading = isLoading; + notifyListeners(); + } + + Future getVendors(String text) async { + if (_loading == true) return -2; + _loading = true; + notifyListeners(); + Response response; + try { + response = await ApiManager.instance.get("${URLs.getSuppliersAutoComplete}?searchText=$text"); + _stateCode = response.statusCode; + if (response.statusCode >= 200 && response.statusCode < 300) { + // client's request was successfully received + List suppliersJson = json.decode(response.body)["data"]; + _vendors = suppliersJson.map((type) => Supplier.fromJson(type)).toList(); + } + _loading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + _loading = false; + _stateCode = -1; + notifyListeners(); + return -1; + } + } +} diff --git a/lib/main.dart b/lib/main.dart index 466e0528..03427e46 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -63,6 +63,7 @@ import 'controllers/providers/api/status_drop_down/report/service_report_repair_ import 'controllers/providers/api/status_drop_down/report/service_report_status_provider.dart'; import 'controllers/providers/api/status_drop_down/report/service_report_types_provider.dart'; import 'controllers/providers/api/status_drop_down/report/service_types_provider.dart'; +import 'controllers/providers/api/status_drop_down/report/vendor_provider.dart'; import 'controllers/providers/api/user_provider.dart'; import 'controllers/providers/settings/setting_provider.dart'; import 'views/pages/device_transfer/request_device_transfer.dart'; @@ -81,15 +82,17 @@ void main() async { } runApp(ChangeNotifierProvider( create: (_) => SettingProvider(), - child: MyApp(), + child: const MyApp(), )); } class MyApp extends StatelessWidget { + const MyApp({Key key}) : super(key: key); + // This widget is the root of your application. @override Widget build(BuildContext context) { - final _settingProvider = Provider.of(context); + final settingProvider = Provider.of(context); return MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => UserProvider()), @@ -131,6 +134,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => ServiceReportMaintenanceSituationProvider()), ChangeNotifierProvider(create: (_) => ServiceReportUsersProvider()), ChangeNotifierProvider(create: (_) => ServiceReportAssistantsEmployeeProvider()), + ChangeNotifierProvider(create: (_) => VendorProvider()), ], child: GestureDetector( onTap: () { @@ -160,7 +164,7 @@ class MyApp extends StatelessWidget { Locale('en'), Locale('ar'), ], - locale: Locale(_settingProvider.language ?? 'en'), + locale: Locale(settingProvider.language ?? 'en'), initialRoute: SplashScreen.id, routes: { SplashScreen.id: (_) => const SplashScreen(), diff --git a/lib/models/service_request/search_work_order.dart b/lib/models/service_request/search_work_order.dart index 7f4efa55..564b7dd6 100644 --- a/lib/models/service_request/search_work_order.dart +++ b/lib/models/service_request/search_work_order.dart @@ -286,7 +286,7 @@ class SearchWorkOrder { if (repairLocation?.id != null) { map['repairLocation'] = repairLocation.toMap(); } - if (reason != null) { + if (reason != null && reason.id != null) { map['reason'] = reason.toMap(); } // map['startofWorkTime'] = startofWorkTime; @@ -308,7 +308,7 @@ class SearchWorkOrder { if (attachmentsWorkOrder != null) { map['attachmentsWorkOrder'] = attachmentsWorkOrder.map((v) => v.toJson()).toList(); } - if (equipmentStatus != null) { + if (equipmentStatus != null && equipmentStatus.id != null) { map['equipmentStatus'] = equipmentStatus.toMap(); } if (suppEngineerWorkOrders != null) { @@ -2719,29 +2719,42 @@ class Supplier { Supplier({ this.id, this.suppliername, + this.suppPersons, }); Supplier.fromJson(dynamic json) { id = json['id']; suppliername = json['suppliername']; + if (json['suppPersons'] != null) { + suppPersons = []; + json['suppPersons'].forEach((v) { + suppPersons.add(SuppPersons.fromJson(v)); + }); + } } num id; String suppliername; + List suppPersons; SupplierModel copyWith({ num id, String suppliername, + List suppPersons, }) => SupplierModel( id: id ?? this.id, suppliername: suppliername ?? this.suppliername, + suppPersons: suppPersons ?? this.suppPersons, ); Map toJson() { final map = {}; map['id'] = id ?? 0; map['suppliername'] = suppliername; + if (suppPersons != null) { + map['suppPersons'] = suppPersons.map((v) => v.toJson()).toList(); + } return map; } } diff --git a/lib/models/subtitle.dart b/lib/models/subtitle.dart index 6786ddd1..fe71b3c3 100644 --- a/lib/models/subtitle.dart +++ b/lib/models/subtitle.dart @@ -84,6 +84,7 @@ class Subtitle { String hospital; String device; String deviceImages; + String deviceFiles; String pickDevice; String maintenanceIssue; String create; @@ -359,6 +360,7 @@ class Subtitle { @required this.createServiceRequest, @required this.delete, @required this.deviceImages, + @required this.deviceFiles, @required this.deviceRequired, @required this.general, @required this.maintenanceIssueRequired, @@ -520,6 +522,7 @@ class Subtitle { device: parsedJson["device"], deviceArName: parsedJson["deviceArName"], deviceImages: parsedJson["deviceImages"], + deviceFiles: parsedJson["deviceFiles"], deviceModel: parsedJson["deviceModel"], deviceName: parsedJson["deviceName"], deviceRequired: parsedJson["deviceRequired"], diff --git a/lib/views/pages/sub_workorder/auto_generated_vendor_name.dart b/lib/views/pages/sub_workorder/auto_generated_vendor_name.dart new file mode 100644 index 00000000..41244de8 --- /dev/null +++ b/lib/views/pages/sub_workorder/auto_generated_vendor_name.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_typeahead/flutter_typeahead.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/models/service_request/search_work_order.dart'; + +import '../../../controllers/providers/api/status_drop_down/report/vendor_provider.dart'; +import '../../app_style/colors.dart'; +import '../../app_style/sizing.dart'; + +class AutoGeneratedVendorName extends StatefulWidget { + final String initialValue; + final Function(Supplier) onSearch; + const AutoGeneratedVendorName({Key key, this.initialValue, this.onSearch}) : super(key: key); + + @override + State createState() => _AutoGeneratedVendorNameState(); +} + +class _AutoGeneratedVendorNameState extends State { + TextEditingController _controller; + + @override + void initState() { + super.initState(); + _controller = TextEditingController(text: widget.initialValue ?? ""); + } + + @override + void didUpdateWidget(covariant AutoGeneratedVendorName oldWidget) { + super.didUpdateWidget(oldWidget); + + if (oldWidget.initialValue != widget.initialValue) { + _controller = TextEditingController(text: widget.initialValue ?? ""); + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final vendorProvider = Provider.of(context, listen: false); + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: AColors.inputFieldBackgroundColor, + border: Border.all( + color: const Color(0xffefefef), + ), + borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)), + // boxShadow: [ + // AppStyle.boxShadow + // ] + ), + child: TypeAheadField( + textFieldConfiguration: TextFieldConfiguration( + style: Theme.of(context).textTheme.titleLarge, + controller: _controller, + textAlign: TextAlign.center, + decoration: const InputDecoration( + hintText: "Vendor Name", + border: InputBorder.none, + disabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + textInputAction: TextInputAction.search, + ), + suggestionsCallback: (vale) async { + await vendorProvider.getVendors(_controller.text.trim()); + return vendorProvider.vendors; + }, + itemBuilder: (context, vendor) { + return ListTile(title: Text(vendor.suppliername)); + }, + onSuggestionSelected: (hospital) { + widget.onSearch(hospital); + }, + ), + ); + } +} diff --git a/lib/views/pages/sub_workorder/create_sub_workorder_page.dart b/lib/views/pages/sub_workorder/create_sub_workorder_page.dart index fdfd67e5..f022539f 100644 --- a/lib/views/pages/sub_workorder/create_sub_workorder_page.dart +++ b/lib/views/pages/sub_workorder/create_sub_workorder_page.dart @@ -58,6 +58,7 @@ class _CreateSubWorkOrderPageState extends State { callRequest: CallRequest(id: widget?.workOrder?.callRequest?.id), currentSituation: null, supplier: null, + parentWOId: widget.workOrder.id, ); _serviceReport = ServiceReport(id: widget.workOrder.id, type: widget.workOrder.assetType, equipmentStatus: widget.workOrder.equipmentStatus); _isLoading = true; @@ -313,7 +314,6 @@ class _CreateSubWorkOrderPageState extends State { child: AButton( text: subtitle.create, onPressed: () async { - _subWorkOrders.parentWOId = widget.workOrder.id; _validate = true; setState(() {}); if (validate()) { diff --git a/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart b/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart index e91de02e..ab6ff1b7 100644 --- a/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart +++ b/lib/views/pages/sub_workorder/work_order_details_bottom_sheet.dart @@ -8,11 +8,13 @@ import 'package:test_sa/views/widgets/buttons/app_button.dart'; import 'package:test_sa/views/widgets/status/report/service_report_assistant_employee_menu.dart'; import 'package:test_sa/views/widgets/status/report/service_report_maintenance_situation.dart'; import 'package:test_sa/views/widgets/status/report/service_report_repair_location.dart'; +import 'package:test_sa/views/widgets/status/service_request/supplier_engineers_menu.dart'; import '../../../controllers/localization/localization.dart'; import '../../../controllers/providers/api/status_drop_down/report/service_report_maintenance_situation_provider.dart'; import '../../widgets/timer/app_timer.dart'; import '../../widgets/titles/app_sub_title.dart'; +import 'auto_generated_vendor_name.dart'; class WorkOrderDetailsBottomSheet extends StatefulWidget { final SearchWorkOrder subWorkOrder; @@ -25,12 +27,18 @@ class WorkOrderDetailsBottomSheet extends StatefulWidget { class _WorkOrderDetailsBottomSheetState extends State { final GlobalKey _formKey = GlobalKey(); final SearchWorkOrder _workOrder = SearchWorkOrder(); + bool _showVendorFields = false; + SuppEngineerWorkOrders engineer; @override void initState() { super.initState(); _workOrder.copyFrom(widget.subWorkOrder); + if (_workOrder.calllastSituation?.name?.toLowerCase()?.contains("under repair-vendor") ?? false) { + _showVendorFields = true; + _workOrder.supplier ??= SupplierModel(id: _workOrder?.supplier?.id); + } if (context.mounted) { Provider.of(context, listen: false).reset(); } @@ -118,6 +126,14 @@ class _WorkOrderDetailsBottomSheetState extends State { Navigator.of(context).pushNamed(TrackDeviceTransferPage.id); }, ), - // if (_userProvider?.user != null && _userProvider?.user?.type != UsersTypes.normal_user) - // LandPageItem( - // text: "Search Work Order", - // svgPath: "assets/images/sub_workorder_icon.svg", - // onPressed: () { - // Navigator.of(context).pushNamed(SearchSubWorkOrderPage.id); - // }, - // ), + if (_userProvider?.user != null && _userProvider?.user?.type != UsersTypes.normal_user) + LandPageItem( + text: "Create Sub Work Order", + svgPath: "assets/images/sub_workorder_icon.svg", + onPressed: () { + Navigator.of(context).pushNamed(SearchSubWorkOrderPage.id); + }, + ), ], ), ], diff --git a/lib/views/pages/user/requests/create_request.dart b/lib/views/pages/user/requests/create_request.dart index ebc23e44..8c4855e2 100644 --- a/lib/views/pages/user/requests/create_request.dart +++ b/lib/views/pages/user/requests/create_request.dart @@ -291,9 +291,9 @@ class CreateRequestPageState extends State { enable: widget.serviceRequest != null ? false : true, ), 12.height, - MultiImagesPicker( - label: _subtitle.deviceImages, - images: _deviceImages, + MultiFilesPicker( + label: _subtitle.deviceFiles, + files: _deviceImages, enabled: widget.serviceRequest == null ? true : false, ), 12.height, diff --git a/lib/views/pages/user/requests/report/edit_service_report.dart b/lib/views/pages/user/requests/report/edit_service_report.dart index b783e5a6..828e6b11 100644 --- a/lib/views/pages/user/requests/report/edit_service_report.dart +++ b/lib/views/pages/user/requests/report/edit_service_report.dart @@ -754,9 +754,9 @@ class _EditServiceReportState extends State with TickerProvid const SizedBox( height: 4, ), - MultiImagesPicker( + MultiFilesPicker( label: "", - images: _images, + files: _images, ), // AMiniOneImagePicker( // //error: _validate && _serviceReport.image == null, diff --git a/lib/views/widgets/images/multi_image_picker.dart b/lib/views/widgets/images/multi_image_picker.dart index fe200bd9..784c42b2 100644 --- a/lib/views/widgets/images/multi_image_picker.dart +++ b/lib/views/widgets/images/multi_image_picker.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -13,31 +14,31 @@ import 'package:test_sa/views/widgets/buttons/app_flat_button.dart'; import 'multi_image_picker_item.dart'; -class MultiImagesPicker extends StatefulWidget { +class MultiFilesPicker extends StatefulWidget { final String label; final bool error; - final List images; + final List files; final bool enabled; - const MultiImagesPicker({Key key, this.images, this.label, this.error = false, this.enabled = true}) : super(key: key); + const MultiFilesPicker({Key key, this.files, this.label, this.error = false, this.enabled = true}) : super(key: key); @override - _MultiImagesPickerState createState() => _MultiImagesPickerState(); + _MultiFilesPickerState createState() => _MultiFilesPickerState(); } -class _MultiImagesPickerState extends State with TickerProviderStateMixin { +class _MultiFilesPickerState extends State with TickerProviderStateMixin { Size _size; @override Widget build(BuildContext context) { _size = MediaQuery.of(context).size; - Subtitle _subtitle = AppLocalization.of(context).subtitle; + Subtitle subtitle = AppLocalization.of(context).subtitle; return Container( - padding: EdgeInsets.all(12), + padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Color(0xfff5f5f5), + color: const Color(0xfff5f5f5), border: Border.all( - color: Color(0xffefefef), + color: const Color(0xffefefef), ), borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)), ), @@ -49,7 +50,7 @@ class _MultiImagesPickerState extends State with TickerProvid children: [ Expanded( child: Text( - widget.label ?? _subtitle.images, + widget.label ?? subtitle.images, style: Theme.of(context).textTheme.headline6.copyWith( fontSize: 14, ), @@ -57,10 +58,11 @@ class _MultiImagesPickerState extends State with TickerProvid ), ), AFlatButton( - text: _subtitle.add, + text: subtitle.add, onPressed: widget.enabled ? () { - onImagePick(_subtitle); + // onImagePick(_subtitle); + onFilePicker(subtitle); } : null, ), @@ -68,14 +70,14 @@ class _MultiImagesPickerState extends State with TickerProvid ), 12.height, AnimatedSize( - duration: Duration(milliseconds: 400), + duration: const Duration(milliseconds: 400), child: !widget.error - ? SizedBox.shrink() + ? const SizedBox.shrink() : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - _subtitle.imagesRequired, + subtitle.imagesRequired, style: Theme.of(context).textTheme.headline6.copyWith( fontSize: 14, color: AColors.red, @@ -89,9 +91,9 @@ class _MultiImagesPickerState extends State with TickerProvid ), ), AnimatedSwitcher( - duration: Duration(milliseconds: 400), + duration: const Duration(milliseconds: 400), child: Container( - key: ValueKey(widget.images.length), + key: ValueKey(widget.files.length), width: _size.width, height: 200 * AppStyle.getScaleFactor(context), padding: EdgeInsets.all( @@ -102,16 +104,17 @@ class _MultiImagesPickerState extends State with TickerProvid border: Border.all(color: Theme.of(context).primaryColor, width: 2), borderRadius: BorderRadius.circular(8 * AppStyle.getScaleFactor(context)), ), - child: widget.images.isEmpty + child: widget.files.isEmpty ? MaterialButton( onPressed: widget.enabled ? () { - onImagePick(_subtitle); + // onImagePick(_subtitle); + onFilePicker(subtitle); } : null, child: Center( child: Icon( - Icons.add_a_photo_outlined, + Icons.file_upload, size: 48 * AppStyle.getScaleFactor(context), color: Theme.of(context).primaryColor, )), @@ -122,12 +125,12 @@ class _MultiImagesPickerState extends State with TickerProvid scrollDirection: Axis.horizontal, mainAxisSpacing: 10, crossAxisSpacing: 10, - children: List.generate(widget.images.length, (index) { - File _image = widget.images[index]; - return MultiImagesPickerItem( - image: _image, + children: List.generate(widget.files.length, (index) { + File _image = widget.files[index]; + return MultiFilesPickerItem( + file: _image, onRemoveTap: (image) { - widget.images.remove(image); + widget.files.remove(image); setState(() {}); }, ); @@ -140,9 +143,23 @@ class _MultiImagesPickerState extends State with TickerProvid ); } - onImagePick(Subtitle _subtitle) async { - if (widget.images.length >= 5) { - Fluttertoast.showToast(msg: _subtitle.maxImagesNumberIs5); + onFilePicker(Subtitle subtitle) async { + FilePickerResult result = await FilePicker.platform.pickFiles( + type: FileType.custom, + allowMultiple: true, + allowedExtensions: ['jpg', 'png', 'pdf', 'doc', 'docx', 'xlsx', 'pptx'], + ); + if (result != null) { + for (var path in result.paths) { + widget.files.insert(0, File(path)); + } + setState(() {}); + } + } + + onImagePick(Subtitle subtitle) async { + if (widget.files.length >= 5) { + Fluttertoast.showToast(msg: subtitle.maxImagesNumberIs5); return; } ImageSource source = await showDialog( @@ -150,13 +167,13 @@ class _MultiImagesPickerState extends State with TickerProvid builder: (dialogContext) => CupertinoAlertDialog( actions: [ TextButton( - child: Text(_subtitle.pickFromCamera), + child: Text(subtitle.pickFromCamera), onPressed: () { Navigator.of(dialogContext).pop(ImageSource.camera); }, ), TextButton( - child: Text(_subtitle.pickFromGallery), + child: Text(subtitle.pickFromGallery), onPressed: () { Navigator.of(dialogContext).pop(ImageSource.gallery); }, @@ -168,9 +185,9 @@ class _MultiImagesPickerState extends State with TickerProvid final pickedFile = await ImagePicker().pickImage(source: source, imageQuality: 70, maxWidth: 800, maxHeight: 800); if (pickedFile != null) { - File _fileImage = File(pickedFile.path); - if (_fileImage != null) { - widget.images.insert(0, _fileImage); + File fileImage = File(pickedFile.path); + if (fileImage != null) { + widget.files.insert(0, fileImage); setState(() {}); } } diff --git a/lib/views/widgets/images/multi_image_picker_item.dart b/lib/views/widgets/images/multi_image_picker_item.dart index e540cc2b..3f1d5805 100644 --- a/lib/views/widgets/images/multi_image_picker_item.dart +++ b/lib/views/widgets/images/multi_image_picker_item.dart @@ -5,54 +5,67 @@ import 'package:test_sa/views/app_style/colors.dart'; import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/widgets/buttons/app_back_button.dart'; -class MultiImagesPickerItem extends StatelessWidget { - final File image; +class MultiFilesPickerItem extends StatelessWidget { + final File file; final Function(File) onRemoveTap; - const MultiImagesPickerItem({Key key, this.image, this.onRemoveTap}) : super(key: key); + const MultiFilesPickerItem({Key key, this.file, this.onRemoveTap}) : super(key: key); @override Widget build(BuildContext context) { + var isImage = file.path.split(".").last.toLowerCase() == "png" || file.path.split(".").last.toLowerCase() == "jpg"; + var isPdf = file.path.split(".").last.toLowerCase() == "pdf"; + var isExcel = file.path.split(".").last.toLowerCase() == "xlsx"; return Container( width: 80 * AppStyle.getScaleFactor(context), height: 80 * AppStyle.getScaleFactor(context), - decoration: - BoxDecoration(boxShadow: [BoxShadow(color: Colors.black38, blurRadius: 2)], image: DecorationImage(image: FileImage(image), fit: BoxFit.cover), borderRadius: BorderRadius.circular(8)), + decoration: BoxDecoration( + boxShadow: [BoxShadow(color: isImage ? Colors.black38 : AColors.cyan.withOpacity(0.5), blurRadius: 2)], + image: DecorationImage( + image: isImage + ? FileImage(file) + : AssetImage("assets/images/${isPdf ? "pdf" : isExcel ? "excel" : "doc"}.png"), + fit: BoxFit.cover, + ), + borderRadius: BorderRadius.circular(8), + ), child: MaterialButton( padding: EdgeInsets.zero, onPressed: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => Scaffold( - body: SafeArea( - child: Stack( - children: [ - Center( - child: InteractiveViewer( - child: Image( - image: FileImage(image), - )), - ), - ABackButton(), - ], - ), + if (isImage) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => Scaffold( + body: SafeArea( + child: Stack( + children: [ + Center(child: isImage ? InteractiveViewer(child: Image(image: FileImage(file))) : const SizedBox.shrink()), + const ABackButton(), + ], ), - ))); + ), + ), + ), + ); + } }, child: Align( - alignment: Alignment.topRight, - child: IconButton( - padding: const EdgeInsets.all(2.0), - icon: Container( - padding: EdgeInsets.all(1), - decoration: BoxDecoration(color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.3), borderRadius: BorderRadius.circular(8)), - child: Icon( - Icons.remove_circle, - color: AColors.red, - )), - onPressed: () { - onRemoveTap(image); - }, - )), + alignment: Alignment.topRight, + child: IconButton( + padding: const EdgeInsets.all(2.0), + icon: Container( + padding: const EdgeInsets.all(1), + decoration: BoxDecoration(color: Theme.of(context).scaffoldBackgroundColor.withOpacity(.3), borderRadius: BorderRadius.circular(8)), + child: const Icon( + Icons.remove_circle, + color: AColors.red, + ), + ), + onPressed: () { + onRemoveTap(file); + }, + ), + ), ), ); } diff --git a/lib/views/widgets/sound/record_sound.dart b/lib/views/widgets/sound/record_sound.dart index b56c1a5e..d10119e6 100644 --- a/lib/views/widgets/sound/record_sound.dart +++ b/lib/views/widgets/sound/record_sound.dart @@ -24,7 +24,6 @@ class _RecordSoundState extends State { FlutterSoundRecorder _myRecorder = FlutterSoundRecorder(); bool _recorderIsOpened = false; bool _recording = false; - bool _fastTab = false; String _record; Artboard _rive; @@ -69,13 +68,14 @@ class _RecordSoundState extends State { } _startRecording() async { - _fastTab = false; // await Permission.camera PermissionStatus status = await Permission.microphone.request(); if (!status.isGranted) { PermissionStatus status = await Permission.microphone.request(); - Fluttertoast.showToast(msg: "Permission Denied"); - return; + if (!status.isGranted) { + Fluttertoast.showToast(msg: "Permission Denied"); + return; + } } _rive.addController(SimpleAnimation('recording')); if (!_recorderIsOpened) { @@ -91,7 +91,6 @@ class _RecordSoundState extends State { _stopRecording() async { if (!_recording) { - _fastTab = true; setState(() {}); return; } @@ -182,11 +181,6 @@ class _RecordSoundState extends State { color: Colors.transparent, child: GestureDetector( //key: ValueKey("voice"), - child: const Padding( - padding: EdgeInsets.all(12.0), - child: Icon(Icons.mic), - ), - onTapDown: widget.enabled ? (TapDownDetails details) async { _startRecording(); @@ -202,6 +196,8 @@ class _RecordSoundState extends State { _cancelRecording(); } : null, + //key: ValueKey("voice"), + child: const Padding(padding: EdgeInsets.all(12.0), child: Icon(Icons.mic)), ), ), ], diff --git a/lib/views/widgets/status/service_request/supplier_engineers_menu.dart b/lib/views/widgets/status/service_request/supplier_engineers_menu.dart new file mode 100644 index 00000000..44b30c84 --- /dev/null +++ b/lib/views/widgets/status/service_request/supplier_engineers_menu.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/models/service_request/search_work_order.dart'; +import 'package:test_sa/views/app_style/colors.dart'; +import 'package:test_sa/views/app_style/sizing.dart'; + +class SupplierEngineersMenu extends StatefulWidget { + final List engineers; + final SuppEngineerWorkOrders initialValue; + final Function(SuppEngineerWorkOrders) onSelect; + + const SupplierEngineersMenu({Key key, this.engineers, this.onSelect, this.initialValue}) : super(key: key); + + @override + SingleAssistantEmployeeMenuState createState() => SingleAssistantEmployeeMenuState(); +} + +class SingleAssistantEmployeeMenuState extends State { + SuppPersons _selectedEngineer; + + @override + void setState(VoidCallback fn) { + if (mounted) super.setState(fn); + } + + @override + void didUpdateWidget(covariant SupplierEngineersMenu oldWidget) { + if (widget.initialValue != null) { + final result = widget.engineers?.where((element) { + return element?.id == widget.initialValue?.supplierContactId; + }); + if (result.isNotEmpty) { + _selectedEngineer = result.first; + } else { + _selectedEngineer = null; + } + if ((widget.initialValue?.supplierContactId ?? "") != (_selectedEngineer?.id ?? "")) { + onSelect(_selectedEngineer); + } + } else { + _selectedEngineer = null; + } + super.didUpdateWidget(oldWidget); + } + + @override + void initState() { + if (widget.initialValue != null) { + final result = widget.engineers?.where((element) { + return element?.id == widget.initialValue?.supplierContactId; + }); + if (result.isNotEmpty) _selectedEngineer = result.first; + if (widget.initialValue?.supplierContactId != _selectedEngineer?.id) { + onSelect(_selectedEngineer); + } + } + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: AColors.inputFieldBackgroundColor, + border: Border.all( + color: const Color(0xffefefef), + ), + borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)), + // boxShadow: const [ + // AppStyle.boxShadow + // ] + ), + child: DropdownButton( + value: _selectedEngineer, + iconSize: 24, + icon: const Icon(Icons.keyboard_arrow_down_rounded), + elevation: 0, + isExpanded: true, + hint: Text("Vendor Engineer", style: Theme.of(context).textTheme.titleMedium), + style: TextStyle(color: Theme.of(context).primaryColor), + underline: const SizedBox.shrink(), + onChanged: (SuppPersons newValue) { + setState(() { + _selectedEngineer = newValue; + }); + onSelect(newValue); + }, + items: widget.engineers?.map>((SuppPersons value) { + return DropdownMenuItem( + value: value, + child: Text( + value?.personName ?? "NULL", + style: Theme.of(context).textTheme.titleMedium.copyWith( + color: Theme.of(context).primaryColor, + fontSize: 11, + //fontWeight: FontWeight.bold + ), + ), + ); + })?.toList(), + ), + ); + } + + void onSelect(SuppPersons engineer) { + widget.onSelect( + SuppEngineerWorkOrders( + id: engineer?.supplierId, + supplierContactId: engineer?.id, + personName: engineer?.personName, + contact: engineer?.contact, + externalEngCode: engineer?.externalEngCode, + email: engineer?.email, + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 4f82161b..076379b6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,7 +61,7 @@ dependencies: flare_flutter: ^3.0.2 signature: ^5.3.0 flutter_svg: ^1.1.6 - + file_picker: ^5.2.5 dev_dependencies: flutter_test: