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:
2026-03-08 19:59:13 +01:00
parent 9f54bc3ab1
commit 915abe9d74
8 changed files with 307 additions and 45 deletions
+35 -18
View File
@@ -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>