Service request design done

main_design2.0
zaid_daoud 2 years ago
parent 0170c6f5cb
commit e419b17bd1

@ -0,0 +1,4 @@
<svg width="16" height="24" viewBox="0 0 16 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.32812 12.6562H2.86313C3.20484 15.0384 5.25891 16.875 7.73438 16.875C10.2098 16.875 12.2639 15.0384 12.6056 12.6562H9.14062V11.25H12.6562V9.14062H9.14062V7.73438H12.6562V5.625H9.14062V4.21875H12.6056C12.2639 1.83656 10.2098 0 7.73438 0C5.25891 0 3.20484 1.83656 2.86313 4.21875H6.32812V5.625H2.8125V7.73438H6.32812V9.14062H2.8125V11.25H6.32812V12.6562Z" fill="#767676"/>
<path d="M8.4375 19.6561C12.3741 19.2994 15.4688 15.9806 15.4688 11.9531C15.2906 11.9531 14.4632 11.9531 14.0625 11.9531C14.0625 15.446 11.2205 18.2812 7.73438 18.2812C4.245 18.2812 1.40625 15.4425 1.40625 11.9531H0C0 15.9806 3.09469 19.2994 7.03125 19.6561V22.5938H2.10938V24H13.3594V22.5938H8.4375V19.6561Z" fill="#767676"/>
</svg>

After

Width:  |  Height:  |  Size: 812 B

@ -0,0 +1,3 @@
<svg width="24" height="20" viewBox="0 0 24 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.3186 17.8143L12.9972 0.69713C12.7363 0.264525 12.268 0 11.7628 0C11.2576 0 10.7892 0.264479 10.5284 0.69713L0.207042 17.8143C-0.0613895 18.2595 -0.0693386 18.8148 0.186319 19.2674C0.441976 19.7201 0.921541 20 1.4414 20H22.0842C22.604 20 23.0836 19.72 23.3393 19.2674C23.5949 18.8147 23.587 18.2595 23.3186 17.8143ZM11.7705 5.92101C12.3633 5.92101 12.8648 6.25543 12.8648 6.84816C12.8648 8.65683 12.6521 11.256 12.6521 13.0646C12.6521 13.5358 12.1353 13.7334 11.7705 13.7334C11.2842 13.7334 10.8738 13.5358 10.8738 13.0646C10.8738 11.256 10.661 8.65683 10.661 6.84816C10.661 6.25543 11.1473 5.92101 11.7705 5.92101ZM11.7857 17.1228C11.117 17.1228 10.6153 16.5757 10.6153 15.9525C10.6153 15.3142 11.1169 14.7822 11.7857 14.7822C12.4088 14.7822 12.9408 15.3142 12.9408 15.9525C12.9408 16.5757 12.4088 17.1228 11.7857 17.1228Z" fill="#767676"/>
</svg>

After

Width:  |  Height:  |  Size: 957 B

File diff suppressed because one or more lines are too long

@ -8,6 +8,7 @@ import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/lookup.dart';
import 'package:test_sa/models/user.dart'; import 'package:test_sa/models/user.dart';
@Deprecated("Use the one inside lib/providers folder")
class ServiceRequestPriorityProvider extends ChangeNotifier { class ServiceRequestPriorityProvider extends ChangeNotifier {
//reset provider data //reset provider data
void reset() { void reset() {

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:lottie/lottie.dart';
import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart';
@ -144,4 +145,11 @@ extension FilesExtension on String {
BoxFit fit = BoxFit.contain, BoxFit fit = BoxFit.contain,
}) => }) =>
Image.asset("assets/images/$this.png", width: width?.toScreenWidth, height: height?.toScreenHeight, color: color, fit: fit); Image.asset("assets/images/$this.png", width: width?.toScreenWidth, height: height?.toScreenHeight, color: color, fit: fit);
LottieBuilder toLottieAsset({
int width,
int height,
BoxFit fit = BoxFit.contain,
bool repeat = true,
}) =>
Lottie.asset("assets/lottie/$this.json", width: width?.toScreenWidth, height: height?.toScreenHeight, fit: fit, repeat: repeat);
} }

