wacrm/src/lib/broadcast-status.ts
Arnas Donauskas 77dfa4f165
chore: switch accent palette to Hostinger violet, drop emerald green
The legal brief on third-party trademark use says: avoid the WhatsApp
teal/green palette and any visual treatment that could read as a
WhatsApp/Meta product. Emerald-500 was used as the primary brand colour
across hero, CTAs, sidebar, charts, badges, and the favicon — close
enough in hue to WhatsApp's signature green to muddy the line.

Swapping to Tailwind violet (which matches the Hostinger brand purple
#7c3aed) makes the visual identity unambiguously ours.

Scope:
- Mechanical Tailwind class swap: emerald-* → violet-* across src/.
- src/app/globals.css: --primary, --ring, --sidebar-primary, --sidebar-ring,
  --chart-1 oklch tokens swapped from emerald (oklch 0.696 0.17 162) to
  Hostinger-aligned violet (oklch 0.526 0.247 293).
- src/app/icon.tsx: favicon background #10b981 → #7c3aed.
- src/app/opengraph-image.tsx: badge bg, accent text, gradient.
- src/components/landing/hero.tsx + cta-banner.tsx: radial-gradient hex.
- src/components/landing/mock/analytics-mock.tsx: marketing chart line.
- src/components/dashboard/conversations-chart.tsx + response-time-chart.tsx:
  "Outgoing" series colour. Distinguishable from "Incoming" blue at a
  glance; we can revisit the chart pair later for stronger colourblind
  contrast (separate concern).
- README.md: license badge colour parameter.

Out of scope:
- src/components/settings/tag-manager.tsx still offers "Emerald" as one
  of the user-pickable contact-tag colours alongside Blue / Yellow / Red
  / etc. — that's a user data choice, not a brand mark.

Contrast check: white text on #7c3aed measures ~4.78:1 (WCAG AA passing
for normal body text). Violet-400 (#a78bfa) on slate-950 measures ~10:1
for accent text, well clear of AA.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:57:12 +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-violet-500/10 text-violet-400 border-violet-500/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-violet-500/10 text-violet-400 border-violet-500/20",
},
read: {
label: "Read",
classes: "bg-violet-500/10 text-violet-300 border-violet-500/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
);
}