menu_home.dart 61 KB

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