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:
@@ -4,6 +4,7 @@ import { toast } from 'sonner'
|
||||
import { UserPlus, Trash2, Pencil, ChevronDown, ChevronUp, ChevronRight, Settings, RefreshCw, CheckCircle2, XCircle, Clock, DollarSign, Layers, AlertTriangle, Upload, FileBox, Plus, X, LayoutDashboard } from 'lucide-react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import api from '../api/client'
|
||||
import ConfirmModal from '../components/ConfirmModal'
|
||||
import TemplateEditor from '../components/admin/TemplateEditor'
|
||||
import PricingTierTable from '../components/admin/PricingTierTable'
|
||||
import OutputTypeTable from '../components/admin/OutputTypeTable'
|
||||
@@ -191,6 +192,7 @@ export default function AdminPage() {
|
||||
const [smtpDraft, setSmtpDraft] = useState<Partial<Settings>>({})
|
||||
const smtp = { ...settings, ...smtpDraft } as Settings
|
||||
|
||||
const [confirmState, setConfirmState] = useState<{ open: boolean; title: string; message: string; onConfirm: () => void }>({ open: false, title: '', message: '', onConfirm: () => {} })
|
||||
const [showTenantDashboardModal, setShowTenantDashboardModal] = useState(false)
|
||||
const { data: tenantDefaultWidgets } = useQuery<WidgetConfig[]>({
|
||||
queryKey: ['tenant-default-dashboard'],
|
||||
@@ -276,7 +278,17 @@ export default function AdminPage() {
|
||||
{user.is_active ? 'active' : 'inactive'}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => { if (confirm('Delete user?')) deleteUserMut.mutate(user.id) }}
|
||||
onClick={() => {
|
||||
setConfirmState({
|
||||
open: true,
|
||||
title: 'Delete User',
|
||||
message: `Delete user "${user.email}"? This cannot be undone.`,
|
||||
onConfirm: () => {
|
||||
deleteUserMut.mutate(user.id)
|
||||
setConfirmState((s) => ({ ...s, open: false }))
|
||||
},
|
||||
})
|
||||
}}
|
||||
className="text-content-muted hover:text-red-500 transition-colors"
|
||||
title="Delete user"
|
||||
>
|
||||
@@ -1440,7 +1452,17 @@ function AssetLibraryPanel() {
|
||||
</button>
|
||||
<button
|
||||
className="btn-danger text-xs"
|
||||
onClick={() => { if (confirm(`Delete "${lib.name}"?`)) deleteMut.mutate(lib.id) }}
|
||||
onClick={() => {
|
||||
setConfirmState({
|
||||
open: true,
|
||||
title: 'Delete Asset Library',
|
||||
message: `Delete "${lib.name}"?`,
|
||||
onConfirm: () => {
|
||||
deleteMut.mutate(lib.id)
|
||||
setConfirmState((s) => ({ ...s, open: false }))
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
</button>
|
||||
@@ -1484,6 +1506,14 @@ function AssetLibraryPanel() {
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ConfirmModal
|
||||
open={confirmState.open}
|
||||
title={confirmState.title}
|
||||
message={confirmState.message}
|
||||
onConfirm={confirmState.onConfirm}
|
||||
onCancel={() => setConfirmState((s) => ({ ...s, open: false }))}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user