@ -1,6 +1,6 @@
{ {
"serverErrorMessage": "الخدمة غير متاحة حاليا", "serverErrorMessage": "الخدمة غير متاحة حاليا",
"failedRequestMessage": "الفشل في إكمال الطلب", "failedRequestMessage": "فشل إكمال الطلب",
"successfulRequestMessage": "تم إكمال الطلب بنجاح", "successfulRequestMessage": "تم إكمال الطلب بنجاح",
"requestLockMessage": "انتظر حتى إكمال الطلب", "requestLockMessage": "انتظر حتى إكمال الطلب",
"cancel": "إلغاء", "cancel": "إلغاء",
@ -252,5 +252,11 @@
"serialNumber" : "الرقم التسلسلي", "serialNumber" : "الرقم التسلسلي",
"device" : "الجهاز", "device" : "الجهاز",
"pickAsset" : "إختر جهاز", "pickAsset" : "إختر جهاز",
"firstAction" : "First Action" "firstAction" : "First Action",
"priority" : "الأولوية",
"equipmentStatus" : "حالة المعدات",
"attachImage" : "إرفاق صورة",
"callComments" : "",
"comments" : "تعليقات",
"recordVoice" : "تسجيل صوت"
} }

@ -254,5 +254,11 @@
"pickAsset" : "Pick Asset", "pickAsset" : "Pick Asset",
"firstAction" : "First Action", "firstAction" : "First Action",
"workOrder" : "Work Orders", "workOrder" : "Work Orders",
"viewWorkOrder" : "View All Work Order" "viewWorkOrder" : "View All Work Order",
"priority" : "Priority",
"equipmentStatus" : "Equipment Status",
"attachImage" : "Attach Image",
"callComments" : "Call Comments",
"comments": "Comments",
"recordVoice" : "Record Voice"
} }

@ -3,10 +3,9 @@ import 'dart:io';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:localization/localization.dart'; import 'package:localization/localization.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:test_sa/controllers/localization/localization.dart';
import 'package:test_sa/controllers/providers/api/asset_transfer_provider.dart'; import 'package:test_sa/controllers/providers/api/asset_transfer_provider.dart';
import 'package:test_sa/controllers/providers/api/departments_provider.dart'; import 'package:test_sa/controllers/providers/api/departments_provider.dart';
import 'package:test_sa/controllers/providers/api/devices_provider.dart'; import 'package:test_sa/controllers/providers/api/devices_provider.dart';
@ -55,6 +54,8 @@ import 'package:test_sa/providers/gas_request_providers/cylinder_type_provider.d
import 'package:test_sa/providers/gas_request_providers/gas_types_provider.dart'; import 'package:test_sa/providers/gas_request_providers/gas_types_provider.dart';
import 'package:test_sa/providers/gas_request_providers/site_provider.dart'; import 'package:test_sa/providers/gas_request_providers/site_provider.dart';
import 'package:test_sa/providers/loading_list_notifier.dart'; import 'package:test_sa/providers/loading_list_notifier.dart';
import 'package:test_sa/providers/service_request_providers/equipment_status_provider.dart';
import 'package:test_sa/providers/service_request_providers/priority_provider.dart';
import 'package:test_sa/views/pages/device_transfer/request_device_transfer.dart'; import 'package:test_sa/views/pages/device_transfer/request_device_transfer.dart';
import 'package:test_sa/views/pages/device_transfer/track_device_transfer.dart'; import 'package:test_sa/views/pages/device_transfer/track_device_transfer.dart';
import 'package:test_sa/views/pages/sub_workorder/create_sub_workorder_page.dart'; import 'package:test_sa/views/pages/sub_workorder/create_sub_workorder_page.dart';
@ -78,8 +79,6 @@ import 'controllers/providers/api/user_provider.dart';
import 'controllers/providers/settings/setting_provider.dart'; import 'controllers/providers/settings/setting_provider.dart';
import 'new_views/pages/new_gas_refill_request_page.dart'; import 'new_views/pages/new_gas_refill_request_page.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
if (Platform.isIOS) { if (Platform.isIOS) {
@ -159,6 +158,8 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => ServiceReportMaintenanceSituationProvider()), ChangeNotifierProvider(create: (_) => ServiceReportMaintenanceSituationProvider()),
ChangeNotifierProvider(create: (_) => ServiceReportUsersProvider()), ChangeNotifierProvider(create: (_) => ServiceReportUsersProvider()),
ChangeNotifierProvider(create: (_) => ServiceReportAssistantsEmployeeProvider()), ChangeNotifierProvider(create: (_) => ServiceReportAssistantsEmployeeProvider()),
ChangeNotifierProvider(create: (_) => PriorityProvider()),
ChangeNotifierProvider(create: (_) => EquipmentStatusProvider()),
], ],
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {

@ -1,4 +1,7 @@
import 'package:flutter/src/widgets/framework.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/models/device/device.dart'; import 'package:test_sa/models/device/device.dart';
import 'package:test_sa/models/fault_description.dart'; import 'package:test_sa/models/fault_description.dart';
import 'package:test_sa/models/lookup.dart'; import 'package:test_sa/models/lookup.dart';
@ -138,4 +141,18 @@ class ServiceRequest {
priority: Lookup.fromJson(parsedJson['priority']), priority: Lookup.fromJson(parsedJson['priority']),
); );
} }
Future<bool> validateNewRequest(BuildContext context) async {
if (device == null) {
await Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.device}");
return false;
} else if (priority == null) {
await Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.priority}");
return false;
} else if (defectType == null) {
await Fluttertoast.showToast(msg: "${context.translation.youHaveToSelect} ${context.translation.equipmentStatus}");
return false;
}
return true;
}
} }

