Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 
 
 
 

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