|
- // 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: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:flutter_gen/gen_l10n/app_localizations.dart';
- import 'package:qadirneyriz/config/config.dart';
- import 'package:qadirneyriz/diologs/diolog_add_location.dart';
- import 'package:qadirneyriz/models/meetings/meetings_model.dart';
- import 'package:qadirneyriz/screens/meeting_summary/state.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 MeetingSummaryScreen extends StatefulWidget {
- final Datum meetingItem;
- const MeetingSummaryScreen({
- Key? key,
- required this.meetingItem,
- }) : super(key: key);
-
- @override
- State<MeetingSummaryScreen> createState() => _MeetingSummaryScreenState();
- }
-
- class _MeetingSummaryScreenState extends State<MeetingSummaryScreen> {
- late TextEditingController _textControllerDescription;
- late MeetingSummaryState state;
- @override
- void initState() {
- super.initState();
- _textControllerDescription = TextEditingController();
- if (widget.meetingItem.description != null) {
- _textControllerDescription.text = widget.meetingItem.description ?? '';
- }
- Future.delayed(Duration.zero, () async {
- state = Provider.of<MeetingSummaryState>(context, listen: false);
- await state.getStringFiles(id: widget.meetingItem.id ?? 0);
- });
- }
-
- @override
- void dispose() {
- _textControllerDescription.dispose();
- super.dispose();
- }
-
- @override
- Widget build(BuildContext context) {
- final int id = widget.meetingItem.id ?? 0;
- return Scaffold(
- body: Consumer<MeetingSummaryState>(
- builder: (context, value, child) {
- switch (value.stringsFilsStatus[id]) {
- case Status.ready:
- return CustomScrollView(
- slivers: <Widget>[
- CustomAppbar(
- title: AppLocalizations.of(context)!.meetingsummary,
- ),
- SliverPadding(
- padding:
- const EdgeInsets.symmetric(vertical: 5, horizontal: 2),
- sliver: SliverToBoxAdapter(
- child: CustomCardMeeting(
- status: widget.meetingItem.accepted ?? 0,
- titel: widget.meetingItem.subject != null
- ? widget.meetingItem.subject!.subject ?? ''
- : '',
- date: widget.meetingItem.dateJalali ?? '',
- location: widget.meetingItem.location != null
- ? widget.meetingItem.location!.address ?? ''
- : '',
- fromTime: widget.meetingItem.azHour ?? '',
- toTime: widget.meetingItem.taHour ?? '',
- cardId: widget.meetingItem.id ?? -1,
- ),
- ),
- ),
- // if (widget.meetingItem.description == null)
- SliverToBoxAdapter(
- child: Column(
- children: [
- Padding(
- padding: const EdgeInsets.symmetric(
- vertical: 20, horizontal: 8),
- 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[id] != null &&
- state.filesStringModel[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[id]!.length,
- itemBuilder:
- (BuildContext context, int index) {
- return Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 20, vertical: 10),
- child: deleteFilesButton(state, id,
- state.filesStringModel[id]![index]),
- );
- },
- ),
- ],
- ),
- ),
- ),
- Padding(
- padding: const EdgeInsets.symmetric(
- vertical: 15, horizontal: 8),
- child: ReceiptUploadDialog(
- state: value,
- ),
- ),
- submitSummaryButton(context, state),
- ],
- ),
- ),
- if (widget.meetingItem.description != null &&
- state.filesStringModel[id] != null &&
- state.filesStringModel[id]!.isNotEmpty)
- SliverToBoxAdapter(
- child: Padding(
- padding: const EdgeInsets.only(
- top: 5, bottom: 40, left: 10, right: 10),
- child: downloadButton(state, id),
- ),
- )
- ],
- );
- case Status.loading:
- return const LoadingWidget();
- case Status.error:
- return CustomErrorWidget(
- onPressed: () async {
- await state.getStringFiles(id: id);
- },
- );
-
- default:
- return Container();
- }
- },
- ),
- );
- }
-
- Widget deleteFilesButton(MeetingSummaryState 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<bool>(
- 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, MeetingSummaryState 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.meetingItem.id ?? -1,
- description: _textControllerDescription.text,
- meetingFiles: state.selectedFiles ?? []);
-
- if (status == Status.ready) {
- await state.getStringFiles(id: widget.meetingItem.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<bool> 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(MeetingSummaryState 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 MeetingSummaryState state;
- const ReceiptUploadDialog({
- Key? key,
- required this.state,
- }) : super(key: key);
-
- @override
- _ReceiptUploadDialogState createState() => _ReceiptUploadDialogState();
- }
-
- class _ReceiptUploadDialogState extends State<ReceiptUploadDialog> {
- @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<PlatformFile> 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),
- );
- }
- }
|