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:
@@ -13,6 +13,9 @@ import { estimatePrice } from '../api/pricing'
|
||||
import type { Product, RenderPosition } from '../api/products'
|
||||
import type { OutputType } from '../api/outputTypes'
|
||||
|
||||
const formatCurrency = (amount: number) =>
|
||||
new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(amount)
|
||||
|
||||
const CATEGORIES = [
|
||||
{ key: 'TRB', label: 'TRB' },
|
||||
{ key: 'Kugellager', label: 'Kugellager' },
|
||||
@@ -620,7 +623,7 @@ export default function NewProductOrderPage() {
|
||||
<span className="text-sm text-content-muted">
|
||||
{selectedProducts.size} product{selectedProducts.size !== 1 ? 's' : ''} · {orderLines.length} render job{orderLines.length !== 1 ? 's' : ''}
|
||||
{priceEstimate && priceEstimate.total > 0 && (
|
||||
<> · Estimated: <span className="font-semibold text-content-secondary">{priceEstimate.total.toFixed(2)}</span></>
|
||||
<> · Estimated: <span className="font-semibold text-content-secondary">{formatCurrency(priceEstimate.total)}</span></>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -695,7 +698,7 @@ export default function NewProductOrderPage() {
|
||||
{(() => {
|
||||
const price = getLinePrice(line.product.id, line.outputType.id)
|
||||
return price != null ? (
|
||||
<span className="font-medium text-content-secondary">{price.toFixed(2)}</span>
|
||||
<span className="font-medium text-content-secondary">{formatCurrency(price)}</span>
|
||||
) : (
|
||||
<span className="text-content-muted">—</span>
|
||||
)
|
||||
@@ -741,7 +744,7 @@ export default function NewProductOrderPage() {
|
||||
<span className="text-sm text-content-muted">
|
||||
{orderLines.length} render job{orderLines.length !== 1 ? 's' : ''}
|
||||
{priceEstimate && priceEstimate.total > 0 && (
|
||||
<> · Estimated: <span className="font-semibold text-content-secondary">{priceEstimate.total.toFixed(2)}</span></>
|
||||
<> · Estimated: <span className="font-semibold text-content-secondary">{formatCurrency(priceEstimate.total)}</span></>
|
||||
)}
|
||||
</span>
|
||||
<button
|
||||
@@ -839,7 +842,7 @@ function ProductOutputRow({
|
||||
<p className="text-xs text-content-muted">
|
||||
{ot.renderer} · {ot.output_format.toUpperCase()}
|
||||
{ot.price_per_item != null && (
|
||||
<> · <span className="text-emerald-600 font-medium">{ot.price_per_item.toFixed(2)}</span></>
|
||||
<> · <span className="text-emerald-600 font-medium">{formatCurrency(ot.price_per_item)}</span></>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user