@ -0,0 +1,33 @@
import 'package:dotted_border/dotted_border.dart';
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 '../app_style/app_color.dart';
class AppDashedButton extends StatelessWidget {
final String title;
final VoidCallback onPressed;
const AppDashedButton({@required this.title, @required this.onPressed, Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 2.toScreenWidth),
decoration: BoxDecoration(color: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(10)),
child: DottedBorder(
strokeWidth: 2,
padding: EdgeInsets.symmetric(vertical: 16.toScreenHeight, horizontal: 16.toScreenWidth),
color: context.isDark ? AppColor.primary40 : AppColor.primary60,
dashPattern: const [4, 3],
radius: const Radius.circular(10),
borderType: BorderType.RRect,
child: title.heading6(context).custom(color: context.isDark ? AppColor.primary40 : AppColor.primary60).center,
),
).onPress(onPressed);
}
}

@ -0,0 +1,36 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import '../../controllers/api_routes/api_manager.dart';
import '../../controllers/api_routes/urls.dart';
import '../../models/lookup.dart';
class EquipmentStatusProvider extends LoadingListNotifier<Lookup> {
@override
Future getDate() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.equipmentStatus);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}

@ -0,0 +1,36 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:test_sa/providers/loading_list_notifier.dart';
import '../../controllers/api_routes/api_manager.dart';
import '../../controllers/api_routes/urls.dart';
import '../../models/lookup.dart';
class PriorityProvider extends LoadingListNotifier<Lookup> {
@override
Future getDate() async {
if (loading == true) return -2;
loading = true;
notifyListeners();
loading = true;
notifyListeners();
try {
Response response = await ApiManager.instance.get(URLs.getServiceReportPriority);
stateCode = response.statusCode;
if (response.statusCode >= 200 && response.statusCode < 300) {
// client's request was successfully received
List categoriesListJson = json.decode(response.body)["data"];
items = categoriesListJson.map((item) => Lookup.fromJson(item)).toList();
}
loading = false;
notifyListeners();
return response.statusCode;
} catch (error) {
loading = false;
stateCode = -1;
notifyListeners();
return -1;
}
}
}

