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

342 lines
13 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/meeting/diolog_meetings_filters.dart';
  10. import 'package:qadirneyriz/screens/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/card_meeting.dart';
  15. import 'package:qadirneyriz/widgets/custom_appbar.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 MeetingsScreen extends StatefulWidget {
  22. const MeetingsScreen({super.key});
  23. @override
  24. State<MeetingsScreen> createState() => _MeetingsScreenState();
  25. }
  26. class _MeetingsScreenState extends State<MeetingsScreen> {
  27. late ScrollController _scrollController;
  28. late MeetingsState meetingsState;
  29. @override
  30. void initState() {
  31. _scrollController = ScrollController();
  32. _scrollController.addListener(_scrollListener);
  33. meetingsState = Provider.of<MeetingsState>(context, listen: false);
  34. Future.delayed(Duration.zero, () async {
  35. await meetingsState.getMeetings();
  36. meetingsState.clearFilters();
  37. });
  38. super.initState();
  39. }
  40. @override
  41. void dispose() {
  42. _scrollController.dispose();
  43. super.dispose();
  44. }
  45. _scrollListener() async {
  46. if (_scrollController.offset >=
  47. _scrollController.position.maxScrollExtent &&
  48. !_scrollController.position.outOfRange) {
  49. if (!meetingsState.pageEndedMeetings) {
  50. await meetingsState.nextPageMeetings(
  51. toDate: meetingsState.toDate,
  52. fromDate: meetingsState.fromDate,
  53. location: meetingsState.selectedLocationId,
  54. subject: meetingsState.selectedSubjectId,
  55. meetingManager: meetingsState.selectedManagersId,
  56. meetingStatus: meetingsState.selectedStatusId);
  57. }
  58. }
  59. }
  60. @override
  61. Widget build(BuildContext context) {
  62. DateTime now = DateTime.now();
  63. String dateMiladi = setting.userLocalDb.getUser().language == 'fa'
  64. ? Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now))
  65. : DateFormat('yyyy-MM-dd').format(now);
  66. String dateJalali = Tools.convertToPersianDigits(
  67. '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}');
  68. // فرمت کردن تاریخ
  69. return Consumer<MeetingsState>(
  70. builder: (context, value, child) {
  71. return RefreshIndicator(
  72. onRefresh: () async {
  73. await meetingsState.getMeetings();
  74. },
  75. child: CustomScrollView(
  76. physics: AlwaysScrollableScrollPhysics(),
  77. controller: _scrollController,
  78. slivers: <Widget>[
  79. const CustomAppbar(),
  80. SliverToBoxAdapter(
  81. child: TodayWidget(
  82. formattedDate:
  83. setting.userLocalDb.getUser().language == 'en'
  84. ? dateMiladi
  85. : dateJalali),
  86. ),
  87. SliverToBoxAdapter(
  88. child: Padding(
  89. padding:
  90. const EdgeInsets.symmetric(vertical: 30, horizontal: 15),
  91. child: Row(
  92. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  93. crossAxisAlignment: CrossAxisAlignment.center,
  94. children: [
  95. Text(
  96. style: const TextStyle(fontSize: 14),
  97. AppLocalizations.of(context)!.meetings,
  98. ),
  99. IconButtonCustom(
  100. iconColor: value.hasActiveFilters()
  101. ? Colors.white
  102. : config.ui.secendGreen,
  103. backColor: value.hasActiveFilters()
  104. ? config.ui.secendGreen
  105. : Colors.white,
  106. icon: FontAwesomeIcons.sliders,
  107. onTap: () {
  108. showModalBottomSheet(
  109. isScrollControlled: true,
  110. useSafeArea: true,
  111. context: context,
  112. builder: (context) {
  113. return DiologMeetingsFilters();
  114. },
  115. );
  116. },
  117. )
  118. ],
  119. ),
  120. ),
  121. ),
  122. meetingsList(value),
  123. (value.paginationMeetings == Status.ready ||
  124. value.paginationMeetings == Status.empty)
  125. ? const SliverToBoxAdapter()
  126. : const SliverToBoxAdapter(
  127. child: Center(
  128. child: LoadingWidget(
  129. size: 10,
  130. ),
  131. ),
  132. )
  133. ],
  134. ),
  135. );
  136. },
  137. );
  138. }
  139. Widget meetingsList(MeetingsState state) {
  140. switch (state.statusMeetings) {
  141. case Status.ready:
  142. return SliverList.builder(
  143. itemBuilder: (context, index) {
  144. final userRole = setting.userLocalDb.getUser().role;
  145. final items = state.meetingsModel!.data![index];
  146. return Padding(
  147. padding: const EdgeInsets.all(8.0),
  148. child: CustomCardMeeting(
  149. status: items.accepted ?? 0,
  150. titel:
  151. items.subject != null ? items.subject!.subject ?? '' : '',
  152. fromTime: items.azHour ?? '',
  153. toTime: items.taHour ?? "",
  154. location: items.location != null
  155. ? items.location!.address ?? ''
  156. : '',
  157. date: items.dateJalali ?? '',
  158. cardId: items.id ?? 0,
  159. onSelectedMoreButton: (value) async {
  160. switch (value) {
  161. case 'edit':
  162. await context.pushNamed('meetingedit',
  163. pathParameters: {'id': items.id.toString()});
  164. meetingsState.getMeetings();
  165. case 'confirm':
  166. acceptMeeting(state, context, items.id ?? -1);
  167. case 'cancel':
  168. cancelMeeting(state, context, items.id ?? -1);
  169. case 'report':
  170. if (userRole == 1 && items.description != null) {
  171. await context.pushNamed(
  172. 'meetinsammary',
  173. extra: items, // `items` should be a Datum instance
  174. );
  175. await meetingsState.getMeetings();
  176. } else if (userRole == 1 && items.description == null) {
  177. Tools.showCustomSnackBar(
  178. text:
  179. AppLocalizations.of(context)!.thereisnosummary,
  180. isError: true,
  181. context,
  182. );
  183. } else {
  184. await context.pushNamed(
  185. 'meetinsammary',
  186. extra: items, // `items` should be a Datum instance
  187. );
  188. await meetingsState.getMeetings();
  189. }
  190. default:
  191. }
  192. },
  193. itemBuilderMoreButton: (context) => [
  194. if (userRole == 0 || userRole == 2)
  195. PopupMenuItem(
  196. value: 'edit',
  197. child: Row(
  198. children: [
  199. Icon(
  200. Icons.edit,
  201. color: Colors.green,
  202. size: 17,
  203. ),
  204. SizedBox(width: 8),
  205. Text(
  206. AppLocalizations.of(context)!.editmeeting,
  207. style: TextStyle(fontSize: 12),
  208. ),
  209. ],
  210. ),
  211. ),
  212. if ((userRole == 0 || userRole == 2) &&
  213. items.accepted == 0)
  214. PopupMenuItem(
  215. enabled:
  216. state.statusAcceptMeeting != Status.loading,
  217. value: 'confirm',
  218. child: Row(
  219. children: [
  220. Icon(
  221. Icons.check_circle,
  222. color: Colors.green,
  223. size: 17,
  224. ),
  225. SizedBox(width: 8),
  226. Text(
  227. AppLocalizations.of(context)!.acceptmeeting,
  228. style: TextStyle(fontSize: 12),
  229. ),
  230. ],
  231. ),
  232. ),
  233. if ((userRole == 0 || userRole == 2) &&
  234. items.accepted == 0)
  235. PopupMenuItem(
  236. enabled:
  237. state.statusCancelMeeting != Status.loading,
  238. value: 'cancel',
  239. child: Row(
  240. children: [
  241. Icon(
  242. Icons.cancel,
  243. color: Colors.green,
  244. size: 17,
  245. ),
  246. SizedBox(width: 8),
  247. Text(
  248. AppLocalizations.of(context)!.cancelmeeting,
  249. style: TextStyle(fontSize: 12),
  250. ),
  251. ],
  252. ),
  253. ),
  254. PopupMenuItem(
  255. value: 'report',
  256. child: Row(
  257. children: [
  258. Icon(
  259. Icons.receipt_long,
  260. color: Colors.green,
  261. size: 17,
  262. ),
  263. SizedBox(width: 8),
  264. Text(
  265. AppLocalizations.of(context)!.meetingsummary,
  266. style: TextStyle(fontSize: 12),
  267. ),
  268. ],
  269. ),
  270. ),
  271. ]),
  272. );
  273. },
  274. itemCount: state.meetingsModel!.data!.length,
  275. );
  276. case Status.loading:
  277. return SliverFillRemaining(child: const LoadingWidget());
  278. case Status.error:
  279. return SliverFillRemaining(
  280. child: CustomErrorWidget(
  281. onPressed: () async {
  282. await state.getMeetings(refresh: true);
  283. },
  284. ),
  285. );
  286. case Status.empty:
  287. return SliverFillRemaining(child: EmptyStateWidget());
  288. default:
  289. return Container();
  290. }
  291. }
  292. void cancelMeeting(
  293. MeetingsState state, BuildContext context, int cardId) async {
  294. final status = await state.cancelMeeting(id: cardId);
  295. if (status == Status.ready) {
  296. Tools.showCustomSnackBar(
  297. text: AppLocalizations.of(context)!.meetingcanceled,
  298. isError: false,
  299. context,
  300. );
  301. await meetingsState.getMeetings();
  302. } else {
  303. Tools.showCustomSnackBar(
  304. text: AppLocalizations.of(context)!.error,
  305. isError: true,
  306. context,
  307. );
  308. }
  309. }
  310. void acceptMeeting(
  311. MeetingsState 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)!.meetingaccepted,
  316. isError: false,
  317. context,
  318. );
  319. await meetingsState.getMeetings();
  320. } else {
  321. Tools.showCustomSnackBar(
  322. text: AppLocalizations.of(context)!.error,
  323. isError: true,
  324. context,
  325. );
  326. }
  327. }
  328. }