import 'package:diplomaticquarterapp/config/config.dart'; import 'package:diplomaticquarterapp/core/enum/viewstate.dart'; import 'package:diplomaticquarterapp/core/viewModels/feedback/feedback_view_model.dart'; import 'package:diplomaticquarterapp/core/viewModels/project_view_model.dart'; import 'package:diplomaticquarterapp/models/Appointments/AppoimentAllHistoryResultList.dart'; import 'package:diplomaticquarterapp/pages/base/base_view.dart'; import 'package:diplomaticquarterapp/services/robo_search/event_provider.dart'; import 'package:diplomaticquarterapp/uitl/app_toast.dart'; import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; import 'package:diplomaticquarterapp/uitl/gif_loader_dialog_utils.dart'; import 'package:diplomaticquarterapp/uitl/translations_delegate_base.dart'; import 'package:diplomaticquarterapp/widgets/avatar/large_avatar.dart'; import 'package:diplomaticquarterapp/widgets/bottom_options/BottomSheet.dart'; import 'package:diplomaticquarterapp/widgets/buttons/button.dart'; import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; import 'package:diplomaticquarterapp/widgets/buttons/secondary_button.dart'; import 'package:diplomaticquarterapp/widgets/data_display/text.dart'; import 'package:diplomaticquarterapp/widgets/input/text_field.dart'; import 'package:diplomaticquarterapp/widgets/others/StarRating.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; import 'package:diplomaticquarterapp/widgets/others/floating_button_search.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:speech_to_text/speech_recognition_error.dart'; import 'dart:convert'; import 'dart:io'; import 'appointment_history.dart'; import 'package:speech_to_text/speech_to_text.dart' as stt; class SendFeedbackPage extends StatefulWidget { final AppoitmentAllHistoryResultList appointment; final MessageType messageType; const SendFeedbackPage({Key key, this.appointment, this.messageType = MessageType.NON}) : super(key: key); @override _SendFeedbackPageState createState() => _SendFeedbackPageState(); } class _SendFeedbackPageState extends State { TextEditingController titleController = TextEditingController(); TextEditingController messageController = TextEditingController(); List images = []; String title; AppoitmentAllHistoryResultList appointHistory; bool isShowListAppointHistory = true; String message; final formKey = GlobalKey(); MessageType messageType = MessageType.NON; var _currentLocaleId; stt.SpeechToText speech = stt.SpeechToText(); var reconizedWord; var event = RobotProvider(); String getSelected(BuildContext context) { switch (messageType) { case MessageType.ComplaintOnAnAppointment: return TranslationBase.of(context).complainAppo; break; case MessageType.ComplaintWithoutAppointment: return TranslationBase.of(context).complainWithoutAppo; break; case MessageType.Question: return TranslationBase.of(context).question; break; case MessageType.Compliment: return TranslationBase.of(context).compliment; break; case MessageType.Suggestion: return TranslationBase.of(context).suggestion; break; case MessageType.NON: return TranslationBase.of(context).notClassified; break; } return TranslationBase.of(context).notClassified; } setMessageType(MessageType messageType) { setState(() { this.messageType = messageType; this.appointHistory = widget.appointment; }); } @override void initState() { setState(() { this.messageType = widget.messageType; this.appointHistory = widget.appointment; }); requestPermissions(); super.initState(); } @override Widget build(BuildContext context) { ProjectViewModel projectViewModel = Provider.of(context); return BaseView( allowAny: true, builder: (_, model, widget) => AppScaffold( baseViewModel: model, isShowDecPage: false, body: Column( children: [ Expanded( child: SingleChildScrollView( physics: BouncingScrollPhysics(), padding: EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21), child: Form( key: formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( TranslationBase.of(context).likeToHear, textAlign: TextAlign.center, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 23 / 16), ), SizedBox(height: 21), InkWell( onTap: () { confirmBox(model); }, child: Container( padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all( color: Color(0xffefefef), width: 1, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( getSelected(context), style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, letterSpacing: -0.48, color: Color(0xff2B353E), ), ), Icon( Icons.keyboard_arrow_down, size: 22, color: Colors.grey, ) ], ), ), ), SizedBox(height: 12), if (appointHistory != null && messageType == MessageType.ComplaintOnAnAppointment) InkWell( onTap: () { setState(() { isShowListAppointHistory = true; }); }, child: Container( // margin: EdgeInsets.all(8.0), decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(8)), child: Row( children: [ Container( width: projectViewModel.isArabic ? 27 : 20, height: projectViewModel.isArabic ? 105 : 90, decoration: BoxDecoration( //Colors.red[900] Color(0xff404545) color: appointHistory.isLiveCareAppointment ? Color(0xff404545) : !appointHistory.isInOutPatient ? Colors.red[900] : Theme.of(context).primaryColor, borderRadius: BorderRadius.only( topLeft: projectViewModel.isArabic ? Radius.circular(0) : Radius.circular(8), bottomLeft: projectViewModel.isArabic ? Radius.circular(0) : Radius.circular(8), topRight: projectViewModel.isArabic ? Radius.circular(8) : Radius.circular(0), bottomRight: projectViewModel.isArabic ? Radius.circular(8) : Radius.circular(0), ), ), child: RotatedBox( quarterTurns: 3, child: Center( child: Text( appointHistory.isLiveCareAppointment ? TranslationBase.of(context).liveCare.toUpperCase() : !appointHistory.isInOutPatient ? TranslationBase.of(context).inPatient.toUpperCase() : TranslationBase.of(context).outpatient.toUpperCase(), style: TextStyle(color: Colors.white, fontSize: 12), ), )), ), Expanded( flex: 4, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(10.0), child: Row( children: [ Expanded( flex: 1, child: LargeAvatar( name: appointHistory.doctorNameObj, url: appointHistory.doctorImageURL, ), ), Expanded( flex: 4, child: Container( margin: EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Texts( appointHistory.doctorNameObj, bold: true, ), Texts( DateUtil.getMonthDayYearDateFormatted(DateUtil.convertStringToDate(appointHistory.appointmentDate)), variant: 'caption3', ), StarRating(totalAverage: appointHistory.doctorRate.toDouble(), forceStars: true), ], ), ), ), ], ), ), ], ), ), Expanded( flex: 1, child: Center( child: Icon( Icons.arrow_forward_ios, size: 15, ), ), ) ], ), ), ), if (messageType == MessageType.ComplaintOnAnAppointment && model.appointHistoryList.length != 0 && isShowListAppointHistory) Container( height: model.appointHistoryList.length > 2 ? MediaQuery.of(context).size.height * 0.25 : MediaQuery.of(context).size.height * 0.15, child: ListView.builder( itemCount: model.appointHistoryList.length, itemBuilder: (context, index) => InkWell( onTap: () { setState(() { appointHistory = model.appointHistoryList[index]; isShowListAppointHistory = false; }); }, child: Container( margin: EdgeInsets.only(left: 8, right: 8), color: Colors.white, child: Column( children: [ Row( children: [ Expanded( flex: 4, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(10.0), child: Row( children: [ Expanded( flex: 1, child: LargeAvatar( name: model.appointHistoryList[index].doctorNameObj, url: model.appointHistoryList[index].doctorImageURL, ), ), Expanded( flex: 4, child: Container( margin: EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Texts( model.appointHistoryList[index].doctorNameObj, bold: true, ), Texts( DateUtil.getMonthDayYearDateFormatted(DateUtil.convertStringToDate(model.appointHistoryList[index].appointmentDate)), variant: 'caption3', ), StarRating(totalAverage: model.appointHistoryList[index].doctorRate.toDouble(), forceStars: true), ], ), ), ), ], ), ), ], ), ), Expanded( flex: 1, child: Center( child: Icon( Icons.arrow_forward_ios, size: 15, ), ), ) ], ), SizedBox( height: 5, ), Divider( height: 0.5, color: Colors.grey[400], ) ], ), ), ), ), ), inputWidget(TranslationBase.of(context).subject, "xxxxxxxx", titleController), SizedBox(height: 12), inputWidget(TranslationBase.of(context).message, "xxxxxxxx", messageController, lines: 11, suffixTap: () { openSpeechReco(); }), SizedBox(height: 12), InkWell( onTap: () { ImageOptions.showImageOptions(context, (String image, File file) { setState(() { images.add(image); }); }); }, child: Container( padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all( color: Color(0xffefefef), width: 1, ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.attach_file, color: Color(0xff2B353E), ), Text( TranslationBase.of(context).selectAttachment, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 23 / 16), ), ], ), ), ), ...List.generate( images.length, (index) => Container( margin: EdgeInsets.all(10), padding: EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(FontAwesomeIcons.paperclip), SizedBox( width: 8, ), Texts( 'image ${index + 1}.png', ), ], ), InkWell( onTap: () { setState(() { images.remove(images[index]); }); }, child: Icon( FontAwesomeIcons.trashAlt, color: Colors.red[300], )) ], ), )), ], ), ), ), ), Container( color: Colors.white, padding: EdgeInsets.only(top: 16, bottom: 16, right: 21, left: 21), child: DefaultButton( TranslationBase.of(context).send, (titleController.text.toString().isEmpty || messageController.text.toString().isEmpty) ? null : () { final form = formKey.currentState; if (form.validate()) { GifLoaderDialogUtils.showMyDialog(context); model .sendCOCItem( title: titleController.text, attachment: images.length > 0 ? images[0] : "", details: messageController.text, cOCTypeName: getCOCName(), appointHistory: messageType == MessageType.ComplaintOnAnAppointment ? appointHistory : null) .then((value) { if (value) { setState(() { titleController.text = ""; messageController.text = ""; images = []; }); setMessageType(MessageType.NON); GifLoaderDialogUtils.hideDialog(context); AppToast.showSuccessToast(message: TranslationBase.of(context).yourFeedback); } else { AppToast.showErrorToast(message: model.error); GifLoaderDialogUtils.hideDialog(context); } }); } }, color: Color(0xffD02127), textColor: (titleController.text.toString().isEmpty || messageController.text.toString().isEmpty) ? Color(0xff000000) : Colors.white, disabledColor: Color(0xffEAEAEA), ), ), ], ), ), ); } Widget inputWidget(String _labelText, String _hintText, TextEditingController _controller, {VoidCallback suffixTap, bool isEnable = true, bool hasSelection = false, int lines}) { return Container( padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15), alignment: Alignment.center, decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all( color: Color(0xffefefef), width: 1, ), ), child: InkWell( onTap: hasSelection ? () {} : null, child: Row( children: [ Expanded( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _labelText, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44, ), ), TextField( enabled: isEnable, scrollPadding: EdgeInsets.zero, keyboardType: TextInputType.number, controller: _controller, maxLines: lines, onChanged: (value) => {setState(() {})}, style: TextStyle( fontSize: 14, height: 21 / 14, fontWeight: FontWeight.w400, color: Color(0xff2B353E), letterSpacing: -0.44, ), decoration: InputDecoration( isDense: true, hintText: _hintText, hintStyle: TextStyle( fontSize: 14, height: 21 / 14, fontWeight: FontWeight.w400, color: Color(0xff575757), letterSpacing: -0.56, ), suffixIconConstraints: BoxConstraints(minWidth: 50), suffixIcon: suffixTap == null ? null : IconButton(icon: Icon(Icons.mic, color: Color(0xff2E303A)), onPressed: suffixTap), // prefixIconConstraints: BoxConstraints(minWidth: 50), // prefixIcon: suffix == null // ? null // : Text( // "+" + suffix, // style: TextStyle( // fontSize: 14, // height: 21 / 14, // fontWeight: FontWeight.w500, // color: Color(0xff2E303A), // letterSpacing: -0.56, // ), // ), contentPadding: EdgeInsets.zero, border: InputBorder.none, focusedBorder: InputBorder.none, enabledBorder: InputBorder.none, ), ), ], ), ), if (hasSelection) Icon(Icons.keyboard_arrow_down_outlined), ], ), ), ); } String getCOCName() { switch (messageType) { case MessageType.ComplaintOnAnAppointment: return "1"; break; case MessageType.ComplaintWithoutAppointment: return "2"; break; case MessageType.Question: return "3"; break; case MessageType.Compliment: return "4"; break; case MessageType.Suggestion: return "6"; break; case MessageType.NON: return "5"; break; } return ""; } // Show Dialog function void confirmBox(FeedbackViewModel model) { showDialog( context: context, child: FeedbackTypeDialog( messageTypeDialog: messageType, onValueSelected: (MessageType value) { if (value == MessageType.ComplaintOnAnAppointment) { GifLoaderDialogUtils.showMyDialog(context); model.getPatentAppointmentHistory().then((value) { GifLoaderDialogUtils.hideDialog(context); setState(() { appointHistory = null; }); }); } setMessageType(value); }, )); } openSpeechReco() async { new RoboSearch(context: context).showAlertDialog(context); _currentLocaleId = TranslationBase.of(AppGlobal.context).locale.languageCode; bool available = await speech.initialize(onStatus: statusListener, onError: errorListener); if (available) { speech.listen( onResult: resultListener, listenMode: stt.ListenMode.confirmation, localeId: _currentLocaleId == 'en' ? 'en-US' : 'ar-SA', ); } else { print("The user has denied the use of speech recognition."); } } void errorListener(SpeechRecognitionError error) { event.setValue({"searchText": 'null'}); //SpeechToText.closeAlertDialog(context); print(error); } void statusListener(String status) { reconizedWord = status == 'listening' ? 'Lisening...' : 'Sorry....'; } void requestPermissions() async { Map statuses = await [ Permission.microphone, ].request(); print(statuses); } void resultListener(result) { reconizedWord = result.recognizedWords; event.setValue({"searchText": reconizedWord}); if (result.finalResult == true) { setState(() { RoboSearch.closeAlertDialog(context); speech.stop(); messageController.text = reconizedWord + '\n'; }); } } Future initSpeechState() async { bool hasSpeech = await speech.initialize(onError: errorListener, onStatus: statusListener); print(hasSpeech); if (!mounted) return; } } class FeedbackTypeDialog extends StatefulWidget { final Function(MessageType) onValueSelected; final MessageType messageTypeDialog; const FeedbackTypeDialog({Key key, this.onValueSelected, this.messageTypeDialog = MessageType.NON}) : super(key: key); @override State createState() => new FeedbackTypeDialogState(); } class FeedbackTypeDialogState extends State { MessageType messageTypeDialog = MessageType.NON; setMessageDialogType(MessageType messageType) { setState(() { messageTypeDialog = messageType; }); } @override void initState() { messageTypeDialog = widget.messageTypeDialog; super.initState(); } Widget build(BuildContext context) { return BaseView( builder: (_, model, widge) => SimpleDialog( title: Text( TranslationBase.of(context).messageType, textAlign: TextAlign.center, ), children: [ Container( // padding: const EdgeInsets.all(10.0), child: Column( children: [ Divider( height: 2.5, color: Colors.grey[500], ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.NON), child: ListTile( title: Texts(TranslationBase.of(context).notClassified), leading: Radio( value: MessageType.NON, groupValue: messageTypeDialog, activeColor: Theme.of(context).primaryColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.ComplaintOnAnAppointment), child: ListTile( title: Texts(TranslationBase.of(context).complainAppo), leading: Radio( value: MessageType.ComplaintOnAnAppointment, groupValue: messageTypeDialog, activeColor: Theme.of(context).primaryColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.ComplaintWithoutAppointment), child: ListTile( title: Texts(TranslationBase.of(context).complainWithoutAppo), leading: Radio( value: MessageType.ComplaintWithoutAppointment, groupValue: messageTypeDialog, activeColor: Theme.of(context).primaryColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.Question), child: ListTile( title: Texts(TranslationBase.of(context).question), leading: Radio( value: MessageType.Question, groupValue: messageTypeDialog, activeColor: Theme.of(context).primaryColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.Compliment), child: ListTile( title: Texts(TranslationBase.of(context).compliment), leading: Radio( value: MessageType.Compliment, groupValue: messageTypeDialog, activeColor: Theme.of(context).primaryColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Row( children: [ Expanded( flex: 1, child: InkWell( onTap: () => setMessageDialogType(MessageType.Suggestion), child: ListTile( title: Texts(TranslationBase.of(context).suggestion), leading: Radio( value: MessageType.Suggestion, groupValue: messageTypeDialog, activeColor: Theme.of(context).primaryColor, onChanged: (MessageType value) => setMessageDialogType(value), ), ), ), ) ], ), SizedBox( height: 5.0, ), Divider( height: 2.5, color: Colors.grey[500], ), SizedBox( height: 5, ), Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( flex: 1, child: InkWell( onTap: () { Navigator.pop(context); }, child: Padding( padding: EdgeInsets.all(8.0), child: Container( child: Center( child: Texts( TranslationBase.of(context).cancel, color: Theme.of(context).primaryColor, ), ), ), ), ), ), Container( width: 1, height: 30, color: Colors.grey[500], ), Expanded( flex: 1, child: InkWell( onTap: () { widget.onValueSelected(messageTypeDialog); Navigator.pop(context); }, child: Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Texts( TranslationBase.of(context).ok, fontWeight: FontWeight.w400, )), ), )), ], ) ], ), ), ], ), ); } }