@ -1,43 +1,34 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:test_sa/controllers/api_routes/http_status_manger.dart';
import 'package:test_sa/controllers/localization/localization.dart';
import 'package:test_sa/controllers/providers/api/service_requests_provider.dart'; import 'package:test_sa/controllers/providers/api/service_requests_provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.dart';
import 'package:test_sa/controllers/validator/validator.dart';
import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.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/extensions/widget_extensions.dart';
import 'package:test_sa/models/service_request/service_request.dart'; import 'package:test_sa/models/service_request/service_request.dart';
import 'package:test_sa/models/subtitle.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
import 'package:test_sa/views/widgets/app_text_form_field.dart';
import 'package:test_sa/views/widgets/date_and_time/time_picker.dart';
import 'package:test_sa/views/widgets/equipment/pick_asset.dart'; import 'package:test_sa/views/widgets/equipment/pick_asset.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/loaders/loading_manager.dart';
import 'package:test_sa/views/widgets/sound/record_sound.dart'; import 'package:test_sa/views/widgets/sound/record_sound.dart';
import 'package:test_sa/views/widgets/sound/sound_player.dart';
import 'package:test_sa/views/widgets/speech_to_text/speech_to_text.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_defect_types_mune.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_priority_mune.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_through_mune.dart';
import 'package:test_sa/views/widgets/status/service_request/service_request_types_mune.dart';
import 'package:test_sa/views/widgets/titles/app_sub_title.dart';
import '../../../../controllers/providers/api/status_drop_down/service_reqest/service_request_through_provider.dart'; import '../../../../models/lookup.dart';
import '../../../../controllers/providers/api/status_drop_down/service_reqest/service_request_type_provider.dart'; import '../../../../new_views/app_style/app_color.dart';
import '../../../../models/device/device.dart'; import '../../../../new_views/common_widgets/app_dashed_button.dart';
import '../../../../models/enums/translation_keys.dart'; import '../../../../new_views/common_widgets/app_lazy_loading.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/default_app_bar.dart';
import '../../../widgets/status/service_request/service_request_first_action.dart'; import '../../../../new_views/common_widgets/single_item_drop_down_menu.dart';
import '../../../widgets/status/service_request/service_request_loan_availability.dart'; import '../../../../providers/service_request_providers/equipment_status_provider.dart';
import '../../../../providers/service_request_providers/priority_provider.dart';
class CreateRequestPage extends StatefulWidget { class CreateRequestPage extends StatefulWidget {
static const String id = "/create-request"; static const String id = "/create-request";
@ -50,6 +41,8 @@ class CreateRequestPage extends StatefulWidget {
} }
class CreateRequestPageState extends State<CreateRequestPage> { class CreateRequestPageState extends State<CreateRequestPage> {
TextEditingController _commentController;
double _height; double _height;
UserProvider _userProvider; UserProvider _userProvider;
SettingProvider _settingProvider; SettingProvider _settingProvider;
@ -58,21 +51,17 @@ class CreateRequestPageState extends State<CreateRequestPage> {
final List<File> _deviceImages = []; final List<File> _deviceImages = [];
bool _isLoading = false; bool _isLoading = false;
bool _showDatePicker = false; bool _showDatePicker = false;
Device _asset;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>(); final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
TextEditingController _maintenanceController, _commentController, _reviewCommentController;
DateTime _dateTime; DateTime _dateTime;
bool isFirstActionSubmitted = false; bool isFirstActionSubmitted = false;
@override @override
void initState() { void initState() {
_maintenanceController = TextEditingController();
_commentController = TextEditingController(); _commentController = TextEditingController();
if (widget.serviceRequest != null) { if (widget.serviceRequest != null) {
_serviceRequest = widget.serviceRequest; _serviceRequest = widget.serviceRequest;
_asset = _serviceRequest.device;
_deviceImages.addAll(_serviceRequest.devicePhotos.map((e) => File(e)).toList()); _deviceImages.addAll(_serviceRequest.devicePhotos.map((e) => File(e)).toList());
_showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit"; _showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit";
if (_showDatePicker && _serviceRequest.visitDate != null) { if (_showDatePicker && _serviceRequest.visitDate != null) {
@ -107,7 +96,6 @@ class CreateRequestPageState extends State<CreateRequestPage> {
@override @override
void dispose() { void dispose() {
_maintenanceController.dispose();
_commentController.dispose(); _commentController.dispose();
super.dispose(); super.dispose();
} }
@ -127,7 +115,7 @@ class CreateRequestPageState extends State<CreateRequestPage> {
return Scaffold( return Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
appBar: DefaultAppBar(title: context.translation.newServiceRequest), appBar: DefaultAppBar(title: context.translation.newServiceRequest),
body: SafeArea( body: SafeArea(
child: LoadingManager( child: LoadingManager(
isLoading: _isLoading, isLoading: _isLoading,
@ -143,235 +131,58 @@ class CreateRequestPageState extends State<CreateRequestPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
PickAsset( PickAsset(
device: _asset, device: _serviceRequest.device,
onPickAsset: (asset) { onPickAsset: (asset) {
_asset = asset; _serviceRequest.device = asset;
setState(() {}); setState(() {});
}, },
), ),
const SizedBox( 8.height,
height: 8, SingleItemDropDownMenu<Lookup, PriorityProvider>(
), context: context,
const ASubTitle("Priority"), title: context.translation.priority,
const SizedBox( initialValue: _serviceRequest?.priority,
height: 4, onSelect: (value) {
), _serviceRequest.priority = value;
ServiceRequestPriorityMenu(
initialValue: widget.serviceRequest?.priority,
onSelect: (status) {
_serviceRequest.priority = status;
}, },
enabled: widget.serviceRequest == null ? true : false,
),
const SizedBox(
height: 8,
), ),
const ASubTitle("Equipment Status"), 8.height,
const SizedBox( SingleItemDropDownMenu<Lookup, EquipmentStatusProvider>(
height: 4, context: context,
), title: context.translation.equipmentStatus,
ServiceRequestDefectTypesMenu( initialValue: _serviceRequest?.defectType,
initialValue: _serviceRequest.defectType, onSelect: (value) {
onSelect: (status) { _serviceRequest.defectType = value;
_serviceRequest.defectType = status;
}, },
enabled: widget.serviceRequest == null ? true : false,
), ),
12.height, 8.height,
const SizedBox( AppDashedButton(title: _serviceRequest.devicePhotos?.first?.split("/")?.last ?? context.translation.attachImage, onPressed: _attachImage),
height: 8, 16.height,
Align(
alignment: AlignmentDirectional.centerStart,
child: context.translation.callComments.heading5(context),
), ),
const ASubTitle("Type of Request"), 8.height,
const SizedBox( AppTextFormField(
height: 4, controller: _commentController,
labelText: context.translation.comments,
suffixIcon: "warning".toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, width: 24).paddingOnly(end: 16),
), ),
ServiceRequestTypesMenu( 8.height,
initialValue: _serviceRequest.type, RecordSound(
onSelect: (status) { onRecord: (audio) {
_serviceRequest.type = status; _serviceRequest.audio = audio;
}, },
enabled: false,
withDefaultItem: widget.serviceRequest == null,
),
const SizedBox(
height: 8,
),
const ASubTitle("Through"),
const SizedBox(
height: 4,
),
ServiceRequestedThroughMenu(
initialValue: _serviceRequest.requestedThrough,
enabled: false,
),
if (widget.serviceRequest != null) 12.height,
if (widget.serviceRequest != null) const ASubTitle("First Action"),
const SizedBox(
height: 4,
),
if (widget.serviceRequest != null)
ServiceRequestedFirstAction(
initialValue: _serviceRequest.firstAction,
enabled: !isFirstActionSubmitted,
onSelect: (status) {
_dateTime = null;
_serviceRequest.firstAction = status;
_showDatePicker = _serviceRequest.firstAction != null && _serviceRequest.firstAction.name == "Need a visit";
_serviceRequestsProvider.notifyListeners();
},
),
if (_showDatePicker) 12.height,
if (_showDatePicker)
ADateTimePicker(
date: _dateTime,
enable: !isFirstActionSubmitted,
from: DateTime.now(),
onDateTimePicker: (date) {
_dateTime = date;
setState(() {});
},
),
if (widget.serviceRequest != null) 12.height,
if (widget.serviceRequest != null) const ASubTitle("Loan Availability"),
const SizedBox(
height: 4,
),
if (widget.serviceRequest != null)
ServiceRequestedLoanAvailability(
initialValue: _serviceRequest.loanAvailability,
onSelect: (status) {
_serviceRequest.loanAvailability = status;
},
),
12.height,
if (widget.serviceRequest != null) const ASubTitle("Comments"),
const SizedBox(
height: 4,
),
if (widget.serviceRequest != null)
ATextFormField(
controller: _reviewCommentController,
initialValue: _serviceRequest.reviewComment,
hintText: context.translation.comment,
style: Theme.of(context).textTheme.titleMedium,
textInputType: TextInputType.multiline,
onSaved: (value) {
_serviceRequest.reviewComment = value;
},
enable: widget.serviceRequest != null ? false : true,
),
12.height,
MultiFilesPicker(
label: context.translation.deviceFiles,
files: _deviceImages,
enabled: widget.serviceRequest == null ? true : false,
),
12.height,
SpeechToTextButton(
controller: _maintenanceController,
enabled: widget.serviceRequest == null ? true : false, enabled: widget.serviceRequest == null ? true : false,
), ),
12.height, 16.height,
ATextFormField(
controller: _maintenanceController,
initialValue: _serviceRequest.callComments,
hintText: context.translation.maintenanceIssue,
prefixIconData: FontAwesomeIcons.triangleExclamation,
style: Theme.of(context).textTheme.titleLarge,
textInputType: TextInputType.multiline,
validator: (value) => widget.serviceRequest != null || Validator.hasValue(value) ? null : context.translation.maintenanceIssueRequired,
onSaved: (value) {
_serviceRequest.callComments = value;
},
enable: widget.serviceRequest == null ? true : false,
),
12.height,
if (_serviceRequest.audio?.isNotEmpty == true)
ASoundPlayer(
audio: _serviceRequest.audio,
)
else
RecordSound(
onRecord: (audio) {
_serviceRequest.audio = audio;
},
enabled: widget.serviceRequest == null ? true : false,
),
12.height,
if (widget.serviceRequest != null)
ATextFormField(
controller: _commentController,
initialValue: _serviceRequest.comments,
hintText: context.translation.comment,
style: Theme.of(context).textTheme.titleMedium,
textInputType: TextInputType.multiline,
onSaved: (value) {
_serviceRequest.comments = value;
},
),
], ],
), ),
).expanded, ).expanded,
AppFilledButton( AppFilledButton(
onPressed: () async { onPressed: _submit,
if (!_formKey.currentState.validate()) return; label: context.translation.submitRequest,
if (_asset?.id == null) { ),
Fluttertoast.showToast(msg: context.translation.pickDevice);
return;
}
if (_serviceRequest.firstAction?.name == "Need a visit" && _dateTime == null) {
Fluttertoast.showToast(msg: "first action is required");
return;
}
if (widget.serviceRequest != null && (_serviceRequest?.engineerId == null || (_serviceRequest?.engineerId?.isEmpty ?? false))) {
await Fluttertoast.showToast(msg: "No Assigned Employee");
return;
}
_formKey.currentState.save();
_serviceRequest.deviceId = _asset?.id;
if (widget.serviceRequest == null) {
_serviceRequest.type = Provider.of<ServiceRequestTypeProvider>(context, listen: false).getDefaultItem();
}
_serviceRequest.requestedThrough = Provider.of<ServiceRequestedThroughProvider>(context, listen: false).getDefaultItem();
_isLoading = true;
setState(() {});
_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())}";
}
}
int status = 0;
if (widget.serviceRequest == null) {
status = await _serviceRequestsProvider.createRequest(
user: _userProvider.user,
host: _settingProvider.host,
serviceRequest: _serviceRequest,
);
} else {
status = await _serviceRequestsProvider.updateDate(
user: _userProvider.user,
host: _settingProvider.host,
request: _serviceRequest,
date: _dateTime,
);
}
_isLoading = false;
setState(() {});
if (status >= 200 && status < 300) {
Fluttertoast.showToast(
msg: context.translation.successfulRequestMessage,
);
Navigator.of(context).pop();
} else {
String errorMessage = HttpStatusManger.getStatusMessage(status: status, subtitle: context.translation);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(errorMessage),
));
}
},
label: context.translation.submitRequest),
], ],
), ),
).paddingOnly(start: 16, end: 16, bottom: 24, top: 16), ).paddingOnly(start: 16, end: 16, bottom: 24, top: 16),
@ -379,4 +190,81 @@ class CreateRequestPageState extends State<CreateRequestPage> {
), ),
); );
} }
_attachImage() async {
ImageSource source = await showDialog(
context: context,
builder: (dialogContext) => CupertinoAlertDialog(
actions: <Widget>[
TextButton(
child: Text(context.translation.pickFromCamera),
onPressed: () {
Navigator.of(dialogContext).pop(ImageSource.camera);
},
),
TextButton(
child: Text(context.translation.pickFromGallery),
onPressed: () {
Navigator.of(dialogContext).pop(ImageSource.gallery);
},
),
TextButton(
child: Text(context.translation.pickFromFiles),
onPressed: () async {
await _fromFilePicker();
Navigator.pop(context);
},
),
],
),
);
if (source == null) return;
final pickedFile = await ImagePicker().pickImage(source: source, imageQuality: 70, maxWidth: 800, maxHeight: 800);
if (pickedFile != null) {
_serviceRequest.devicePhotos ??= [];
_serviceRequest.devicePhotos?.clear();
_serviceRequest.devicePhotos?.add(pickedFile.path);
setState(() {});
}
}
_fromFilePicker() async {
FilePickerResult result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xlsx', 'pptx'],
);
if (result?.files?.first != null) {
_serviceRequest.devicePhotos ??= [];
_serviceRequest.devicePhotos?.clear();
_serviceRequest.devicePhotos?.add(result?.files?.first?.path);
setState(() {});
}
}
Future<void> _submit() async {
if (_formKey.currentState.validate() && await _serviceRequest.validateNewRequest(context)) {
_formKey.currentState.save();
_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())}";
}
}
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
int status = await _serviceRequestsProvider.createRequest(
user: _userProvider.user,
host: _settingProvider.host,
serviceRequest: _serviceRequest,
);
Navigator.of(context);
if (status >= 200 && status < 300) {
Fluttertoast.showToast(msg: context.translation.successfulRequestMessage);
Navigator.pop(context);
} else {
Fluttertoast.showToast(msg: context.translation.failedRequestMessage);
}
}
}
} }

