25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

486 satır
17 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 =
  62. Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now));
  63. String dateJalali = Tools.convertToPersianDigits(
  64. '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}');
  65. return Consumer<PrivateMeetingsState>(
  66. builder: (context, value, child) {
  67. return RefreshIndicator(
  68. onRefresh: () async {
  69. await privateMeetingsState.getPrivateMeetings();
  70. },
  71. child: CustomScrollView(
  72. controller: _scrollController,
  73. physics: AlwaysScrollableScrollPhysics(),
  74. slivers: <Widget>[
  75. const CustomAppbar(),
  76. SliverToBoxAdapter(
  77. child: TodayWidget(
  78. formattedDate:
  79. setting.userLocalDb.getUser().language == 'en'
  80. ? dateMiladi
  81. : dateJalali),
  82. ),
  83. SliverToBoxAdapter(
  84. child: Padding(
  85. padding:
  86. const EdgeInsets.symmetric(vertical: 30, horizontal: 15),
  87. child: Row(
  88. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  89. crossAxisAlignment: CrossAxisAlignment.center,
  90. children: [
  91. Text(
  92. style: const TextStyle(fontSize: 14),
  93. AppLocalizations.of(context)!.privatemeeting,
  94. ),
  95. IconButtonCustom(
  96. iconColor: value.hasActiveFilters()
  97. ? Colors.white
  98. : config.ui.secendGreen,
  99. backColor: value.hasActiveFilters()
  100. ? config.ui.secendGreen
  101. : Colors.white,
  102. icon: FontAwesomeIcons.sliders,
  103. onTap: () {
  104. showModalBottomSheet(
  105. isScrollControlled: true,
  106. useSafeArea: true,
  107. context: context,
  108. builder: (context) {
  109. return DiologPrivateMeetingsFilters();
  110. },
  111. );
  112. },
  113. )
  114. ],
  115. ),
  116. ),
  117. ),
  118. privateMeetingsList(value),
  119. (value.privatePaginationMeetings == Status.ready ||
  120. value.privatePaginationMeetings == Status.empty)
  121. ? const SliverToBoxAdapter()
  122. : const SliverToBoxAdapter(
  123. child: Center(
  124. child: LoadingWidget(
  125. size: 10,
  126. ),
  127. ),
  128. )
  129. ],
  130. ),
  131. );
  132. },
  133. );
  134. }
  135. Widget privateMeetingsList(PrivateMeetingsState state) {
  136. switch (state.privateStatusMeetings) {
  137. case Status.ready:
  138. return SliverList.builder(
  139. itemBuilder: (context, index) {
  140. final userRole = setting.userLocalDb.getUser().role;
  141. final items = state.privateMeetingsModel!.data![index];
  142. return PrivateMeetingWidget(
  143. status: items.accepted ?? 0,
  144. date: items.dateJalali ?? '',
  145. time: items.azHour ?? '',
  146. subject:
  147. items.subject != null ? items.subject!.subject ?? '' : '',
  148. location:
  149. items.location != null ? items.location!.address ?? '' : '',
  150. onAcceptButton:
  151. state.statusAcceptMeeting[items.id] != Status.loading
  152. ? () {
  153. acceptPrivateMeeting(state, context, items.id ?? -1);
  154. }
  155. : null,
  156. onCancelButton:
  157. state.statusCancelMeeting[items.id] != Status.loading
  158. ? () {
  159. cancelPrivateMeeting(state, context, items.id ?? -1);
  160. }
  161. : null,
  162. onSelectedMoreButton: (value) async {
  163. switch (value) {
  164. case 'edit':
  165. await context.pushNamed('privatemeetingedit',
  166. pathParameters: {'id': items.id.toString()});
  167. state.getPrivateMeetings();
  168. case 'report':
  169. await context.pushNamed(
  170. 'privatemeetinsammary',
  171. extra: items, // `items` should be a Datum instance
  172. );
  173. default:
  174. }
  175. },
  176. itemBuilderMoreButton: (context) => [
  177. if (userRole == 0 || userRole == 2)
  178. PopupMenuItem(
  179. value: 'edit',
  180. child: Row(
  181. children: [
  182. Icon(
  183. Icons.edit,
  184. color: Colors.green,
  185. size: 17,
  186. ),
  187. SizedBox(width: 8),
  188. Text(
  189. AppLocalizations.of(context)!.editprivatemeeting,
  190. style: TextStyle(fontSize: 12),
  191. ),
  192. ],
  193. ),
  194. ),
  195. PopupMenuItem(
  196. value: 'report',
  197. child: Row(
  198. children: [
  199. Icon(
  200. Icons.receipt_long,
  201. color: Colors.green,
  202. size: 17,
  203. ),
  204. SizedBox(width: 8),
  205. Text(
  206. AppLocalizations.of(context)!.meetingsummary,
  207. style: TextStyle(fontSize: 12),
  208. ),
  209. ],
  210. ),
  211. ),
  212. ],
  213. );
  214. },
  215. itemCount: state.privateMeetingsModel!.data!.length,
  216. );
  217. case Status.loading:
  218. return SliverFillRemaining(child: const LoadingWidget());
  219. case Status.error:
  220. return SliverFillRemaining(
  221. child: CustomErrorWidget(
  222. onPressed: () async {
  223. await state.getPrivateMeetings(refresh: true);
  224. },
  225. ),
  226. );
  227. case Status.empty:
  228. return SliverFillRemaining(child: EmptyStateWidget());
  229. default:
  230. return Container();
  231. }
  232. }
  233. void cancelPrivateMeeting(
  234. PrivateMeetingsState state, BuildContext context, int cardId) async {
  235. final status = await state.cancelMeeting(id: cardId);
  236. if (status == Status.ready) {
  237. Tools.showCustomSnackBar(
  238. text: AppLocalizations.of(context)!.privatemeetingcanceld,
  239. isError: false,
  240. context,
  241. );
  242. await privateMeetingsState.getPrivateMeetings();
  243. } else {
  244. Tools.showCustomSnackBar(
  245. text: AppLocalizations.of(context)!.error,
  246. isError: true,
  247. context,
  248. );
  249. }
  250. }
  251. void acceptPrivateMeeting(
  252. PrivateMeetingsState state, BuildContext context, int cardId) async {
  253. final status = await state.acceptMeeting(id: cardId);
  254. if (status == Status.ready) {
  255. Tools.showCustomSnackBar(
  256. text: AppLocalizations.of(context)!.privatemeetingaccept,
  257. isError: false,
  258. context,
  259. );
  260. await privateMeetingsState.getPrivateMeetings();
  261. } else {
  262. Tools.showCustomSnackBar(
  263. text: AppLocalizations.of(context)!.error,
  264. isError: true,
  265. context,
  266. );
  267. }
  268. }
  269. }
  270. class PrivateMeetingWidget extends StatelessWidget {
  271. final String date;
  272. final String time;
  273. final String subject;
  274. final String location;
  275. final int status;
  276. final bool hasMoreButton;
  277. final void Function()? onAcceptButton;
  278. final void Function()? onCancelButton;
  279. final void Function(String)? onSelectedMoreButton;
  280. final List<PopupMenuEntry<String>> Function(BuildContext)?
  281. itemBuilderMoreButton;
  282. const PrivateMeetingWidget(
  283. {Key? key,
  284. required this.date,
  285. required this.time,
  286. required this.subject,
  287. required this.location,
  288. required this.status,
  289. this.hasMoreButton = true,
  290. this.onAcceptButton,
  291. this.onCancelButton,
  292. this.onSelectedMoreButton,
  293. this.itemBuilderMoreButton,
  294. th})
  295. : super(key: key);
  296. @override
  297. Widget build(BuildContext context) {
  298. return Padding(
  299. padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
  300. child: Container(
  301. width: 500,
  302. height: 190,
  303. child: Column(
  304. crossAxisAlignment: CrossAxisAlignment.end,
  305. children: [
  306. Padding(
  307. padding: const EdgeInsets.symmetric(horizontal: 10),
  308. child: Text(
  309. this.date,
  310. style: TextStyle(fontSize: 12),
  311. ),
  312. ),
  313. Divider(),
  314. SizedBox(
  315. height: 5,
  316. ),
  317. Container(
  318. decoration: BoxDecoration(
  319. boxShadow: [
  320. BoxShadow(
  321. color: config.ui.mainGray.withOpacity(.1),
  322. spreadRadius: .1,
  323. offset: const Offset(0, 2),
  324. blurRadius: 6)
  325. ],
  326. color: const Color(0xffF4F9F6),
  327. borderRadius: BorderRadius.circular(10)),
  328. child: Padding(
  329. padding:
  330. const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
  331. child: Column(
  332. children: [
  333. Row(
  334. children: [
  335. Text(this.time),
  336. SizedBox(
  337. width: 15,
  338. ),
  339. Container(
  340. width: 3,
  341. height: 45,
  342. decoration: BoxDecoration(
  343. color: Colors.green,
  344. ),
  345. ),
  346. SizedBox(
  347. width: 5,
  348. ),
  349. Expanded(
  350. child: Column(
  351. crossAxisAlignment: CrossAxisAlignment.start,
  352. children: [
  353. Text(
  354. this.subject,
  355. ),
  356. SizedBox(
  357. height: 5,
  358. ),
  359. Text(
  360. this.location,
  361. style: TextStyle(
  362. fontSize: 12, color: Color(0xff9AA8C7)),
  363. ),
  364. ],
  365. ),
  366. ),
  367. if (hasMoreButton) _moreButton(context)
  368. ],
  369. ),
  370. if (this.status == 0)
  371. Padding(
  372. padding: const EdgeInsets.only(top: 15),
  373. child: Row(
  374. mainAxisAlignment: MainAxisAlignment.center,
  375. children: [
  376. CustomButton(
  377. hieght: 30,
  378. text: AppLocalizations.of(context)!.accept,
  379. borderRadius: 5,
  380. color: Color(0xff00A848),
  381. fontSize: 12,
  382. onPressed: this.onAcceptButton,
  383. ),
  384. SizedBox(
  385. width: 7,
  386. ),
  387. CustomButton(
  388. hieght: 30,
  389. text: AppLocalizations.of(context)!.cancel,
  390. color: Colors.red,
  391. textColor: Colors.white,
  392. fontSize: 12,
  393. borderRadius: 5,
  394. onPressed: this.onCancelButton,
  395. ),
  396. SizedBox(
  397. width: 90,
  398. ),
  399. ],
  400. ),
  401. ),
  402. if (this.status == 1 || this.status == 2)
  403. Padding(
  404. padding: const EdgeInsets.only(top: 10),
  405. child: Row(
  406. children: [
  407. SizedBox(
  408. width: 60,
  409. ),
  410. PrivateMeetingLabel(
  411. status: this.status,
  412. ),
  413. ],
  414. ),
  415. )
  416. ],
  417. ),
  418. ),
  419. )
  420. ],
  421. ),
  422. ),
  423. );
  424. }
  425. Widget _moreButton(BuildContext context) {
  426. return PopupMenuButton<String>(
  427. color: Colors.white,
  428. shape: const RoundedRectangleBorder(
  429. borderRadius: BorderRadius.all(
  430. Radius.circular(10.0),
  431. ),
  432. ),
  433. onSelected: onSelectedMoreButton,
  434. itemBuilder: itemBuilderMoreButton!,
  435. icon: const Icon(Icons.more_horiz),
  436. );
  437. }
  438. }
  439. class PrivateMeetingLabel extends StatelessWidget {
  440. final int status;
  441. const PrivateMeetingLabel({
  442. Key? key,
  443. required this.status,
  444. }) : super(key: key);
  445. @override
  446. Widget build(BuildContext context) {
  447. return Container(
  448. width: 82,
  449. child: Center(
  450. child: Padding(
  451. padding: const EdgeInsets.symmetric(vertical: 5),
  452. child: Text(
  453. this.status == 1
  454. ? AppLocalizations.of(context)!.accepted
  455. : AppLocalizations.of(context)!.canceled,
  456. style: TextStyle(fontSize: 12, color: Colors.white),
  457. ),
  458. ),
  459. ),
  460. decoration: BoxDecoration(
  461. color: this.status == 1 ? Colors.green : Colors.red,
  462. borderRadius: BorderRadius.circular(4)),
  463. );
  464. }
  465. }