|
|
|
|
@ -1,5 +1,8 @@
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_svg/flutter_svg.dart';
|
|
|
|
|
import 'package:open_file/open_file.dart';
|
|
|
|
|
import 'package:provider/provider.dart';
|
|
|
|
|
import 'package:share_plus/share_plus.dart';
|
|
|
|
|
import 'package:test_sa/extensions/int_extensions.dart';
|
|
|
|
|
import 'package:test_sa/extensions/string_extensions.dart';
|
|
|
|
|
import 'package:test_sa/extensions/text_extensions.dart';
|
|
|
|
|
@ -14,10 +17,10 @@ import 'package:test_sa/modules/export_module/provider/export_columns_provider.d
|
|
|
|
|
import 'package:test_sa/modules/export_module/provider/export_format_provider.dart';
|
|
|
|
|
import 'package:test_sa/new_views/app_style/app_color.dart';
|
|
|
|
|
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
|
|
|
|
|
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
|
|
|
|
|
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
|
|
|
|
|
import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.dart';
|
|
|
|
|
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
|
|
|
|
|
import 'dart:developer';
|
|
|
|
|
|
|
|
|
|
class ExportFormView<T> extends StatefulWidget {
|
|
|
|
|
final T searchModel;
|
|
|
|
|
@ -81,18 +84,6 @@ class _ExportFormViewState<T> extends State<ExportFormView<T>> {
|
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
|
child: Column(
|
|
|
|
|
children: [
|
|
|
|
|
MultipleItemDropDownMenu<AssetColumnMetadata, AssetColumnsProvider>(
|
|
|
|
|
context: context,
|
|
|
|
|
title: 'Columns',
|
|
|
|
|
showAsBottomSheet: true,
|
|
|
|
|
backgroundColor: AppColor.fieldBgColor(context),
|
|
|
|
|
showShadow: false,
|
|
|
|
|
height: 56.toScreenHeight,
|
|
|
|
|
showCancel: true,
|
|
|
|
|
requestById: widget.moduleType.id,
|
|
|
|
|
onSelect: _updateSelectedColumns,
|
|
|
|
|
),
|
|
|
|
|
18.height,
|
|
|
|
|
SingleItemDropDownMenu<Lookup, ExportFormatProvider>(
|
|
|
|
|
context: context,
|
|
|
|
|
title: 'Format',
|
|
|
|
|
@ -104,6 +95,18 @@ class _ExportFormViewState<T> extends State<ExportFormView<T>> {
|
|
|
|
|
requestById: 1,
|
|
|
|
|
onSelect: _updateSelectedFormat,
|
|
|
|
|
),
|
|
|
|
|
18.height,
|
|
|
|
|
MultipleItemDropDownMenu<AssetColumnMetadata, AssetColumnsProvider>(
|
|
|
|
|
context: context,
|
|
|
|
|
title: 'Columns',
|
|
|
|
|
showAsBottomSheet: true,
|
|
|
|
|
backgroundColor: AppColor.fieldBgColor(context),
|
|
|
|
|
showShadow: false,
|
|
|
|
|
height: 56.toScreenHeight,
|
|
|
|
|
showCancel: true,
|
|
|
|
|
requestById: widget.moduleType.id,
|
|
|
|
|
onSelect: _updateSelectedColumns,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
).toShadowContainer(context).paddingOnly(top: 20, start: 16, end: 16),
|
|
|
|
|
),
|
|
|
|
|
@ -115,14 +118,15 @@ class _ExportFormViewState<T> extends State<ExportFormView<T>> {
|
|
|
|
|
label: 'Export',
|
|
|
|
|
buttonColor: AppColor.primary10,
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
if (selectedColumns.isEmpty) {
|
|
|
|
|
'Please select at least one column to export.'.showToast;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (selectedFormat == null) {
|
|
|
|
|
'Please select an export format.'.showToast;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (selectedColumns.isEmpty) {
|
|
|
|
|
'Please select at least one column to export.'.showToast;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final exportRequest = ExportRequest<T>(
|
|
|
|
|
selectedColumns: selectedColumns.map((col) => col.propertyName ?? '').toList(),
|
|
|
|
|
format: selectedFormat?.name ?? '',
|
|
|
|
|
@ -130,21 +134,34 @@ class _ExportFormViewState<T> extends State<ExportFormView<T>> {
|
|
|
|
|
pageNo: widget.pageNo,
|
|
|
|
|
pageSize: widget.pageSize,
|
|
|
|
|
);
|
|
|
|
|
final exportProvider = Provider.of<ExportApiProvider>(context, listen: false);
|
|
|
|
|
showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading());
|
|
|
|
|
final success = await exportProvider.exportData(exportRequest, widget.moduleType);
|
|
|
|
|
Navigator.pop(context);
|
|
|
|
|
// this is for this This should be handled as per requirement in dialog with share and other options
|
|
|
|
|
if (success) {
|
|
|
|
|
'Export completed successfully!'.showToast;
|
|
|
|
|
} else {
|
|
|
|
|
String errorMessage = exportProvider.errorMessage ?? 'Export failed. Please try again.';
|
|
|
|
|
if (errorMessage.contains('No app installed')) {
|
|
|
|
|
String fileType = selectedFormat?.name ?? 'this file type';
|
|
|
|
|
errorMessage = 'No app found to open $fileType files. Please install a compatible app';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errorMessage.showToast;
|
|
|
|
|
log('📦 ExportRequest created');
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
final exportProvider = Provider.of<ExportApiProvider>(context, listen: false);
|
|
|
|
|
if (!mounted) return;
|
|
|
|
|
|
|
|
|
|
final filePath = await exportProvider.exportData(exportRequest, widget.moduleType, context);
|
|
|
|
|
if (!mounted) return;
|
|
|
|
|
|
|
|
|
|
if (filePath != null && filePath.isNotEmpty) {
|
|
|
|
|
await _showExportSuccessDialog(context, filePath);
|
|
|
|
|
} else if (exportProvider.errorMessage != null) {
|
|
|
|
|
String errorMessage = exportProvider.errorMessage!;
|
|
|
|
|
if (errorMessage.contains('No app installed') || errorMessage.contains('No app found')) {
|
|
|
|
|
String fileType = selectedFormat?.name ?? 'this file type';
|
|
|
|
|
errorMessage = 'No app found to open $fileType files. Please install a compatible app';
|
|
|
|
|
}
|
|
|
|
|
errorMessage.showToast;
|
|
|
|
|
} else {
|
|
|
|
|
'Export failed. Please try again.'.showToast;
|
|
|
|
|
}
|
|
|
|
|
} catch (e, stackTrace) {
|
|
|
|
|
log('❌ Error in export button: $e');
|
|
|
|
|
log('Stack trace: $stackTrace');
|
|
|
|
|
if (mounted) {
|
|
|
|
|
'Error occurred during export: $e'.showToast;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
@ -152,6 +169,103 @@ class _ExportFormViewState<T> extends State<ExportFormView<T>> {
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> _showExportSuccessDialog(BuildContext context, String filePath) async {
|
|
|
|
|
return showDialog(
|
|
|
|
|
context: context,
|
|
|
|
|
barrierDismissible: false,
|
|
|
|
|
builder: (BuildContext dialogContext) {
|
|
|
|
|
return Dialog(
|
|
|
|
|
backgroundColor: AppColor.background(context),
|
|
|
|
|
shape: RoundedRectangleBorder(
|
|
|
|
|
borderRadius: BorderRadius.circular(16),
|
|
|
|
|
),
|
|
|
|
|
child: Padding(
|
|
|
|
|
padding: const EdgeInsets.all(24.0),
|
|
|
|
|
child: Column(
|
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
|
children: [
|
|
|
|
|
SvgPicture.asset(
|
|
|
|
|
'assets/images/done.svg',
|
|
|
|
|
width: 80,
|
|
|
|
|
height: 80,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 24),
|
|
|
|
|
Text(
|
|
|
|
|
'Export Successful',
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
fontWeight: FontWeight.bold,
|
|
|
|
|
color: AppColor.headingTextColor(context),
|
|
|
|
|
),
|
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
|
Text(
|
|
|
|
|
'Your file has been exported successfully',
|
|
|
|
|
style: TextStyle(
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
color: AppColor.lightTextColor(context),
|
|
|
|
|
),
|
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(height: 32),
|
|
|
|
|
Row(
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(
|
|
|
|
|
child: AppFilledButton(
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
Navigator.of(dialogContext).pop();
|
|
|
|
|
log('📂 Opening exported file...');
|
|
|
|
|
final result = await OpenFile.open(filePath);
|
|
|
|
|
if (result.type != ResultType.done) {
|
|
|
|
|
log('⚠️ Could not open file: ${result.message}');
|
|
|
|
|
'No app found to open this file type. File saved to: $filePath'.showToast;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
label: 'Open',
|
|
|
|
|
buttonColor: AppColor.primary10,
|
|
|
|
|
showIcon: true,
|
|
|
|
|
icon: const Icon(Icons.folder_open, color: Colors.white),
|
|
|
|
|
height: 48,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
const SizedBox(width: 12),
|
|
|
|
|
Expanded(
|
|
|
|
|
child: AppFilledButton(
|
|
|
|
|
onPressed: () async {
|
|
|
|
|
Navigator.of(dialogContext).pop();
|
|
|
|
|
log('📤 Sharing exported file...');
|
|
|
|
|
try {
|
|
|
|
|
final xFile = XFile(filePath);
|
|
|
|
|
await Share.shareXFiles(
|
|
|
|
|
[xFile],
|
|
|
|
|
text: 'Exported file',
|
|
|
|
|
);
|
|
|
|
|
log('✅ File shared successfully');
|
|
|
|
|
} catch (e) {
|
|
|
|
|
log('❌ Error sharing file: $e');
|
|
|
|
|
'Failed to share file: $e'.showToast;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
label: 'Share',
|
|
|
|
|
buttonColor: AppColor.green40,
|
|
|
|
|
textColor: AppColor.green60,
|
|
|
|
|
showIcon: true,
|
|
|
|
|
icon: Icon(Icons.share, color: AppColor.green60),
|
|
|
|
|
height: 48,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//check if we need we can show this as well @waseem
|
|
|
|
|
|
|
|
|
|
String _getAppSuggestion(String format) {
|
|
|
|
|
|