history_detail.dart 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. import 'package:easy_localization/easy_localization.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_linkify/flutter_linkify.dart';
  4. import 'package:provider/provider.dart';
  5. import 'package:telnow_mobile_new/src/layouts/functions/detail.dart';
  6. import 'package:telnow_mobile_new/src/layouts/components/template.dart';
  7. import 'package:telnow_mobile_new/src/layouts/web/history_forum.dart';
  8. import 'package:telnow_mobile_new/src/utils/U.dart';
  9. import 'package:telnow_mobile_new/src/utils/provider.dart';
  10. import 'package:timelines_plus/timelines_plus.dart';
  11. import 'package:url_launcher/url_launcher.dart';
  12. class WebHistoryDetailPage extends StatefulWidget {
  13. int index;
  14. WebHistoryDetailPage({required this.index, super.key});
  15. @override
  16. State<WebHistoryDetailPage> createState() => _WebHistoryDetailPageState();
  17. }
  18. class _WebHistoryDetailPageState extends State<WebHistoryDetailPage> {
  19. final DetailFunction detFunc = DetailFunction();
  20. TextEditingController controllerNote = new TextEditingController()..text = '';
  21. Map<String, dynamic> list = {};
  22. Map<String, dynamic> user = {};
  23. var rating = [
  24. {'key': 1, 'image': "assets/image/icon/very_dissatisfied.png", 'label': 'disatisfied'.tr()},
  25. {'key': 2, 'image': "assets/image/icon/dissatisfied.png", 'label': 'lessSatisfied'.tr()},
  26. {'key': 3, 'image': "assets/image/icon/neutral.png", 'label': 'satisfied'.tr()},
  27. {'key': 4, 'image': "assets/image/icon/satisfied.png", 'label': 'verySatisfied'.tr()},
  28. {'key': 5, 'image': "assets/image/icon/very_satisfied.png", 'label': 'reallyPleased'.tr()},
  29. ];
  30. @override
  31. void initState() {
  32. getData();
  33. // TODO: implement initState
  34. super.initState();
  35. }
  36. getData() async{
  37. var res = await detFunc.getMission(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[widget.index]);
  38. setState((){
  39. user = res['user'];
  40. list = res['list'];
  41. });
  42. }
  43. @override
  44. Widget build(BuildContext context) {
  45. return Scaffold(
  46. backgroundColor: backgroundColor,
  47. appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)),
  48. body: Column(
  49. children: [
  50. Container(
  51. padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100),
  52. child: Row(
  53. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  54. children: [
  55. Text('detail'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  56. GestureDetector(
  57. child: Text('buttonBack'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
  58. onTap: ()=>navigateBack(context),
  59. )
  60. ],
  61. ),
  62. ),
  63. divider(),
  64. Expanded(
  65. child: SingleChildScrollView(
  66. padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100),
  67. child: list.isNotEmpty ? Column(
  68. children: [
  69. Row(
  70. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  71. children: [
  72. Text(list[U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w600)),
  73. list['currentState'] != 'DIPROSES' && list['currentState'] != 'DIANTRIKAN' && !list['autoResponse'] ? GestureDetector(
  74. child: Container(
  75. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 15),
  76. decoration: BoxDecoration(color: primaryColor.withValues(alpha: 0.1), border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
  77. child: Row(
  78. children: [
  79. Text('forum'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
  80. SizedBox(width: 5),
  81. U.iconsax('messages-3', color: primaryColor, size: 20)
  82. ],
  83. ),
  84. ),
  85. onTap: (){
  86. Provider.of<HistoryModule>(context, listen: false).setForumFalse(widget.index);
  87. navigateTo(context, WebHistoryForumPage(data: list, user: user));
  88. },
  89. ) : Container()
  90. ],
  91. ),
  92. SizedBox(height: 20),
  93. Row(
  94. crossAxisAlignment: CrossAxisAlignment.start,
  95. children: [
  96. Expanded(
  97. child: Container(
  98. padding: EdgeInsets.all(20),
  99. decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
  100. child: Column(
  101. crossAxisAlignment: CrossAxisAlignment.start,
  102. children: [
  103. requestTiles(image: list['_requestImage'] ?? "null", title: list[U.langColumn(context, 'requestSubject')], subtitle: list[U.langColumn(context, '_subjectDescription')]??'', border: true, vertical: 0),
  104. SizedBox(height: 16),
  105. textHorizontal('ticketNumber'.tr(), list['ticketNo'], copy: true),
  106. SizedBox(height: 16),
  107. user.isNotEmpty?renderRequested():Container(),
  108. textHorizontal('location'.tr(), list['ipphoneExtLocation']),
  109. SizedBox(height: 16),
  110. divider(opacity: 0.05),
  111. SizedBox(height: 16),
  112. list['autoResponse'] ? Column(
  113. crossAxisAlignment: CrossAxisAlignment.start,
  114. children: [
  115. Text('note'.tr(), style: TextStyle(color: textColor)),
  116. SizedBox(height: 5),
  117. list['responseText']!=null&&list['responseText']!=''?Linkify(
  118. text: list['responseText'], style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300),
  119. onOpen: (link) async {
  120. if (await canLaunchUrl(Uri.parse(link.url))) {
  121. await launchUrl(Uri.parse(link.url));
  122. }
  123. },
  124. ):Container(),
  125. list['responseAttachment']!=null&&list['responseAttachment']!=''?list['_isPdf']?GestureDetector(
  126. child: Container(
  127. width: double.infinity,
  128. margin: EdgeInsets.only(top: 8),
  129. child: Center(
  130. child: Column(
  131. children: [
  132. Icon(Icons.picture_as_pdf, color: Colors.deepOrange, size: 50),
  133. Text('seeAttachment'.tr(), style: TextStyle(color: Colors.black45, fontSize: 12))
  134. ],
  135. ),
  136. ),
  137. decoration: BoxDecoration(border: Border.all(color: Colors.deepOrange), borderRadius: BorderRadius.all(Radius.circular(12)))
  138. ),
  139. onTap: () => detFunc.openAttachment(list)
  140. ):GestureDetector(
  141. child: LayoutBuilder(
  142. builder: (context, constraints) {
  143. return Container(
  144. child:Image.network(list['_mobileResponseAttachment'], fit: BoxFit.cover, width: double.infinity, height: constraints.maxWidth/(1.7), loadingBuilder:(BuildContext? context, Widget? child,ImageChunkEvent? loadingProgress) {
  145. if (loadingProgress == null) return child!;
  146. return Container(
  147. height: constraints.maxWidth/(1.7),
  148. child: Center(
  149. child: CircularProgressIndicator(
  150. value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null,
  151. ),
  152. ),
  153. );
  154. }),
  155. );
  156. },
  157. ),
  158. onTap: ()=>navigateTo(context, PhotoPreview('image'.tr(), list['_mobileResponseAttachment'], true))
  159. ):Container()
  160. ],
  161. ) : attachment_new(list),
  162. list['autoResponse'] ? Container() : SizedBox(height: 16),
  163. list['autoResponse'] ? Container() : textVertical('note'.tr(), list['requestNote']!=''?list['requestNote']:'-'),
  164. list['autoResponse'] ? Container() : SizedBox(height: 16),
  165. list['autoResponse'] || list['datetimeScheduled'] == null || list['datetimeScheduled'] == '' ? Container() : Text("${"scheduleMessage".tr()} ${list['datetimeScheduled']}."),
  166. U.newServerVersion(1741166029) && list['_parentTicket'] != null?Column(
  167. crossAxisAlignment: CrossAxisAlignment.start,
  168. children: [
  169. SizedBox(height: 16),
  170. divider(opacity: 0.05),
  171. SizedBox(height: 16),
  172. Text('requestReference'.tr(), style: TextStyle(color: textColor, fontWeight: FontWeight.w600)),
  173. SizedBox(height: 16),
  174. textHorizontal('ticketNumber'.tr(), list['_parentTicket']['ticketNo']),
  175. SizedBox(height: 16),
  176. divider(opacity: 0.05),
  177. SizedBox(height: 16),
  178. textHorizontal('subject'.tr(), list['_parentTicket'][U.langColumn(context, 'requestSubject')]),
  179. SizedBox(height: 16),
  180. textHorizontal('note'.tr(), list['_parentTicket']['requestNote']!=null && list['_parentTicket']['requestNote']!=''?list['_parentTicket']['requestNote']:'-'),
  181. SizedBox(height: 16),
  182. divider(opacity: 0.05),
  183. SizedBox(height: 16),
  184. list['_parentTicket']['_activeHoldRequest'] != null ? Row(
  185. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  186. children: [
  187. Text('state'.tr(), style: TextStyle(fontSize: 14, color: textColor)),
  188. Row(
  189. mainAxisAlignment: MainAxisAlignment.end,
  190. children: [
  191. Image(image: AssetImage('assets/image/general/Watch.png'), width: 20, height: 20),
  192. SizedBox(width: 5),
  193. Text('hold'.tr(), style: TextStyle(fontSize: 14, color: primaryColor)),
  194. ],
  195. )
  196. ],
  197. ) : textHorizontal('state'.tr(), U.renderStatus(list['_parentTicket']['currentState'])),
  198. SizedBox(height: 16),
  199. textHorizontal('description'.tr(), list['_parentTicket'][U.langColumn(context, '_subjectDescription')]??''),
  200. SizedBox(height: 16),
  201. ],
  202. ):Container()
  203. ],
  204. ),
  205. ),
  206. ),
  207. SizedBox(width: 30),
  208. Expanded(
  209. child: Container(
  210. padding: EdgeInsets.all(20),
  211. decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
  212. child: list['autoResponse'] ? Container() : Column(
  213. crossAxisAlignment: CrossAxisAlignment.start,
  214. children: [
  215. Text('activity'.tr(), style: TextStyle(color: textColor, fontWeight: FontWeight.w600)),
  216. SizedBox(height: 16),
  217. list['currentState'] != 'DIBATALKAN' && list['currentState'] != 'DIANTRIKAN' && list['currentState'] != 'DIPROSES' && list['_collaboratorDataFilter'].length > 0?Column(
  218. crossAxisAlignment: CrossAxisAlignment.start,
  219. children: [
  220. Text('servant'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
  221. Container(
  222. padding: EdgeInsets.only(top: 10, left: 16),
  223. child: Column(
  224. crossAxisAlignment: CrossAxisAlignment.start,
  225. children: [
  226. Text('1. ${list['servantNameStart']??'-'}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
  227. Column(
  228. crossAxisAlignment: CrossAxisAlignment.start,
  229. children: List.generate(list['_collaboratorDataFilter'].length, (i) {
  230. return Text('${i+2}. ${list['_collaboratorDataFilter'][i]['name']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis);
  231. }),
  232. )
  233. ],
  234. ),
  235. )
  236. ],
  237. ) : textHorizontal(
  238. list['currentState'] == 'DIBATALKAN'?'canceledBy'.tr():'servant'.tr(),
  239. list['currentState'] == 'DIBATALKAN'?list['servantNameCancel']??'-':list['currentState'] != 'DIANTRIKAN' && list['currentState'] != 'DIPROSES'?list['servantNameStart']??'-':'-'
  240. ),
  241. SizedBox(height: 16),
  242. divider(opacity: 0.05),
  243. SizedBox(height: 16),
  244. Text('timeline'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
  245. SizedBox(height: 16),
  246. Column(
  247. crossAxisAlignment: CrossAxisAlignment.start,
  248. children: [
  249. timeline('stateRequested'.tr(), list['datetimeRequest'] != null ? convertDate(list['datetimeRequest'], context.locale.toString()) : '-', null, null, current: list['currentState'] == 'DIANTRIKAN' || list['currentState'] == 'DIPROSES', first: true),
  250. list['currentState'] == 'DIMULAI' ? timeline('stateDone'.tr(), list['datetimeStart'] != null ? convertDate(list['datetimeStart'], context.locale.toString()) : '-', list['noteStart'], list['noteStartTranslate'], current: list['currentState'] == 'DIMULAI') : Container(),
  251. !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(),
  252. !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(),
  253. 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(),
  254. list['currentState'] == 'DIBATALKAN' ? timeline('stateCanceled'.tr(), list['datetimeCancel'] != null ? convertDate(list['datetimeCancel'], context.locale.toString()) : '-', list['noteCancel'], list['noteCancelTranslate'], current: list['currentState'] == 'DIBATALKAN') : Container(),
  255. ],
  256. ),
  257. suspendPanel(list),
  258. finish_att_new(list),
  259. !list['autoResponse'] && (list['currentState'] == 'TUNTAS' || list['currentState'] == 'DISELESAIKAN') ? Column(
  260. crossAxisAlignment: CrossAxisAlignment.start,
  261. children: [
  262. SizedBox(height: 16),
  263. divider(opacity: 0.05),
  264. U.newServerVersion(1716187681) && list['action'] != null && list['action'] != '' ? Column(
  265. children: [
  266. SizedBox(height: 16),
  267. textHorizontal('action'.tr(), list['action'])
  268. ],
  269. ) : Container(),
  270. SizedBox(height: 16),
  271. Row(
  272. children: [
  273. Text('rate'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14)),
  274. Expanded(
  275. child: list['satisfactionRate'] > 0 ? Row(
  276. mainAxisAlignment: MainAxisAlignment.end,
  277. children: [
  278. Text(rating[list['satisfactionRate']-1]['label'].toString(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
  279. SizedBox(width: 5),
  280. Image(image: AssetImage(rating[list['satisfactionRate']-1]['image'].toString()), width: 25),
  281. ],
  282. ) : Text('unrated'.tr(), style: TextStyle(color: textColor, fontSize: 14), textAlign: TextAlign.end, overflow: TextOverflow.ellipsis),
  283. ),
  284. ],
  285. ),
  286. list['satisfactionRate'] > 0 ? Builder(
  287. builder: (context) {
  288. var aspect = '';
  289. if(context.locale.toString() == 'id' && list['ratingAspectId'] != null){
  290. aspect = list['ratingAspectId'];
  291. } else if(context.locale.toString() == 'en' && list['ratingAspectEn'] != null){
  292. aspect = list['ratingAspectEn'];
  293. } else if(list['ratingAspectOri'] != null){
  294. aspect = list['ratingAspectOri'];
  295. }
  296. return aspect.isNotEmpty ? Column(
  297. children: [
  298. SizedBox(height: 16),
  299. textHorizontal('ratingAspect'.tr(), aspect)
  300. ],
  301. ) : Container();
  302. },
  303. ) : Container(),
  304. ],
  305. ) : Container()
  306. ],
  307. ),
  308. ),
  309. )
  310. ],
  311. )
  312. ],
  313. ) : loadingTemplate(),
  314. ),
  315. ),
  316. list.isEmpty || list['autoResponse'] || list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS' || list['currentState'] == 'DIBATALKAN' ? Container() : Container(
  317. margin: EdgeInsets.symmetric(horizontal: 100),
  318. padding: EdgeInsets.symmetric(vertical: 25),
  319. decoration: BoxDecoration(border: Border(top: BorderSide(color: textColor.withValues(alpha: 0.05)))),
  320. child: list['currentState'] == 'DIMULAI' && list['_canNotCancel'] ? Text('canNotCancel2'.tr(), style: TextStyle(color: textColor, fontSize: 14), textAlign: TextAlign.center) : Row(
  321. crossAxisAlignment: CrossAxisAlignment.start,
  322. children: [
  323. Expanded(
  324. child: SizedBox(
  325. width: double.infinity,
  326. child: TextField(
  327. controller: controllerNote,
  328. style: const TextStyle(fontSize: 14, color: Colors.black),
  329. maxLength: 128,
  330. decoration: InputDecoration(
  331. counterText: '',
  332. counterStyle: TextStyle(color: Colors.transparent, fontSize: 0),
  333. hintText: 'addNoteCancel'.tr(),
  334. hintStyle: TextStyle(color: textColor.withValues(alpha: 0.5), fontSize: 14),
  335. filled: true,
  336. fillColor: backgroundColor,
  337. hoverColor: Colors.black.withValues(alpha: 0.1),
  338. contentPadding: EdgeInsets.symmetric(vertical: 17, horizontal: 20),
  339. prefixIcon: Padding(padding: EdgeInsets.only(left: 20, right: 20), child: U.iconsax('edit-2', color: textColor, size: 24)),
  340. border: InputBorder.none,
  341. enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color(0xff262626).withValues(alpha: 0.5))),
  342. focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: primaryColor)),
  343. isDense: true
  344. ),
  345. ),
  346. ),
  347. ),
  348. SizedBox(width: 30),
  349. 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))
  350. ],
  351. ),
  352. )
  353. ],
  354. ),
  355. );
  356. }
  357. Widget renderRequested(){
  358. if(list['receptionistId'] != null){
  359. if(user['roomAttendant'] != null && user['roomAttendant'] && user['userId'] != list['informantUserId'] && user['userId'] == list['receptionistId']){
  360. return Column(
  361. children: [
  362. textHorizontal('requestedFor'.tr(), list['informantName']??'-'),
  363. SizedBox(height: 16),
  364. ],
  365. );
  366. }
  367. if(user['userId'] == list['informantUserId'] && user['userId'] != list['receptionistId']){
  368. return Column(
  369. children: [
  370. textHorizontal('requestedBy'.tr(), list['receptionistName']??'-'),
  371. SizedBox(height: 16),
  372. ],
  373. );
  374. }
  375. }
  376. return Container();
  377. }
  378. Widget attachment_new(list){
  379. List imageList = [];
  380. if(list['_attachment'] != null){
  381. for(var i = 1; i <= 5; i++){
  382. if(list['_attachment']['_mobileRequestAtt$i'] != null){
  383. imageList.add({'thumb': list['_attachment']['_mobileRequestThumb$i'], 'image': list['_attachment']['_mobileRequestAtt$i']});
  384. }
  385. }
  386. }
  387. return imageList.length > 0 ? Column(
  388. crossAxisAlignment: CrossAxisAlignment.start,
  389. children: [
  390. Text('image'.tr(), style: TextStyle(color: textColor)),
  391. SizedBox(height: 5),
  392. LayoutBuilder(
  393. builder: (context, constraints) {
  394. var imageWidth = ((constraints.maxWidth-32)/5)-5;
  395. return Row(
  396. children: List.generate(imageList.length, (i){
  397. return GestureDetector(
  398. child: Container(
  399. width: imageWidth, height: imageWidth, alignment: Alignment.topRight,
  400. margin: EdgeInsets.only(right: i == 4 ? 0 : 6),
  401. decoration: BoxDecoration(
  402. color: Colors.black12, borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(color: Colors.black26, width: 0.5),
  403. 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)
  404. ),
  405. ),
  406. onTap: ()=>navigateTo(context, PhotoPreviewGallery(title: 'image'.tr(), imageList: imageList, startIndex: i))
  407. );
  408. }),
  409. );
  410. },
  411. )
  412. ],
  413. ) : textVertical('image'.tr(), 'noImgAttach'.tr());
  414. }
  415. Widget finish_att_new(list){
  416. List imageList = [];
  417. if((list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS')){
  418. if(list['_attachment'] != null){
  419. for(var i = 1; i <= 5; i++){
  420. if(list['_attachment']['_mobileFinishAtt$i'] != null){
  421. imageList.add({'thumb': list['_attachment']['_mobileFinishThumb$i'], 'image': list['_attachment']['_mobileFinishAtt$i']});
  422. }
  423. }
  424. }
  425. }
  426. return imageList.length > 0 ? Column(
  427. crossAxisAlignment: CrossAxisAlignment.start,
  428. children: [
  429. SizedBox(height: 16),
  430. Text('finishAttachment'.tr(), style: TextStyle(color: textColor)),
  431. SizedBox(height: 8),
  432. LayoutBuilder(
  433. builder: (context, constraints) {
  434. var imageWidth = ((constraints.maxWidth-32)/5)-5;
  435. return Row(
  436. children: List.generate(imageList.length, (i){
  437. return GestureDetector(
  438. child: Container(
  439. width: imageWidth, height: imageWidth, alignment: Alignment.topRight,
  440. margin: EdgeInsets.only(right: i == 4 ? 0 : 6),
  441. decoration: BoxDecoration(
  442. color: Colors.black12, borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(color: Colors.black26, width: 0.5),
  443. 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)
  444. ),
  445. ),
  446. onTap: ()=>navigateTo(context, PhotoPreviewGallery(title: 'finishAttachment'.tr(), imageList: imageList, startIndex: i))
  447. );
  448. }),
  449. );
  450. },
  451. )
  452. ],
  453. ) : Container();
  454. }
  455. Widget suspendPanel(list){
  456. var activeHold = list['_activeHoldRequest'];
  457. var requestHold = list['_holdRequest'];
  458. return Column(
  459. children: [
  460. activeHold != null ? Padding(padding: EdgeInsets.symmetric(vertical: 15), child: divider(opacity: 0.05)) : Container(),
  461. activeHold != null ? Column(
  462. children: [
  463. Row(
  464. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  465. children: [
  466. Text('state'.tr(), style: TextStyle(fontSize: 14, color: textColor)),
  467. Container(
  468. child: Row(
  469. mainAxisAlignment: MainAxisAlignment.end,
  470. children: [
  471. Image(image: AssetImage('assets/image/general/Watch.png'), width: 20, height: 20),
  472. SizedBox(width: 5),
  473. Text('hold'.tr(), style: TextStyle(fontSize: 14, color: primaryColor)),
  474. ],
  475. ),
  476. )
  477. ],
  478. ),
  479. SizedBox(height: 16),
  480. textHorizontal('description'.tr(), activeHold['description'])
  481. ],
  482. ) : Container(),
  483. requestHold == null || requestHold.length == 0 || (requestHold.length == 1 && requestHold[0]['datetimeEnd'] == null) ? Container() : Column(
  484. crossAxisAlignment: CrossAxisAlignment.start,
  485. children: [
  486. Padding(padding: EdgeInsets.symmetric(vertical: 15), child: divider(opacity: 0.05)),
  487. Text('holdHistory'.tr(), style: TextStyle(fontSize: 14, color: Colors.black)),
  488. SizedBox(height: 5),
  489. Column(
  490. children: List.generate(requestHold.length, (i) {
  491. return requestHold[i]['datetimeEnd'] != null ? Container(
  492. padding: EdgeInsets.symmetric(vertical: 5),
  493. child: Row(
  494. crossAxisAlignment: CrossAxisAlignment.start,
  495. children: [
  496. Text('${i+1}.', style: TextStyle(fontSize: 14, color: Colors.black)),
  497. SizedBox(width: 5),
  498. Expanded(
  499. child: Column(
  500. crossAxisAlignment: CrossAxisAlignment.start,
  501. children: [
  502. Text(requestHold[i]['description'], style: TextStyle(fontSize: 14, color: Colors.black), textAlign: TextAlign.start),
  503. SizedBox(height: 3),
  504. 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))
  505. ],
  506. ),
  507. )
  508. ],
  509. ),
  510. ) : Container();
  511. }),
  512. )
  513. ],
  514. )
  515. ],
  516. );
  517. }
  518. Widget timeline(label, text, note, noteTranslate, {bool first = false, bool current = false}) {
  519. return TimelineTile(
  520. nodeAlign: TimelineNodeAlign.start,
  521. contents: Container(
  522. margin: EdgeInsets.fromLTRB(15, 10, 0, 10),
  523. child: Row(
  524. children: [
  525. Text(label, style: TextStyle(fontSize: 14, color: textColor.withValues(alpha: 0.85))),
  526. SizedBox(width: 5),
  527. Expanded(
  528. child: Column(
  529. crossAxisAlignment: CrossAxisAlignment.end,
  530. children: [
  531. Text(text, style: TextStyle(fontSize: 13, color: textColor)),
  532. note != null && note != '' ? Text(note, style: TextStyle(fontSize: 12, color: primaryColor), textAlign: TextAlign.right) : Container(),
  533. U.autoTranslate() && noteTranslate != null && noteTranslate != '' && note != noteTranslate ? Container(
  534. margin: EdgeInsets.only(top: 1), decoration: BoxDecoration(border: Border(top: BorderSide(color: primaryColor.withValues(alpha: 0.3)))),
  535. child: Text('($noteTranslate)', style: TextStyle(fontSize: 12, color: primaryColor.withValues(alpha: 0.7), fontStyle: FontStyle.italic), textAlign: TextAlign.right),
  536. ) : Container(),
  537. ],
  538. ),
  539. ),
  540. ],
  541. ),
  542. ),
  543. node: TimelineNode(
  544. indicator: DotIndicator(color: current ? primaryColor : Color(0xffE8E8E8)),
  545. startConnector: SolidLineConnector(color: first ? Colors.transparent : Color(0xffE8E8E8)),
  546. endConnector: SolidLineConnector(color: current ? Colors.transparent : Color(0xffE8E8E8)),
  547. ),
  548. );
  549. }
  550. }