@ -3,11 +3,10 @@ import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/models/enums/translation_keys.dart';
import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart';
import 'package:test_sa/views/widgets/equipment/single_device_picker.dart'; import 'package:test_sa/views/widgets/equipment/single_device_picker.dart';
import '../../../models/device/device.dart'; import '../../../models/device/device.dart';
import '../../../new_views/app_style/app_color.dart';
class PickAsset extends StatelessWidget { class PickAsset extends StatelessWidget {
final Function(Device) onPickAsset; final Function(Device) onPickAsset;
@ -19,22 +18,36 @@ class PickAsset extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
children: [ children: [
AppTextFormField( Container(
enable: false, decoration: BoxDecoration(
labelText: context.translation.device, color: Theme.of(context).cardColor,
initialValue: context.translation.site, borderRadius: BorderRadius.circular(10),
suffixIcon: "qr".toSvgAsset(height: 24, fit: BoxFit.fitHeight).paddingOnly(end: 16), boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.03), blurRadius: 14)],
).onPress(() async { ),
Device device = await Navigator.of(context).pushNamed(SingleDevicePicker.id) as Device; child: Row(
onPickAsset(device); children: [
}), Column(
if (device != null) 8.height, crossAxisAlignment: CrossAxisAlignment.start,
children: [
context.translation.device.tinyFont(context).custom(color: context.isDark ? AppColor.neutral40 : AppColor.neutral50),
context.translation.pickAsset.bodyText(context).custom(color: context.isDark ? AppColor.neutral40 : AppColor.neutral50),
],
).onPress(() async {
Device device = await Navigator.of(context).pushNamed(SingleDevicePicker.id) as Device;
onPickAsset(device);
}).expanded,
"qr".toSvgAsset(height: 24, fit: BoxFit.fitHeight).onPress(() {
/// TODO [Zaid] : open qr reader
}),
],
).paddingOnly(start: 16, end: 16, top: 8, bottom: 8),
),
if (device != null) if (device != null)
Card( Card(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
context.translation.myAssets.heading5(context), device.modelDefinition?.assetName?.heading5(context),
8.height, 8.height,
"${context.translation.assetNo}: ${device.assetNumber}".bodyText(context), "${context.translation.assetNo}: ${device.assetNumber}".bodyText(context),
"${context.translation.manufacture}: ${device.modelDefinition?.manufacturerName}".bodyText(context), "${context.translation.manufacture}: ${device.modelDefinition?.manufacturerName}".bodyText(context),
@ -45,7 +58,7 @@ class PickAsset extends StatelessWidget {
"${context.translation.site}: ${device.site?.custName}".bodyText(context), "${context.translation.site}: ${device.site?.custName}".bodyText(context),
], ],
).paddingAll(16), ).paddingAll(16),
), ).paddingOnly(top: 8),
], ],
); );
} }

