- Add React.lazy code splitting for all 15 tool pages - Fix WCAG AA contrast issues (304 text color fixes) - Add ARIA labels and aria-expanded to navigation buttons - Add aria-live for error announcements in tools - Implement responsive ad layout: - Desktop (≥1280px): Right sidebar with 3 ad units - Tablet (1024-1279px): Bottom section with 3 horizontal units - Mobile (<1024px): Fixed bottom banner - Add TabletAdSection component for tablet ad placement - Integrate Onidel affiliate partnership - Update all Adsterra domains to solutionbiologyisle.com - Add release notes for 2026-02-18 updates
116 lines
4.5 KiB
JavaScript
116 lines
4.5 KiB
JavaScript
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-600 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;
|