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.
 
 
 
 
 
 

485 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 = 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. default:
  175. }
  176. },
  177. itemBuilderMoreButton: (context) => [
  178. if (userRole == 0 || userRole == 2)
  179. PopupMenuItem(
  180. value: 'edit',
  181. child: Row(
  182. children: [
  183. Icon(
  184. Icons.edit,
  185. color: Colors.green,
  186. size: 17,
  187. ),
  188. SizedBox(width: 8),
  189. Text(
  190. AppLocalizations.of(context)!.editprivatemeeting,
  191. style: TextStyle(fontSize: 12),
  192. ),
  193. ],
  194. ),
  195. ),
  196. PopupMenuItem(
  197. value: 'report',
  198. child: Row(
  199. children: [
  200. Icon(
  201. Icons.receipt_long,
  202. color: Colors.green,
  203. size: 17,
  204. ),
  205. SizedBox(width: 8),
  206. Text(
  207. AppLocalizations.of(context)!.meetingsummary,
  208. style: TextStyle(fontSize: 12),
  209. ),
  210. ],
  211. ),
  212. ),
  213. ],
  214. );
  215. },
  216. itemCount: state.privateMeetingsModel!.data!.length,
  217. );
  218. case Status.loading:
  219. return SliverFillRemaining(child: const LoadingWidget());
  220. case Status.error:
  221. return SliverFillRemaining(
  222. child: CustomErrorWidget(
  223. onPressed: () async {
  224. await state.getPrivateMeetings(refresh: true);
  225. },
  226. ),
  227. );
  228. case Status.empty:
  229. return SliverFillRemaining(child: EmptyStateWidget());
  230. default:
  231. return Container();
  232. }
  233. }
  234. void cancelPrivateMeeting(
  235. PrivateMeetingsState state, BuildContext context, int cardId) async {
  236. final status = await state.cancelMeeting(id: cardId);
  237. if (status == Status.ready) {
  238. Tools.showCustomSnackBar(
  239. text: AppLocalizations.of(context)!.privatemeetingcanceld,
  240. isError: false,
  241. context,
  242. );
  243. await privateMeetingsState.getPrivateMeetings();
  244. } else {
  245. Tools.showCustomSnackBar(
  246. text: AppLocalizations.of(context)!.error,
  247. isError: true,
  248. context,
  249. );
  250. }
  251. }
  252. void acceptPrivateMeeting(
  253. PrivateMeetingsState state, BuildContext context, int cardId) async {
  254. final status = await state.acceptMeeting(id: cardId);
  255. if (status == Status.ready) {
  256. Tools.showCustomSnackBar(
  257. text: AppLocalizations.of(context)!.privatemeetingaccept,
  258. isError: false,
  259. context,
  260. );
  261. await privateMeetingsState.getPrivateMeetings();
  262. } else {
  263. Tools.showCustomSnackBar(
  264. text: AppLocalizations.of(context)!.error,
  265. isError: true,
  266. context,
  267. );
  268. }
  269. }
  270. }
  271. class PrivateMeetingWidget extends StatelessWidget {
  272. final String date;
  273. final String time;
  274. final String subject;
  275. final String location;
  276. final int status;
  277. final bool hasMoreButton;
  278. final void Function()? onAcceptButton;
  279. final void Function()? onCancelButton;
  280. final void Function(String)? onSelectedMoreButton;
  281. final List<PopupMenuEntry<String>> Function(BuildContext)?
  282. itemBuilderMoreButton;
  283. const PrivateMeetingWidget(
  284. {Key? key,
  285. required this.date,
  286. required this.time,
  287. required this.subject,
  288. required this.location,
  289. required this.status,
  290. this.hasMoreButton = true,
  291. this.onAcceptButton,
  292. this.onCancelButton,
  293. this.onSelectedMoreButton,
  294. this.itemBuilderMoreButton,
  295. th})
  296. : super(key: key);
  297. @override
  298. Widget build(BuildContext context) {
  299. return Padding(
  300. padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
  301. child: Container(
  302. child: Column(
  303. crossAxisAlignment: CrossAxisAlignment.end,
  304. children: [
  305. Padding(
  306. padding: const EdgeInsets.symmetric(horizontal: 10),
  307. child: Text(
  308. this.date,
  309. style: TextStyle(fontSize: 12),
  310. ),
  311. ),
  312. Divider(),
  313. SizedBox(
  314. height: 5,
  315. ),
  316. Container(
  317. decoration: BoxDecoration(
  318. boxShadow: [
  319. BoxShadow(
  320. color: config.ui.mainGray.withOpacity(.1),
  321. spreadRadius: .1,
  322. offset: const Offset(0, 2),
  323. blurRadius: 6)
  324. ],
  325. color: const Color(0xffF4F9F6),
  326. borderRadius: BorderRadius.circular(10)),
  327. child: Padding(
  328. padding:
  329. const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
  330. child: Column(
  331. children: [
  332. Row(
  333. children: [
  334. Text(this.time),
  335. SizedBox(
  336. width: 15,
  337. ),
  338. Container(
  339. width: 3,
  340. height: 45,
  341. decoration: BoxDecoration(
  342. color: Colors.green,
  343. ),
  344. ),
  345. SizedBox(
  346. width: 5,
  347. ),
  348. Expanded(
  349. child: Column(
  350. crossAxisAlignment: CrossAxisAlignment.start,
  351. children: [
  352. Text(
  353. this.subject,
  354. ),
  355. SizedBox(
  356. height: 5,
  357. ),
  358. Text(
  359. this.location,
  360. style: TextStyle(
  361. fontSize: 12, color: Color(0xff9AA8C7)),
  362. ),
  363. ],
  364. ),
  365. ),
  366. if (hasMoreButton) _moreButton(context)
  367. ],
  368. ),
  369. if (this.status == 0)
  370. Padding(
  371. padding: const EdgeInsets.only(top: 15),
  372. child: Row(
  373. mainAxisAlignment: MainAxisAlignment.center,
  374. children: [
  375. CustomButton(
  376. hieght: 30,
  377. text: AppLocalizations.of(context)!.accept,
  378. borderRadius: 5,
  379. color: Color(0xff00A848),
  380. fontSize: 12,
  381. onPressed: this.onAcceptButton,
  382. ),
  383. SizedBox(
  384. width: 7,
  385. ),
  386. CustomButton(
  387. hieght: 30,
  388. text: AppLocalizations.of(context)!.cancel,
  389. color: Colors.red,
  390. textColor: Colors.white,
  391. fontSize: 12,
  392. borderRadius: 5,
  393. onPressed: this.onCancelButton,
  394. ),
  395. SizedBox(
  396. width: 90,
  397. ),
  398. ],
  399. ),
  400. ),
  401. if (this.status == 1 || this.status == 2)
  402. Padding(
  403. padding: const EdgeInsets.only(top: 10),
  404. child: Row(
  405. children: [
  406. SizedBox(
  407. width: 60,
  408. ),
  409. PrivateMeetingLabel(
  410. status: this.status,
  411. ),
  412. ],
  413. ),
  414. )
  415. ],
  416. ),
  417. ),
  418. )
  419. ],
  420. ),
  421. ),
  422. );
  423. }
  424. Widget _moreButton(BuildContext context) {
  425. return PopupMenuButton<String>(
  426. color: Colors.white,
  427. shape: const RoundedRectangleBorder(
  428. borderRadius: BorderRadius.all(
  429. Radius.circular(10.0),
  430. ),
  431. ),
  432. onSelected: onSelectedMoreButton,
  433. itemBuilder: itemBuilderMoreButton!,
  434. icon: const Icon(Icons.more_horiz),
  435. );
  436. }
  437. }
  438. class PrivateMeetingLabel extends StatelessWidget {
  439. final int status;
  440. const PrivateMeetingLabel({
  441. Key? key,
  442. required this.status,
  443. }) : super(key: key);
  444. @override
  445. Widget build(BuildContext context) {
  446. return Container(
  447. width: 82,
  448. child: Center(
  449. child: Padding(
  450. padding: const EdgeInsets.symmetric(vertical: 5),
  451. child: Text(
  452. this.status == 1
  453. ? AppLocalizations.of(context)!.accepted
  454. : AppLocalizations.of(context)!.canceled,
  455. style: TextStyle(fontSize: 12, color: Colors.white),
  456. ),
  457. ),
  458. ),
  459. decoration: BoxDecoration(
  460. color: this.status == 1 ? Colors.green : Colors.red,
  461. borderRadius: BorderRadius.circular(4)),
  462. );
  463. }
  464. }