menu_home.dart 54 KB

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