wacrm/src/lib/broadcast-status.ts
Arnas Donauskas 32022c13fe
feat(theme): sweep remaining violet-* → primary across all surfaces
PR #132 (color themes) only tokenized the high-visibility chrome.
Inbox / contacts / pipelines / broadcasts / automations / auth /
settings sub-forms still had ~150 hard-coded `violet-*` classes
across ~32 files, so picking a non-violet theme felt half-broken —
sidebar and header changed colour, but message bubbles, deal cards,
and the broadcast wizard all stayed violet.

This is the mechanical follow-up: replace every `violet-N(/N)?`
with `primary(/N)?` so the theme picker themes the whole app.

Two non-trivial swaps inside the pass:
- `text-white` on a `bg-primary` button → `text-primary-foreground`.
  Emerald and Amber define a *dark* `--primary-foreground` because
  their primaries are bright; `text-white` over those would tank
  contrast.
- `bg-primary hover:bg-primary` (hover identical to base — original
  code had this latent bug from `hover:bg-violet-500`) → adds `/90`
  on hover so the lift is visible.

Test update:
- `broadcast-status.test.ts` asserted shade numbers (`bg-X-N/10`).
  Relaxed the regex to also accept token form (`bg-X/10`) since
  brand-accent status badges now ride the active theme.

Files touched: 49. Lint clean (existing warnings only), 173/173
tests, typecheck + build green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 17:00:32 +03:00

92 lines
2.4 KiB
TypeScript

/**
* Shared status badge config for broadcasts + recipients.
*
* Previously `statusConfig` was defined inline in both
* /broadcasts/page.tsx and /broadcasts/[id]/page.tsx with slight
* drift risk. One source of truth now.
*
* Dark-theme only — bg-*-500/10 + text-*-400 + border-*-500/20.
*/
import type { BroadcastStatus, RecipientStatus } from "@/types";
export interface StatusDisplay {
label: string;
classes: string;
/**
* Set true for statuses that should pulse in the UI to convey
* "live / in-flight" — currently only `sending`.
*/
pulse?: boolean;
}
export const broadcastStatusConfig: Record<BroadcastStatus, StatusDisplay> = {
draft: {
label: "Draft",
classes: "bg-slate-500/10 text-slate-400 border-slate-500/20",
},
scheduled: {
label: "Scheduled",
classes: "bg-blue-500/10 text-blue-400 border-blue-500/20",
},
sending: {
label: "Sending",
classes: "bg-yellow-500/10 text-yellow-400 border-yellow-500/20",
pulse: true,
},
sent: {
label: "Sent",
classes: "bg-primary/10 text-primary border-primary/20",
},
failed: {
label: "Failed",
classes: "bg-red-500/10 text-red-400 border-red-500/20",
},
};
export const recipientStatusConfig: Record<RecipientStatus, StatusDisplay> = {
pending: {
label: "Pending",
classes: "bg-slate-500/10 text-slate-400 border-slate-500/20",
},
sent: {
label: "Sent",
classes: "bg-blue-500/10 text-blue-400 border-blue-500/20",
},
delivered: {
label: "Delivered",
classes: "bg-primary/10 text-primary border-primary/20",
},
read: {
label: "Read",
classes: "bg-primary/10 text-primary border-primary/20",
},
replied: {
label: "Replied",
classes: "bg-purple-500/10 text-purple-400 border-purple-500/20",
},
failed: {
label: "Failed",
classes: "bg-red-500/10 text-red-400 border-red-500/20",
},
};
/**
* Tolerant lookup — callers often have a generic string status
* coming from Supabase. Falls back to the "draft" / "pending"
* entry so the UI never crashes on an unknown value.
*/
export function getBroadcastStatus(status: string): StatusDisplay {
return (
broadcastStatusConfig[status as BroadcastStatus] ??
broadcastStatusConfig.draft
);
}
export function getRecipientStatus(status: string): StatusDisplay {
return (
recipientStatusConfig[status as RecipientStatus] ??
recipientStatusConfig.pending
);
}