request_success.dart 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'package:easy_localization/easy_localization.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter_linkify/flutter_linkify.dart';
  6. import 'package:open_file/open_file.dart';
  7. import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
  8. import 'package:telnow_mobile_new/src/layouts/components/template.dart';
  9. import 'package:telnow_mobile_new/src/utils/U.dart';
  10. import 'package:url_launcher/url_launcher.dart';
  11. class WebReqSuccessPage extends StatefulWidget {
  12. Map<String, dynamic> user;
  13. final String ticketNo;
  14. final bool fromSearch;
  15. WebReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
  16. @override
  17. State<WebReqSuccessPage> createState() => _WebReqSuccessPageState();
  18. }
  19. class _WebReqSuccessPageState extends State<WebReqSuccessPage> {
  20. final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
  21. Map<String, dynamic> list = {};
  22. @override
  23. void initState() {
  24. getMission();
  25. // TODO: implement initState
  26. super.initState();
  27. }
  28. getMission() async{
  29. var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null, context);
  30. if(data != null) {
  31. if (data['datetimeScheduled'] != null && data['datetimeScheduled'] != '' && data['noteFormat'] == 'DATE') {
  32. var date = data['datetimeScheduled'];
  33. data['datetimeScheduled'] = DateFormat('dd MMM yyyy HH:mm', 'id').format(DateTime.parse(date));
  34. }
  35. if (data['noteStart'] == 'escalationNoteBySystem'){
  36. data['noteStart'] = 'escalationNoteBySystem'.tr();
  37. }
  38. setState((){
  39. list = data;
  40. });
  41. }
  42. }
  43. backAction(){
  44. int count = 0;
  45. int page = widget.fromSearch ? 3 : 2;
  46. Navigator.of(context).popUntil((_) => count++ >= page);
  47. }
  48. @override
  49. Widget build(BuildContext context) {
  50. return Scaffold(
  51. backgroundColor: backgroundColor,
  52. appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)),
  53. body: Column(
  54. children: [
  55. Container(
  56. width: double.infinity,
  57. padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100),
  58. child: Text('detail'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  59. ),
  60. divider(),
  61. Expanded(
  62. child: list.isNotEmpty?SingleChildScrollView(
  63. padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100),
  64. child: Column(
  65. crossAxisAlignment: CrossAxisAlignment.start,
  66. children: [
  67. Text(list[U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w600)),
  68. SizedBox(height: 16),
  69. Row(
  70. crossAxisAlignment: CrossAxisAlignment.start,
  71. children: [
  72. Expanded(
  73. child: Container(
  74. padding: EdgeInsets.all(20),
  75. decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
  76. child: Column(
  77. crossAxisAlignment: CrossAxisAlignment.start,
  78. children: [
  79. Row(
  80. children: [
  81. imageTiles(imageUrl: list['_requestImage'] ?? "null", width: 110, height: 90),
  82. SizedBox(width: 20),
  83. Expanded(
  84. child: Column(
  85. crossAxisAlignment: CrossAxisAlignment.start,
  86. children: [
  87. Text(list[U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), maxLines: 2, overflow: TextOverflow.ellipsis),
  88. dashed(),
  89. Text(list[U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), maxLines: 3, overflow: TextOverflow.ellipsis)
  90. ],
  91. ),
  92. ),
  93. ],
  94. ),
  95. SizedBox(height: 20),
  96. textHorizontal('ticketNumber'.tr(), list['ticketNo'], copy: true),
  97. SizedBox(height: 16),
  98. renderRequested(),
  99. textHorizontal('location'.tr(), list['ipphoneExtLocation']),
  100. SizedBox(height: 16),
  101. divider(opacity: 0.05),
  102. SizedBox(height: 16),
  103. list['autoResponse'] ? Column(
  104. crossAxisAlignment: CrossAxisAlignment.start,
  105. children: [
  106. Text('note'.tr(), style: TextStyle(color: textColor)),
  107. SizedBox(height: 5),
  108. list['responseText']!=null&&list['responseText']!=''?Linkify(
  109. text: list['responseText'], style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300),
  110. onOpen: (link) async {
  111. if (await canLaunchUrl(Uri.parse(link.url))) {
  112. await launchUrl(Uri.parse(link.url));
  113. }
  114. },
  115. ):Container(),
  116. list['responseAttachment']!=null&&list['responseAttachment']!=''?list['_isPdf']?GestureDetector(
  117. child: Container(
  118. width: double.infinity,
  119. margin: EdgeInsets.only(top: 8),
  120. decoration: BoxDecoration(
  121. color: Colors.white,
  122. border: Border.all(color: Colors.deepOrange),
  123. borderRadius: BorderRadius.all(Radius.circular(12)),
  124. ),
  125. child: Center(
  126. child: Column(
  127. children: [
  128. Icon(Icons.picture_as_pdf, color: Colors.deepOrange, size: 50),
  129. Text('seeAttachment'.tr(), style: TextStyle(color: Colors.black45, fontSize: 12))
  130. ],
  131. ),
  132. )
  133. ),
  134. onTap: () async{
  135. var filePath = await token.getPath()+'/TelNow/Files/';
  136. if(!File(filePath+getFileName(list['responseAttachment'])).existsSync()){
  137. // print("file isnot ready yet");
  138. await apiAuthProvider.downloadImage(list['responseAttachment'], filePath+getFileName(list['responseAttachment']));
  139. }
  140. await OpenFile.open(filePath+getFileName(list['responseAttachment']));
  141. }
  142. ):LayoutBuilder(
  143. builder: (context, constraints) {
  144. return GestureDetector(
  145. child: Container(
  146. child:Image.network(list['_mobileResponseAttachment'], fit: BoxFit.cover, width: double.infinity, height: constraints.maxWidth/(1.7), loadingBuilder:(BuildContext? context, Widget? child,ImageChunkEvent? loadingProgress) {
  147. if (loadingProgress == null) return child!;
  148. return Container(
  149. height: constraints.maxWidth/(1.7),
  150. child: Center(
  151. child: CircularProgressIndicator(
  152. value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null,
  153. ),
  154. ),
  155. );
  156. },
  157. ),
  158. ),
  159. onTap: ()=>navigateTo(context, PhotoPreview('image'.tr(), list['_mobileResponseAttachment'], true))
  160. );
  161. },
  162. ):Container()
  163. ],
  164. ) : attachment_new(list),
  165. list['autoResponse'] ? Container() : SizedBox(height: 16),
  166. list['autoResponse'] ? Container() : textVertical('note'.tr(), list['requestNote'] != '' ? list['requestNote'] : '-'),
  167. list['autoResponse'] ? Container() : SizedBox(height: 16),
  168. list['autoResponse'] || list['datetimeScheduled'] == null || list['datetimeScheduled'] == '' ? Container() : Text("${"scheduleMessage".tr()} ${list['datetimeScheduled']}."),
  169. ],
  170. ),
  171. ),
  172. ),
  173. SizedBox(width: 30),
  174. Expanded(
  175. child: Column(
  176. crossAxisAlignment: CrossAxisAlignment.start,
  177. children: [
  178. infoContainer('reqSuccessMsg'.tr()),
  179. SizedBox(height: 16),
  180. buttonTemplate(text: 'btnWait'.tr(), action: ()=>backAction(), width: double.infinity)
  181. ],
  182. )
  183. )
  184. ],
  185. )
  186. ],
  187. ),
  188. ):loadingTemplate(),
  189. )
  190. ],
  191. ),
  192. );
  193. }
  194. Widget renderRequested(){
  195. if(list['receptionistId'] != null){
  196. if(widget.user['roomAttendant'] && widget.user['userId'] != list['informantUserId'] && widget.user['userId'] == list['receptionistId']){
  197. return Column(
  198. children: [
  199. textHorizontal('requestedFor'.tr(), list['informantName']??'-'),
  200. SizedBox(height: 16),
  201. ],
  202. );
  203. }
  204. if(widget.user['userId'] == list['informantUserId'] && widget.user['userId'] != list['receptionistId']){
  205. return Column(
  206. children: [
  207. textHorizontal('requestedBy'.tr(), list['receptionistName']??'-'),
  208. SizedBox(height: 16),
  209. ],
  210. );
  211. }
  212. }
  213. return Container();
  214. }
  215. getFileName(pdfUrl){
  216. var imgSplit = pdfUrl.toString().split('/');
  217. return imgSplit[imgSplit.length-1];
  218. }
  219. Widget attachment_new(list){
  220. List imageList = [];
  221. if(list['_attachment'] != null){
  222. for(var i = 1; i <= 5; i++){
  223. if(list['_attachment']['_mobileRequestAtt$i'] != null){
  224. imageList.add({'thumb': list['_attachment']['_mobileRequestThumb$i'], 'image': list['_attachment']['_mobileRequestAtt$i']});
  225. }
  226. }
  227. }
  228. return imageList.length > 0 ? Column(
  229. crossAxisAlignment: CrossAxisAlignment.start,
  230. children: [
  231. Text('image'.tr(), style: TextStyle(color: textColor)),
  232. SizedBox(height: 5),
  233. LayoutBuilder(
  234. builder: (context, constraints) {
  235. var imageWidth = ((constraints.maxWidth-32)/5)-5;
  236. return Row(
  237. children: List.generate(imageList.length, (i){
  238. return GestureDetector(
  239. child: Container(
  240. width: imageWidth, height: imageWidth, alignment: Alignment.topRight,
  241. margin: EdgeInsets.only(right: i == 4 ? 0 : 6),
  242. decoration: BoxDecoration(
  243. color: Colors.black12, borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(color: Colors.black26, width: 0.5),
  244. 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)
  245. ),
  246. ),
  247. onTap: ()=>navigateTo(context, PhotoPreviewGallery(title: 'image'.tr(), imageList: imageList, startIndex: i))
  248. );
  249. }),
  250. );
  251. },
  252. )
  253. ],
  254. ) : textVertical('image'.tr(), 'noImgAttach'.tr());
  255. }
  256. }
  257. // -----------------------------------------------------------------------------------------------------------------------------
  258. class WebReqSuccessPendingPage extends StatefulWidget {
  259. Map<String, dynamic> data;
  260. final bool fromSearch;
  261. WebReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
  262. @override
  263. State<WebReqSuccessPendingPage> createState() => _WebReqSuccessPendingPageState();
  264. }
  265. class _WebReqSuccessPendingPageState extends State<WebReqSuccessPendingPage> {
  266. @override
  267. Widget build(BuildContext context) {
  268. return Scaffold(
  269. backgroundColor: backgroundColor,
  270. appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)),
  271. body: Column(
  272. children: [
  273. Container(
  274. width: double.infinity,
  275. padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100),
  276. child: Text('detail'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  277. ),
  278. divider(),
  279. Expanded(
  280. child: SingleChildScrollView(
  281. padding: EdgeInsets.symmetric(vertical: 25, horizontal: 100),
  282. child: widget.data.isNotEmpty?Column(
  283. crossAxisAlignment: CrossAxisAlignment.start,
  284. children: [
  285. Text(widget.data['title'], style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w600)),
  286. SizedBox(height: 16),
  287. Row(
  288. crossAxisAlignment: CrossAxisAlignment.start,
  289. children: [
  290. Container(
  291. padding: EdgeInsets.all(20),
  292. decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
  293. child: Column(
  294. crossAxisAlignment: CrossAxisAlignment.start,
  295. children: [
  296. Row(
  297. children: [
  298. imageTiles(imageUrl: widget.data['image'] ?? "null", width: 110, height: 90),
  299. SizedBox(width: 20),
  300. Expanded(
  301. child: Column(
  302. crossAxisAlignment: CrossAxisAlignment.start,
  303. children: [
  304. Text(widget.data[U.langColumn(context, 'sub')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), maxLines: 2, overflow: TextOverflow.ellipsis),
  305. dashed(),
  306. Text(widget.data[U.langColumn(context, 'desc')]??'', style: TextStyle(color: textColor), maxLines: 3, overflow: TextOverflow.ellipsis)
  307. ],
  308. ),
  309. ),
  310. ],
  311. ),
  312. SizedBox(height: 20),
  313. widget.data['others']?Column(
  314. children: [
  315. textHorizontal('requestedFor'.tr(), widget.data['data']['user_id']),
  316. SizedBox(height: 16),
  317. ],
  318. ):Container(),
  319. textHorizontal('location'.tr(), widget.data['location']),
  320. SizedBox(height: 16),
  321. divider(opacity: 0.05),
  322. SizedBox(height: 16),
  323. attachment_new(widget.data['imageList']),
  324. SizedBox(height: 16),
  325. textVertical('note'.tr(), widget.data['note'] != '' ? widget.data['note'] : '-'),
  326. ],
  327. ),
  328. ),
  329. SizedBox(width: 30),
  330. Expanded(
  331. child: Column(
  332. crossAxisAlignment: CrossAxisAlignment.start,
  333. children: [
  334. Container(
  335. width: double.infinity,
  336. padding: EdgeInsets.all(12),
  337. child: Column(
  338. crossAxisAlignment: CrossAxisAlignment.center,
  339. children: [
  340. Icon(Icons.warning_amber_rounded, color: Colors.red),
  341. SizedBox(height: 5),
  342. Text('pending_info_success'.tr(), style: TextStyle(color: textColor), textAlign: TextAlign.center)
  343. ],
  344. ),
  345. decoration: BoxDecoration(
  346. color: Colors.red.withValues(alpha: 0.1),
  347. border: Border.all(color: Colors.red),
  348. borderRadius: BorderRadius.all(Radius.circular(12))
  349. ),
  350. ),
  351. SizedBox(height: 16),
  352. buttonTemplate(text: 'btnWait'.tr(), action: ()=>backAction(), width: double.infinity)
  353. ],
  354. )
  355. )
  356. ],
  357. )
  358. ],
  359. ):loadingTemplate(),
  360. ),
  361. )
  362. ],
  363. ),
  364. );
  365. }
  366. Widget attachment_new(List images){
  367. List imageList = [];
  368. images.forEach((element) {
  369. imageList.add(base64Decode(element));
  370. });
  371. return imageList.length > 0 ? Column(
  372. crossAxisAlignment: CrossAxisAlignment.start,
  373. children: [
  374. Text('image'.tr(), style: TextStyle(color: textColor)),
  375. SizedBox(height: 5),
  376. LayoutBuilder(
  377. builder: (context, constraints) {
  378. var imageWidth = ((constraints.maxWidth-32)/5)-5;
  379. return Row(
  380. children: List.generate(imageList.length, (i){
  381. return GestureDetector(
  382. child: Container(
  383. width: imageWidth, height: imageWidth, alignment: Alignment.topRight,
  384. margin: EdgeInsets.only(right: i == 4 ? 0 : 6),
  385. decoration: BoxDecoration(
  386. color: Colors.black12, borderRadius: BorderRadius.all(Radius.circular(5)), border: Border.all(color: Colors.black26, width: 0.5),
  387. image: DecorationImage(image: MemoryImage(imageList[i]), fit: BoxFit.cover)
  388. ),
  389. ),
  390. onTap: ()=>navigateTo(context, PhotoPreviewGallery(title: 'image'.tr(), imageList: imageList, startIndex: i, isUrl: false))
  391. );
  392. }),
  393. );
  394. },
  395. )
  396. ],
  397. ) : textVertical('image'.tr(), 'noImgAttach'.tr());
  398. }
  399. backAction(){
  400. int count = 0;
  401. int page = widget.fromSearch ? 3 : 2;
  402. Navigator.of(context).popUntil((_) => count++ >= page);
  403. }
  404. }