Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

543 rindas
19 KiB

  1. // ignore_for_file: public_member_api_docs, sort_constructors_first
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_gen/gen_l10n/app_localizations.dart';
  4. import 'package:font_awesome_flutter/font_awesome_flutter.dart';
  5. import 'package:go_router/go_router.dart';
  6. import 'package:intl/intl.dart';
  7. import 'package:provider/provider.dart';
  8. import 'package:qadirneyriz/config/config.dart';
  9. import 'package:qadirneyriz/screens/private_meeting/dilog_privateMeetings_filters.dart';
  10. import 'package:qadirneyriz/screens/private_meeting/state.dart';
  11. import 'package:qadirneyriz/setting/setting.dart';
  12. import 'package:qadirneyriz/utils/enums/status.dart';
  13. import 'package:qadirneyriz/utils/tools/tools.dart';
  14. import 'package:qadirneyriz/widgets/custom_appbar.dart';
  15. import 'package:qadirneyriz/widgets/custom_button.dart';
  16. import 'package:qadirneyriz/widgets/empty_widget.dart';
  17. import 'package:qadirneyriz/widgets/error_widget.dart';
  18. import 'package:qadirneyriz/widgets/icon_button.dart';
  19. import 'package:qadirneyriz/widgets/loading_widget.dart';
  20. import 'package:qadirneyriz/widgets/today_widget.dart';
  21. class PrivateMeetingsScreen extends StatefulWidget {
  22. const PrivateMeetingsScreen({super.key});
  23. @override
  24. State<PrivateMeetingsScreen> createState() => _PrivateMeetingsScreenState();
  25. }
  26. class _PrivateMeetingsScreenState extends State<PrivateMeetingsScreen> {
  27. late ScrollController _scrollController;
  28. late PrivateMeetingsState privateMeetingsState;
  29. @override
  30. void initState() {
  31. super.initState();
  32. _scrollController = ScrollController();
  33. _scrollController.addListener(_scrollListener);
  34. privateMeetingsState =
  35. Provider.of<PrivateMeetingsState>(context, listen: false);
  36. Future.delayed(Duration.zero, () async {
  37. privateMeetingsState.clearFilters();
  38. await privateMeetingsState.getPrivateMeetings();
  39. });
  40. privateMeetingsState.setAllFiltersForThen();
  41. }
  42. // ذخیره فیلترهای اولیه برای مقایسه در آیند
  43. _scrollListener() async {
  44. if (_scrollController.offset >=
  45. _scrollController.position.maxScrollExtent &&
  46. !_scrollController.position.outOfRange) {
  47. if (!privateMeetingsState.pageEndedPrivateMeetings) {
  48. await privateMeetingsState.nextPagePrivateMeetings(
  49. toDate: privateMeetingsState.toDate,
  50. fromDate: privateMeetingsState.fromDate,
  51. location: privateMeetingsState.selectedLocationId,
  52. subject: privateMeetingsState.selectedSubjectId,
  53. meetingManager: privateMeetingsState.selectedManagersId,
  54. meetingStatus: privateMeetingsState.selectedStatusId);
  55. }
  56. }
  57. }
  58. @override
  59. Widget build(BuildContext context) {
  60. DateTime now = DateTime.now();
  61. String dateMiladi = setting.userLocalDb.getUser().language == 'fa'
  62. ? Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now))
  63. : DateFormat('yyyy-MM-dd').format(now);
  64. String dateJalali = Tools.convertToPersianDigits(
  65. '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}');
  66. return Consumer<PrivateMeetingsState>(
  67. builder: (context, value, child) {
  68. return RefreshIndicator(
  69. onRefresh: () async {
  70. await privateMeetingsState.getPrivateMeetings();
  71. },
  72. child: CustomScrollView(
  73. controller: _scrollController,
  74. physics: AlwaysScrollableScrollPhysics(),
  75. slivers: <Widget>[
  76. const CustomAppbar(),
  77. SliverToBoxAdapter(
  78. child: TodayWidget(
  79. formattedDate:
  80. setting.userLocalDb.getUser().language == 'en'
  81. ? dateMiladi
  82. : dateJalali),
  83. ),
  84. SliverToBoxAdapter(
  85. child: Padding(
  86. padding:
  87. const EdgeInsets.symmetric(vertical: 30, horizontal: 15),
  88. child: Row(
  89. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  90. crossAxisAlignment: CrossAxisAlignment.center,
  91. children: [
  92. Text(
  93. style: const TextStyle(fontSize: 14),
  94. AppLocalizations.of(context)!.privatemeeting,
  95. ),
  96. IconButtonCustom(
  97. iconColor: value.hasActiveFilters()
  98. ? Colors.white
  99. : config.ui.secendGreen,
  100. backColor: value.hasActiveFilters()
  101. ? config.ui.secendGreen
  102. : Colors.white,
  103. icon: FontAwesomeIcons.sliders,
  104. onTap: () {
  105. showModalBottomSheet(
  106. isScrollControlled: true,
  107. useSafeArea: true,
  108. context: context,
  109. builder: (context) {
  110. return DiologPrivateMeetingsFilters();
  111. },
  112. );
  113. },
  114. )
  115. ],
  116. ),
  117. ),
  118. ),
  119. privateMeetingsList(value),
  120. (value.privatePaginationMeetings == Status.ready ||
  121. value.privatePaginationMeetings == Status.empty)
  122. ? const SliverToBoxAdapter()
  123. : const SliverToBoxAdapter(
  124. child: Center(
  125. child: LoadingWidget(
  126. size: 10,
  127. ),
  128. ),
  129. )
  130. ],
  131. ),
  132. );
  133. },
  134. );
  135. }
  136. Widget privateMeetingsList(PrivateMeetingsState state) {
  137. switch (state.privateStatusMeetings) {
  138. case Status.ready:
  139. return SliverList.builder(
  140. itemBuilder: (context, index) {
  141. final userRole = setting.userLocalDb.getUser().role;
  142. final items = state.privateMeetingsModel!.data![index];
  143. return PrivateMeetingWidget(
  144. status: items.accepted ?? 0,
  145. date: items.dateJalali ?? '',
  146. time: items.azHour ?? '',
  147. subject:
  148. items.subject != null ? items.subject!.subject ?? '' : '',
  149. location:
  150. items.location != null ? items.location!.address ?? '' : '',
  151. onAcceptButton:
  152. state.statusAcceptMeeting[items.id] != Status.loading
  153. ? () {
  154. acceptPrivateMeeting(state, context, items.id ?? -1);
  155. }
  156. : null,
  157. onCancelButton:
  158. state.statusCancelMeeting[items.id] != Status.loading
  159. ? () {
  160. cancelPrivateMeeting(state, context, items.id ?? -1);
  161. }
  162. : null,
  163. onSelectedMoreButton: (value) async {
  164. switch (value) {
  165. case 'edit':
  166. await context.pushNamed('privatemeetingedit',
  167. pathParameters: {'id': items.id.toString()});
  168. state.getPrivateMeetings();
  169. case 'report':
  170. await context.pushNamed(
  171. 'privatemeetinsammary',
  172. extra: items, // `items` should be a Datum instance
  173. );
  174. case 'cancel':
  175. cancelPrivateMeeting(state, context, items.id ?? -1);
  176. case 'delete':
  177. deletePrivateMeeting(state, context, items.id ?? -1);
  178. default:
  179. }
  180. },
  181. itemBuilderMoreButton: (context) => [
  182. if (userRole == 0 || userRole == 2)
  183. PopupMenuItem(
  184. value: 'edit',
  185. child: Row(
  186. children: [
  187. Icon(
  188. Icons.edit,
  189. color: Colors.green,
  190. size: 17,
  191. ),
  192. SizedBox(width: 8),
  193. Text(
  194. AppLocalizations.of(context)!.editprivatemeeting,
  195. style: TextStyle(fontSize: 12),
  196. ),
  197. ],
  198. ),
  199. ),
  200. PopupMenuItem(
  201. value: 'report',
  202. child: Row(
  203. children: [
  204. Icon(
  205. Icons.receipt_long,
  206. color: Colors.green,
  207. size: 17,
  208. ),
  209. SizedBox(width: 8),
  210. Text(
  211. AppLocalizations.of(context)!.meetingsummary,
  212. style: TextStyle(fontSize: 12),
  213. ),
  214. ],
  215. ),
  216. ),
  217. PopupMenuItem(
  218. enabled: state.statusCancelMeeting != Status.loading,
  219. value: 'cancel',
  220. child: Row(
  221. children: [
  222. Icon(
  223. Icons.cancel,
  224. color: Colors.green,
  225. size: 17,
  226. ),
  227. SizedBox(width: 8),
  228. Text(
  229. AppLocalizations.of(context)!.cancelmeeting,
  230. style: TextStyle(fontSize: 12),
  231. ),
  232. ],
  233. ),
  234. ),
  235. if (userRole == 0 || userRole == 2)
  236. PopupMenuItem(
  237. value: 'delete',
  238. child: Row(
  239. children: [
  240. Icon(
  241. Icons.delete,
  242. color: Colors.green,
  243. size: 17,
  244. ),
  245. SizedBox(width: 8),
  246. Text(
  247. AppLocalizations.of(context)!.deleteprivatemeeting,
  248. style: TextStyle(fontSize: 12),
  249. ),
  250. ],
  251. ),
  252. ),
  253. ],
  254. );
  255. },
  256. itemCount: state.privateMeetingsModel!.data!.length,
  257. );
  258. case Status.loading:
  259. return SliverFillRemaining(child: const LoadingWidget());
  260. case Status.error:
  261. return SliverFillRemaining(
  262. child: CustomErrorWidget(
  263. onPressed: () async {
  264. await state.getPrivateMeetings(refresh: true);
  265. },
  266. ),
  267. );
  268. case Status.empty:
  269. return SliverFillRemaining(child: EmptyStateWidget());
  270. default:
  271. return Container();
  272. }
  273. }
  274. void cancelPrivateMeeting(
  275. PrivateMeetingsState state, BuildContext context, int cardId) async {
  276. final status = await state.cancelMeeting(id: cardId);
  277. if (status == Status.ready) {
  278. Tools.showCustomSnackBar(
  279. text: AppLocalizations.of(context)!.privatemeetingcanceld,
  280. isError: false,
  281. context,
  282. );
  283. await privateMeetingsState.getPrivateMeetings();
  284. } else {
  285. Tools.showCustomSnackBar(
  286. text: AppLocalizations.of(context)!.error,
  287. isError: true,
  288. context,
  289. );
  290. }
  291. }
  292. void deletePrivateMeeting(
  293. PrivateMeetingsState state, BuildContext context, int cardId) async {
  294. final status = await state.deleteMeeting(id: cardId);
  295. if (status == Status.ready) {
  296. Tools.showCustomSnackBar(
  297. text: AppLocalizations.of(context)!.privatemeetingdeleted,
  298. isError: false,
  299. context,
  300. );
  301. await privateMeetingsState.getPrivateMeetings();
  302. } else {
  303. Tools.showCustomSnackBar(
  304. text: AppLocalizations.of(context)!.error,
  305. isError: true,
  306. context,
  307. );
  308. }
  309. }
  310. void acceptPrivateMeeting(
  311. PrivateMeetingsState state, BuildContext context, int cardId) async {
  312. final status = await state.acceptMeeting(id: cardId);
  313. if (status == Status.ready) {
  314. Tools.showCustomSnackBar(
  315. text: AppLocalizations.of(context)!.privatemeetingaccept,
  316. isError: false,
  317. context,
  318. );
  319. await privateMeetingsState.getPrivateMeetings();
  320. } else {
  321. Tools.showCustomSnackBar(
  322. text: AppLocalizations.of(context)!.error,
  323. isError: true,
  324. context,
  325. );
  326. }
  327. }
  328. }
  329. class PrivateMeetingWidget extends StatelessWidget {
  330. final String date;
  331. final String time;
  332. final String subject;
  333. final String location;
  334. final int status;
  335. final bool hasMoreButton;
  336. final void Function()? onAcceptButton;
  337. final void Function()? onCancelButton;
  338. final void Function(String)? onSelectedMoreButton;
  339. final List<PopupMenuEntry<String>> Function(BuildContext)?
  340. itemBuilderMoreButton;
  341. const PrivateMeetingWidget(
  342. {Key? key,
  343. required this.date,
  344. required this.time,
  345. required this.subject,
  346. required this.location,
  347. required this.status,
  348. this.hasMoreButton = true,
  349. this.onAcceptButton,
  350. this.onCancelButton,
  351. this.onSelectedMoreButton,
  352. this.itemBuilderMoreButton,
  353. th})
  354. : super(key: key);
  355. @override
  356. Widget build(BuildContext context) {
  357. return Padding(
  358. padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
  359. child: Container(
  360. child: Column(
  361. crossAxisAlignment: CrossAxisAlignment.end,
  362. children: [
  363. Padding(
  364. padding: const EdgeInsets.symmetric(horizontal: 10),
  365. child: Text(
  366. this.date,
  367. style: TextStyle(fontSize: 12),
  368. ),
  369. ),
  370. Divider(),
  371. SizedBox(
  372. height: 5,
  373. ),
  374. Container(
  375. decoration: BoxDecoration(
  376. boxShadow: [
  377. BoxShadow(
  378. color: config.ui.mainGray.withOpacity(.1),
  379. spreadRadius: .1,
  380. offset: const Offset(0, 2),
  381. blurRadius: 6)
  382. ],
  383. color: const Color(0xffF4F9F6),
  384. borderRadius: BorderRadius.circular(10)),
  385. child: Padding(
  386. padding:
  387. const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
  388. child: Column(
  389. children: [
  390. Row(
  391. children: [
  392. Text(this.time),
  393. SizedBox(
  394. width: 15,
  395. ),
  396. Container(
  397. width: 3,
  398. height: 45,
  399. decoration: BoxDecoration(
  400. color: Colors.green,
  401. ),
  402. ),
  403. SizedBox(
  404. width: 5,
  405. ),
  406. Expanded(
  407. child: Column(
  408. crossAxisAlignment: CrossAxisAlignment.start,
  409. children: [
  410. Text(
  411. this.subject,
  412. ),
  413. SizedBox(
  414. height: 5,
  415. ),
  416. Text(
  417. this.location,
  418. style: TextStyle(
  419. fontSize: 12, color: Color(0xff9AA8C7)),
  420. ),
  421. ],
  422. ),
  423. ),
  424. if (hasMoreButton) _moreButton(context)
  425. ],
  426. ),
  427. if (this.status == 0)
  428. Padding(
  429. padding: const EdgeInsets.only(top: 15),
  430. child: Row(
  431. mainAxisAlignment: MainAxisAlignment.center,
  432. children: [
  433. CustomButton(
  434. hieght: 30,
  435. text: AppLocalizations.of(context)!.accept,
  436. borderRadius: 5,
  437. color: Color(0xff00A848),
  438. fontSize: 12,
  439. onPressed: this.onAcceptButton,
  440. ),
  441. SizedBox(
  442. width: 7,
  443. ),
  444. CustomButton(
  445. hieght: 30,
  446. text: AppLocalizations.of(context)!.cancel,
  447. color: Colors.red,
  448. textColor: Colors.white,
  449. fontSize: 12,
  450. borderRadius: 5,
  451. onPressed: this.onCancelButton,
  452. ),
  453. SizedBox(
  454. width: 90,
  455. ),
  456. ],
  457. ),
  458. ),
  459. if (this.status == 1 || this.status == 2)
  460. Padding(
  461. padding: const EdgeInsets.only(top: 10),
  462. child: Row(
  463. children: [
  464. SizedBox(
  465. width: 60,
  466. ),
  467. PrivateMeetingLabel(
  468. status: this.status,
  469. ),
  470. ],
  471. ),
  472. )
  473. ],
  474. ),
  475. ),
  476. )
  477. ],
  478. ),
  479. ),
  480. );
  481. }
  482. Widget _moreButton(BuildContext context) {
  483. return PopupMenuButton<String>(
  484. color: Colors.white,
  485. shape: const RoundedRectangleBorder(
  486. borderRadius: BorderRadius.all(
  487. Radius.circular(10.0),
  488. ),
  489. ),
  490. onSelected: onSelectedMoreButton,
  491. itemBuilder: itemBuilderMoreButton!,
  492. icon: const Icon(Icons.more_horiz),
  493. );
  494. }
  495. }
  496. class PrivateMeetingLabel extends StatelessWidget {
  497. final int status;
  498. const PrivateMeetingLabel({
  499. Key? key,
  500. required this.status,
  501. }) : super(key: key);
  502. @override
  503. Widget build(BuildContext context) {
  504. return Container(
  505. width: 82,
  506. child: Center(
  507. child: Padding(
  508. padding: const EdgeInsets.symmetric(vertical: 5),
  509. child: Text(
  510. this.status == 1
  511. ? AppLocalizations.of(context)!.accepted
  512. : AppLocalizations.of(context)!.canceled,
  513. style: TextStyle(fontSize: 12, color: Colors.white),
  514. ),
  515. ),
  516. ),
  517. decoration: BoxDecoration(
  518. color: this.status == 1 ? Colors.green : Colors.red,
  519. borderRadius: BorderRadius.circular(4)),
  520. );
  521. }
  522. }