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.
 
 
 
 
 
 

423 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. ExpansionTileCustom(
  193. title:
  194. AppLocalizations.of(context)!.meetingmanager,
  195. widgets: <Widget>[
  196. ListView.builder(
  197. primary: false,
  198. physics: NeverScrollableScrollPhysics(),
  199. shrinkWrap: true,
  200. itemCount:
  201. globalState.meetingsManagerModel!.length,
  202. itemBuilder:
  203. (BuildContext context, int index) {
  204. final items = globalState
  205. .meetingsManagerModel![index];
  206. return RadioListTile<int>(
  207. toggleable: true,
  208. groupValue:
  209. reportState.selectedManagersId,
  210. value: items.id ?? -1,
  211. title: Text(
  212. items.name ?? '',
  213. style: TextStyle(
  214. fontWeight: FontWeight.w100,
  215. fontSize: 14),
  216. maxLines: 1,
  217. overflow: TextOverflow.ellipsis,
  218. ),
  219. activeColor: config.ui.secendGreen,
  220. onChanged: (int? newValue) {
  221. reportState
  222. .selectManager(newValue ?? null);
  223. },
  224. );
  225. },
  226. ),
  227. ],
  228. ),
  229. ExpansionTileCustom(
  230. title: AppLocalizations.of(context)!.subject,
  231. widgets: <Widget>[
  232. ListView.builder(
  233. primary: false,
  234. physics: NeverScrollableScrollPhysics(),
  235. shrinkWrap: true,
  236. itemCount: globalState.subjectsModel!.length,
  237. itemBuilder:
  238. (BuildContext context, int index) {
  239. final items =
  240. globalState.subjectsModel![index];
  241. return RadioListTile<int>(
  242. toggleable: true,
  243. groupValue: reportState.selectedSubjectId,
  244. value: items.id ?? -1,
  245. title: Text(
  246. items.subject ?? '',
  247. maxLines: 1,
  248. overflow: TextOverflow.ellipsis,
  249. style: TextStyle(
  250. fontWeight: FontWeight.w100,
  251. fontSize: 14),
  252. ),
  253. activeColor: config.ui.secendGreen,
  254. onChanged: (int? newValue) {
  255. reportState
  256. .selectSubject(newValue ?? null);
  257. },
  258. );
  259. },
  260. ),
  261. ],
  262. ),
  263. Divider(),
  264. SizedBox(
  265. height: 250,
  266. child: ListView.builder(
  267. physics: NeverScrollableScrollPhysics(),
  268. shrinkWrap: true,
  269. primary: false,
  270. itemCount: meetingStatuses.length,
  271. itemBuilder: (BuildContext context, int index) {
  272. final items = meetingStatuses[index];
  273. return RadioListTile<int>(
  274. toggleable: true,
  275. groupValue: reportState.selectedStatusId,
  276. value: items.id,
  277. title: Text(
  278. items.title,
  279. maxLines: 1,
  280. style: TextStyle(
  281. fontWeight: FontWeight.w100,
  282. fontSize: 14),
  283. overflow: TextOverflow.ellipsis,
  284. ),
  285. activeColor: config.ui.secendGreen,
  286. onChanged: (int? newValue) {
  287. reportState.selectStatusMeeting(
  288. newValue ?? null);
  289. },
  290. );
  291. },
  292. ),
  293. ),
  294. Padding(
  295. padding: const EdgeInsets.symmetric(
  296. horizontal: 20, vertical: 50),
  297. child: downloadButton(reportState),
  298. )
  299. ],
  300. ),
  301. ],
  302. ),
  303. ),
  304. ),
  305. ],
  306. );
  307. case Status.loading:
  308. return const LoadingWidget();
  309. case Status.error:
  310. return CustomErrorWidget(
  311. onPressed: () async {
  312. await globalState.getAllFiltersItems(refresh: true);
  313. },
  314. );
  315. default:
  316. return Container();
  317. }
  318. },
  319. );
  320. }
  321. CustomButton downloadButton(ReportState state) {
  322. switch (state.statusDownload) {
  323. case Status.loading:
  324. return CustomButton(
  325. borderRadius: 15,
  326. hieght: 50,
  327. text: AppLocalizations.of(context)!.loading,
  328. width: double.infinity,
  329. );
  330. default:
  331. return CustomButton(
  332. borderRadius: 15,
  333. hieght: 50,
  334. text: AppLocalizations.of(context)!.downloadreport,
  335. width: double.infinity,
  336. onPressed: () async {
  337. bool hasPermission = await hasStoragePermission();
  338. if (!hasPermission) {
  339. Tools.showCustomSnackBar(context,
  340. text: 'Permission denied. Please allow storage access.',
  341. isError: true);
  342. return;
  343. }
  344. // Download the file
  345. await state.downloadReport(
  346. toDate: reportState!.toDate,
  347. fromDate: reportState!.fromDate,
  348. location: reportState!.selectedLocationId,
  349. subject: reportState!.selectedSubjectId,
  350. meetingManager: reportState!.selectedManagersId,
  351. status: reportState!.selectedStatusId);
  352. if (state.statusDownload == Status.ready) {
  353. await OpenFile.open(state.messageDownload);
  354. // print(status.message);
  355. } else {
  356. Tools.showCustomSnackBar(
  357. context,
  358. text: AppLocalizations.of(context)!.error,
  359. isError: true,
  360. );
  361. }
  362. },
  363. );
  364. }
  365. }
  366. Future<bool> hasStoragePermission() async {
  367. if (Platform.isAndroid) {
  368. final status = await Permission.storage.status;
  369. if (status != PermissionStatus.granted) {
  370. final result = await Permission.manageExternalStorage.request();
  371. if (result == PermissionStatus.granted) {
  372. return true;
  373. }
  374. } else {
  375. return true;
  376. }
  377. } else {
  378. return true;
  379. }
  380. return false;
  381. }
  382. }
  383. class LineButtomSheet extends StatelessWidget {
  384. const LineButtomSheet({
  385. super.key,
  386. });
  387. @override
  388. Widget build(BuildContext context) {
  389. return Container(
  390. margin: const EdgeInsets.only(top: 8.0),
  391. width: 30.0,
  392. height: 3.0,
  393. decoration: BoxDecoration(
  394. color: Colors.grey.shade400,
  395. borderRadius: BorderRadius.circular(24.0),
  396. ),
  397. );
  398. }
  399. }
  400. class MeetingsStatus {
  401. int id;
  402. String title;
  403. MeetingsStatus({
  404. required this.id,
  405. required this.title,
  406. });
  407. }