menu_home.dart 61 KB

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