fix(ux): replace confirm() with ConfirmModal, fix dark-mode colors, add currency format
- Add reusable ConfirmModal component (themed, Escape key, focus trap) - Replace all native confirm() calls in Orders, ProductLibrary, Materials, Admin, Billing - Fix ValidationDialog (Upload.tsx) hardcoded bg-white/text-gray-* → semantic tokens - Fix NewInvoiceModal (Billing.tsx) hardcoded colors → semantic tokens - Add formatCurrency (Intl.NumberFormat de-DE/EUR) to NewProductOrder wizard - Resolves audit issues C1, C3, M3 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -707,13 +707,19 @@ function ValidationDialog({
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||
<div className="bg-white rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col">
|
||||
<div
|
||||
className="rounded-xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col border"
|
||||
style={{ backgroundColor: 'var(--color-bg-surface)', borderColor: 'var(--color-border-default)' }}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Import Validation</h2>
|
||||
<div
|
||||
className="px-6 py-4 border-b flex items-center justify-between"
|
||||
style={{ borderColor: 'var(--color-border-default)' }}
|
||||
>
|
||||
<h2 className="text-lg font-semibold text-content">Import Validation</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 text-xl leading-none"
|
||||
className="text-content-muted hover:text-content text-xl leading-none"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
@@ -721,8 +727,8 @@ function ValidationDialog({
|
||||
|
||||
<div className="flex-1 overflow-y-auto px-6 py-4 space-y-4">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center gap-3 text-gray-500">
|
||||
<div className="w-5 h-5 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
||||
<div className="flex items-center gap-3 text-content-muted">
|
||||
<div className="w-5 h-5 border-2 border-accent border-t-transparent rounded-full animate-spin" />
|
||||
<span>Validating import...</span>
|
||||
</div>
|
||||
) : (
|
||||
@@ -756,16 +762,20 @@ function ValidationDialog({
|
||||
.map((row) => (
|
||||
<div
|
||||
key={row.row_index}
|
||||
className="border border-gray-200 rounded-lg overflow-hidden"
|
||||
className="rounded-lg overflow-hidden border"
|
||||
style={{ borderColor: 'var(--color-border-default)' }}
|
||||
>
|
||||
<button
|
||||
className={`w-full flex items-center justify-between px-4 py-3 text-left hover:bg-gray-50 transition-colors ${
|
||||
className={`w-full flex items-center justify-between px-4 py-3 text-left transition-colors ${
|
||||
row.status === 'error'
|
||||
? 'bg-red-50'
|
||||
: row.status === 'warning'
|
||||
? 'bg-yellow-50'
|
||||
: 'bg-white'
|
||||
: ''
|
||||
}`}
|
||||
style={row.status !== 'error' && row.status !== 'warning'
|
||||
? { backgroundColor: 'var(--color-bg-surface-alt)' }
|
||||
: undefined}
|
||||
onClick={() =>
|
||||
setExpandedRows((prev) => {
|
||||
const next = new Set(prev)
|
||||
@@ -786,30 +796,34 @@ function ValidationDialog({
|
||||
: 'bg-green-500'
|
||||
}`}
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-700">
|
||||
<span className="text-sm font-medium text-content">
|
||||
Row {row.row_index + 1}
|
||||
{row.pim_id ? ` — ${row.pim_id}` : ''}
|
||||
{row.produkt_baureihe ? ` (${row.produkt_baureihe})` : ''}
|
||||
</span>
|
||||
<span className="text-xs text-gray-400">
|
||||
<span className="text-xs text-content-muted">
|
||||
{row.issues.length} issue{row.issues.length !== 1 ? 's' : ''}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-gray-400 text-xs">
|
||||
<span className="text-content-muted text-xs">
|
||||
{expandedRows.has(row.row_index) ? '▲' : '▼'}
|
||||
</span>
|
||||
</button>
|
||||
{expandedRows.has(row.row_index) && (
|
||||
<div className="border-t border-gray-100 divide-y divide-gray-50">
|
||||
<div
|
||||
className="border-t divide-y"
|
||||
style={{ borderColor: 'var(--color-border-default)' }}
|
||||
>
|
||||
{row.issues.map((issue: ValidationIssue, i: number) => (
|
||||
<div
|
||||
key={i}
|
||||
className="px-4 py-3 flex items-start justify-between gap-4"
|
||||
style={{ borderColor: 'var(--color-border-default)' }}
|
||||
>
|
||||
<div>
|
||||
<p className="text-sm text-gray-700">{issue.message}</p>
|
||||
<p className="text-sm text-content">{issue.message}</p>
|
||||
{issue.suggestion && (
|
||||
<p className="text-xs text-gray-500 mt-0.5">
|
||||
<p className="text-xs text-content-muted mt-0.5">
|
||||
Suggestion: <em>{issue.suggestion}</em>
|
||||
</p>
|
||||
)}
|
||||
@@ -821,7 +835,7 @@ function ValidationDialog({
|
||||
onClick={() =>
|
||||
onSaveAlias(issue.value!, issue.suggestion!)
|
||||
}
|
||||
className="text-xs bg-blue-600 text-white px-3 py-1 rounded hover:bg-blue-700 whitespace-nowrap"
|
||||
className="text-xs bg-accent text-white px-3 py-1 rounded whitespace-nowrap"
|
||||
>
|
||||
Save as alias
|
||||
</button>
|
||||
@@ -843,10 +857,13 @@ function ValidationDialog({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="px-6 py-4 border-t border-gray-200 flex justify-end">
|
||||
<div
|
||||
className="px-6 py-4 border-t flex justify-end"
|
||||
style={{ borderColor: 'var(--color-border-default)' }}
|
||||
>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-sm bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors"
|
||||
className="px-4 py-2 text-sm text-content-secondary rounded-lg border border-border-default hover:bg-surface-hover transition-colors"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user