export improvements

main_production_RSG
WaseemAbbasi22 2 months ago
parent 359cd30c5c
commit 8cd3e8aa3b

@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
@ -17,15 +16,12 @@ import 'package:test_sa/modules/cm_module/views/components/bottom_sheets/service
import 'package:test_sa/modules/cx_module/chat/chat_provider.dart';
import 'package:test_sa/modules/cx_module/chat/chat_widget.dart';
import 'package:test_sa/modules/export_module/provider/export_provider.dart';
import 'package:test_sa/modules/export_module/widgets/file_success_dialog.dart';
import 'package:test_sa/modules/loan_module/pages/create_loan_request_page.dart';
import 'package:test_sa/modules/loan_module/pages/loan_equipment_detail_page.dart';
import 'package:test_sa/modules/loan_module/provider/loan_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/default_app_bar.dart';
import 'package:open_file/open_file.dart';
import 'package:share_plus/share_plus.dart';
import 'dart:developer';
import 'views/components/history_log_view.dart';
import 'views/components/service_request_detail_view.dart';
@ -176,7 +172,8 @@ class _CMDetailPageState extends State<CMDetailPage> {
}),
// Print Button
IconButton(
icon: const Icon(Icons.print),
icon: "download".toSvgAsset(height: 24, width: 24, color: AppColor.icon2Color(context)),
padding: EdgeInsets.zero,
onPressed: _handlePrint,
),
IconButton(
@ -255,105 +252,6 @@ class _CMDetailPageState extends State<CMDetailPage> {
cProvider.getUserAutoLoginTokenSilent(moduleId, widget.requestId, title, myEmployeeId, receiver);
}
/// Show success dialog after file is downloaded - using same implementation as export module
Future<void> _showSuccessDialog(String filePath) async {
if (!mounted) return;
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(
'Print Successful',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppColor.headingTextColor(context),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
Text(
'Your PDF has been generated 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 printed 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 printed file...');
try {
final xFile = XFile(filePath);
await Share.shareXFiles(
[xFile],
text: 'Printed 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,
),
),
],
),
],
),
),
);
},
);
}
/// Handle print button press
Future<void> _handlePrint() async {
try {
@ -366,25 +264,22 @@ class _CMDetailPageState extends State<CMDetailPage> {
);
if (filePath != null) {
await _showSuccessDialog(filePath);
// Show success dialog using the reusable FileSuccessDialog widget
await FileSuccessDialog.show(
context: context,
filePath: filePath,
title: 'Print Successful',
message: 'Your PDF has been generated successfully',
logPrefix: 'printed',
);
} else if (exportProvider.errorMessage != null) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(exportProvider.errorMessage!),
backgroundColor: AppColor.red60,
),
);
exportProvider.errorMessage?.showToast;
}
}
} catch (error) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Print failed: $error'),
backgroundColor: AppColor.red60,
),
);
'Print Failed'.showToast;
}
}
}

