menu_home.dart 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  1. import 'package:cached_network_image/cached_network_image.dart';
  2. import 'package:carousel_slider/carousel_slider.dart';
  3. import 'package:drag_and_drop_lists/drag_and_drop_lists.dart';
  4. import 'package:easy_localization/easy_localization.dart';
  5. import 'package:easy_refresh/easy_refresh.dart';
  6. import 'package:firebase_messaging/firebase_messaging.dart';
  7. import 'package:flutter/foundation.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:flutter_cache_manager/flutter_cache_manager.dart';
  10. import 'package:flutter_linkify/flutter_linkify.dart';
  11. import 'package:provider/provider.dart';
  12. import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
  13. import 'package:telnow_mobile_new/src/injector/injector.dart';
  14. import 'package:telnow_mobile_new/src/layouts/functions/home.dart';
  15. import 'package:permission_handler/permission_handler.dart';
  16. import 'package:telnow_mobile_new/src/layouts/mobile/banner_detail.dart';
  17. import 'package:telnow_mobile_new/src/layouts/mobile/message_list.dart';
  18. import 'package:telnow_mobile_new/src/layouts/mobile/request_create.dart';
  19. import 'package:telnow_mobile_new/src/layouts/mobile/request_select.dart';
  20. import 'package:telnow_mobile_new/src/layouts/components/template.dart';
  21. import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
  22. import 'package:telnow_mobile_new/src/utils/C.dart';
  23. import 'package:telnow_mobile_new/src/utils/U.dart';
  24. import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
  25. import 'package:telnow_mobile_new/src/utils/provider.dart';
  26. import 'package:url_launcher/url_launcher.dart';
  27. class MobHomePage extends StatefulWidget {
  28. const MobHomePage({super.key});
  29. @override
  30. State<MobHomePage> createState() => _MobHomePageState();
  31. }
  32. class _MobHomePageState extends State<MobHomePage> with WidgetsBindingObserver {
  33. final HomeFunction homeFunc = HomeFunction();
  34. final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
  35. bool _timeLimit = false;
  36. String _localeIndex = 'id';
  37. @override
  38. void initState() {
  39. Provider.of<ServiceModule>(context, listen: false).reset();
  40. Provider.of<UserModule>(context, listen: false).reset();
  41. WidgetsBinding.instance.addPostFrameCallback((_) {
  42. Future.delayed(Duration(seconds: 1), (){
  43. homeFunc.getProfileData(context);
  44. });
  45. FirebaseMessaging.onMessage.listen((event) {
  46. if(event.data['type'] == 'MESSAGE'){
  47. homeFunc.getUnreadMessages(context);
  48. }
  49. });
  50. });
  51. WidgetsBinding.instance.addObserver(this);
  52. checkCompatibility();
  53. // TODO: implement initState
  54. super.initState();
  55. }
  56. checkCompatibility() async{
  57. if(await U.isCompatibleWith(VersionKey.multiBahasa)) {
  58. homeFunc.getContactCenter(context);
  59. }
  60. }
  61. @override
  62. void dispose() {
  63. WidgetsBinding.instance.removeObserver(this);
  64. super.dispose();
  65. }
  66. @override
  67. Widget build(BuildContext context) {
  68. _localeIndex = U.newServerVersion(1754624839) ? U.getLangIndex(context.locale.toString()) : context.locale.toString();
  69. return Provider.of<UserModule>(context).user().isNotEmpty?Scaffold(
  70. backgroundColor: backgroundColor,
  71. body: Column(
  72. children: [
  73. Container(
  74. width: double.infinity,
  75. color: primaryColor,
  76. padding: EdgeInsets.symmetric(horizontal: 16, vertical: 16),
  77. child: SafeArea(
  78. child: Column(
  79. crossAxisAlignment: CrossAxisAlignment.start,
  80. children: [
  81. Row(
  82. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  83. children: [
  84. Text('${Provider.of<UserModule>(context).user()['name']}${(Provider.of<UserModule>(context).user()['guestName'] != null && Provider.of<UserModule>(context).user()['guestName'] != '' ? ' - ${Provider.of<UserModule>(context).user()['guestName']}' : '')}', style: TextStyle(color: Colors.white, fontSize: 18), overflow: TextOverflow.ellipsis),
  85. Row(
  86. children: [
  87. //TODO: tambah pengecekan server version disini
  88. FutureBuilder<bool>(
  89. future: U.isCompatibleWith(VersionKey.multiBahasa),
  90. builder: (context, snapshot) {
  91. if (!snapshot.hasData) {
  92. return SizedBox(); // or a loading spinner
  93. }
  94. final isCompatible = snapshot.data!;
  95. final hasContactCenter = Provider.of<ServiceModule>(context, listen: false)
  96. .contactCenter()
  97. .toString()
  98. .isNotEmpty;
  99. if (isCompatible && hasContactCenter) {
  100. return GestureDetector(
  101. onTap: () => launchUrl(Uri.parse(Provider.of<ServiceModule>(context, listen: false).contactCenter().toString())),
  102. child: U.iconsax('phone-number', color: Colors.white, size: 26.0)
  103. ); // replace with the widget you want to show
  104. } else {
  105. return SizedBox(); // or nothing
  106. }
  107. },
  108. ),
  109. // U.isCompatibleWith(VersionKey.multiBahasa) && Provider.of<ServiceModule>(context, listen: false).contactCenter().toString() != '' ? GestureDetector(
  110. // onTap: () => launchUrl(Uri.parse(Provider.of<ServiceModule>(context, listen: false).contactCenter().toString())),
  111. // child: U.iconsax('phone-number', color: Colors.white, size: 26.0)
  112. // ):SizedBox(),
  113. SizedBox(width: 8),
  114. GestureDetector(
  115. child: Stack(
  116. alignment: Alignment.topRight,
  117. children: [
  118. U.iconsax('sms-notification', color: Colors.white, size: 32.0),
  119. Provider.of<ServiceModule>(context).unreadMessage() ? Container(
  120. width: 13, height: 13, margin: EdgeInsets.only(top: 1.3),
  121. decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.all(Radius.circular(50))),
  122. ):Container()
  123. ],
  124. ),
  125. onTap: ()=>navigateTo(context, MobMessageListPage(Provider.of<UserModule>(context, listen: false).user())).then((_){
  126. homeFunc.getUnreadMessages(context);
  127. }),
  128. )
  129. ],
  130. )
  131. ],
  132. ),
  133. Provider.of<UserModule>(context).profile()['greeting'][context.locale.toString()] != null ? Text(Provider.of<UserModule>(context).profile()['greeting'][context.locale.toString()], style: TextStyle(color: Colors.white, fontSize: 14), overflow: TextOverflow.ellipsis) : Container(),
  134. SizedBox(height: 8),
  135. Row(
  136. children: [
  137. Expanded(
  138. child: GestureDetector(
  139. child: Container(
  140. padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12,),
  141. decoration: BoxDecoration(
  142. color: Colors.white,
  143. borderRadius: BorderRadius.all(Radius.circular(50))
  144. ),
  145. child: Row(
  146. children: [
  147. U.iconsax('search-normal-1', color: textColor),
  148. SizedBox(width: 13),
  149. Expanded(child: Text(Provider.of<UserModule>(context).profile()['searchText'][context.locale.toString()] != null ? Provider.of<UserModule>(context).profile()['searchText'][context.locale.toString()] : 'searchAsk'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.5), fontSize: 14), overflow: TextOverflow.ellipsis))
  150. ],
  151. ),
  152. ),
  153. onTap: () => navigateTo(context, MobReqSelectPage(user: Provider.of<UserModule>(context, listen: false).user(), title: 'search'.tr(), scope: Provider.of<ServiceModule>(context, listen: false).scoopeValue(), placeholder: Provider.of<UserModule>(context, listen: false).profile()['searchText'][context.locale.toString()] != null ? Provider.of<UserModule>(context, listen: false).profile()['searchText'][context.locale.toString()] : 'searchAsk'.tr())).then((val){
  154. val = val??true;
  155. if(val) homeFunc.getFrequentlyRequested(context);
  156. }),
  157. ),
  158. ),
  159. ],
  160. )
  161. ],
  162. ),
  163. ),
  164. ),
  165. Expanded(
  166. child: Container(
  167. alignment: Alignment.topCenter,
  168. width: U.bodyWidth(context),
  169. child: EasyRefresh(
  170. triggerAxis: Axis.vertical,
  171. header: MaterialHeader(clamping: true, color: primaryColor),
  172. onRefresh: ()=>homeFunc.onRefresh(context),
  173. child: SingleChildScrollView(
  174. child: Column(
  175. crossAxisAlignment: CrossAxisAlignment.start,
  176. children: [
  177. dndStatus(),
  178. Container(
  179. margin: EdgeInsets.fromLTRB(16, 16, 16, 16),
  180. child: Row(
  181. children: [
  182. GestureDetector(child: Icon(Icons.info_outline, color: Color(0xff0021CE), size: 20), onTap: () => showSuccess(context, "scopeDesc".tr()),),
  183. SizedBox(width: 8),
  184. Text('scope'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
  185. SizedBox(width: 16),
  186. GestureDetector(
  187. child: Container(
  188. padding: EdgeInsets.fromLTRB(16, 4, 16, 4),
  189. child: Row(
  190. children: [
  191. Text(Provider.of<ServiceModule>(context).scoopeName(), style: TextStyle(color: primaryColor, fontSize: 14)),
  192. SizedBox(width: 8),
  193. U.iconsax('arrow-down-1', color: primaryColor, size: 14),
  194. ],
  195. ),
  196. decoration: BoxDecoration(
  197. color: primaryColor.withValues(alpha: 0.1),
  198. borderRadius: BorderRadius.all(Radius.circular(50))
  199. ),
  200. ),
  201. onTap: (){
  202. if(Provider.of<ServiceModule>(context, listen: false).scoope().isNotEmpty){
  203. showModalBottomSheet<void>(
  204. context: context,
  205. backgroundColor: Colors.white,
  206. builder: (BuildContext contexts) {
  207. return SafeArea(
  208. child: Column(
  209. mainAxisSize: MainAxisSize.min,
  210. children: [
  211. Container(
  212. margin: EdgeInsets.symmetric(vertical: 16),
  213. child: Text('changeScope'.tr(), style: TextStyle(color: textColor, fontSize: 14), textAlign: TextAlign.center),
  214. ),
  215. divider(),
  216. SizedBox(height: 16),
  217. Column(
  218. children: List.generate(Provider.of<ServiceModule>(context).getScoopeLength(), (i){
  219. return GestureDetector(
  220. child: Container(
  221. color: Colors.white,
  222. padding: EdgeInsets.symmetric(vertical: 16, horizontal: 16),
  223. child: Row(
  224. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  225. children: [
  226. Text(Provider.of<ServiceModule>(context).scoope()[i]['value'], style: TextStyle(color: textColor, fontSize: 14)),
  227. Icon(Provider.of<ServiceModule>(context).checkScoope(Provider.of<ServiceModule>(context).scoope()[i]['key'])?Icons.radio_button_checked:Icons.radio_button_off, color: Provider.of<ServiceModule>(context).checkScoope(Provider.of<ServiceModule>(context).scoope()[i]['key'])?primaryColor:Colors.black45, size: 22),
  228. ],
  229. ),
  230. ),
  231. onTap: () async{
  232. await sharedPreferencesManager.putString(SharedPreferencesManager.keyScoope, Provider.of<ServiceModule>(context, listen: false).scoope()[i]['key']);
  233. Provider.of<ServiceModule>(context, listen: false).setScoopeValue(Provider.of<ServiceModule>(context, listen: false).scoope()[i]['key']);
  234. Provider.of<ServiceModule>(context, listen: false).setScoopeName(Provider.of<ServiceModule>(context, listen: false).scoope()[i]['value']);
  235. Navigator.of(contexts).pop();
  236. var profile = Provider.of<UserModule>(context, listen: false).profile();
  237. if(profile['topMenu']['show'] != null && profile['topMenu']['show'] == true) {
  238. if(U.newServerVersion(1709864293)){
  239. homeFunc.getTopMenuNew(context);
  240. }
  241. else{
  242. homeFunc.getTopMenu(context);
  243. homeFunc.getReqGroup(context);
  244. }
  245. }
  246. if(profile['specialOffer'] != null && profile['specialOffer']['show'] == true) homeFunc.getSpecialOffer(context);
  247. if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) homeFunc.getFrequentlyRequested(context);
  248. if(profile['banner'] != null && profile['banner']['show'] == true) homeFunc.getBanner(context);
  249. if(profile['quickAction'] != null && profile['quickAction']['show'] == true) homeFunc.getQuickAction(context);
  250. }
  251. );
  252. }),
  253. ),
  254. SizedBox(height: 16),
  255. ],
  256. ),
  257. );
  258. },
  259. );
  260. }
  261. },
  262. )
  263. ],
  264. ),
  265. ),
  266. Provider.of<UserModule>(context).profile()['topMenu']['show'] != null && Provider.of<UserModule>(context).profile()['topMenu']['show'] == true ? Container(
  267. margin: EdgeInsets.symmetric(horizontal: 16),
  268. child: Provider.of<ServiceModule>(context).topMenu() != null && Provider.of<ServiceModule>(context).reqGroup() != null?Column(
  269. children: List.generate(((Provider.of<ServiceModule>(context).topMenu()!.length+1)/4).ceil(), (i){
  270. double size = U.bodyWidth(context)/6;
  271. return Row(
  272. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  273. crossAxisAlignment: CrossAxisAlignment.start,
  274. children: List.generate(4, (j){
  275. int index = i==0?j:j+(4*i);
  276. bool hideOther = index == Provider.of<ServiceModule>(context).topMenu()!.length && U.servantDisplay() && Provider.of<ServiceModule>(context).reqGroup()!.length == 0;
  277. return index <= Provider.of<ServiceModule>(context).topMenu()!.length ? GestureDetector(
  278. child: Column(
  279. children: [
  280. i>0?SizedBox(height: hideOther ? 0:16):Container(),
  281. hideOther ? Container(width: size) : index == Provider.of<ServiceModule>(context).topMenu()!.length ? Container(
  282. child: Container(
  283. padding: EdgeInsets.all(5),
  284. child: SizedBox(
  285. width: size, height: size,
  286. child: U.iconsax('category', color: Color(0xff564F4F), size: kIsWeb?40:34),
  287. ),
  288. decoration: BoxDecoration(
  289. color: Colors.white,
  290. borderRadius: BorderRadius.all(Radius.circular(20)),
  291. boxShadow: [BoxShadow(color: Colors.grey.withValues(alpha: 0.3), blurRadius: 3, offset: Offset(0, 1))]
  292. ),
  293. ),
  294. ):categoryContainer(context: context, iconUrl: Provider.of<ServiceModule>(context).topMenu()![index]['iconUrl']),
  295. SizedBox(height: hideOther ? 0:8),
  296. hideOther ? Container(width: 70) : SizedBox(
  297. width: 70,
  298. child: Text(index == Provider.of<ServiceModule>(context).topMenu()!.length? 'more'.tr() : U.servantDisplay() ? Provider.of<ServiceModule>(context).topMenu()![index]['description'] : Provider.of<ServiceModule>(context).topMenu()![index][U.langColumn(context, 'description')]??'', style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center),
  299. )
  300. ],
  301. ),
  302. onTap: (){
  303. if(index == Provider.of<ServiceModule>(context, listen: false).topMenu()!.length){
  304. if(!hideOther){
  305. navigateTo(context, MobMenuDisplayPage(scope: Provider.of<ServiceModule>(context, listen: false).scoopeValue()));
  306. }
  307. }
  308. else{
  309. navigateTo(context, MobReqSelectPage(user: Provider.of<UserModule>(context, listen: false).user(), title: U.servantDisplay() ? Provider.of<ServiceModule>(context, listen: false).topMenu()![index]['description'] : Provider.of<ServiceModule>(context, listen: false).topMenu()![index][U.langColumn(context, 'description')]??'', scope: Provider.of<ServiceModule>(context, listen: false).scoopeValue(), groupCode: Provider.of<ServiceModule>(context, listen: false).topMenu()![index]['code'])).then((val){
  310. val = val??true;
  311. if(val) homeFunc.getFrequentlyRequested(context);
  312. });
  313. }
  314. },
  315. ):Container(width: size);
  316. }),
  317. );
  318. }),
  319. ):shimmerTopMenu(context),
  320. ):Container(),
  321. Provider.of<UserModule>(context).profile()['specialOffer'] != null && Provider.of<UserModule>(context).profile()['specialOffer']['show'] == true ? Container(
  322. margin: EdgeInsets.only(top: 36),
  323. child: Column(
  324. crossAxisAlignment: CrossAxisAlignment.start,
  325. children: [
  326. Padding(
  327. padding: EdgeInsetsDirectional.only(start: 16),
  328. child: Text(
  329. Provider.of<UserModule>(context).profile()['specialOffer']['label'][_localeIndex]??'Not Set',
  330. style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500),
  331. )),
  332. SizedBox(height: 12),
  333. Provider.of<ServiceModule>(context).specialOffer().length > 0 ? SingleChildScrollView(
  334. scrollDirection: Axis.horizontal,
  335. child: Row(
  336. children: List.generate(Provider.of<ServiceModule>(context).specialOffer().length, (i) => GestureDetector(
  337. child: Container(
  338. width: U.bodyWidth(context) - 60,
  339. margin: EdgeInsets.only(left: i==0?16:0, right: 16),
  340. padding: EdgeInsets.only(right: 16),
  341. decoration: BoxDecoration(
  342. color: Colors.white,
  343. border: Border.all(color: textColor.withValues(alpha: 0.15)),
  344. borderRadius: BorderRadius.all(Radius.circular(12))
  345. ),
  346. child: Row(
  347. children: [
  348. imageTiles(imageUrl: Provider.of<ServiceModule>(context).specialOffer()[i]['_mobileImage'] ?? "null"),
  349. SizedBox(width: 16),
  350. Expanded(
  351. child: Column(
  352. crossAxisAlignment: CrossAxisAlignment.start,
  353. children: [
  354. Text(Provider.of<ServiceModule>(context).specialOffer()[i][U.langColumn(context, 'subject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  355. dashed(),
  356. Text(Provider.of<ServiceModule>(context).specialOffer()[i][U.langColumn(context, 'subjectDescription')], style: TextStyle(color: textColor, fontSize: 13, fontWeight: FontWeight.w300), maxLines: 2, overflow: TextOverflow.ellipsis)
  357. ],
  358. ),
  359. )
  360. ],
  361. ),
  362. ),
  363. onTap: ()=>navigateTo(context, MobReqCreatePage(user: Provider.of<UserModule>(context, listen: false).user(), request: Provider.of<ServiceModule>(context, listen: false).specialOffer()[i])),
  364. )),
  365. ),
  366. ) : emptyWidget(),
  367. ],
  368. ),
  369. ):Container(),
  370. Provider.of<UserModule>(context).profile()['frequentlyRequested'] != null && Provider.of<UserModule>(context).profile()['frequentlyRequested']['show'] == true ? Container(
  371. margin: EdgeInsets.only(top: 36),
  372. child: Column(
  373. crossAxisAlignment: CrossAxisAlignment.start,
  374. children: [
  375. Padding(padding: EdgeInsetsDirectional.only(start: 16), child: Text(Provider.of<UserModule>(context).profile()['frequentlyRequested']['label'][_localeIndex]??'Not Set', style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500))),
  376. SizedBox(height: 12),
  377. Provider.of<ServiceModule>(context).data().length > 0 ? SingleChildScrollView(
  378. scrollDirection: Axis.horizontal,
  379. child: Row(
  380. mainAxisAlignment: MainAxisAlignment.center,
  381. children: List.generate(Provider.of<ServiceModule>(context).data().length, (i){
  382. return GestureDetector(
  383. child: Container(
  384. margin: EdgeInsets.only(left: i==0?16:0, right: 16),
  385. width: U.bodyWidth(context)/(kIsWeb?3.8:2.6), height: U.bodyWidth(context)/(kIsWeb?3:2),
  386. decoration: BoxDecoration(
  387. color: Colors.white,
  388. border: Border.all(color: textColor.withValues(alpha: 0.15)),
  389. borderRadius: BorderRadius.all(Radius.circular(12))
  390. ),
  391. child: Column(
  392. crossAxisAlignment: CrossAxisAlignment.start,
  393. children: [
  394. Expanded(
  395. child: Container(
  396. width: double.infinity, height: double.infinity,
  397. child: Provider.of<ServiceModule>(context).data()[i]['_mobileImage'] != null ? null : Center(child: Text("noImage".tr(), style: TextStyle(fontStyle: FontStyle.italic, color: Colors.black38), textAlign: TextAlign.center)),
  398. decoration: BoxDecoration(
  399. color: textColor.withValues(alpha: 0.1),
  400. borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
  401. image: Provider.of<ServiceModule>(context).data()[i]['_mobileImage'] != null ? DecorationImage(
  402. image: CachedNetworkImageProvider(Provider.of<ServiceModule>(context).data()[i]['_mobileImage']+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(Provider.of<ServiceModule>(context).data()[i]['_mobileImage']))),
  403. fit: BoxFit.cover,
  404. ):null,
  405. ),
  406. ),
  407. ),
  408. Container(
  409. padding: EdgeInsets.symmetric(vertical: 12, horizontal: 6),
  410. child: Column(
  411. crossAxisAlignment: CrossAxisAlignment.start,
  412. children: [
  413. Text(Provider.of<ServiceModule>(context).data()[i][U.langColumn(context, 'subject')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
  414. SizedBox(height: 5),
  415. Text(Provider.of<ServiceModule>(context).data()[i]['_frequentlyCount'].toString() + 'times'.tr(), style: TextStyle(color: textColor, fontSize: 14, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis)
  416. ],
  417. ),
  418. )
  419. ],
  420. ),
  421. ),
  422. onTap: ()=>navigateTo(context, MobReqCreatePage(user: Provider.of<UserModule>(context, listen: false).user(), request: Provider.of<ServiceModule>(context, listen: false).data()[i])).then((val){
  423. val = val??true;
  424. if(val) homeFunc.getFrequentlyRequested(context);
  425. }),
  426. );
  427. }),
  428. ),
  429. ) : emptyWidget(),
  430. ],
  431. ),
  432. ):Container(),
  433. Provider.of<UserModule>(context).profile()['banner'] != null && Provider.of<UserModule>(context).profile()['banner']['show'] == true ? Container(
  434. margin: EdgeInsets.only(top: 36, left: 16, right: 16),
  435. child: Column(
  436. crossAxisAlignment: CrossAxisAlignment.start,
  437. children: [
  438. Text(Provider.of<UserModule>(context).profile()['banner']['label'][_localeIndex]??'Not Set', style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500)),
  439. SizedBox(height: 12),
  440. Provider.of<ServiceModule>(context).banner().isNotEmpty ? CarouselSlider.builder(
  441. itemCount: Provider.of<ServiceModule>(context).banner().length,
  442. options: CarouselOptions(
  443. height: U.bodyWidth(context)/(kIsWeb?1.7:1.5),
  444. initialPage: 0,
  445. viewportFraction: 1,
  446. autoPlay: true,
  447. autoPlayInterval: Duration(seconds: 5),
  448. autoPlayAnimationDuration: Duration(seconds: 1),
  449. autoPlayCurve: Curves.fastOutSlowIn,
  450. enlargeCenterPage: true,
  451. enableInfiniteScroll: true,
  452. scrollDirection: Axis.horizontal,
  453. ),
  454. itemBuilder: (BuildContext context, int i, int pageViewIndex) => GestureDetector(
  455. child: Container(
  456. width: double.infinity,
  457. decoration: BoxDecoration(
  458. color: Colors.white,
  459. border: Border.all(color: textColor.withValues(alpha: 0.15)),
  460. borderRadius: BorderRadius.all(Radius.circular(12))
  461. ),
  462. child: Column(
  463. crossAxisAlignment: CrossAxisAlignment.start,
  464. children: [
  465. // Expanded(child: imageTiles(imageUrl: Provider.of<ServiceModule>(context).banner()[i]['image'], width: double.infinity, height: double.infinity)),
  466. Expanded(
  467. child: Container(
  468. width: double.infinity, height: double.infinity,
  469. child: Provider.of<ServiceModule>(context).banner()[i]['image'] != null ? null : Center(child: Text("noImage".tr(), style: TextStyle(fontStyle: FontStyle.italic, color: Colors.black38), textAlign: TextAlign.center)),
  470. decoration: BoxDecoration(
  471. color: textColor.withValues(alpha: 0.1),
  472. borderRadius: BorderRadius.all(Radius.circular(12)),
  473. image: Provider.of<ServiceModule>(context).banner()[i]['image'] != null ? DecorationImage(
  474. image: CachedNetworkImageProvider(Provider.of<ServiceModule>(context).banner()[i]['image']+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(Provider.of<ServiceModule>(context).banner()[i]['image']))),
  475. fit: BoxFit.cover,
  476. ):null,
  477. ),
  478. ),
  479. ),
  480. Container(
  481. padding: EdgeInsets.all(12),
  482. child: Column(
  483. crossAxisAlignment: CrossAxisAlignment.start,
  484. children: [
  485. Text(Provider.of<ServiceModule>(context).banner()[i][U.langColumn(context, 'title')] != null && Provider.of<ServiceModule>(context).banner()[i][U.langColumn(context, 'title')] != '' ? Provider.of<ServiceModule>(context).banner()[i][U.langColumn(context, 'title')]:Provider.of<ServiceModule>(context).banner()[i]['titleEn']!=null?Provider.of<ServiceModule>(context).banner()[i]['titleEn']:Provider.of<ServiceModule>(context).banner()[i]['title'], style: TextStyle(color: textColor, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  486. SizedBox(height: 5),
  487. Linkify(
  488. text: Provider.of<ServiceModule>(context).banner()[i][U.langColumn(context, 'description')] != null && Provider.of<ServiceModule>(context).banner()[i][U.langColumn(context, 'description')] != '' ? Provider.of<ServiceModule>(context).banner()[i][U.langColumn(context, 'description')]:Provider.of<ServiceModule>(context).banner()[i]['descriptionEn']!=null?Provider.of<ServiceModule>(context).banner()[i]['descriptionEn']:Provider.of<ServiceModule>(context).banner()[i]['description'], style: TextStyle(color: textColor, fontSize: 14), maxLines: 2, overflow: TextOverflow.ellipsis,
  489. onOpen: (link) async {
  490. if (await canLaunchUrl(Uri.parse(link.url))) {
  491. await launchUrl(Uri.parse(link.url));
  492. }
  493. },
  494. )
  495. ],
  496. ),
  497. ),
  498. Padding(
  499. padding: const EdgeInsets.fromLTRB(12.0, 8.0, 8.0, 8.0),
  500. child: Row(
  501. mainAxisAlignment: MainAxisAlignment.center,
  502. children: List.generate(Provider.of<ServiceModule>(context).banner().length, (index) => Padding(
  503. padding: const EdgeInsets.only(right: 8.0),
  504. child: Container(
  505. width: index == i ? 16.0 : 8.0,
  506. height: 8.0,
  507. decoration: BoxDecoration(
  508. borderRadius: BorderRadius.all(Radius.circular(8)),
  509. color: primaryColor.withValues(alpha: index == i ? 1 : 0.3)
  510. ),
  511. ),
  512. ))
  513. ),
  514. )
  515. ],
  516. ),
  517. ),
  518. onTap: ()=>navigateTo(context, MobBannerDetailPage(user: Provider.of<UserModule>(context, listen: false).user(), data: Provider.of<ServiceModule>(context, listen: false).banner()[i])).then((val){
  519. val = val??true;
  520. if(val) homeFunc.getFrequentlyRequested(context);
  521. }),
  522. ),
  523. ): emptyWidget(left: 0)
  524. ],
  525. ),
  526. ):Container(),
  527. Provider.of<UserModule>(context).profile()['quickAction'] != null && Provider.of<UserModule>(context).profile()['quickAction']['show'] == true ? Container(
  528. margin: EdgeInsets.only(top: 36, left: 16, right: 16),
  529. child: Column(
  530. crossAxisAlignment: CrossAxisAlignment.start,
  531. children: [
  532. Text(Provider.of<UserModule>(context).profile()['quickAction']['label'][_localeIndex]??'Not Set', style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500)),
  533. SizedBox(height: 12),
  534. Provider.of<ServiceModule>(context).quickAct().length > 0 ? Container(
  535. decoration: BoxDecoration(
  536. color: Colors.white,
  537. border: Border.all(color: textColor.withValues(alpha: 0.15)),
  538. borderRadius: BorderRadius.all(Radius.circular(12))
  539. ),
  540. child: Column(
  541. children: List.generate(Provider.of<ServiceModule>(context).quickAct().length, (i){
  542. return GestureDetector(
  543. child: Container(
  544. padding: EdgeInsets.all(12),
  545. decoration: BoxDecoration(
  546. color: Colors.white,
  547. borderRadius: BorderRadius.only(
  548. topLeft: Radius.circular(i==0?12:0),
  549. topRight: Radius.circular(i==0?12:0),
  550. bottomLeft: Radius.circular(i==Provider.of<ServiceModule>(context).quickAct().length-1?12:0),
  551. bottomRight: Radius.circular(i==Provider.of<ServiceModule>(context).quickAct().length-1?12:0),
  552. )
  553. ),
  554. child: Row(
  555. children: [
  556. imageTiles(imageUrl: Provider.of<ServiceModule>(context).quickAct()[i]['_mobileImage'] ?? "null", width: 50, height: 40, radius: 5),
  557. SizedBox(width: 16),
  558. Expanded(
  559. child: Text(Provider.of<ServiceModule>(context).quickAct()[i][U.langColumn(context, 'subject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
  560. ),
  561. U.iconsax('arrow-right-3', size: 14, color: textColor.withValues(alpha: 0.75)),
  562. ],
  563. ),
  564. ),
  565. onTap: ()=>navigateTo(context, MobReqCreatePage(user: Provider.of<UserModule>(context, listen: false).user(), request: Provider.of<ServiceModule>(context, listen: false).quickAct()[i])),
  566. );
  567. }),
  568. ),
  569. ) : emptyWidget(left: 0)
  570. ],
  571. ),
  572. ):Container(),
  573. SizedBox(height: 16)
  574. ],
  575. ),
  576. ),
  577. ),
  578. ),
  579. )
  580. ],
  581. ),
  582. ) : Provider.of<UserModule>(context).resetData() ? RefreshPage(() {
  583. Provider.of<UserModule>(context, listen: false).setResetData(false);
  584. homeFunc.getProfileData(context);
  585. }) : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},);
  586. }
  587. Widget emptyWidget({double left = 16}){
  588. return Padding(padding: EdgeInsets.only(left: left), child: Text("notAvailable".tr(), style: TextStyle(color: textColor.withValues(alpha: 0.5), fontSize: 14, fontWeight: FontWeight.w400)));
  589. }
  590. Widget dndStatus(){
  591. return Provider.of<UserModule>(context).user()['checkedIn'] && Provider.of<UserModule>(context).houseKeeping() && Provider.of<UserModule>(context).dndStatus()?GestureDetector(
  592. child: Container(
  593. width: double.infinity,
  594. padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16),
  595. decoration: BoxDecoration(
  596. color: Colors.red.withValues(alpha: 0.15),
  597. border: Border(bottom: BorderSide(color: Colors.red, width: 1.5))
  598. ),
  599. child: Row(
  600. children: [
  601. Expanded(child: Text('info_dnd'.tr(), style: TextStyle(color: textColor, fontSize: 14))),
  602. U.iconsax('arrow-down-1', color: textColor, size: 18)
  603. ],
  604. ),
  605. ),
  606. onTap: (){
  607. showModalBottomSheet(
  608. context: context,
  609. backgroundColor: Colors.transparent,
  610. builder: (context) => Container(
  611. padding: EdgeInsets.fromLTRB(15, 15, 15, 30),
  612. decoration: BoxDecoration(
  613. color: Colors.white,
  614. borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25))
  615. ),
  616. child: Column(
  617. mainAxisSize: MainAxisSize.min,
  618. children: [
  619. Container(
  620. width: 40, height: 4, margin: EdgeInsets.only(bottom: 30),
  621. decoration: BoxDecoration(color: Colors.black12, borderRadius: BorderRadius.all(Radius.circular(10))),
  622. ),
  623. Text('msg_dnd'.tr(), style: TextStyle(fontSize: 14, color: textColor), textAlign: TextAlign.center),
  624. ],
  625. )
  626. ),
  627. );
  628. },
  629. ):Container();
  630. }
  631. }
  632. //------------------------------------------------------------------------------
  633. class MobMenuDisplayPage extends StatefulWidget {
  634. String scope;
  635. MobMenuDisplayPage({required this.scope, Key? key}) : super(key: key);
  636. @override
  637. State<MobMenuDisplayPage> createState() => _MobMenuDisplayPageState();
  638. }
  639. class _MobMenuDisplayPageState extends State<MobMenuDisplayPage> {
  640. @override
  641. Widget build(BuildContext context) {
  642. return Scaffold(
  643. backgroundColor: backgroundColor,
  644. appBar: appBarTemplate(context: context, title: 'topMenu'.tr(), action: U.servantDisplay()?null:[
  645. Center(
  646. child: GestureDetector(
  647. child: Container(
  648. padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
  649. margin: EdgeInsets.only(right: 15),
  650. decoration: BoxDecoration(
  651. color: primaryColor.withValues(alpha: 0.1),
  652. borderRadius: BorderRadius.all(Radius.circular(50))
  653. ),
  654. child: Text('customize'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
  655. ),
  656. onTap: (){
  657. navigateTo(context, MobMenuEditorPage(scope: widget.scope));
  658. },
  659. ),
  660. )
  661. ]),
  662. body: Column(
  663. crossAxisAlignment: CrossAxisAlignment.start,
  664. children: [
  665. Container(
  666. padding: EdgeInsets.fromLTRB(16, 8, 16, 0),
  667. child: Column(
  668. children: List.generate((Provider.of<ServiceModule>(context).topMenu()!.length/4).ceil(), (i){
  669. return Row(
  670. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  671. crossAxisAlignment: CrossAxisAlignment.start,
  672. children: List.generate(4, (j){
  673. int index = i==0?j:j+(4*i);
  674. return index < Provider.of<ServiceModule>(context).topMenu()!.length ? GestureDetector(
  675. child: Column(
  676. children: [
  677. i>0?SizedBox(height: 16):Container(),
  678. categoryContainer(context: context, iconUrl: Provider.of<ServiceModule>(context).topMenu()![index]['iconUrl']),
  679. SizedBox(height: 8),
  680. SizedBox(
  681. width: 70,
  682. child: Text(U.servantDisplay() ? Provider.of<ServiceModule>(context).topMenu()![index]['description'] : Provider.of<ServiceModule>(context).topMenu()![index][U.langColumn(context, 'description')]??'', style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center),
  683. )
  684. ],
  685. ),
  686. onTap: ()=>navigateTo(context, MobReqSelectPage(user: Provider.of<UserModule>(context, listen: false).user(), title: U.servantDisplay() ? Provider.of<ServiceModule>(context, listen: false).topMenu()![index]['description'] : Provider.of<ServiceModule>(context, listen: false).topMenu()![index][U.langColumn(context, 'description')]??'', scope: widget.scope, groupCode: Provider.of<ServiceModule>(context, listen: false).topMenu()![index]['code'])),
  687. ) : Container(width: 70);
  688. }),
  689. );
  690. }),
  691. ),
  692. ),
  693. Container(
  694. margin: EdgeInsets.fromLTRB(16, 36, 16, 16),
  695. child: Text('availableMenu'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500)),
  696. ),
  697. Expanded(
  698. child: SingleChildScrollView(
  699. padding: EdgeInsets.fromLTRB(16, 5, 16, 16),
  700. child: SafeArea(
  701. child: Column(
  702. children: List.generate((Provider.of<ServiceModule>(context).reqGroup()!.length/4).ceil(), (i){
  703. return Row(
  704. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  705. crossAxisAlignment: CrossAxisAlignment.start,
  706. children: List.generate(4, (j){
  707. int index = i==0?j:j+(4*i);
  708. return index < Provider.of<ServiceModule>(context).reqGroup()!.length ? GestureDetector(
  709. child: Column(
  710. children: [
  711. i>0?SizedBox(height: 16):Container(),
  712. categoryContainer(context: context, iconUrl: Provider.of<ServiceModule>(context).reqGroup()![index]['iconUrl']),
  713. SizedBox(height: 8),
  714. SizedBox(
  715. width: 70,
  716. child: Text(U.servantDisplay() ? Provider.of<ServiceModule>(context).reqGroup()![index]['description'] : Provider.of<ServiceModule>(context).reqGroup()![index][U.langColumn(context, 'description')], style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center),
  717. )
  718. ],
  719. ),
  720. onTap: ()=>navigateTo(context, MobReqSelectPage(user: Provider.of<UserModule>(context, listen: false).user(), title: U.servantDisplay() ? Provider.of<ServiceModule>(context, listen: false).reqGroup()![index]['description'] : Provider.of<ServiceModule>(context, listen: false).reqGroup()![index][U.langColumn(context, 'description')], scope: widget.scope, groupCode: Provider.of<ServiceModule>(context, listen: false).reqGroup()![index]['code'])),
  721. ) : Container(width: 70);
  722. }),
  723. );
  724. }),
  725. ),
  726. ),
  727. ),
  728. )
  729. ],
  730. ),
  731. );
  732. }
  733. }
  734. //------------------------------------------------------------------------------
  735. class MobMenuEditorPage extends StatefulWidget {
  736. String scope;
  737. MobMenuEditorPage({required this.scope, Key? key}) : super(key: key);
  738. @override
  739. State<MobMenuEditorPage> createState() => _MobMenuEditorPageState();
  740. }
  741. class _MobMenuEditorPageState extends State<MobMenuEditorPage> {
  742. List data = [];
  743. List available = [];
  744. @override
  745. void initState() {
  746. data.addAll(Provider.of<ServiceModule>(context, listen: false).topMenu()!);
  747. available.addAll(Provider.of<ServiceModule>(context, listen: false).reqGroup()!);
  748. super.initState();
  749. }
  750. @override
  751. Widget build(BuildContext context) {
  752. return Scaffold(
  753. backgroundColor: backgroundColor,
  754. appBar: appBarTemplate(context: context, title: 'topMenu'.tr(), action: [
  755. Center(
  756. child: GestureDetector(
  757. child: Container(
  758. padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
  759. margin: EdgeInsets.only(right: 15),
  760. child: Text('done'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
  761. decoration: BoxDecoration(
  762. color: primaryColor.withValues(alpha: 0.1),
  763. borderRadius: BorderRadius.all(Radius.circular(50))
  764. ),
  765. ),
  766. onTap: ()async{
  767. if(data.isNotEmpty){
  768. List codes = [];
  769. data.forEach((element) {
  770. codes.add(element['code']);
  771. });
  772. var res = await ApiAuthProvider().postData('/api/informants/topMenu/${widget.scope}', {'topMenu': codes.join(';')}, null, context);
  773. if(res != null){
  774. Provider.of<ServiceModule>(context, listen: false).setTopMenu(data);
  775. Provider.of<ServiceModule>(context, listen: false).setReqGroup(available);
  776. navigateBack(context);
  777. navigateBack(context);
  778. navigateTo(context, MobMenuDisplayPage(scope: widget.scope));
  779. }
  780. } else if(available.isEmpty) {
  781. navigateBack(context);
  782. } else {
  783. showError(context, "selectAlert".tr());
  784. }
  785. },
  786. ),
  787. )
  788. ]),
  789. body: SingleChildScrollView(
  790. child: Column(
  791. crossAxisAlignment: CrossAxisAlignment.start,
  792. children: [
  793. DragAndDropLists(
  794. disableScrolling: true,
  795. lastItemTargetHeight: 8,
  796. lastListTargetSize: 0,
  797. children: [
  798. DragAndDropList(
  799. contentsWhenEmpty: Padding(
  800. padding: const EdgeInsets.only(top: 16.0, bottom: 16.0),
  801. child: Text("emptyMenuAlert".tr(), style: TextStyle(fontStyle: FontStyle.italic, color: primaryColor.withValues(alpha: 0.60)),),
  802. ),
  803. canDrag: false,
  804. children: List<DragAndDropItem>.generate(data.length, (i){
  805. return DragAndDropItem(
  806. child: Container(
  807. padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
  808. child: Row(
  809. children: [
  810. categoryContainer(context: context, iconUrl: data[i]['iconUrl']),
  811. SizedBox(width: 12),
  812. Expanded(
  813. child: Column(
  814. crossAxisAlignment: CrossAxisAlignment.start,
  815. children: [
  816. Text(data[i][U.langColumn(context, 'description')], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
  817. SizedBox(height: 4),
  818. Text(data[i][U.langColumn(context, 'description')], style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300), overflow: TextOverflow.ellipsis),
  819. ],
  820. ),
  821. ),
  822. GestureDetector(
  823. child: U.iconsax('bold/minus-cirlce', color: Color(0xffD81010)),
  824. onTap: (){
  825. setState(() {
  826. available.add(data[i]);
  827. data.remove(data[i]);
  828. });
  829. },
  830. ),
  831. SizedBox(width: 40)
  832. ],
  833. ),
  834. ),
  835. );
  836. }),
  837. )
  838. ],
  839. onItemReorder: (int oldItemIndex, int oldListIndex, int newItemIndex, int newListIndex) {
  840. setState(() {
  841. var movedItem = data.removeAt(oldItemIndex);
  842. data.insert(newItemIndex, movedItem);
  843. });
  844. },
  845. onListReorder: (int oldListIndex, int newListIndex) {},
  846. itemDragHandle: DragHandle(
  847. child: Padding(padding: EdgeInsets.only(right: 16), child: Icon(Icons.menu_rounded, color: textColor)),
  848. ),
  849. ),
  850. separator(),
  851. Container(
  852. margin: EdgeInsets.fromLTRB(16, 16, 16, 13),
  853. child: Text('availableMenu'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500)),
  854. ),
  855. SafeArea(
  856. child: Column(
  857. children: List.generate(available.length, (i){
  858. return Container(
  859. padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
  860. child: Row(
  861. children: [
  862. categoryContainer(context: context, iconUrl: available[i]['iconUrl']),
  863. SizedBox(width: 12),
  864. Expanded(
  865. child: Column(
  866. crossAxisAlignment: CrossAxisAlignment.start,
  867. children: [
  868. Text(available[i][U.langColumn(context, 'description')], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
  869. SizedBox(height: 4),
  870. Text(available[i][U.langColumn(context, 'description')], style: TextStyle(color: textColor, fontSize: 12, fontWeight: FontWeight.w300), overflow: TextOverflow.ellipsis),
  871. ],
  872. ),
  873. ),
  874. GestureDetector(
  875. child: U.iconsax('bold/add-circle', color: Color(0xffD81010)),
  876. onTap: (){
  877. if(data.length < 7){
  878. setState(() {
  879. data.add(available[i]);
  880. available.remove(available[i]);
  881. });
  882. }
  883. else{
  884. showError(context, 'maxMenu'.tr());
  885. }
  886. },
  887. ),
  888. ],
  889. ),
  890. );
  891. }),
  892. ),
  893. ),
  894. SizedBox(height: 8),
  895. ],
  896. ),
  897. ),
  898. );
  899. }
  900. }