import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_linkify/flutter_linkify.dart'; import 'package:provider/provider.dart'; import 'package:telnow_mobile_new/src/layouts/functions/detail.dart'; import 'package:telnow_mobile_new/src/layouts/components/template.dart'; import 'package:telnow_mobile_new/src/layouts/web/history_forum.dart'; import 'package:telnow_mobile_new/src/utils/U.dart'; import 'package:telnow_mobile_new/src/utils/provider.dart'; import 'package:timelines_plus/timelines_plus.dart'; import 'package:url_launcher/url_launcher.dart'; class WebHistoryDetailPage extends StatefulWidget { int index; WebHistoryDetailPage({required this.index, super.key}); @override State createState() => _WebHistoryDetailPageState(); } class _WebHistoryDetailPageState extends State { final DetailFunction detFunc = DetailFunction(); TextEditingController controllerNote = new TextEditingController()..text = ''; Map list = {}; Map user = {}; var rating = [ {'key': 1, 'image': "assets/image/icon/very_dissatisfied.png", 'label': 'disatisfied'.tr()}, {'key': 2, 'image': "assets/image/icon/dissatisfied.png", 'label': 'lessSatisfied'.tr()}, {'key': 3, 'image': "assets/image/icon/neutral.png", 'label': 'satisfied'.tr()}, {'key': 4, 'image': "assets/image/icon/satisfied.png", 'label': 'verySatisfied'.tr()}, {'key': 5, 'image': "assets/image/icon/very_satisfied.png", 'label': 'reallyPleased'.tr()}, ]; @override void initState() { getData(); // TODO: implement initState super.initState(); } getData() async{ var res = await detFunc.getMission(context, Provider.of(context, listen: false).dataMisi()[widget.index]); setState((){ user = res['user']; list = res['list']; }); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: backgroundColor, appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)), body: Column( children: [ Container( padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('detail'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis), GestureDetector( child: Text('buttonBack'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)), onTap: ()=>navigateBack(context), ) ], ), ), divider(), Expanded( child: SingleChildScrollView( padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100), child: list.isNotEmpty ? Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(list[U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w600)), list['currentState'] != 'DIPROSES' && list['currentState'] != 'DIANTRIKAN' && !list['autoResponse'] ? GestureDetector( child: Container( padding: EdgeInsets.symmetric(vertical: 10, horizontal: 15), decoration: BoxDecoration(color: primaryColor.withValues(alpha: 0.1), border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(50))), child: Row( children: [ Text('forum'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)), SizedBox(width: 5), U.iconsax('messages-3', color: primaryColor, size: 20) ], ), ), onTap: (){ Provider.of(context, listen: false).setForumFalse(widget.index); navigateTo(context, WebHistoryForumPage(data: list, user: user)); }, ) : Container() ], ), SizedBox(height: 20), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ requestTiles(image: list['_requestImage'] ?? "null", title: list[U.langColumn(context, 'requestSubject')], subtitle: list[U.langColumn(context, '_subjectDescription')]??'', border: true, vertical: 0), SizedBox(height: 16), textHorizontal('ticketNumber'.tr(), list['ticketNo'], copy: true), SizedBox(height: 16), user.isNotEmpty?renderRequested():Container(), textHorizontal('location'.tr(), list['ipphoneExtLocation']), SizedBox(height: 16), divider(opacity: 0.05), SizedBox(height: 16), list['autoResponse'] ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('note'.tr(), style: TextStyle(color: textColor)), SizedBox(height: 5), list['responseText']!=null&&list['responseText']!=''?Linkify( text: list['responseText'], style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300), onOpen: (link) async { if (await canLaunchUrl(Uri.parse(link.url))) { await launchUrl(Uri.parse(link.url)); } }, ):Container(), list['responseAttachment']!=null&&list['responseAttachment']!=''?list['_isPdf']?GestureDetector( child: Container( width: double.infinity, margin: EdgeInsets.only(top: 8), child: Center( child: Column( children: [ Icon(Icons.picture_as_pdf, color: Colors.deepOrange, size: 50), Text('seeAttachment'.tr(), style: TextStyle(color: Colors.black45, fontSize: 12)) ], ), ), decoration: BoxDecoration(border: Border.all(color: Colors.deepOrange), borderRadius: BorderRadius.all(Radius.circular(12))) ), onTap: () => detFunc.openAttachment(list) ):GestureDetector( child: LayoutBuilder( builder: (context, constraints) { return Container( child:Image.network(list['_mobileResponseAttachment'], fit: BoxFit.cover, width: double.infinity, height: constraints.maxWidth/(1.7), loadingBuilder:(BuildContext? context, Widget? child,ImageChunkEvent? loadingProgress) { if (loadingProgress == null) return child!; return Container( height: constraints.maxWidth/(1.7), child: Center( child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, ), ), ); }), ); }, ), onTap: ()=>navigateTo(context, PhotoPreview('image'.tr(), list['_mobileResponseAttachment'], true)) ):Container() ], ) : attachment_new(list), list['autoResponse'] ? Container() : SizedBox(height: 16), list['autoResponse'] ? Container() : textVertical('note'.tr(), list['requestNote']!=''?list['requestNote']:'-'), list['autoResponse'] ? Container() : SizedBox(height: 16), list['autoResponse'] || list['datetimeScheduled'] == null || list['datetimeScheduled'] == '' ? Container() : Text("${"scheduleMessage".tr()} ${list['datetimeScheduled']}."), U.newServerVersion(1741166029) && list['_parentTicket'] != null?Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16), divider(opacity: 0.05), SizedBox(height: 16), Text('requestReference'.tr(), style: TextStyle(color: textColor, fontWeight: FontWeight.w600)), SizedBox(height: 16), textHorizontal('ticketNumber'.tr(), list['_parentTicket']['ticketNo']), SizedBox(height: 16), divider(opacity: 0.05), SizedBox(height: 16), textHorizontal('subject'.tr(), list['_parentTicket'][U.langColumn(context, 'requestSubject')]), SizedBox(height: 16), textHorizontal('note'.tr(), list['_parentTicket']['requestNote']!=null && list['_parentTicket']['requestNote']!=''?list['_parentTicket']['requestNote']:'-'), SizedBox(height: 16), divider(opacity: 0.05), SizedBox(height: 16), list['_parentTicket']['_activeHoldRequest'] != null ? Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('state'.tr(), style: TextStyle(fontSize: 14, color: textColor)), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Image(image: AssetImage('assets/image/general/Watch.png'), width: 20, height: 20), SizedBox(width: 5), Text('hold'.tr(), style: TextStyle(fontSize: 14, color: primaryColor)), ], ) ], ) : textHorizontal('state'.tr(), U.renderStatus(list['_parentTicket']['currentState'])), SizedBox(height: 16), textHorizontal('description'.tr(), list['_parentTicket'][U.langColumn(context, '_subjectDescription')]??''), SizedBox(height: 16), ], ):Container() ], ), ), ), SizedBox(width: 30), Expanded( child: Container( padding: EdgeInsets.all(20), decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))), child: list['autoResponse'] ? Container() : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('activity'.tr(), style: TextStyle(color: textColor, fontWeight: FontWeight.w600)), SizedBox(height: 16), list['currentState'] != 'DIBATALKAN' && list['currentState'] != 'DIANTRIKAN' && list['currentState'] != 'DIPROSES' && list['_collaboratorDataFilter'].length > 0?Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('servant'.tr(), style: TextStyle(color: textColor, fontSize: 14)), Container( padding: EdgeInsets.only(top: 10, left: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('1. ${list['servantNameStart']??'-'}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis), Column( crossAxisAlignment: CrossAxisAlignment.start, children: List.generate(list['_collaboratorDataFilter'].length, (i) { return Text('${i+2}. ${list['_collaboratorDataFilter'][i]['name']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis); }), ) ], ), ) ], ) : textHorizontal( list['currentState'] == 'DIBATALKAN'?'canceledBy'.tr():'servant'.tr(), list['currentState'] == 'DIBATALKAN'?list['servantNameCancel']??'-':list['currentState'] != 'DIANTRIKAN' && list['currentState'] != 'DIPROSES'?list['servantNameStart']??'-':'-' ), SizedBox(height: 16), divider(opacity: 0.05), SizedBox(height: 16), Text('timeline'.tr(), style: TextStyle(color: textColor, fontSize: 14)), SizedBox(height: 16), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ timeline('stateRequested'.tr(), list['datetimeRequest'] != null ? convertDate(list['datetimeRequest'], context.locale.toString()) : '-', null, null, current: list['currentState'] == 'DIANTRIKAN' || list['currentState'] == 'DIPROSES', first: true), list['currentState'] == 'DIMULAI' ? timeline('stateDone'.tr(), list['datetimeStart'] != null ? convertDate(list['datetimeStart'], context.locale.toString()) : '-', list['noteStart'], list['noteStartTranslate'], current: list['currentState'] == 'DIMULAI') : Container(), !list['autoResponse'] && (list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS') ? timeline('startDoing'.tr(), list['datetimeStart'] != null ? convertDate(list['datetimeStart'], context.locale.toString()) : '-', list['noteStart'], list['noteStartTranslate']) : Container(), !list['autoResponse'] && (list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS') ? timeline('stateFinish'.tr(), list['datetimeFinish'] != null ? convertDate(list['datetimeFinish'], context.locale.toString()) : '-', list['noteFinish'], list['noteFinishTranslate'], current: list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS') : Container(), list['autoResponse'] && list['currentState'] == 'TUNTAS' ? timeline('stateFinish'.tr(), list['datetimeComplete'] != null ? convertDate(list['datetimeComplete'], context.locale.toString()) : '-', list['noteComplete'], list['noteCompleteTranslate'], current: list['currentState'] == 'TUNTAS') : Container(), list['currentState'] == 'DIBATALKAN' ? timeline('stateCanceled'.tr(), list['datetimeCancel'] != null ? convertDate(list['datetimeCancel'], context.locale.toString()) : '-', list['noteCancel'], list['noteCancelTranslate'], current: list['currentState'] == 'DIBATALKAN') : Container(), ], ), suspendPanel(list), finish_att_new(list), !list['autoResponse'] && (list['currentState'] == 'TUNTAS' || list['currentState'] == 'DISELESAIKAN') ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16), divider(opacity: 0.05), U.newServerVersion(1716187681) && list['action'] != null && list['action'] != '' ? Column( children: [ SizedBox(height: 16), textHorizontal('action'.tr(), list['action']) ], ) : Container(), SizedBox(height: 16), Row( children: [ Text('rate'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14)), Expanded( child: list['satisfactionRate'] > 0 ? Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Text(rating[list['satisfactionRate']-1]['label'].toString(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis), SizedBox(width: 5), Image(image: AssetImage(rating[list['satisfactionRate']-1]['image'].toString()), width: 25), ], ) : Text('unrated'.tr(), style: TextStyle(color: textColor, fontSize: 14), textAlign: TextAlign.end, overflow: TextOverflow.ellipsis), ), ], ), list['satisfactionRate'] > 0 ? Builder( builder: (context) { var aspect = ''; if(context.locale.toString() == 'id' && list['ratingAspectId'] != null){ aspect = list['ratingAspectId']; } else if(context.locale.toString() == 'en' && list['ratingAspectEn'] != null){ aspect = list['ratingAspectEn']; } else if(list['ratingAspectOri'] != null){ aspect = list['ratingAspectOri']; } return aspect.isNotEmpty ? Column( children: [ SizedBox(height: 16), textHorizontal('ratingAspect'.tr(), aspect) ], ) : Container(); }, ) : Container(), ], ) : Container() ], ), ), ) ], ) ], ) : loadingTemplate(), ), ), list.isEmpty || list['autoResponse'] || list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS' || list['currentState'] == 'DIBATALKAN' ? Container() : Container( margin: EdgeInsets.symmetric(horizontal: 100), padding: EdgeInsets.symmetric(vertical: 25), decoration: BoxDecoration(border: Border(top: BorderSide(color: textColor.withValues(alpha: 0.05)))), child: list['currentState'] == 'DIMULAI' && list['_canNotCancel'] ? Text('canNotCancel2'.tr(), style: TextStyle(color: textColor, fontSize: 14), textAlign: TextAlign.center) : Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: SizedBox( width: double.infinity, child: TextField( controller: controllerNote, style: const TextStyle(fontSize: 14, color: Colors.black), maxLength: 128, decoration: InputDecoration( counterText: '', counterStyle: TextStyle(color: Colors.transparent, fontSize: 0), hintText: 'addNoteCancel'.tr(), hintStyle: TextStyle(color: textColor.withValues(alpha: 0.5), fontSize: 14), filled: true, fillColor: backgroundColor, hoverColor: Colors.black.withValues(alpha: 0.1), contentPadding: EdgeInsets.symmetric(vertical: 17, horizontal: 20), prefixIcon: Padding(padding: EdgeInsets.only(left: 20, right: 20), child: U.iconsax('edit-2', color: textColor, size: 24)), border: InputBorder.none, enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color(0xff262626).withValues(alpha: 0.5))), focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: primaryColor)), isDense: true ), ), ), ), SizedBox(width: 30), buttonTemplate(text: 'buttonCancel'.tr(), width: 200, height: 45, backgroundColor: textColor.withValues(alpha: 0.1), textColor: textColor.withValues(alpha: 0.75), borderColor: textColor.withValues(alpha: 0.20), action: () => detFunc.cancelRequest(context, list, controllerNote)) ], ), ) ], ), ); } Widget renderRequested(){ if(list['receptionistId'] != null){ if(user['roomAttendant'] != null && user['roomAttendant'] && user['userId'] != list['informantUserId'] && user['userId'] == list['receptionistId']){ return Column( children: [ textHorizontal('requestedFor'.tr(), list['informantName']??'-'), SizedBox(height: 16), ], ); } if(user['userId'] == list['informantUserId'] && user['userId'] != list['receptionistId']){ return Column( children: [ textHorizontal('requestedBy'.tr(), list['receptionistName']??'-'), SizedBox(height: 16), ], ); } } return Container(); } Widget attachment_new(list){ List imageList = []; if(list['_attachment'] != null){ for(var i = 1; i <= 5; i++){ if(list['_attachment']['_mobileRequestAtt$i'] != null){ imageList.add({'thumb': list['_attachment']['_mobileRequestThumb$i'], 'image': list['_attachment']['_mobileRequestAtt$i']}); } } } return imageList.length > 0 ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('image'.tr(), style: TextStyle(color: textColor)), SizedBox(height: 5), LayoutBuilder( builder: (context, constraints) { var imageWidth = ((constraints.maxWidth-32)/5)-5; return Row( children: List.generate(imageList.length, (i){ return GestureDetector( child: Container( width: imageWidth, height: imageWidth, alignment: Alignment.topRight, margin: EdgeInsets.only(right: i == 4 ? 0 : 6), decoration: BoxDecoration( color: Colors.black12, borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(color: Colors.black26, width: 0.5), image: imageList[i]['thumb'] != null ? DecorationImage(image: NetworkImage(imageList[i]['thumb']), fit: BoxFit.cover) : DecorationImage(image: AssetImage('assets/image/error/ImageNotFound.png'), fit: BoxFit.cover) ), ), onTap: ()=>navigateTo(context, PhotoPreviewGallery(title: 'image'.tr(), imageList: imageList, startIndex: i)) ); }), ); }, ) ], ) : textVertical('image'.tr(), 'noImgAttach'.tr()); } Widget finish_att_new(list){ List imageList = []; if((list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS')){ if(list['_attachment'] != null){ for(var i = 1; i <= 5; i++){ if(list['_attachment']['_mobileFinishAtt$i'] != null){ imageList.add({'thumb': list['_attachment']['_mobileFinishThumb$i'], 'image': list['_attachment']['_mobileFinishAtt$i']}); } } } } return imageList.length > 0 ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 16), Text('finishAttachment'.tr(), style: TextStyle(color: textColor)), SizedBox(height: 8), LayoutBuilder( builder: (context, constraints) { var imageWidth = ((constraints.maxWidth-32)/5)-5; return Row( children: List.generate(imageList.length, (i){ return GestureDetector( child: Container( width: imageWidth, height: imageWidth, alignment: Alignment.topRight, margin: EdgeInsets.only(right: i == 4 ? 0 : 6), decoration: BoxDecoration( color: Colors.black12, borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(color: Colors.black26, width: 0.5), image: imageList[i]['thumb'] != null ? DecorationImage(image: NetworkImage(imageList[i]['thumb']), fit: BoxFit.cover) : DecorationImage(image: AssetImage('assets/image/error/ImageNotFound.png'), fit: BoxFit.cover) ), ), onTap: ()=>navigateTo(context, PhotoPreviewGallery(title: 'finishAttachment'.tr(), imageList: imageList, startIndex: i)) ); }), ); }, ) ], ) : Container(); } Widget suspendPanel(list){ var activeHold = list['_activeHoldRequest']; var requestHold = list['_holdRequest']; return Column( children: [ activeHold != null ? Padding(padding: EdgeInsets.symmetric(vertical: 15), child: divider(opacity: 0.05)) : Container(), activeHold != null ? Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('state'.tr(), style: TextStyle(fontSize: 14, color: textColor)), Container( child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Image(image: AssetImage('assets/image/general/Watch.png'), width: 20, height: 20), SizedBox(width: 5), Text('hold'.tr(), style: TextStyle(fontSize: 14, color: primaryColor)), ], ), ) ], ), SizedBox(height: 16), textHorizontal('description'.tr(), activeHold['description']) ], ) : Container(), requestHold == null || requestHold.length == 0 || (requestHold.length == 1 && requestHold[0]['datetimeEnd'] == null) ? Container() : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding(padding: EdgeInsets.symmetric(vertical: 15), child: divider(opacity: 0.05)), Text('holdHistory'.tr(), style: TextStyle(fontSize: 14, color: Colors.black)), SizedBox(height: 5), Column( children: List.generate(requestHold.length, (i) { return requestHold[i]['datetimeEnd'] != null ? Container( padding: EdgeInsets.symmetric(vertical: 5), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('${i+1}.', style: TextStyle(fontSize: 14, color: Colors.black)), SizedBox(width: 5), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(requestHold[i]['description'], style: TextStyle(fontSize: 14, color: Colors.black), textAlign: TextAlign.start), SizedBox(height: 3), Text('${DateFormat('dd MMM yyyy, HH:mm', context.locale.toString()).format(DateTime.parse(requestHold[i]['datetimeStart']))} - ${DateFormat('dd MMM yyyy, HH:mm', context.locale.toString()).format(DateTime.parse(requestHold[i]['datetimeEnd']))}', style: TextStyle(fontSize: 14, color: primaryColor)) ], ), ) ], ), ) : Container(); }), ) ], ) ], ); } Widget timeline(label, text, note, noteTranslate, {bool first = false, bool current = false}) { return TimelineTile( nodeAlign: TimelineNodeAlign.start, contents: Container( margin: EdgeInsets.fromLTRB(15, 10, 0, 10), child: Row( children: [ Text(label, style: TextStyle(fontSize: 14, color: textColor.withValues(alpha: 0.85))), SizedBox(width: 5), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text(text, style: TextStyle(fontSize: 13, color: textColor)), note != null && note != '' ? Text(note, style: TextStyle(fontSize: 12, color: primaryColor), textAlign: TextAlign.right) : Container(), U.autoTranslate() && noteTranslate != null && noteTranslate != '' && note != noteTranslate ? Container( margin: EdgeInsets.only(top: 1), decoration: BoxDecoration(border: Border(top: BorderSide(color: primaryColor.withValues(alpha: 0.3)))), child: Text('($noteTranslate)', style: TextStyle(fontSize: 12, color: primaryColor.withValues(alpha: 0.7), fontStyle: FontStyle.italic), textAlign: TextAlign.right), ) : Container(), ], ), ), ], ), ), node: TimelineNode( indicator: DotIndicator(color: current ? primaryColor : Color(0xffE8E8E8)), startConnector: SolidLineConnector(color: first ? Colors.transparent : Color(0xffE8E8E8)), endConnector: SolidLineConnector(color: current ? Colors.transparent : Color(0xffE8E8E8)), ), ); } }