@ -15,6 +15,7 @@ import 'package:test_sa/modules/export_module/models/export_module_type.dart';
import 'package:test_sa/modules/export_module/provider/export_provider.dart';
import 'package:test_sa/modules/export_module/provider/export_columns_provider.dart';
import 'package:test_sa/modules/export_module/provider/export_format_provider.dart';
import 'package:test_sa/modules/export_module/widgets/file_success_dialog.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/default_app_bar.dart';
@ -22,6 +23,8 @@ import 'package:test_sa/new_views/common_widgets/multiple_item_drop_down_menu.da
import 'package:test_sa/new_views/common_widgets/single_item_drop_down_menu.dart';
import 'dart:developer';
import 'package:test_sa/views/widgets/item_views/info_header_widget.dart';
class ExportFormView<T> extends StatefulWidget {
final T searchModel;
final ExportModuleType moduleType;
@ -133,7 +136,13 @@ class _ExportFormViewState<T> extends State<ExportFormView<T>> {
if (!mounted) return;
if (filePath != null && filePath.isNotEmpty) {
await _showExportSuccessDialog(context, filePath);
await FileSuccessDialog.show(
context: context,
filePath: filePath,
title: 'Export Successful',
message: 'Your file has been exported successfully',
logPrefix: 'exported',
);
} else if (exportProvider.errorMessage != null) {
String errorMessage = exportProvider.errorMessage!;
if (errorMessage.contains('No app installed') || errorMessage.contains('No app found')) {
@ -158,101 +167,6 @@ 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

@ -10,6 +10,7 @@ import 'package:test_sa/controllers/api_routes/urls.dart';
import 'package:test_sa/modules/export_module/models/export_form_model.dart';
import 'package:test_sa/modules/export_module/models/export_module_type.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/extensions/context_extension.dart';
class ExportApiProvider extends ChangeNotifier {
bool _isExporting = false;
@ -28,6 +29,12 @@ class ExportApiProvider extends ChangeNotifier {
_isExporting = true;
_errorMessage = null;
notifyListeners();
// Capture colors before showing dialog to avoid Provider context issues
// Use isDarkNotListen to safely access provider without listening
final backgroundColor = context.isDarkNotListen ? AppColor.containerDark : Colors.white;
final textColor = context.isDarkNotListen ? AppColor.white10 : AppColor.white936;
ProgressDialog pd = ProgressDialog(context: context);
pd.show(
max: 100,
@ -35,10 +42,10 @@ class ExportApiProvider extends ChangeNotifier {
progressBgColor: Colors.transparent,
progressValueColor: AppColor.primary10,
msgFontWeight: FontWeight.normal,
// backgroundColor: AppColor.background(context),
// msgColor: AppColor.headingTextColor(context),
valueColor: AppColor.primary10,
msgColor: textColor,
valueColor: textColor,
msgFontSize: 18,
backgroundColor: backgroundColor,
);
IOSink? sink;
@ -48,7 +55,7 @@ class ExportApiProvider extends ChangeNotifier {
final String apiUrl = _getApiUrlForModule(moduleType);
final Map<String, dynamic> body = _buildRequestBody(exportRequest, moduleType);
if (!context.mounted) return null;
pd.update(msg: 'Sending request to server...');
pd.update(msg: 'Sending request to server');
log('📡 Sending export request...');
final startTime = DateTime.now();
@ -247,14 +254,23 @@ class ExportApiProvider extends ChangeNotifier {
_errorMessage = null;
notifyListeners();
// Capture colors before showing dialog to avoid Provider context issues
// Use isDarkNotListen to safely access provider without listening
final backgroundColor = context.isDarkNotListen ? AppColor.containerDark : Colors.white;
final textColor = context.isDarkNotListen ? AppColor.white10 : AppColor.white936;
ProgressDialog pd = ProgressDialog(context: context);
pd.show(
max: 100,
msg: 'Preparing print...',
msg: 'Preparing print',
progressBgColor: Colors.transparent,
progressValueColor: AppColor.primary10,
msgFontWeight: FontWeight.normal,
valueColor: AppColor.primary10,
msgFontSize: 18,
valueColor: textColor,
backgroundColor: backgroundColor,
msgColor: textColor,
);
IOSink? sink;
@ -262,7 +278,7 @@ class ExportApiProvider extends ChangeNotifier {
try {
if (!context.mounted) return null;
pd.update(msg: 'Sending print request...');
pd.update(msg: 'Sending print request');
log('📡 Sending print request...');
final startTime = DateTime.now();

@ -0,0 +1,134 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:open_file/open_file.dart';
import 'package:share_plus/share_plus.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
/// Reusable success dialog for file operations (export, print, etc.)
class FileSuccessDialog {
/// Shows a success dialog with Open and Share options
///
/// [context] - The build context
/// [filePath] - Path to the successfully generated file
/// [title] - Dialog title (e.g., 'Export Successful', 'Print Successful')
/// [message] - Dialog message (e.g., 'Your file has been exported successfully')
/// [logPrefix] - Prefix for log messages (e.g., 'exported', 'printed')
static Future<void> show({
required BuildContext context,
required String filePath,
required String title,
required String message,
String logPrefix = 'file',
}) 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: [
// Close button
Align(
alignment: Alignment.topRight,
child: 'clear'.toSvgAsset(
color: AppColor.iconColor(context),
width: 18,
height: 18,
).onPress(() {
Navigator.of(dialogContext).pop();
}),
),
// Success icon
'done'.toSvgAsset(color: AppColor.green70, width: 56, height: 56),
const SizedBox(height: 24),
// Title
Text(
title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: AppColor.headingTextColor(context),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
// Message
Text(
message,
style: TextStyle(
fontSize: 18,
color: AppColor.lightTextColor(context),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
// Action buttons
Row(
children: [
// Open button
Expanded(
child: AppFilledButton(
onPressed: () async {
log('📂 Opening $logPrefix 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),
// Share button
Expanded(
child: AppFilledButton(
onPressed: () async {
log('📤 Sharing $logPrefix file...');
try {
final xFile = XFile(filePath);
await Share.shareXFiles(
[xFile],
text: '${title.replaceAll(' Successful', '')} 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: const Icon(Icons.share, color: AppColor.green60),
height: 48,
),
),
],
),
],
),
),
);
},
);
}
}
Loading…
Cancel
Save