menu_home.dart 57 KB

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