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.
 
 
 
 
 
 

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