25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

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