Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

429 Zeilen
18 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 CustomScrollView(
  37. slivers: <Widget>[
  38. const CustomAppbar(),
  39. SliverToBoxAdapter(
  40. child: TodayWidget(
  41. formattedDate: setting.userLocalDb.getUser().language == 'en'
  42. ? dateMiladi
  43. : dateJalali),
  44. ),
  45. SliverFillRemaining(
  46. child: FiltersItemInReport(),
  47. )
  48. ],
  49. );
  50. }
  51. }
  52. class FiltersItemInReport extends StatefulWidget {
  53. const FiltersItemInReport({
  54. super.key,
  55. });
  56. @override
  57. State<FiltersItemInReport> createState() => _FiltersItemInReportState();
  58. }
  59. class _FiltersItemInReportState extends State<FiltersItemInReport> {
  60. ReportState? reportState;
  61. GlobalState? globalState;
  62. @override
  63. void initState() {
  64. reportState = Provider.of<ReportState>(context, listen: false);
  65. globalState = Provider.of<GlobalState>(context, listen: false);
  66. Future.delayed(Duration.zero, () async {
  67. await globalState!.getAllFiltersItems();
  68. });
  69. super.initState();
  70. }
  71. @override
  72. Widget build(BuildContext context) {
  73. List<MeetingsStatus> meetingStatuses = [
  74. MeetingsStatus(id: 1, title: AppLocalizations.of(context)!.donemeetings),
  75. MeetingsStatus(
  76. id: 2, title: AppLocalizations.of(context)!.adjournedmeetings),
  77. MeetingsStatus(
  78. id: 3, title: AppLocalizations.of(context)!.canceldmeetings),
  79. MeetingsStatus(
  80. id: 4, title: AppLocalizations.of(context)!.meetingswaitingtobeheld),
  81. ];
  82. return Consumer2<ReportState, GlobalState>(
  83. builder: (context, reportState, globalState, child) {
  84. switch (globalState.allFiltersStatus) {
  85. case Status.ready:
  86. return Column(
  87. children: [
  88. Expanded(
  89. child: SingleChildScrollView(
  90. child: Column(
  91. children: [
  92. Column(
  93. mainAxisSize: MainAxisSize.max,
  94. children: [
  95. ExpansionTileCustom(
  96. title: AppLocalizations.of(context)!.date,
  97. widgets: <Widget>[
  98. Row(
  99. crossAxisAlignment: CrossAxisAlignment.end,
  100. mainAxisAlignment:
  101. MainAxisAlignment.spaceBetween,
  102. children: [
  103. PickerCustom(
  104. showDate: reportState.fromDate.isNotEmpty
  105. ? reportState.fromDate
  106. : AppLocalizations.of(context)!
  107. .selectdate, // Show selected date or prompt
  108. onTap: () {
  109. showDialog(
  110. context: context,
  111. builder: (context) {
  112. return Dialog(
  113. child: Tools
  114. .shamsiDateCalendarWidget(
  115. context,
  116. (newDate) {
  117. String fromDateString =
  118. '${newDate.year}/${newDate.month}/${newDate.day}';
  119. reportState.setFromDates(
  120. fromDateString); // Update the selected date
  121. },
  122. ),
  123. );
  124. },
  125. );
  126. },
  127. ),
  128. Text(
  129. AppLocalizations.of(context)!.to,
  130. ),
  131. PickerCustom(
  132. showDate: reportState.toDate.isNotEmpty
  133. ? reportState.toDate
  134. : AppLocalizations.of(context)!
  135. .selectdate, // Show selected date or prompt
  136. onTap: () {
  137. showDialog(
  138. context: context,
  139. builder: (context) {
  140. return Dialog(
  141. child: Tools
  142. .shamsiDateCalendarWidget(
  143. context,
  144. (newDate) {
  145. String toDateString =
  146. '${newDate.year}/${newDate.month}/${newDate.day}';
  147. reportState.setToDates(
  148. toDateString); // Update the selected date
  149. },
  150. ),
  151. );
  152. },
  153. );
  154. },
  155. ),
  156. ],
  157. )
  158. ],
  159. ),
  160. ExpansionTileCustom(
  161. title: AppLocalizations.of(context)!.location,
  162. widgets: <Widget>[
  163. ListView.builder(
  164. primary: false,
  165. physics: NeverScrollableScrollPhysics(),
  166. shrinkWrap: true,
  167. itemCount: globalState.locationsModel!.length,
  168. itemBuilder:
  169. (BuildContext context, int index) {
  170. final items =
  171. globalState.locationsModel![index];
  172. return RadioListTile<int>(
  173. toggleable: true,
  174. groupValue:
  175. reportState.selectedLocationId,
  176. value: items.id ?? -1,
  177. title: Text(
  178. items.address ?? '',
  179. maxLines: 1,
  180. style: TextStyle(
  181. fontWeight: FontWeight.w100,
  182. fontSize: 14),
  183. overflow: TextOverflow.ellipsis,
  184. ),
  185. activeColor: config.ui.secendGreen,
  186. onChanged: (int? newValue) {
  187. reportState
  188. .selectLocation(newValue ?? null);
  189. },
  190. );
  191. },
  192. ),
  193. ],
  194. ),
  195. if (setting.userLocalDb.getUser().role != 1)
  196. ExpansionTileCustom(
  197. title: AppLocalizations.of(context)!
  198. .meetingmanager,
  199. widgets: <Widget>[
  200. ListView.builder(
  201. primary: false,
  202. physics: NeverScrollableScrollPhysics(),
  203. shrinkWrap: true,
  204. itemCount: globalState
  205. .meetingsManagerModel!.length,
  206. itemBuilder:
  207. (BuildContext context, int index) {
  208. final items = globalState
  209. .meetingsManagerModel![index];
  210. return RadioListTile<int>(
  211. toggleable: true,
  212. groupValue:
  213. reportState.selectedManagersId,
  214. value: items.id ?? -1,
  215. title: Text(
  216. items.name ?? '',
  217. style: TextStyle(
  218. fontWeight: FontWeight.w100,
  219. fontSize: 14),
  220. maxLines: 1,
  221. overflow: TextOverflow.ellipsis,
  222. ),
  223. activeColor: config.ui.secendGreen,
  224. onChanged: (int? newValue) {
  225. reportState
  226. .selectManager(newValue ?? null);
  227. },
  228. );
  229. },
  230. ),
  231. ],
  232. ),
  233. ExpansionTileCustom(
  234. title: AppLocalizations.of(context)!.subject,
  235. widgets: <Widget>[
  236. ListView.builder(
  237. primary: false,
  238. physics: NeverScrollableScrollPhysics(),
  239. shrinkWrap: true,
  240. itemCount: globalState.subjectsModel!.length,
  241. itemBuilder:
  242. (BuildContext context, int index) {
  243. final items =
  244. globalState.subjectsModel![index];
  245. return RadioListTile<int>(
  246. toggleable: true,
  247. groupValue: reportState.selectedSubjectId,
  248. value: items.id ?? -1,
  249. title: Text(
  250. items.subject ?? '',
  251. maxLines: 1,
  252. overflow: TextOverflow.ellipsis,
  253. style: TextStyle(
  254. fontWeight: FontWeight.w100,
  255. fontSize: 14),
  256. ),
  257. activeColor: config.ui.secendGreen,
  258. onChanged: (int? newValue) {
  259. reportState
  260. .selectSubject(newValue ?? null);
  261. },
  262. );
  263. },
  264. ),
  265. ],
  266. ),
  267. Divider(),
  268. if (setting.userLocalDb.getUser().role != 1)
  269. SizedBox(
  270. height: 300,
  271. child: ListView.builder(
  272. physics: NeverScrollableScrollPhysics(),
  273. shrinkWrap: true,
  274. primary: false,
  275. itemCount: meetingStatuses.length,
  276. itemBuilder:
  277. (BuildContext context, int index) {
  278. final items = meetingStatuses[index];
  279. return RadioListTile<int>(
  280. toggleable: true,
  281. groupValue: reportState.selectedStatusId,
  282. value: items.id,
  283. title: Text(
  284. items.title,
  285. maxLines: 1,
  286. style: TextStyle(
  287. fontWeight: FontWeight.w100,
  288. fontSize: 14),
  289. overflow: TextOverflow.ellipsis,
  290. ),
  291. activeColor: config.ui.secendGreen,
  292. onChanged: (int? newValue) {
  293. reportState.selectStatusMeeting(
  294. newValue ?? null);
  295. },
  296. );
  297. },
  298. ),
  299. ),
  300. Padding(
  301. padding: const EdgeInsets.symmetric(
  302. horizontal: 20, vertical: 50),
  303. child: downloadButton(reportState),
  304. )
  305. ],
  306. ),
  307. ],
  308. ),
  309. ),
  310. ),
  311. ],
  312. );
  313. case Status.loading:
  314. return const LoadingWidget();
  315. case Status.error:
  316. return CustomErrorWidget(
  317. onPressed: () async {
  318. await globalState.getAllFiltersItems(refresh: true);
  319. },
  320. );
  321. default:
  322. return Container();
  323. }
  324. },
  325. );
  326. }
  327. CustomButton downloadButton(ReportState state) {
  328. switch (state.statusDownload) {
  329. case Status.loading:
  330. return CustomButton(
  331. borderRadius: 15,
  332. hieght: 50,
  333. text: AppLocalizations.of(context)!.loading,
  334. width: double.infinity,
  335. );
  336. default:
  337. return CustomButton(
  338. borderRadius: 15,
  339. hieght: 50,
  340. text: AppLocalizations.of(context)!.downloadreport,
  341. width: double.infinity,
  342. onPressed: () async {
  343. bool hasPermission = await hasStoragePermission();
  344. if (!hasPermission) {
  345. Tools.showCustomSnackBar(context,
  346. text: 'Permission denied. Please allow storage access.',
  347. isError: true);
  348. return;
  349. }
  350. // Download the file
  351. await state.downloadReport(
  352. toDate: reportState!.toDate,
  353. fromDate: reportState!.fromDate,
  354. location: reportState!.selectedLocationId,
  355. subject: reportState!.selectedSubjectId,
  356. meetingManager: reportState!.selectedManagersId,
  357. status: reportState!.selectedStatusId);
  358. if (state.statusDownload == Status.ready) {
  359. await OpenFile.open(state.messageDownload);
  360. // print(status.message);
  361. } else {
  362. Tools.showCustomSnackBar(
  363. context,
  364. text: AppLocalizations.of(context)!.error,
  365. isError: true,
  366. );
  367. }
  368. },
  369. );
  370. }
  371. }
  372. Future<bool> hasStoragePermission() async {
  373. if (Platform.isAndroid) {
  374. final status = await Permission.storage.status;
  375. if (status != PermissionStatus.granted) {
  376. final result = await Permission.manageExternalStorage.request();
  377. if (result == PermissionStatus.granted) {
  378. return true;
  379. }
  380. } else {
  381. return true;
  382. }
  383. } else {
  384. return true;
  385. }
  386. return false;
  387. }
  388. }
  389. class LineButtomSheet extends StatelessWidget {
  390. const LineButtomSheet({
  391. super.key,
  392. });
  393. @override
  394. Widget build(BuildContext context) {
  395. return Container(
  396. margin: const EdgeInsets.only(top: 8.0),
  397. width: 30.0,
  398. height: 3.0,
  399. decoration: BoxDecoration(
  400. color: Colors.grey.shade400,
  401. borderRadius: BorderRadius.circular(24.0),
  402. ),
  403. );
  404. }
  405. }
  406. class MeetingsStatus {
  407. int id;
  408. String title;
  409. MeetingsStatus({
  410. required this.id,
  411. required this.title,
  412. });
  413. }