diff --git a/lib/providers/service_request_providers/equipment_status_provider.dart b/lib/providers/service_request_providers/equipment_status_provider.dart index 71a5760f..d3280398 100644 --- a/lib/providers/service_request_providers/equipment_status_provider.dart +++ b/lib/providers/service_request_providers/equipment_status_provider.dart @@ -13,8 +13,6 @@ class EquipmentStatusProvider extends LoadingListNotifier { if (loading == true) return -2; loading = true; notifyListeners(); - loading = true; - notifyListeners(); try { Response response = await ApiManager.instance.get(URLs.equipmentStatus); stateCode = response.statusCode; diff --git a/lib/providers/service_request_providers/type_of_request_provider.dart b/lib/providers/service_request_providers/type_of_request_provider.dart index 5650b3db..85a072c9 100644 --- a/lib/providers/service_request_providers/type_of_request_provider.dart +++ b/lib/providers/service_request_providers/type_of_request_provider.dart @@ -11,8 +11,7 @@ class TypeOfRequestProvider extends LoadingListNotifier { @override Future getDate() async { if (loading == true) return -2; - loading = true; - notifyListeners(); + loading = true; notifyListeners(); try { diff --git a/lib/views/pages/user/requests/create_service_request_page.dart b/lib/views/pages/user/requests/create_service_request_page.dart index 5d94ebe9..bed86ba9 100644 --- a/lib/views/pages/user/requests/create_service_request_page.dart +++ b/lib/views/pages/user/requests/create_service_request_page.dart @@ -1,6 +1,6 @@ -import 'dart:convert'; import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/providers/api/service_requests_provider.dart'; @@ -13,20 +13,19 @@ import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/models/service_request/pending_service_request_model.dart'; import 'package:test_sa/models/service_request/service_request.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/providers/service_request_providers/equipment_status_provider.dart'; import 'package:test_sa/providers/service_request_providers/requested_through_provider.dart'; +import 'package:test_sa/views/pages/user/requests/pending_requests_screen.dart'; import 'package:test_sa/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart'; -import 'package:test_sa/views/widgets/equipment/pick_asset.dart'; +import 'package:test_sa/views/widgets/equipment/asset_picker.dart'; import 'package:test_sa/views/widgets/images/multi_image_picker.dart'; import 'package:test_sa/views/widgets/loaders/loading_manager.dart'; import 'package:test_sa/views/widgets/sound/record_sound.dart'; import 'package:test_sa/views/widgets/speech_to_text/speech_to_text.dart'; -import '../../../../models/lookup.dart'; import '../../../../new_views/app_style/app_color.dart'; import '../../../../new_views/common_widgets/app_text_form_field.dart'; import '../../../../new_views/common_widgets/default_app_bar.dart'; -import '../../../../new_views/common_widgets/single_item_drop_down_menu.dart'; -import '../../../../providers/service_request_providers/equipment_status_provider.dart'; import '../../../../providers/service_request_providers/priority_provider.dart'; import '../../../../providers/service_request_providers/type_of_request_provider.dart'; @@ -105,13 +104,27 @@ class CreateServiceRequestPageState extends State { return url.startsWith("/") || url.startsWith("file://") || url.substring(1).startsWith(':\\'); } + bool priority; + + void getData() { + Provider.of(context).getDate(); + Provider.of(context).getDate(); + Provider.of(context).getDate(); + Provider.of(context).getDate(); + } + + PendingAssetServiceRequest pendingAssetServiceRequest; + @override Widget build(BuildContext context) { _height = MediaQuery.of(context).size.height; _userProvider = Provider.of(context); - _settingProvider = Provider.of(context); _serviceRequestsProvider = Provider.of(context); + if (_settingProvider == null) { + _settingProvider = Provider.of(context); + getData(); + } return Scaffold( key: _scaffoldKey, @@ -130,55 +143,155 @@ class CreateServiceRequestPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - PickAsset( - device: _serviceRequest.device, - onPickAsset: (asset) { - _serviceRequest.device = asset; - setState(() {}); - }, - ), - 8.height, - SingleItemDropDownMenu( - context: context, - title: context.translation.priority, - initialValue: _serviceRequest?.priority, - onSelect: (value) { - _serviceRequest.priority = value; - }, - ), - 8.height, - SingleItemDropDownMenu( - context: context, - title: context.translation.equipmentStatus, - initialValue: _serviceRequest?.defectType, - onSelect: (value) { - _serviceRequest.defectType = value; - }, - ), - 8.height, - Consumer(builder: (context, snapshot, _) { - return SingleItemDropDownMenu( - context: context, - enabled: false, - title: context.translation.source, - initialValue: snapshot.items?.firstWhere((element) => element.value == 3, orElse: () => null), - ); - }), - 8.height, - Consumer(builder: (context, snapshot, _) { - return SingleItemDropDownMenu( - context: context, - title: context.translation.requestType, - enabled: false, - initialValue: snapshot.items?.firstWhere((element) => element.value == 1, orElse: () => null), - // onSelect: (value) { - // _serviceRequest.type = value; - // }, - ); - }), - 8.height, - MultiFilesPicker(label: context.translation.attachImage, files: _deviceImages), - ((_serviceRequest.devicePhotos?.isNotEmpty ?? false) ? 16 : 8).height, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + AssetPicker( + device: _serviceRequest.device, + showLoading: checkPendingRequest, + onPick: (asset) async { + pendingAssetServiceRequest = null; + _serviceRequest.device = asset; + await checkAssetForPendingServiceRequest(asset.id); + + if (pendingAssetServiceRequest != null && pendingAssetServiceRequest.details.isNotEmpty) { + showPendingRequestBottomSheet(); + } + }, + ), + if (pendingAssetServiceRequest != null && pendingAssetServiceRequest.details.isNotEmpty) ...[ + 8.height, + Row( + children: [ + const Icon(Icons.warning, color: Color(0xffEE404C), size: 14), + 8.width, + Text( + "This asset already have ${pendingAssetServiceRequest.details.length} request pending", + style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xff7D859A), decoration: TextDecoration.underline), + ).expanded, + ], + ).onPress(() { + showPendingRequests(); + }), + ], + 16.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(context.translation.priority, style: Theme.of(context).textTheme.bodyLarge), + Consumer(builder: (cxt, snapshot, _) { + _serviceRequest?.priority ??= snapshot.items?.firstWhere((element) => element.value == 0, orElse: () => null); + return Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + thumbColor: Color(0xffF63939), + activeColor: AppColor.blueStatus(context).withOpacity(.25), + value: _serviceRequest?.priority?.value != 0, + onChanged: (state) { + if (state) { + _serviceRequest?.priority = snapshot.items?.firstWhere((element) => element.value == 1, orElse: () => null); + } else { + _serviceRequest?.priority = snapshot.items?.firstWhere((element) => element.value == 0, orElse: () => null); + } + setState(() {}); + }).toShimmer(isShow: snapshot.loading), + ); + }), + ], + ), + 16.height, + Consumer(builder: (cxt, snapshot, _) { + try { + _serviceRequest?.defectType ??= snapshot.items?.first; + } catch (ex) { + print("snapshot.items:${snapshot.items?.length}"); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text(context.translation.equipmentStatus, style: Theme.of(context).textTheme.bodyLarge), + 8.height, + Wrap( + runSpacing: 8, + spacing: 8, + children: [ + for (var element in snapshot.items) + Row( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 24, + height: 24, + child: Radio( + value: element, + activeColor: Colors.red, + fillColor: MaterialStateColor.resolveWith((states) { + if (states.contains(MaterialState.selected)) return Color(0xff3DA5E5); + return Color(0xffE0E0E0); + }), + groupValue: _serviceRequest?.defectType, + onChanged: (state) { + setState(() { + _serviceRequest?.defectType = element; + }); + }), + ), + 8.width, + Text(element.name, style: Theme.of(context).textTheme.bodySmall), + ], + ) + ], + ), + ], + ); + }), + 16.height, + MultiFilesPicker(label: context.translation.attachImage, files: _deviceImages, showAsGrid: true), + ], + ).toShadowContainer(context), + + // SingleItemDropDownMenu( + // context: context, + // title: context.translation.priority, + // initialValue: _serviceRequest?.priority, + // onSelect: (value) { + // _serviceRequest.priority = value; + // }, + // ), + // 8.height, + // SingleItemDropDownMenu( + // context: context, + // title: context.translation.equipmentStatus, + // initialValue: _serviceRequest?.defectType, + // onSelect: (value) { + // _serviceRequest.defectType = value; + // }, + // ), + + // Consumer(builder: (context, snapshot, _) { + // return SingleItemDropDownMenu( + // context: context, + // enabled: false, + // title: context.translation.source, + // initialValue: snapshot.items?.firstWhere((element) => element.value == 3, orElse: () => null), + // ); + // }), + // 8.height, + // Consumer(builder: (context, snapshot, _) { + // return SingleItemDropDownMenu( + // context: context, + // title: context.translation.requestType, + // enabled: false, + // initialValue: snapshot.items?.firstWhere((element) => element.value == 1, orElse: () => null), + // // onSelect: (value) { + // // _serviceRequest.type = value; + // // }, + // ); + // }), + Align( alignment: AlignmentDirectional.centerStart, child: context.translation.callComments.heading5(context), @@ -223,55 +336,68 @@ class CreateServiceRequestPageState extends State { bool checkPendingRequest = false; - Future checkAssetForPendingServiceRequest(int assetId) async { - checkPendingRequest = true; - setState(() {}); - - PendingAssetServiceRequest pendingAssetServiceRequest = await _serviceRequestsProvider.checkAssetPendingRequest(assetId); - - checkPendingRequest = false; - setState(() {}); - - if (pendingAssetServiceRequest.details.isEmpty) return true; + void showPendingRequests() { + Navigator.of(context).push(MaterialPageRoute(builder: (_) => PendingServiceRequestScreen(pendingAssetServiceRequest))); + } - bool submit = (await showModalBottomSheet( + void showPendingRequestBottomSheet() async { + bool view = (await showModalBottomSheet( context: context, - isScrollControlled: true, + isDismissible: false, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(20), ), ), clipBehavior: Clip.antiAliasWithSaveLayer, - builder: (BuildContext context) => PendingRequestBottomSheet(pendingAssetServiceRequest), + builder: (BuildContext context) => PendingRequestBottomSheet(pendingAssetServiceRequest, _serviceRequest.device), )) as bool; - return submit ?? false; + if (view) { + showPendingRequests(); + } + } + + Future checkAssetForPendingServiceRequest(int assetId) async { + checkPendingRequest = true; + setState(() {}); + + pendingAssetServiceRequest = await _serviceRequestsProvider.checkAssetPendingRequest(assetId); + await Future.delayed(Duration(seconds: 1)); + + checkPendingRequest = false; + setState(() {}); } Future _submit() async { _serviceRequest?.requestedThrough = Provider.of(context, listen: false).items?.firstWhere((element) => element.value == 3, orElse: () => null); _serviceRequest?.type = Provider.of(context, listen: false).items?.firstWhere((element) => element.value == 1, orElse: () => null); - if (_formKey.currentState.validate() && await _serviceRequest.validateNewRequest(context)) { - _formKey.currentState.save(); - bool canSubmitRequest = await checkAssetForPendingServiceRequest(_serviceRequest.device.id); - if (!canSubmitRequest) { - return; - } + print("_serviceRequest?.requestedThrough:${_serviceRequest?.requestedThrough.toJson()}"); + print("_serviceRequest?.type:${_serviceRequest?.type.toJson()}"); + print("_serviceRequest?.priority:${_serviceRequest?.priority.toJson()}"); + return; - _serviceRequest.devicePhotos = _deviceImages.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList(); - if (_serviceRequest.audio != null) { - if (_isLocalUrl(_serviceRequest.audio)) { - final File file = File(_serviceRequest.audio); - _serviceRequest.audio = "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}"; - } - } - await _serviceRequestsProvider.createRequest( - context: context, - user: _userProvider.user, - host: _settingProvider.host, - serviceRequest: _serviceRequest, - ); - } + // if (_formKey.currentState.validate() && await _serviceRequest.validateNewRequest(context)) { + // _formKey.currentState.save(); + // + // bool canSubmitRequest = await checkAssetForPendingServiceRequest(_serviceRequest.device.id); + // if (!canSubmitRequest) { + // return; + // } + // + // _serviceRequest.devicePhotos = _deviceImages.map((e) => _isLocalUrl(e.path) ? "${e.path.split("/").last}|${base64Encode(e.readAsBytesSync())}" : e.path).toList(); + // if (_serviceRequest.audio != null) { + // if (_isLocalUrl(_serviceRequest.audio)) { + // final File file = File(_serviceRequest.audio); + // _serviceRequest.audio = "${file.path.split("/").last}|${base64Encode(file.readAsBytesSync())}"; + // } + // } + // await _serviceRequestsProvider.createRequest( + // context: context, + // user: _userProvider.user, + // host: _settingProvider.host, + // serviceRequest: _serviceRequest, + // ); + // } } } diff --git a/lib/views/pages/user/requests/pending_requests_screen.dart b/lib/views/pages/user/requests/pending_requests_screen.dart new file mode 100644 index 00000000..9f252c24 --- /dev/null +++ b/lib/views/pages/user/requests/pending_requests_screen.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/service_request/pending_service_request_model.dart'; +import 'package:test_sa/models/service_request/service_request.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/views/pages/user/requests/service_request_details.dart'; + +class PendingServiceRequestScreen extends StatelessWidget { + final PendingAssetServiceRequest pendingAssetServiceRequest; + + PendingServiceRequestScreen(this.pendingAssetServiceRequest, {Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: DefaultAppBar(title: "Pending Requests (${pendingAssetServiceRequest.details.length})"), + body: ListView.separated( + itemCount: pendingAssetServiceRequest.details.length, + padding: EdgeInsets.all(16), + separatorBuilder: (cxt, index) => 12.height, + itemBuilder: (cxt, index) => Container( + padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + Text( + pendingAssetServiceRequest.details[index].message.cleanupWhitespace?.capitalizeFirstOfEach ?? "", + style: Theme.of(context).textTheme.bodyLarge, + ).expanded, + Icon(Icons.arrow_forward_ios, size: 16) + ], + ), + ).onPress(() { + Navigator.of(context).push(MaterialPageRoute( + builder: (_) => ServiceRequestDetailsPage( + serviceRequest: ServiceRequest(id: pendingAssetServiceRequest.details[index].id.toString()), + ))); + }))); + } +} diff --git a/lib/views/widgets/bottom_sheets/asset_detail_bottom_sheet.dart b/lib/views/widgets/bottom_sheets/asset_detail_bottom_sheet.dart new file mode 100644 index 00000000..f92aa52c --- /dev/null +++ b/lib/views/widgets/bottom_sheets/asset_detail_bottom_sheet.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/controllers/api_routes/urls.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/device/asset.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; + +class AssetDetailBottomSheet extends StatelessWidget { + Asset asset; + + AssetDetailBottomSheet(this.asset, {Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + (asset?.modelDefinition?.assetName?.cleanupWhitespace?.capitalizeFirstOfEach ?? "-").heading5(context), + 16.height, + AspectRatio( + aspectRatio: 358 / 136, + child: Container( + width: 95, + height: 95, + decoration: ShapeDecoration( + color: AppColor.neutral30, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + image: DecorationImage( + fit: BoxFit.cover, + image: NetworkImage(asset?.assetPhoto != null ? URLs.getFileUrl(asset.assetPhoto) : "https://www.lasteelcraft.com/images/no-image-available.png"), + )), + ), + ), + 16.height, + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "${context.translation.assetNo}: ${asset.assetNumber}".bodyText(context), + "${context.translation.modelName}: ${asset.modelDefinition.modelName}".bodyText(context), + "${context.translation.supplier}: ${asset.supplier?.suppliername ?? "-"}".bodyText(context), + "${context.translation.manufacture}: ${asset.modelDefinition.manufacturerName}".bodyText(context), + //"${context.translation.location}: ${assetModel.site.custName?.cleanupWhitespace?.capitalizeFirstOfEach}".bodyText(context), + ], + ).expanded, + 8.width, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "${context.translation.snNo}: ${asset.assetSerialNo}".bodyText(context), + "${context.translation.site}: ${asset?.site?.custName?.cleanupWhitespace?.capitalizeFirstOfEach ?? "-"}".bodyText(context), + "${context.translation.building}: ${asset?.building?.name?.cleanupWhitespace?.capitalizeFirstOfEach ?? "-"}".bodyText(context), + "${context.translation.floor}: ${asset?.floor?.name?.cleanupWhitespace?.capitalizeFirstOfEach ?? "-"}".bodyText(context), + "${context.translation.md}: ${asset?.department?.departmentName?.cleanupWhitespace?.capitalizeFirstOfEach ?? "-"}".bodyText(context), + "${context.translation.room}: ${asset?.room?.value ?? "-"}".bodyText(context), + ], + ).expanded, + ], + ), + 8.height, + if ((asset.modelDefinition.assetDescription ?? "").isNotEmpty) ...[ + 8.height, + const Divider(color: AppColor.neutral30, height: 1, thickness: 1), + 8.height, + asset.modelDefinition.assetDescription.bodyText(context), + ] + ], + ) + ], + ).paddingAll(16); + } +} diff --git a/lib/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart b/lib/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart index 21e9fdb4..d7e1f1d5 100644 --- a/lib/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart +++ b/lib/views/widgets/bottom_sheets/pending_request_bottom_sheet.dart @@ -3,54 +3,38 @@ import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/device/asset.dart'; import 'package:test_sa/models/service_request/pending_service_request_model.dart'; -import 'package:test_sa/models/service_request/service_request.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; -import 'package:test_sa/views/pages/user/requests/service_request_details.dart'; class PendingRequestBottomSheet extends StatelessWidget { - PendingAssetServiceRequest pendingAssetServiceRequest; + final PendingAssetServiceRequest pendingAssetServiceRequest; + final Asset device; - PendingRequestBottomSheet(this.pendingAssetServiceRequest, {Key key}) : super(key: key); + PendingRequestBottomSheet(this.pendingAssetServiceRequest, this.device, {Key key}) : super(key: key); @override Widget build(BuildContext context) { return Container( - height: MediaQuery.of(context).size.height * .6, padding: const EdgeInsets.all(21), child: Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - pendingAssetServiceRequest.headerMessage.heading5(context), - "${pendingAssetServiceRequest.details.length} found".bodyText(context), - 8.height, - ListView.separated( - itemCount: pendingAssetServiceRequest.details.length, - padding: EdgeInsets.only(top: 8), - separatorBuilder: (cxt, index) => 12.height, - itemBuilder: (cxt, index) => Container( - padding: EdgeInsets.symmetric(vertical: 16, horizontal: 8), - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: BorderRadius.circular(16), - ), - child: Row( - children: [ - Text( - pendingAssetServiceRequest.details[index].message.cleanupWhitespace?.capitalizeFirstOfEach ?? "", - style: Theme.of(context).textTheme.bodyLarge, - ).expanded, - Icon(Icons.arrow_forward_ios, size: 16) - ], - ), - ).onPress(() { - Navigator.of(context).push(MaterialPageRoute( - builder: (_) => ServiceRequestDetailsPage( - serviceRequest: ServiceRequest(id: pendingAssetServiceRequest.details[index].id.toString()), - ))); - })).expanded, - 8.height, + "Alert!".heading4(context), + 12.height, + Text( + "${pendingAssetServiceRequest.details.length} pending service requests found", + style: TextStyle( + fontSize: 16.toScreenWidth, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.normal, + decoration: TextDecoration.none, + ), + ), + "${device.assetNumber} (${device.modelDefinition?.assetName?.cleanupWhitespace?.capitalizeFirstOfEach ?? ""})".bodyText(context), + 16.height, Row( children: [ AppFilledButton( @@ -64,13 +48,14 @@ class PendingRequestBottomSheet extends StatelessWidget { }).expanded, 16.width, AppFilledButton( - label: "Continue", + label: "View", maxWidth: true, onPressed: () { Navigator.pop(context, true); }).expanded, ], ), + 8.height, ], ), ); diff --git a/lib/views/widgets/equipment/asset_picker.dart b/lib/views/widgets/equipment/asset_picker.dart new file mode 100644 index 00000000..68b72b4d --- /dev/null +++ b/lib/views/widgets/equipment/asset_picker.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/views/widgets/bottom_sheets/asset_detail_bottom_sheet.dart'; +import 'package:test_sa/views/widgets/equipment/single_device_picker.dart'; + +import '../../../models/device/asset.dart'; +import '../../../new_views/app_style/app_color.dart'; + +class AssetPicker extends StatelessWidget { + final Function(Asset) onPick; + final Asset device; + final bool editable; + final bool showAssetInfo; + final bool forPPM; + final bool showLoading; + + AssetPicker({Key key, this.editable = true, this.device, this.onPick, this.showAssetInfo = true, this.forPPM = false, this.showLoading = false}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + if (device == null) + Container( + height: 50, + alignment: Alignment.center, + decoration: BoxDecoration( + color: AppColor.blueStatus(context), + borderRadius: BorderRadius.circular(10), + // boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 14)], + ), + padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + "qr".toSvgAsset(height: 22, fit: BoxFit.fitHeight, color: context.isDark ? AppColor.neutral20 : Colors.white), + 8.width, + "Scan or Pick Asset".bodyText(context).custom(color: context.isDark ? AppColor.neutral20 : Colors.white), + ], + ), + ).onPress(() async { + Asset device = await Navigator.of(context).pushNamed(MyAssetsPage.id) as Asset; + if (device != null) { + onPick(device); + } + }) + else + Container( + height: 50, + alignment: Alignment.center, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all(color: AppColor.blueStatus(context), width: 2), + // boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 14)], + ), + padding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 8.toScreenHeight), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + "qr".toSvgAsset(height: 22, fit: BoxFit.fitHeight, color: context.isDark ? AppColor.neutral20 : Colors.black87), + 8.width, + "Re-Scan or Pick Asset".bodyText(context).custom(color: context.isDark ? AppColor.neutral20 : Colors.black87), + ], + ), + ).onPress(() async { + Asset device = await Navigator.of(context).pushNamed(MyAssetsPage.id) as Asset; + if (device != null) { + onPick(device); + } + }), + if (device != null && showAssetInfo) + Container( + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: showLoading ? Colors.white : Color(0xffF4F6FC), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Color(0xff212936).withOpacity(.03), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + children: [ + Text( + device.modelDefinition?.assetName?.cleanupWhitespace?.capitalizeFirstOfEach ?? "", + style: TextStyle( + fontSize: 14.toScreenWidth, + fontWeight: FontWeight.w500, + fontStyle: FontStyle.normal, + color: Colors.black87, + decoration: TextDecoration.none, + ), + ).toShimmer(isShow: showLoading).expanded, + const Icon( + Icons.info, + color: Color(0xff7D859A), + size: 20, + ), + ], + ), + 8.height, + "${context.translation.assetNo}: ${device.assetNumber}".bodyText2(context).toShimmer(isShow: showLoading), + 2.height, + // "${context.translation.manufacture}: ${device.modelDefinition?.manufacturerName}".bodyText(context), + "${context.translation.model}: ${device.modelDefinition?.modelName}".bodyText2(context).toShimmer(isShow: showLoading), + // "${context.translation.serialNumber}: ${device.assetNumber}".bodyText(context), + // const Divider().defaultStyle(context), + // "${context.translation.department}: ${device.department?.departmentName}".bodyText(context), + // "${context.translation.site}: ${device.site?.custName}".bodyText(context), + ], + ), + ).onPress(() { + showModalBottomSheet( + context: context, + isScrollControlled: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + builder: (BuildContext context) => AssetDetailBottomSheet(device), + ); + }).paddingOnly(top: 16), + ], + ); + } +} diff --git a/lib/views/widgets/images/multi_image_picker.dart b/lib/views/widgets/images/multi_image_picker.dart index 17417f6c..abd00bb3 100644 --- a/lib/views/widgets/images/multi_image_picker.dart +++ b/lib/views/widgets/images/multi_image_picker.dart @@ -7,6 +7,8 @@ import 'package:fluttertoast/fluttertoast.dart'; import 'package:image_picker/image_picker.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; import '../../../new_views/common_widgets/app_dashed_button.dart'; import 'multi_image_picker_item.dart'; @@ -17,8 +19,9 @@ class MultiFilesPicker extends StatefulWidget { final List files; final bool enabled, onlyImages; final Function(List) onChange; + final bool showAsGrid; - const MultiFilesPicker({Key key, this.files, this.label, this.error = false, this.enabled = true, this.onlyImages = false, this.onChange}) : super(key: key); + const MultiFilesPicker({Key key, this.files, this.label, this.error = false, this.enabled = true, this.onlyImages = false, this.onChange,this.showAsGrid=false}) : super(key: key); @override State createState() => _MultiFilesPickerState(); @@ -30,7 +33,7 @@ class _MultiFilesPickerState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AppDashedButton(title: widget.label, onPressed: (widget.enabled == false) ? () {} : onFilePicker), + AppDashedButton(title: widget.label, onPressed: (widget.enabled == false) ? () {} : widget.showAsGrid ? showFileSourceSheet:onFilePicker), 16.height, if (widget.files?.isNotEmpty ?? false) Wrap( @@ -74,6 +77,83 @@ class _MultiFilesPickerState extends State { } } + void showFileSourceSheet() async { + if (widget.files.length >= 5) { + Fluttertoast.showToast(msg: context.translation.maxImagesNumberIs5); + return; + } + + ImageSource source = (await showModalBottomSheet( + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + builder: (BuildContext context) => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Attach File".heading4(context), + 12.height, + GridView( + padding: EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 12, mainAxisSpacing: 12), + children: [ + gridItem(Icons.camera_enhance_rounded, context.translation.pickFromCamera).onPress(() => Navigator.of(context).pop(ImageSource.camera)), + gridItem(Icons.image_rounded, context.translation.pickFromGallery).onPress(() => Navigator.of(context).pop(ImageSource.gallery)), + gridItem(Icons.file_present_rounded, context.translation.pickFromFiles).onPress(() async { + await fromFilePicker(); + Navigator.pop(context); + }), + ], + ), + 12.height, + ], + ).paddingAll(21), + )) as ImageSource; + + if (source == null) return; + + 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.files.add(fileImage); + if (widget.onChange != null) { + widget.onChange(widget.files); + } + setState(() {}); + } + } + } + + Widget gridItem(IconData iconData, String title) { + return Container( + padding: EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Color(0xffF1F1F1), width: 1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon(iconData, color: Color(0xff7D859A), size: 36), + Text( + title, + style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500), + ), + ], + ), + ); + } + onFilePicker() async { if (widget.files.length >= 5) { Fluttertoast.showToast(msg: context.translation.maxImagesNumberIs5); diff --git a/lib/views/widgets/sound/record_sound.dart b/lib/views/widgets/sound/record_sound.dart index a3e7826b..c02e9922 100644 --- a/lib/views/widgets/sound/record_sound.dart +++ b/lib/views/widgets/sound/record_sound.dart @@ -39,6 +39,8 @@ class _RecordSoundState extends State { Timer _timer; TextEditingController _timeController; + FocusNode node = FocusNode(); + @override void setState(VoidCallback fn) { if (mounted) super.setState(fn); @@ -48,6 +50,7 @@ class _RecordSoundState extends State { void initState() { super.initState(); _timeController = TextEditingController(); + node.unfocus(); _recorderIsOpened = true; // RecordMp3.instance.start(recordFilePath, (type) { // // record fail callback @@ -99,11 +102,15 @@ class _RecordSoundState extends State { } _timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { + String duration = Duration(seconds: timer?.tick).toString(); + duration = duration.substring(duration.indexOf(":") + 1, duration.indexOf(".")); + String recordTime = ((timer?.tick ?? 0) / 60)?.toStringAsFixed(2)?.replaceFirst(".", ":"); + // print("recordTime:$recordTime"); if (recordTime.length == 4 || recordTime.length == 7) { recordTime = "0$recordTime"; } - _timeController.text = recordTime; + _timeController.text = duration; }); }); _rive.addController(SimpleAnimation('recording')); @@ -156,6 +163,7 @@ class _RecordSoundState extends State { @override Widget build(BuildContext context) { + if (node.hasFocus && widget.enabled) node.unfocus(); return Column( children: [ Stack( @@ -163,6 +171,7 @@ class _RecordSoundState extends State { children: [ AppTextFormField( enable: widget.enabled, + node: node, controller: _timeController, labelText: context.translation.recordVoice, initialValue: (_timeController?.text?.isEmpty ?? true) ? "00:00" : _timeController?.text,