WFDASH-WEBAPP/src/lib/quote-status.ts
Dave 43de97501c feat(tv): add TV dashboard + refine workflows
Add full PIN-protected TV dashboard stack including auth, redirects, map view, APIs, and server services.

Also include these critical refinements:
- Overhaul PO number generation to use per-company monthly sequences
- Rework quote rejection to bounce quotes back to operator queues instead of marking them terminal
- Add post_processing order tab group for clearer workflow visibility
- Fix collection pool tooltip positioning, overflow clipping, and default collapsed state
- Refactor customer list API, clean up unused form props, and fix item intel popover target ID handling
- Add comprehensive tests for new components and update existing test expectations
2026-08-13 20:55:22 +02:00

105 lines
5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Theme-driven status palettes. Every entry uses semantic tokens
* (--info / --warn / --success / --destructive) so the colour
* palette follows the active theme — switching from crisp→nvidia→neon
* changes which exact hue "approved" renders as, but the *meaning*
* (success = green, warning = amber, etc.) stays consistent.
*
* Keeping the semantic names intentional: they're named after what
* the status MEANS to the operator, not after a Tailwind palette
* name. That way the suite works across every theme in the picker.
*/
const quoteStatusClasses: Record<string, string> = {
// In-flight / awaiting further action — info (cool blue)
new: "border-info/30 bg-info/10 text-info",
claimed: "border-info/30 bg-info/10 text-info",
processed: "border-info/30 bg-info/10 text-info",
emailed: "border-info/30 bg-info/10 text-info",
sending: "border-info/40 bg-info/15 text-info",
template: "border-info/30 bg-info/10 text-info",
// Soft warning — needs operator attention soon
//
// `text-warn-foreground` is a dark amber (~oklch 0.13–0.22
// across themes) designed to sit on top of a LIGHT amber tint
// (bg-warn/15). In light mode the badge background is a
// pleasant pale wash and the dark text pops.
//
// In dark mode the same `bg-warn/15` blends into the dark card
// and produces a near-black amber tint, which the dark
// foreground then sits on invisibly — classic dark-on-dark.
//
// `dark:text-warn` swaps to the full bright warn hue
// (~oklch 0.78 in dark themes) so the text is luminous amber
// on the muted dark tint. Light-mode behaviour is unchanged.
approval_pending:
"border-warn/40 bg-warn/15 text-warn-foreground dark:text-warn",
// Partial-approval variants — added 2026-07-24. Same warning palette
// as approval_pending (operator + rep both treat these as "needs
// eyes") but a different border weight so the rep can tell at a
// glance that the submission is incomplete. Same dark-mode
// treatment as approval_pending — see note above.
partial_approval_pending:
"border-warn/30 bg-warn/10 text-warn-foreground dark:text-warn",
follow_up:
"border-warn/40 bg-warn/15 text-warn-foreground dark:text-warn",
// Terminal-positive
approved: "border-success/30 bg-success/10 text-success",
complete: "border-success/30 bg-success/10 text-success",
// Partial-approved (terminal for this batch, but a continuation quote
// exists with the remaining items). Slightly muted success colour so
// the operator can tell the original isn't "fully done" — the
// continuation is the live quote.
partial_approved: "border-success/30 bg-success/5 text-success/80",
// Terminal-negative
rejected: "border-destructive/30 bg-destructive/10 text-destructive",
email_failed: "border-destructive/30 bg-destructive/10 text-destructive",
mail_failed: "border-destructive/40 bg-destructive/15 text-destructive",
unmatched_bounce: "border-destructive/30 bg-destructive/10 text-destructive",
problem: "border-destructive/30 bg-destructive/10 text-destructive",
// Neutral terminal
cancelled: "border-muted bg-muted text-muted-foreground",
};
export function quoteStatusClassName(status: string) {
return quoteStatusClasses[status] || "border-muted bg-muted text-muted-foreground";
}
/**
* Human-friendly status label. Most statuses just replace underscores
* with spaces ("approval_pending" → "approval pending"), but a handful
* of legacy PB status strings render better with bespoke labels so the
* operator reads them as workflow states rather than raw enum values:
*
* emailed → "Awaiting reply"
* (the email was sent to the customer; we're waiting for them to come back.
* The legacy enum `emailed` doesn't read like a state on its own.)
* sending → "Sending…"
* (transient — outbound send in flight, n8n is processing.)
* follow_up → "Follow up"
* (just title-case it for nicer rendering.)
*/
const STATUS_LABELS: Record<string, string> = {
emailed: "Awaiting reply",
sending: "Sending…",
follow_up: "Follow up",
approval_pending: "Pending approval",
// Partial-approval labels — distinguish "full quote awaiting eyes"
// from "5 of 15 awaiting eyes". The "partial" prefix keeps the
// signal clear in lists, filters, and badge tooltips.
partial_approval_pending: "Partial — pending approval",
partial_approved: "Partial — sent",
unmatched_bounce: "Unmatched bounce",
email_failed: "Email failed",
mail_failed: "Mail failed",
// "delivery_note" is the status the office gives a quote that
// prices a delivery note. The operator claims it, fills in
// prices, and sends it without going through the regular
// approval queue — the items are already with the customer, so
// approval is implicit. See the v2.1 sendQuoteEmail change for
// the matching skip-approval logic.
delivery_note: "Delivery note",
};
export function formatQuoteStatus(status: string) {
return STATUS_LABELS[status] || status.replace(/_/g, " ");
}