You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

935 rivejä
40 KiB

  1. import 'dart:io';
  2. import 'package:flutter/material.dart';
  3. import 'package:intl/intl.dart';
  4. import 'package:open_file/open_file.dart';
  5. import 'package:permission_handler/permission_handler.dart';
  6. import 'package:provider/provider.dart';
  7. import 'package:qadirneyriz/config/config.dart';
  8. import 'package:qadirneyriz/global/global_state/global_state.dart';
  9. import 'package:qadirneyriz/screens/report/state.dart';
  10. import 'package:qadirneyriz/setting/setting.dart';
  11. import 'package:qadirneyriz/utils/enums/status.dart';
  12. import 'package:qadirneyriz/utils/tools/tools.dart';
  13. import 'package:qadirneyriz/widgets/ExpansionTileCustom.dart';
  14. import 'package:qadirneyriz/widgets/custom_appbar.dart';
  15. import 'package:qadirneyriz/widgets/custom_button.dart';
  16. import 'package:qadirneyriz/widgets/error_widget.dart';
  17. import 'package:qadirneyriz/widgets/loading_widget.dart';
  18. import 'package:qadirneyriz/widgets/picker.dart';
  19. import 'package:qadirneyriz/widgets/today_widget.dart';
  20. import 'package:flutter_gen/gen_l10n/app_localizations.dart';
  21. class ReportScreen extends StatefulWidget {
  22. const ReportScreen({super.key});
  23. @override
  24. State<ReportScreen> createState() => _ReportScreenState();
  25. }
  26. class _ReportScreenState extends State<ReportScreen> {
  27. @override
  28. Widget build(BuildContext context) {
  29. DateTime now = DateTime.now();
  30. String dateMiladi = setting.userLocalDb.getUser().language == 'fa'
  31. ? Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now))
  32. : DateFormat('yyyy-MM-dd').format(now);
  33. String dateJalali = Tools.convertToPersianDigits(
  34. '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}');
  35. // فرمت کردن تاریخ
  36. return DefaultTabController(
  37. length: 2,
  38. child: CustomScrollView(
  39. slivers: <Widget>[
  40. const CustomAppbar(),
  41. SliverToBoxAdapter(
  42. child: TodayWidget(
  43. formattedDate: setting.userLocalDb.getUser().language == 'en'
  44. ? dateMiladi
  45. : dateJalali),
  46. ),
  47. SliverToBoxAdapter(
  48. child: TabBar(
  49. indicatorColor: config.ui.mainGreen,
  50. labelColor: config.ui.mainGreen,
  51. unselectedLabelColor: config.ui.mainGreen,
  52. tabs: [
  53. Tab(
  54. child: Text(
  55. AppLocalizations.of(context)!.meetings,
  56. style: TextStyle(fontSize: 14),
  57. ),
  58. ),
  59. Tab(
  60. child: Text(
  61. AppLocalizations.of(context)!.privatemeeting,
  62. style: TextStyle(fontSize: 14),
  63. ),
  64. ),
  65. ],
  66. ),
  67. ),
  68. SliverFillRemaining(
  69. child: TabBarView(children: [
  70. FiltersItemInMeetingsReport(),
  71. PrivateMeetingsReportsItems(),
  72. ]),
  73. )
  74. ],
  75. ),
  76. );
  77. }
  78. }
  79. class FiltersItemInMeetingsReport extends StatefulWidget {
  80. const FiltersItemInMeetingsReport({
  81. super.key,
  82. });
  83. @override
  84. State<FiltersItemInMeetingsReport> createState() =>
  85. _FiltersItemInMeetingsReportState();
  86. }
  87. class _FiltersItemInMeetingsReportState
  88. extends State<FiltersItemInMeetingsReport> {
  89. ReportState? reportState;
  90. GlobalState? globalState;
  91. @override
  92. void initState() {
  93. reportState = Provider.of<ReportState>(context, listen: false);
  94. globalState = Provider.of<GlobalState>(context, listen: false);
  95. Future.delayed(Duration.zero, () async {
  96. await globalState!.getAllFiltersItems();
  97. });
  98. super.initState();
  99. }
  100. @override
  101. Widget build(BuildContext context) {
  102. List<MeetingsStatus> meetingStatuses = [
  103. MeetingsStatus(id: 1, title: AppLocalizations.of(context)!.donemeetings),
  104. MeetingsStatus(
  105. id: 2, title: AppLocalizations.of(context)!.adjournedmeetings),
  106. MeetingsStatus(
  107. id: 3, title: AppLocalizations.of(context)!.canceldmeetings),
  108. MeetingsStatus(
  109. id: 4, title: AppLocalizations.of(context)!.meetingswaitingtobeheld),
  110. ];
  111. return Consumer2<ReportState, GlobalState>(
  112. builder: (context, reportState, globalState, child) {
  113. switch (globalState.allFiltersStatus) {
  114. case Status.ready:
  115. return Column(
  116. children: [
  117. Expanded(
  118. child: SingleChildScrollView(
  119. child: Column(
  120. children: [
  121. Column(
  122. mainAxisSize: MainAxisSize.max,
  123. children: [
  124. ExpansionTileCustom(
  125. title: AppLocalizations.of(context)!.date,
  126. widgets: <Widget>[
  127. Row(
  128. crossAxisAlignment: CrossAxisAlignment.end,
  129. mainAxisAlignment:
  130. MainAxisAlignment.spaceBetween,
  131. children: [
  132. PickerCustom(
  133. showDate: reportState.fromDate.isNotEmpty
  134. ? reportState.fromDate
  135. : AppLocalizations.of(context)!
  136. .selectdate, // Show selected date or prompt
  137. onTap: () {
  138. showDialog(
  139. context: context,
  140. builder: (context) {
  141. return Dialog(
  142. child: Tools
  143. .shamsiDateCalendarWidget(
  144. context,
  145. (newDate) {
  146. String fromDateString =
  147. '${newDate.year}/${newDate.month}/${newDate.day}';
  148. reportState.setFromDates(
  149. fromDateString); // Update the selected date
  150. },
  151. ),
  152. );
  153. },
  154. );
  155. },
  156. ),
  157. Text(
  158. AppLocalizations.of(context)!.to,
  159. ),
  160. PickerCustom(
  161. showDate: reportState.toDate.isNotEmpty
  162. ? reportState.toDate
  163. : AppLocalizations.of(context)!
  164. .selectdate, // Show selected date or prompt
  165. onTap: () {
  166. showDialog(
  167. context: context,
  168. builder: (context) {
  169. return Dialog(
  170. child: Tools
  171. .shamsiDateCalendarWidget(
  172. context,
  173. (newDate) {
  174. String toDateString =
  175. '${newDate.year}/${newDate.month}/${newDate.day}';
  176. reportState.setToDates(
  177. toDateString); // Update the selected date
  178. },
  179. ),
  180. );
  181. },
  182. );
  183. },
  184. ),
  185. ],
  186. )
  187. ],
  188. ),
  189. ExpansionTileCustom(
  190. title: AppLocalizations.of(context)!.location,
  191. widgets: <Widget>[
  192. ListView.builder(
  193. primary: false,
  194. physics: NeverScrollableScrollPhysics(),
  195. shrinkWrap: true,
  196. itemCount: globalState.locationsModel!.length,
  197. itemBuilder:
  198. (BuildContext context, int index) {
  199. final items =
  200. globalState.locationsModel![index];
  201. return RadioListTile<int>(
  202. toggleable: true,
  203. groupValue:
  204. reportState.selectedLocationId,
  205. value: items.id ?? -1,
  206. title: Text(
  207. items.address ?? '',
  208. maxLines: 1,
  209. style: TextStyle(
  210. fontWeight: FontWeight.w100,
  211. fontSize: 14),
  212. overflow: TextOverflow.ellipsis,
  213. ),
  214. activeColor: config.ui.secendGreen,
  215. onChanged: (int? newValue) {
  216. reportState
  217. .selectLocation(newValue ?? null);
  218. },
  219. );
  220. },
  221. ),
  222. ],
  223. ),
  224. if (setting.userLocalDb.getUser().role != 1)
  225. ExpansionTileCustom(
  226. title: AppLocalizations.of(context)!
  227. .meetingmanager,
  228. widgets: <Widget>[
  229. ListView.builder(
  230. primary: false,
  231. physics: NeverScrollableScrollPhysics(),
  232. shrinkWrap: true,
  233. itemCount: globalState
  234. .meetingsManagerModel!.length,
  235. itemBuilder:
  236. (BuildContext context, int index) {
  237. final items = globalState
  238. .meetingsManagerModel![index];
  239. return RadioListTile<int>(
  240. toggleable: true,
  241. groupValue:
  242. reportState.selectedManagersId,
  243. value: items.id ?? -1,
  244. title: Text(
  245. items.name ?? '',
  246. style: TextStyle(
  247. fontWeight: FontWeight.w100,
  248. fontSize: 14),
  249. maxLines: 1,
  250. overflow: TextOverflow.ellipsis,
  251. ),
  252. activeColor: config.ui.secendGreen,
  253. onChanged: (int? newValue) {
  254. reportState
  255. .selectManager(newValue ?? null);
  256. },
  257. );
  258. },
  259. ),
  260. ],
  261. ),
  262. ExpansionTileCustom(
  263. title: AppLocalizations.of(context)!.subject,
  264. widgets: <Widget>[
  265. ListView.builder(
  266. primary: false,
  267. physics: NeverScrollableScrollPhysics(),
  268. shrinkWrap: true,
  269. itemCount: globalState.subjectsModel!.length,
  270. itemBuilder:
  271. (BuildContext context, int index) {
  272. final items =
  273. globalState.subjectsModel![index];
  274. return RadioListTile<int>(
  275. toggleable: true,
  276. groupValue: reportState.selectedSubjectId,
  277. value: items.id ?? -1,
  278. title: Text(
  279. items.subject ?? '',
  280. maxLines: 1,
  281. overflow: TextOverflow.ellipsis,
  282. style: TextStyle(
  283. fontWeight: FontWeight.w100,
  284. fontSize: 14),
  285. ),
  286. activeColor: config.ui.secendGreen,
  287. onChanged: (int? newValue) {
  288. reportState
  289. .selectSubject(newValue ?? null);
  290. },
  291. );
  292. },
  293. ),
  294. ],
  295. ),
  296. Divider(),
  297. if (setting.userLocalDb.getUser().role != 1)
  298. SizedBox(
  299. height: 300,
  300. child: ListView.builder(
  301. physics: NeverScrollableScrollPhysics(),
  302. shrinkWrap: true,
  303. primary: false,
  304. itemCount: meetingStatuses.length,
  305. itemBuilder:
  306. (BuildContext context, int index) {
  307. final items = meetingStatuses[index];
  308. return RadioListTile<int>(
  309. toggleable: true,
  310. groupValue: reportState.selectedStatusId,
  311. value: items.id,
  312. title: Text(
  313. items.title,
  314. maxLines: 1,
  315. style: TextStyle(
  316. fontWeight: FontWeight.w100,
  317. fontSize: 14),
  318. overflow: TextOverflow.ellipsis,
  319. ),
  320. activeColor: config.ui.secendGreen,
  321. onChanged: (int? newValue) {
  322. reportState.selectStatusMeeting(
  323. newValue ?? null);
  324. },
  325. );
  326. },
  327. ),
  328. ),
  329. Row(
  330. mainAxisAlignment: MainAxisAlignment.center,
  331. children: [
  332. downloadButtonXlsx(reportState),
  333. const SizedBox(
  334. width: 20,
  335. ),
  336. downloadButtonPdf(reportState),
  337. ],
  338. )
  339. ],
  340. ),
  341. ],
  342. ),
  343. ),
  344. ),
  345. ],
  346. );
  347. case Status.loading:
  348. return const LoadingWidget();
  349. case Status.error:
  350. return CustomErrorWidget(
  351. onPressed: () async {
  352. await globalState.getAllFiltersItems(refresh: true);
  353. },
  354. );
  355. default:
  356. return Container();
  357. }
  358. },
  359. );
  360. }
  361. CustomButton downloadButtonXlsx(ReportState state) {
  362. switch (state.statusDownload['xlsx']) {
  363. case Status.loading:
  364. return CustomButton(
  365. fontSize: 14,
  366. borderRadius: 10,
  367. hieght: 40,
  368. text: AppLocalizations.of(context)!.loading,
  369. width: 150,
  370. );
  371. default:
  372. return CustomButton(
  373. fontSize: 14,
  374. borderRadius: 10,
  375. hieght: 40,
  376. text: AppLocalizations.of(context)!.downloadxlsx,
  377. width: 150,
  378. onPressed: () async {
  379. bool hasPermission = await hasStoragePermission();
  380. if (!hasPermission) {
  381. Tools.showCustomSnackBar(context,
  382. text: 'Permission denied. Please allow storage access.',
  383. isError: true);
  384. return;
  385. }
  386. // Download the file
  387. final status = await state.downloadReport(
  388. format: 'xlsx',
  389. toDate: reportState!.toDate,
  390. fromDate: reportState!.fromDate,
  391. location: reportState!.selectedLocationId,
  392. subject: reportState!.selectedSubjectId,
  393. meetingManager: reportState!.selectedManagersId,
  394. status: reportState!.selectedStatusId);
  395. // print(status);
  396. if (state.statusDownload['xlsx'] == Status.ready) {
  397. await OpenFile.open(state.messageDownload);
  398. } else {
  399. Tools.showCustomSnackBar(
  400. context,
  401. text: AppLocalizations.of(context)!.error,
  402. isError: true,
  403. );
  404. }
  405. },
  406. );
  407. }
  408. }
  409. CustomButton downloadButtonPdf(ReportState state) {
  410. switch (state.statusDownload['pdf']) {
  411. case Status.loading:
  412. return CustomButton(
  413. fontSize: 14,
  414. borderRadius: 10,
  415. hieght: 40,
  416. text: AppLocalizations.of(context)!.loading,
  417. width: 150,
  418. );
  419. default:
  420. return CustomButton(
  421. borderRadius: 10,
  422. fontSize: 14,
  423. hieght: 40,
  424. text: AppLocalizations.of(context)!.downloadPdf,
  425. width: 150,
  426. onPressed: () async {
  427. bool hasPermission = await hasStoragePermission();
  428. if (!hasPermission) {
  429. Tools.showCustomSnackBar(context,
  430. text: 'Permission denied. Please allow storage access.',
  431. isError: true);
  432. return;
  433. }
  434. // Download the file
  435. await state.downloadReport(
  436. format: 'pdf',
  437. toDate: reportState!.toDate,
  438. fromDate: reportState!.fromDate,
  439. location: reportState!.selectedLocationId,
  440. subject: reportState!.selectedSubjectId,
  441. meetingManager: reportState!.selectedManagersId,
  442. status: reportState!.selectedStatusId);
  443. if (state.statusDownload['pdf'] == Status.ready) {
  444. await OpenFile.open(state.messageDownload);
  445. // print(status.message);
  446. } else {
  447. Tools.showCustomSnackBar(
  448. context,
  449. text: AppLocalizations.of(context)!.error,
  450. isError: true,
  451. );
  452. }
  453. },
  454. );
  455. }
  456. }
  457. Future<bool> hasStoragePermission() async {
  458. if (Platform.isAndroid) {
  459. final status = await Permission.storage.status;
  460. if (status != PermissionStatus.granted) {
  461. final result = await Permission.manageExternalStorage.request();
  462. if (result == PermissionStatus.granted) {
  463. return true;
  464. }
  465. } else {
  466. return true;
  467. }
  468. } else {
  469. return true;
  470. }
  471. return false;
  472. }
  473. }
  474. class PrivateMeetingsReportsItems extends StatefulWidget {
  475. const PrivateMeetingsReportsItems({super.key});
  476. @override
  477. State<PrivateMeetingsReportsItems> createState() =>
  478. _PrivateMeetingsReportsItemsState();
  479. }
  480. class _PrivateMeetingsReportsItemsState
  481. extends State<PrivateMeetingsReportsItems> {
  482. ReportState? reportState;
  483. GlobalState? globalState;
  484. @override
  485. void initState() {
  486. reportState = Provider.of<ReportState>(context, listen: false);
  487. globalState = Provider.of<GlobalState>(context, listen: false);
  488. // Future.delayed(Duration.zero, () async {
  489. // await globalState!.getAllFiltersItems();
  490. // });
  491. super.initState();
  492. }
  493. @override
  494. Widget build(BuildContext context) {
  495. List<MeetingsStatus> meetingStatuses = [
  496. MeetingsStatus(
  497. id: 1, title: AppLocalizations.of(context)!.doneprivatemeetings),
  498. MeetingsStatus(
  499. id: 2, title: AppLocalizations.of(context)!.adjournedprivatemeetings),
  500. MeetingsStatus(
  501. id: 3, title: AppLocalizations.of(context)!.canceldprivatemeetings),
  502. MeetingsStatus(
  503. id: 4,
  504. title: AppLocalizations.of(context)!.privatemeetingswaitingtobeheld),
  505. ];
  506. return Consumer2<ReportState, GlobalState>(
  507. builder: (context, reportState, globalState, child) {
  508. switch (globalState.allFiltersStatus) {
  509. case Status.ready:
  510. return Column(
  511. children: [
  512. Expanded(
  513. child: SingleChildScrollView(
  514. child: Column(
  515. children: [
  516. Column(
  517. mainAxisSize: MainAxisSize.max,
  518. children: [
  519. ExpansionTileCustom(
  520. title: AppLocalizations.of(context)!.date,
  521. widgets: <Widget>[
  522. Row(
  523. crossAxisAlignment: CrossAxisAlignment.end,
  524. mainAxisAlignment:
  525. MainAxisAlignment.spaceBetween,
  526. children: [
  527. PickerCustom(
  528. showDate: reportState
  529. .fromDatePrivateMeeting.isNotEmpty
  530. ? reportState.fromDatePrivateMeeting
  531. : AppLocalizations.of(context)!
  532. .selectdate, // Show selected date or prompt
  533. onTap: () {
  534. showDialog(
  535. context: context,
  536. builder: (context) {
  537. return Dialog(
  538. child: Tools
  539. .shamsiDateCalendarWidget(
  540. context,
  541. (newDate) {
  542. String
  543. fromDateStringPrivateMeeting =
  544. '${newDate.year}/${newDate.month}/${newDate.day}';
  545. reportState
  546. .setFromDatesPrivateMeeting(
  547. fromDateStringPrivateMeeting); // Update the selected date
  548. },
  549. ),
  550. );
  551. },
  552. );
  553. },
  554. ),
  555. Text(
  556. AppLocalizations.of(context)!.to,
  557. ),
  558. PickerCustom(
  559. showDate: reportState
  560. .toDatePrivateMeeting.isNotEmpty
  561. ? reportState.toDatePrivateMeeting
  562. : AppLocalizations.of(context)!
  563. .selectdate, // Show selected date or prompt
  564. onTap: () {
  565. showDialog(
  566. context: context,
  567. builder: (context) {
  568. return Dialog(
  569. child: Tools
  570. .shamsiDateCalendarWidget(
  571. context,
  572. (newDate) {
  573. String
  574. toDateStringPrivateMeeting =
  575. '${newDate.year}/${newDate.month}/${newDate.day}';
  576. reportState
  577. .setToDatesPrivateMeeting(
  578. toDateStringPrivateMeeting); // Update the selected date
  579. },
  580. ),
  581. );
  582. },
  583. );
  584. },
  585. ),
  586. ],
  587. )
  588. ],
  589. ),
  590. ExpansionTileCustom(
  591. title: AppLocalizations.of(context)!.location,
  592. widgets: <Widget>[
  593. ListView.builder(
  594. primary: false,
  595. physics: NeverScrollableScrollPhysics(),
  596. shrinkWrap: true,
  597. itemCount: globalState.locationsModel!.length,
  598. itemBuilder:
  599. (BuildContext context, int index) {
  600. final items =
  601. globalState.locationsModel![index];
  602. return RadioListTile<int>(
  603. toggleable: true,
  604. groupValue: reportState
  605. .selectedLocationIdPrivateMeeting,
  606. value: items.id ?? -1,
  607. title: Text(
  608. items.address ?? '',
  609. maxLines: 1,
  610. style: TextStyle(
  611. fontWeight: FontWeight.w100,
  612. fontSize: 14),
  613. overflow: TextOverflow.ellipsis,
  614. ),
  615. activeColor: config.ui.secendGreen,
  616. onChanged: (int? newValue) {
  617. reportState
  618. .selectLocationPrivateMeeting(
  619. newValue ?? null);
  620. },
  621. );
  622. },
  623. ),
  624. ],
  625. ),
  626. if (setting.userLocalDb.getUser().role != 1)
  627. ExpansionTileCustom(
  628. title: AppLocalizations.of(context)!
  629. .meetingmanager,
  630. widgets: <Widget>[
  631. ListView.builder(
  632. primary: false,
  633. physics: NeverScrollableScrollPhysics(),
  634. shrinkWrap: true,
  635. itemCount: globalState
  636. .meetingsManagerModel!.length,
  637. itemBuilder:
  638. (BuildContext context, int index) {
  639. final items = globalState
  640. .meetingsManagerModel![index];
  641. return RadioListTile<int>(
  642. toggleable: true,
  643. groupValue: reportState
  644. .selectedManagersIdPrivateMeeting,
  645. value: items.id ?? -1,
  646. title: Text(
  647. items.name ?? '',
  648. style: TextStyle(
  649. fontWeight: FontWeight.w100,
  650. fontSize: 14),
  651. maxLines: 1,
  652. overflow: TextOverflow.ellipsis,
  653. ),
  654. activeColor: config.ui.secendGreen,
  655. onChanged: (int? newValue) {
  656. reportState
  657. .selectManagerPrivateMeeting(
  658. newValue ?? null);
  659. },
  660. );
  661. },
  662. ),
  663. ],
  664. ),
  665. ExpansionTileCustom(
  666. title: AppLocalizations.of(context)!.subject,
  667. widgets: <Widget>[
  668. ListView.builder(
  669. primary: false,
  670. physics: NeverScrollableScrollPhysics(),
  671. shrinkWrap: true,
  672. itemCount: globalState.subjectsModel!.length,
  673. itemBuilder:
  674. (BuildContext context, int index) {
  675. final items =
  676. globalState.subjectsModel![index];
  677. return RadioListTile<int>(
  678. toggleable: true,
  679. groupValue: reportState
  680. .selectedSubjectIdPrivateMeeting,
  681. value: items.id ?? -1,
  682. title: Text(
  683. items.subject ?? '',
  684. maxLines: 1,
  685. overflow: TextOverflow.ellipsis,
  686. style: TextStyle(
  687. fontWeight: FontWeight.w100,
  688. fontSize: 14),
  689. ),
  690. activeColor: config.ui.secendGreen,
  691. onChanged: (int? newValue) {
  692. reportState.selectSubjectPrivateMeeting(
  693. newValue ?? null);
  694. },
  695. );
  696. },
  697. ),
  698. ],
  699. ),
  700. Divider(),
  701. if (setting.userLocalDb.getUser().role != 1)
  702. SizedBox(
  703. height: 300,
  704. child: ListView.builder(
  705. physics: NeverScrollableScrollPhysics(),
  706. shrinkWrap: true,
  707. primary: false,
  708. itemCount: meetingStatuses.length,
  709. itemBuilder:
  710. (BuildContext context, int index) {
  711. final items = meetingStatuses[index];
  712. return RadioListTile<int>(
  713. toggleable: true,
  714. groupValue: reportState
  715. .selectedStatusIdPrivateMeeting,
  716. value: items.id,
  717. title: Text(
  718. items.title,
  719. maxLines: 1,
  720. style: TextStyle(
  721. fontWeight: FontWeight.w100,
  722. fontSize: 14),
  723. overflow: TextOverflow.ellipsis,
  724. ),
  725. activeColor: config.ui.secendGreen,
  726. onChanged: (int? newValue) {
  727. reportState
  728. .selectStatusMeetingPrivateMeeting(
  729. newValue ?? null);
  730. },
  731. );
  732. },
  733. ),
  734. ),
  735. Row(
  736. mainAxisAlignment: MainAxisAlignment.center,
  737. children: [
  738. downloadButtonXlsx(reportState),
  739. SizedBox(
  740. width: 20,
  741. ),
  742. downloadButtonPdf(reportState),
  743. ],
  744. )
  745. ],
  746. ),
  747. ],
  748. ),
  749. ),
  750. ),
  751. ],
  752. );
  753. case Status.loading:
  754. return const LoadingWidget();
  755. case Status.error:
  756. return CustomErrorWidget(
  757. onPressed: () async {
  758. await globalState.getAllFiltersItems(refresh: true);
  759. },
  760. );
  761. default:
  762. return Container();
  763. }
  764. },
  765. );
  766. }
  767. CustomButton downloadButtonXlsx(ReportState state) {
  768. switch (state.statusDownloadPrivateMeeting['xlsx']) {
  769. case Status.loading:
  770. return CustomButton(
  771. fontSize: 14,
  772. borderRadius: 10,
  773. hieght: 40,
  774. text: AppLocalizations.of(context)!.loading,
  775. width: 150,
  776. );
  777. default:
  778. return CustomButton(
  779. fontSize: 14,
  780. borderRadius: 10,
  781. hieght: 40,
  782. text: AppLocalizations.of(context)!.downloadxlsx,
  783. width: 150,
  784. onPressed: () async {
  785. bool hasPermission = await hasStoragePermission();
  786. if (!hasPermission) {
  787. Tools.showCustomSnackBar(context,
  788. text: 'Permission denied. Please allow storage access.',
  789. isError: true);
  790. return;
  791. }
  792. // Download the file
  793. final status = await state.downloadReportPrivateMeeting(
  794. format: 'xlsx',
  795. toDate: reportState!.toDatePrivateMeeting,
  796. fromDate: reportState!.fromDatePrivateMeeting,
  797. location: reportState!.selectedLocationIdPrivateMeeting,
  798. subject: reportState!.selectedSubjectIdPrivateMeeting,
  799. meetingManager: reportState!.selectedManagersIdPrivateMeeting,
  800. status: reportState!.selectedStatusIdPrivateMeeting);
  801. print(status);
  802. if (state.statusDownloadPrivateMeeting['xlsx'] == Status.ready) {
  803. await OpenFile.open(state.messageDownloadPrivateMeeting);
  804. } else {
  805. Tools.showCustomSnackBar(
  806. context,
  807. text: AppLocalizations.of(context)!.error,
  808. isError: true,
  809. );
  810. }
  811. },
  812. );
  813. }
  814. }
  815. CustomButton downloadButtonPdf(ReportState state) {
  816. switch (state.statusDownloadPrivateMeeting['pdf']) {
  817. case Status.loading:
  818. return CustomButton(
  819. fontSize: 14,
  820. borderRadius: 10,
  821. hieght: 40,
  822. text: AppLocalizations.of(context)!.loading,
  823. width: 150,
  824. );
  825. default:
  826. return CustomButton(
  827. fontSize: 14,
  828. borderRadius: 10,
  829. hieght: 40,
  830. text: AppLocalizations.of(context)!.downloadPdf,
  831. width: 150,
  832. onPressed: () async {
  833. bool hasPermission = await hasStoragePermission();
  834. if (!hasPermission) {
  835. Tools.showCustomSnackBar(context,
  836. text: 'Permission denied. Please allow storage access.',
  837. isError: true);
  838. return;
  839. }
  840. // Download the file
  841. await state.downloadReportPrivateMeeting(
  842. format: 'pdf',
  843. toDate: reportState!.toDatePrivateMeeting,
  844. fromDate: reportState!.fromDatePrivateMeeting,
  845. location: reportState!.selectedLocationIdPrivateMeeting,
  846. subject: reportState!.selectedSubjectIdPrivateMeeting,
  847. meetingManager: reportState!.selectedManagersIdPrivateMeeting,
  848. status: reportState!.selectedStatusIdPrivateMeeting);
  849. if (state.statusDownloadPrivateMeeting['pdf'] == Status.ready) {
  850. await OpenFile.open(state.messageDownloadPrivateMeeting);
  851. // print(status.message);
  852. } else {
  853. Tools.showCustomSnackBar(
  854. context,
  855. text: AppLocalizations.of(context)!.error,
  856. isError: true,
  857. );
  858. }
  859. },
  860. );
  861. }
  862. }
  863. Future<bool> hasStoragePermission() async {
  864. if (Platform.isAndroid) {
  865. final status = await Permission.storage.status;
  866. if (status != PermissionStatus.granted) {
  867. final result = await Permission.manageExternalStorage.request();
  868. if (result == PermissionStatus.granted) {
  869. return true;
  870. }
  871. } else {
  872. return true;
  873. }
  874. } else {
  875. return true;
  876. }
  877. return false;
  878. }
  879. }
  880. class LineButtomSheet extends StatelessWidget {
  881. const LineButtomSheet({
  882. super.key,
  883. });
  884. @override
  885. Widget build(BuildContext context) {
  886. return Container(
  887. margin: const EdgeInsets.only(top: 8.0),
  888. width: 30.0,
  889. height: 3.0,
  890. decoration: BoxDecoration(
  891. color: Colors.grey.shade400,
  892. borderRadius: BorderRadius.circular(24.0),
  893. ),
  894. );
  895. }
  896. }
  897. class MeetingsStatus {
  898. int id;
  899. String title;
  900. MeetingsStatus({
  901. required this.id,
  902. required this.title,
  903. });
  904. }