@ -1,17 +1,20 @@
import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:rive/rive.dart';
import 'package:test_sa/views/widgets/buttons/app_icon_button2.dart';
import 'package:test_sa/views/widgets/buttons/app_small_button.dart';
import 'package:test_sa/views/widgets/sound/sound_player.dart';
import 'package:record_mp3/record_mp3.dart'; import 'package:record_mp3/record_mp3.dart';
import '../../app_style/sizing.dart'; import 'package:rive/rive.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/app_style/app_color.dart';
import '../../../new_views/common_widgets/app_text_form_field.dart';
class RecordSound extends StatefulWidget { class RecordSound extends StatefulWidget {
final Function(String) onRecord; final Function(String) onRecord;
@ -30,6 +33,8 @@ class _RecordSoundState extends State<RecordSound> {
bool _recording = false; bool _recording = false;
String _record; String _record;
Artboard _rive; Artboard _rive;
Timer _timer;
TextEditingController _timeController;
@override @override
void setState(VoidCallback fn) { void setState(VoidCallback fn) {
@ -39,7 +44,7 @@ class _RecordSoundState extends State<RecordSound> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_timeController = TextEditingController();
_recorderIsOpened = true; _recorderIsOpened = true;
// RecordMp3.instance.start(recordFilePath, (type) { // RecordMp3.instance.start(recordFilePath, (type) {
// // record fail callback // // record fail callback
@ -70,6 +75,7 @@ class _RecordSoundState extends State<RecordSound> {
@override @override
void dispose() { void dispose() {
_timeController?.dispose();
// Be careful : you must `close` the audio session when you have finished with it. // Be careful : you must `close` the audio session when you have finished with it.
RecordMp3.instance.stop(); RecordMp3.instance.stop();
//_myRecorder.closeRecorder(); //_myRecorder.closeRecorder();
@ -80,7 +86,6 @@ class _RecordSoundState extends State<RecordSound> {
String recordingFileDirectory; String recordingFileDirectory;
_startRecording() async { _startRecording() async {
// await Permission.camera
PermissionStatus status = await Permission.microphone.request(); PermissionStatus status = await Permission.microphone.request();
if (!status.isGranted) { if (!status.isGranted) {
PermissionStatus status = await Permission.microphone.request(); PermissionStatus status = await Permission.microphone.request();
@ -89,6 +94,11 @@ class _RecordSoundState extends State<RecordSound> {
return; return;
} }
} }
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
setState(() {
_timeController.text = ((timer?.tick ?? 0) / 60)?.toStringAsFixed(2);
});
});
_rive.addController(SimpleAnimation('recording')); _rive.addController(SimpleAnimation('recording'));
if (!_recorderIsOpened) { if (!_recorderIsOpened) {
// await _myRecorder.openRecorder(); // await _myRecorder.openRecorder();
@ -111,7 +121,9 @@ class _RecordSoundState extends State<RecordSound> {
setState(() {}); setState(() {});
return; return;
} }
if (_timer?.isActive ?? false) {
_timer.cancel();
}
RecordMp3.instance.stop(); RecordMp3.instance.stop();
//String path = (await _myRecorder.stopRecorder()).toString(); //String path = (await _myRecorder.stopRecorder()).toString();
@ -137,94 +149,30 @@ class _RecordSoundState extends State<RecordSound> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Stack(
padding: const EdgeInsets.only(left: 12, right: 0), alignment: AlignmentDirectional.centerEnd,
decoration: BoxDecoration( children: [
color: const Color(0xfff5f5f5), AppTextFormField(
border: Border.all( enable: false,
color: const Color(0xffefefef), controller: _timeController,
labelText: context.translation.recordVoice,
initialValue: (_timeController?.text?.isEmpty ?? true) ? "00:00" : _timeController?.text,
suffixIcon: (_recording ? "record".toLottieAsset(height: 24) : (_record != null ? "trash" : "mic").toSvgAsset(color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, height: 24))
.paddingOnly(end: 16),
), ),
borderRadius: BorderRadius.circular(AppStyle.borderRadius * AppStyle.getScaleFactor(context)), SizedBox(height: 50.toScreenHeight, width: 50.toScreenWidth).onPress(() {
), if (_recording) {
child: Column( _stopRecording();
children: [ } else if (_record != null) {
Row( _timeController?.text = "00:00";
children: [ widget.onRecord(null);
Expanded( _record = null;
child: _recording setState(() {});
? Row( } else {
children: [ _startRecording();
ASmallButton( }
text: "done", })
onPressed: () { ],
_stopRecording();
},
),
Expanded(
child: Stack(
children: [
SizedBox(
height: 24 * AppStyle.getScaleFactor(context),
child: Rive(
artboard: _rive,
)),
InkWell(
child: SizedBox(
height: 32 * AppStyle.getScaleFactor(context),
width: MediaQuery.of(context).size.width,
),
onTap: () {
_cancelRecording();
},
),
],
),
),
],
)
: _record != null
? Row(
children: [
Expanded(child: ASoundPlayer(audio: _record)),
AIconButton2(
iconData: Icons.delete,
onPressed: () {
widget.onRecord(null);
_record = null;
setState(() {});
},
)
],
)
: const Text("Record Voice"),
),
Material(
color: Colors.transparent,
child: GestureDetector(
//key: ValueKey("voice"),
onTapDown: widget.enabled
? (TapDownDetails details) async {
_startRecording();
}
: null,
onTapUp: widget.enabled
? (TapUpDetails details) async {
_stopRecording();
}
: null,
onTapCancel: widget.enabled
? () async {
_cancelRecording();
}
: null,
//key: ValueKey("voice"),
child: const Padding(padding: EdgeInsets.all(12.0), child: Icon(Icons.mic)),
),
),
],
),
],
),
); );
} }
} }

@ -209,6 +209,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.8" version: "0.7.8"
dotted_border:
dependency: "direct main"
description:
name: dotted_border
sha256: "108837e11848ca776c53b30bc870086f84b62ed6e01c503ed976e8f8c7df9c04"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -661,6 +669,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
lottie:
dependency: "direct main"
description:
name: lottie
sha256: "23522951540d20a57a60202ed7022e6376bed206a4eee1c347a91f58bd57eb9f"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:

@ -68,6 +68,8 @@ dependencies:
path_provider: ^2.1.0 path_provider: ^2.1.0
open_file: ^3.3.2 open_file: ^3.3.2
localization: ^2.1.0 localization: ^2.1.0
dotted_border: ^2.1.0
lottie: ^2.3.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -97,6 +99,7 @@ flutter:
assets: assets:
- assets/ - assets/
- assets/images/ - assets/images/
- assets/lottie/
- assets/subtitles/ - assets/subtitles/
- assets/rives/ - assets/rives/
- assets/translations/ - assets/translations/

Loading…
Cancel
Save