feat: Invoice Editor improvements and code cleanup
Major Invoice Editor updates: - ✅ Fixed tripled scrollbar issue by removing unnecessary overflow classes - ✅ Implemented dynamic currency system with JSON data loading - ✅ Fixed F4 PDF generation error with proper paper size handling - ✅ Added proper padding to Total section matching table headers - ✅ Removed print functionality (users can print from PDF download) - ✅ Streamlined preview toolbar: Back, Size selector, Download PDF - ✅ Fixed all ESLint warnings and errors - ✅ Removed console.log statements across codebase for cleaner production - ✅ Added border-top to Total section for better visual consistency - ✅ Improved print CSS and removed JSX warnings Additional improvements: - Added currencies.json to public folder for proper HTTP access - Enhanced MinimalTemplate with better spacing and layout - Clean build with no warnings or errors - Updated release notes with new features
This commit is contained in:
115
src/components/NavigationConfirmModal.js
Normal file
115
src/components/NavigationConfirmModal.js
Normal file
@@ -0,0 +1,115 @@
|
||||
import React from 'react';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
|
||||
const NavigationConfirmModal = ({ isOpen, onConfirm, onCancel, targetPath, hasData }) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
const getDataSummary = () => {
|
||||
try {
|
||||
const invoiceData = localStorage.getItem('currentInvoice');
|
||||
const objectData = localStorage.getItem('objectEditorData');
|
||||
const tableData = localStorage.getItem('tableEditorData');
|
||||
|
||||
const summary = [];
|
||||
|
||||
if (invoiceData) {
|
||||
const parsed = JSON.parse(invoiceData);
|
||||
if (parsed.invoiceNumber) summary.push(`Invoice #${parsed.invoiceNumber}`);
|
||||
if (parsed.company?.name) summary.push(`Company information (${parsed.company.name})`);
|
||||
if (parsed.client?.name) summary.push(`Client information (${parsed.client.name})`);
|
||||
if (parsed.items?.length > 0) summary.push(`${parsed.items.length} line items`);
|
||||
}
|
||||
|
||||
if (objectData) {
|
||||
const parsed = JSON.parse(objectData);
|
||||
if (parsed && Object.keys(parsed).length > 0) {
|
||||
summary.push(`Object data with ${Object.keys(parsed).length} properties`);
|
||||
}
|
||||
}
|
||||
|
||||
if (tableData) {
|
||||
const parsed = JSON.parse(tableData);
|
||||
if (parsed && parsed.length > 0) {
|
||||
summary.push(`Table data with ${parsed.length} rows`);
|
||||
}
|
||||
}
|
||||
|
||||
return summary;
|
||||
} catch (error) {
|
||||
return ['Unsaved data'];
|
||||
}
|
||||
};
|
||||
|
||||
const dataSummary = getDataSummary();
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full overflow-hidden">
|
||||
{/* Header */}
|
||||
<div className="px-6 py-4 border-b border-gray-200 dark:border-gray-700 bg-amber-50 dark:bg-amber-900/20">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex-shrink-0">
|
||||
<AlertTriangle className="h-6 w-6 text-amber-600 dark:text-amber-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-amber-900 dark:text-amber-100">
|
||||
Confirm Navigation
|
||||
</h3>
|
||||
<p className="text-sm text-amber-700 dark:text-amber-300">
|
||||
You have unsaved data that will be lost
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="px-6 py-4">
|
||||
<p className="text-gray-700 dark:text-gray-300 mb-4">
|
||||
You currently have unsaved data that will be lost if you leave this page. Are you sure you want to continue?
|
||||
</p>
|
||||
|
||||
{dataSummary.length > 0 && (
|
||||
<div className="bg-gray-50 dark:bg-gray-700 rounded-md p-3 mb-4">
|
||||
<p className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
You currently have:
|
||||
</p>
|
||||
<ul className="text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
{dataSummary.map((item, index) => (
|
||||
<li key={index} className="flex items-center">
|
||||
<span className="w-1.5 h-1.5 bg-amber-500 rounded-full mr-2 flex-shrink-0"></span>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-md p-3">
|
||||
<p className="text-blue-800 dark:text-blue-200 text-sm">
|
||||
<strong>Tip:</strong> Consider saving or exporting your current work before proceeding.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="px-6 py-4 bg-gray-50 dark:bg-gray-700 border-t border-gray-200 dark:border-gray-600 flex justify-end gap-3">
|
||||
<button
|
||||
onClick={onCancel}
|
||||
className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded-md hover:bg-gray-50 dark:hover:bg-gray-500 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={onConfirm}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-amber-600 hover:bg-amber-700 rounded-md transition-colors flex items-center gap-2"
|
||||
>
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
Continue & Lose Data
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavigationConfirmModal;
|
||||
Reference in New Issue
Block a user