// ignore_for_file: public_member_api_docs, sort_constructors_first import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:go_router/go_router.dart'; import 'package:open_file/open_file.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:qadirneyriz/config/config.dart'; import 'package:qadirneyriz/models/private_meeting/private_meetings_model.dart'; import 'package:qadirneyriz/screens/private_meeting_summary/state.dart'; import 'package:qadirneyriz/setting/setting.dart'; import 'package:qadirneyriz/utils/enums/status.dart'; import 'package:qadirneyriz/utils/tools/tools.dart'; import 'package:qadirneyriz/widgets/card_meeting.dart'; import 'package:qadirneyriz/widgets/custom_appbar.dart'; import 'package:qadirneyriz/widgets/custom_button.dart'; import 'package:qadirneyriz/widgets/error_widget.dart'; import 'package:qadirneyriz/widgets/loading_widget.dart'; class PrivateMeetingSummaryScreen extends StatefulWidget { final DatumInPrivateMeeting itemInPrivateMeeting; const PrivateMeetingSummaryScreen({ Key? key, required this.itemInPrivateMeeting, }) : super(key: key); @override State createState() => _PrivateMeetingSummaryScreenState(); } class _PrivateMeetingSummaryScreenState extends State { late TextEditingController _textControllerDescription; late PrivateMeetingSummaryState state; @override void initState() { super.initState(); _textControllerDescription = TextEditingController(); if (widget.itemInPrivateMeeting.description != null) { _textControllerDescription.text = widget.itemInPrivateMeeting.description ?? ''; } Future.delayed(Duration.zero, () async { state = Provider.of(context, listen: false); await state.getStringFiles(id: widget.itemInPrivateMeeting.id ?? 0); }); } @override void dispose() { _textControllerDescription.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: Consumer( builder: (context, value, child) { switch (value.stringsFilsStatus[widget.itemInPrivateMeeting.id]) { case Status.ready: return CustomScrollView( slivers: [ CustomAppbar( title: AppLocalizations.of(context)!.meetingsummary, ), SliverToBoxAdapter( child: CustomCardMeeting( status: widget.itemInPrivateMeeting.accepted ?? 0, titel: widget.itemInPrivateMeeting.subject != null ? widget.itemInPrivateMeeting.subject!.subject ?? '' : '', date: widget.itemInPrivateMeeting.dateJalali ?? '', location: widget.itemInPrivateMeeting.location != null ? widget.itemInPrivateMeeting.location!.address ?? '' : '', fromTime: widget.itemInPrivateMeeting.azHour ?? '', toTime: widget.itemInPrivateMeeting.taHour ?? '', cardId: widget.itemInPrivateMeeting.id ?? -1, ), ), SliverToBoxAdapter( child: Column( children: [ Padding( padding: const EdgeInsets.all(10.0), child: Container( decoration: BoxDecoration( color: const Color(0xffF4F9F6), boxShadow: [ BoxShadow( color: config.ui.mainGray.withOpacity(.1), spreadRadius: .1, offset: const Offset(0, 2), blurRadius: 6, ), ], borderRadius: const BorderRadius.all(Radius.circular(12)), ), child: CustomTextArea( hintText: AppLocalizations.of(context)! .descriptionofthemeeting, controller: _textControllerDescription, ), ), ), if (state.filesStringModel[ widget.itemInPrivateMeeting.id] != null && state .filesStringModel[ widget.itemInPrivateMeeting.id]! .isNotEmpty) Padding( padding: EdgeInsets.all(10), child: Container( decoration: BoxDecoration( color: const Color(0xffF4F9F6), boxShadow: [ BoxShadow( color: config.ui.mainGray.withOpacity(.1), spreadRadius: .1, offset: const Offset(0, 2), blurRadius: 6, ), ], borderRadius: const BorderRadius.all(Radius.circular(12)), ), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 20), child: Text( AppLocalizations.of(context)!.files, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: config.ui.mainGreen, ), ), ), ListView.builder( physics: NeverScrollableScrollPhysics(), shrinkWrap: true, padding: EdgeInsets.all(0), itemCount: state .filesStringModel[ widget.itemInPrivateMeeting.id]! .length, itemBuilder: (BuildContext context, int index) { return Padding( padding: const EdgeInsets.symmetric( horizontal: 20, vertical: 10), child: deleteFilesButton( value, widget.itemInPrivateMeeting.id ?? -1, state.filesStringModel[widget .itemInPrivateMeeting .id]![index]), ); }, ), ], ), ), ), Padding( padding: const EdgeInsets.all(10.0), child: ReceiptUploadDialog( state: value, ), ), Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: submitSummaryButton(context, value), ), ], ), ), if (widget.itemInPrivateMeeting.description != null && state.filesStringModel[widget.itemInPrivateMeeting.id!] != null && state.filesStringModel[widget.itemInPrivateMeeting.id!]! .isNotEmpty) SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only( top: 5, bottom: 40, left: 10, right: 10), child: downloadButton( value, widget.itemInPrivateMeeting.id ?? -1), ), ) ], ); case Status.loading: return const LoadingWidget(); case Status.error: return CustomErrorWidget( onPressed: () async { await state.getStringFiles( id: widget.itemInPrivateMeeting.id ?? 0); }, ); default: return Container(); } }, ), ); } Widget deleteFilesButton( PrivateMeetingSummaryState state, int id, String text) { switch (state.statusDeleteFile) { case Status.loading: return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(Icons.cancel_outlined), Text(text), ], ); default: return InkWell( onTap: () async { final shouldProceed = await showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text( AppLocalizations.of(context)!.acceptoperetion, ), content: Text( AppLocalizations.of(context)!.areusuretodeletfile, ), actions: [ TextButton( onPressed: () { // لغو عملیات Navigator.of(context).pop(false); }, child: Text( AppLocalizations.of(context)!.cancel, ), ), TextButton( onPressed: () { // تأیید عملیات Navigator.of(context).pop(true); }, child: Text( AppLocalizations.of(context)!.accept, ), ), ], ); }, ); // اگر کاربر تأیید کرد، عملیات انجام شود if (shouldProceed == true) { final status = await state.deleteFileSummary(id: id, text: text); if (status == Status.ready) { await state.getStringFiles(id: id); // context.pop(); } } }, child: state.filesStringModel[id] != null ? Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(Icons.cancel_outlined), Text(text), ], ) : Container(), ); } } CustomButton submitSummaryButton( BuildContext context, PrivateMeetingSummaryState state) { switch (state.statusMinuteMeeting) { case Status.loading: return CustomButton( hieght: 50, text: AppLocalizations.of(context)!.loading); default: return CustomButton( hieght: 50, text: AppLocalizations.of(context)!.submitsummarymeeting, onPressed: () async { if (_textControllerDescription.text == '') { // call add new subject Tools.showCustomSnackBar( text: AppLocalizations.of(context)!.enterdescription, isError: true, context, ); } // else if (state.selectedFiles == null) { // // call add new subject // Tools.showCustomSnackBar( // text: AppLocalizations.of(context)!.enterfile, // isError: true, // context, // ); // } else { final status = await state.addMinuteMeeting( id: widget.itemInPrivateMeeting.id ?? -1, description: _textControllerDescription.text, meetingFiles: state.selectedFiles ?? []); if (status == Status.ready) { await state.getStringFiles( id: widget.itemInPrivateMeeting.id ?? -1); context.pop(); Tools.showCustomSnackBar( text: AppLocalizations.of(context)!.donesummary, isError: false, context, ); } else { Tools.showCustomSnackBar( text: state.errorsMinuteMeeting == null ? state.messageMinuteMeeting ?? AppLocalizations.of(context)!.haserror : Tools.combineErrorMessages( state.errorsMinuteMeeting ?? {}), isError: true, context, ); } } }, ); } } Future hasStoragePermission() async { if (Platform.isAndroid) { final status = await Permission.storage.status; if (status != PermissionStatus.granted) { final result = await Permission.manageExternalStorage.request(); if (result == PermissionStatus.granted) { return true; } } else { return true; } } else { return true; } return false; } CustomButton downloadButton(PrivateMeetingSummaryState state, int id) { switch (state.statusDownload) { case Status.loading: return CustomButton( borderRadius: 15, hieght: 50, text: AppLocalizations.of(context)!.loading, width: double.infinity, ); default: return CustomButton( borderRadius: 15, hieght: 50, text: AppLocalizations.of(context)!.downloadreport, width: double.infinity, onPressed: () async { bool hasPermission = await hasStoragePermission(); if (!hasPermission) { Tools.showCustomSnackBar(context, text: AppLocalizations.of(context)!.needpermission, isError: true); return; } // Download the file await state.downloadSummary(id: id); if (state.statusDownload == Status.ready) { try { await OpenFile.open(state.messageDownload); } catch (e) { Tools.showCustomSnackBar( context, text: AppLocalizations.of(context)!.needzipapp, isError: true, ); } } else { Tools.showCustomSnackBar( context, text: AppLocalizations.of(context)!.error, isError: true, ); } }, ); } } } class CustomTextArea extends StatelessWidget { final String hintText; final TextEditingController controller; final int maxLines; final int minLines; const CustomTextArea({ Key? key, required this.hintText, required this.controller, this.maxLines = 20, this.minLines = 4, }) : super(key: key); @override Widget build(BuildContext context) { return TextField( controller: controller, maxLines: maxLines, minLines: minLines, decoration: InputDecoration( hintText: hintText, hintStyle: TextStyle(color: Colors.black.withOpacity(.4), fontSize: 13), border: InputBorder.none, contentPadding: const EdgeInsets.all(12.0), ), ); } } class ReceiptUploadDialog extends StatefulWidget { final PrivateMeetingSummaryState state; const ReceiptUploadDialog({ Key? key, required this.state, }) : super(key: key); @override _ReceiptUploadDialogState createState() => _ReceiptUploadDialogState(); } class _ReceiptUploadDialogState extends State { @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( color: const Color(0xffF4F9F6), boxShadow: [ BoxShadow( color: config.ui.mainGray.withOpacity(.1), spreadRadius: .1, offset: const Offset(0, 2), blurRadius: 6, ), ], borderRadius: const BorderRadius.all(Radius.circular(12)), ), child: Padding( padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 10), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( AppLocalizations.of(context)!.fileupload, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: config.ui.mainGreen, ), ), const SizedBox(height: 20), // Upload box FileBorderBox( child: GestureDetector( onTap: widget.state.pickFiles, child: Column( children: [ Icon(Icons.cloud_upload_outlined, size: 30, color: config.ui.mainGreen), Text( AppLocalizations.of(context)!.selectfile, style: TextStyle(fontSize: 12, color: config.ui.mainGreen), ), ], ), ), ), // File preview if (widget.state.selectedFiles != null) FilePreview( files: widget.state.selectedFiles!, onDelete: widget.state.removeFile, ), ], ), ), ); } } class FilePreview extends StatelessWidget { final List files; final void Function(int) onDelete; const FilePreview({ super.key, required this.files, required this.onDelete, }); @override Widget build(BuildContext context) { return ListView.builder( shrinkWrap: true, itemCount: files.length, physics: const NeverScrollableScrollPhysics(), itemBuilder: (BuildContext context, int index) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), margin: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), border: Border(bottom: BorderSide(color: config.ui.secendGreen)), ), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ const Icon(Icons.file_present_outlined, color: Color(0xffD0D5ED)), const SizedBox(width: 10), Expanded( child: Text( files[index].name, style: const TextStyle(fontSize: 12), ), ), IconButton( icon: Icon(Icons.delete, color: config.ui.secendGreen), onPressed: () => onDelete(index), ), ], ), ); }, ); } } class FileBorderBox extends StatelessWidget { final Widget child; const FileBorderBox({super.key, required this.child}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), border: Border.all( color: config.ui.mainGreen, style: BorderStyle.solid, width: .5, ), ), child: Center(child: child), ); } }