mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 17:29:45 +00:00
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>
This commit is contained in:
parent
dc3eac34e3
commit
32022c13fe
@ -46,8 +46,8 @@ export default function ForgotPasswordPage() {
|
|||||||
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
||||||
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
||||||
<CardHeader className="items-center text-center">
|
<CardHeader className="items-center text-center">
|
||||||
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-violet-500/10">
|
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
||||||
<CheckCircle className="h-6 w-6 text-violet-500" />
|
<CheckCircle className="h-6 w-6 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-xl text-white">
|
<CardTitle className="text-xl text-white">
|
||||||
Check your email
|
Check your email
|
||||||
@ -77,8 +77,8 @@ export default function ForgotPasswordPage() {
|
|||||||
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
||||||
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
||||||
<CardHeader className="items-center text-center">
|
<CardHeader className="items-center text-center">
|
||||||
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-violet-500/10">
|
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
||||||
<MessageSquare className="h-6 w-6 text-violet-500" />
|
<MessageSquare className="h-6 w-6 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-xl text-white">Reset password</CardTitle>
|
<CardTitle className="text-xl text-white">Reset password</CardTitle>
|
||||||
<CardDescription className="text-slate-400">
|
<CardDescription className="text-slate-400">
|
||||||
@ -104,14 +104,14 @@ export default function ForgotPasswordPage() {
|
|||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
required
|
required
|
||||||
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-violet-500 focus-visible:ring-violet-500/20"
|
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-primary focus-visible:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="mt-2 h-10 w-full bg-violet-600 text-white hover:bg-violet-500 disabled:opacity-50"
|
className="mt-2 h-10 w-full bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{loading ? "Sending..." : "Send reset link"}
|
{loading ? "Sending..." : "Send reset link"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -47,8 +47,8 @@ export default function LoginPage() {
|
|||||||
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
||||||
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
||||||
<CardHeader className="items-center text-center">
|
<CardHeader className="items-center text-center">
|
||||||
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-violet-500/10">
|
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
||||||
<MessageSquare className="h-6 w-6 text-violet-500" />
|
<MessageSquare className="h-6 w-6 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-xl text-white">Welcome back</CardTitle>
|
<CardTitle className="text-xl text-white">Welcome back</CardTitle>
|
||||||
<CardDescription className="text-slate-400">
|
<CardDescription className="text-slate-400">
|
||||||
@ -74,7 +74,7 @@ export default function LoginPage() {
|
|||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
required
|
required
|
||||||
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-violet-500 focus-visible:ring-violet-500/20"
|
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-primary focus-visible:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ export default function LoginPage() {
|
|||||||
</Label>
|
</Label>
|
||||||
<Link
|
<Link
|
||||||
href="/forgot-password"
|
href="/forgot-password"
|
||||||
className="text-sm text-violet-500 hover:text-violet-400"
|
className="text-sm text-primary hover:text-primary/80"
|
||||||
>
|
>
|
||||||
Forgot password?
|
Forgot password?
|
||||||
</Link>
|
</Link>
|
||||||
@ -97,14 +97,14 @@ export default function LoginPage() {
|
|||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-violet-500 focus-visible:ring-violet-500/20"
|
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-primary focus-visible:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="mt-2 h-10 w-full bg-violet-600 text-white hover:bg-violet-500 disabled:opacity-50"
|
className="mt-2 h-10 w-full bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{loading ? "Signing in..." : "Sign in"}
|
{loading ? "Signing in..." : "Sign in"}
|
||||||
</Button>
|
</Button>
|
||||||
@ -114,7 +114,7 @@ export default function LoginPage() {
|
|||||||
Don't have an account?{" "}
|
Don't have an account?{" "}
|
||||||
<Link
|
<Link
|
||||||
href="/signup"
|
href="/signup"
|
||||||
className="text-violet-500 hover:text-violet-400"
|
className="text-primary hover:text-primary/80"
|
||||||
>
|
>
|
||||||
Create account
|
Create account
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -66,8 +66,8 @@ export default function SignupPage() {
|
|||||||
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
||||||
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
||||||
<CardHeader className="items-center text-center">
|
<CardHeader className="items-center text-center">
|
||||||
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-violet-500/10">
|
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
||||||
<CheckCircle className="h-6 w-6 text-violet-500" />
|
<CheckCircle className="h-6 w-6 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-xl text-white">
|
<CardTitle className="text-xl text-white">
|
||||||
Check your email
|
Check your email
|
||||||
@ -97,8 +97,8 @@ export default function SignupPage() {
|
|||||||
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
<div className="flex min-h-screen items-center justify-center bg-slate-950 px-4">
|
||||||
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
<Card className="w-full max-w-md border-slate-800 bg-slate-900">
|
||||||
<CardHeader className="items-center text-center">
|
<CardHeader className="items-center text-center">
|
||||||
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-violet-500/10">
|
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
||||||
<MessageSquare className="h-6 w-6 text-violet-500" />
|
<MessageSquare className="h-6 w-6 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<CardTitle className="text-xl text-white">Create account</CardTitle>
|
<CardTitle className="text-xl text-white">Create account</CardTitle>
|
||||||
<CardDescription className="text-slate-400">
|
<CardDescription className="text-slate-400">
|
||||||
@ -124,7 +124,7 @@ export default function SignupPage() {
|
|||||||
value={fullName}
|
value={fullName}
|
||||||
onChange={(e) => setFullName(e.target.value)}
|
onChange={(e) => setFullName(e.target.value)}
|
||||||
required
|
required
|
||||||
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-violet-500 focus-visible:ring-violet-500/20"
|
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-primary focus-visible:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -139,7 +139,7 @@ export default function SignupPage() {
|
|||||||
value={email}
|
value={email}
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
required
|
required
|
||||||
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-violet-500 focus-visible:ring-violet-500/20"
|
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-primary focus-visible:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ export default function SignupPage() {
|
|||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-violet-500 focus-visible:ring-violet-500/20"
|
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-primary focus-visible:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -169,14 +169,14 @@ export default function SignupPage() {
|
|||||||
value={confirmPassword}
|
value={confirmPassword}
|
||||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-violet-500 focus-visible:ring-violet-500/20"
|
className="border-slate-700 bg-slate-800 text-white placeholder:text-slate-500 focus-visible:border-primary focus-visible:ring-primary/20"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="mt-2 h-10 w-full bg-violet-600 text-white hover:bg-violet-500 disabled:opacity-50"
|
className="mt-2 h-10 w-full bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{loading ? "Creating account..." : "Create account"}
|
{loading ? "Creating account..." : "Create account"}
|
||||||
</Button>
|
</Button>
|
||||||
@ -186,7 +186,7 @@ export default function SignupPage() {
|
|||||||
Already have an account?{" "}
|
Already have an account?{" "}
|
||||||
<Link
|
<Link
|
||||||
href="/login"
|
href="/login"
|
||||||
className="text-violet-500 hover:text-violet-400"
|
className="text-primary hover:text-primary/80"
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export default function EditAutomationPage({
|
|||||||
<p className="text-sm text-red-400">{error}</p>
|
<p className="text-sm text-red-400">{error}</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => router.push("/automations")}
|
onClick={() => router.push("/automations")}
|
||||||
className="text-sm text-violet-400 hover:text-violet-300"
|
className="text-sm text-primary hover:text-primary/80"
|
||||||
>
|
>
|
||||||
Back to Automations
|
Back to Automations
|
||||||
</button>
|
</button>
|
||||||
@ -65,7 +65,7 @@ export default function EditAutomationPage({
|
|||||||
if (!initial) {
|
if (!initial) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-screen items-center justify-center">
|
<div className="flex h-screen items-center justify-center">
|
||||||
<Loader2 className="h-6 w-6 animate-spin text-violet-500" />
|
<Loader2 className="h-6 w-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,7 @@ export default function AutomationLogsPage({
|
|||||||
if (!automation || logs === null) {
|
if (!automation || logs === null) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-64 items-center justify-center">
|
<div className="flex h-64 items-center justify-center">
|
||||||
<Loader2 className="h-6 w-6 animate-spin text-violet-500" />
|
<Loader2 className="h-6 w-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ export default function AutomationLogsPage({
|
|||||||
function StatusBadge({ status }: { status: AutomationLog["status"] }) {
|
function StatusBadge({ status }: { status: AutomationLog["status"] }) {
|
||||||
const classes =
|
const classes =
|
||||||
status === "success"
|
status === "success"
|
||||||
? "border-violet-500/30 bg-violet-500/10 text-violet-300"
|
? "border-primary/30 bg-primary/10 text-primary"
|
||||||
: status === "partial"
|
: status === "partial"
|
||||||
? "border-amber-500/30 bg-amber-500/10 text-amber-300"
|
? "border-amber-500/30 bg-amber-500/10 text-amber-300"
|
||||||
: "border-red-500/30 bg-red-500/10 text-red-300"
|
: "border-red-500/30 bg-red-500/10 text-red-300"
|
||||||
@ -190,7 +190,7 @@ function StepRow({ result }: { result: AutomationLogStepResult }) {
|
|||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full",
|
"mt-0.5 flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full",
|
||||||
ok ? "bg-violet-500/20 text-violet-400" : "bg-red-500/20 text-red-400",
|
ok ? "bg-primary/20 text-primary" : "bg-red-500/20 text-red-400",
|
||||||
)}
|
)}
|
||||||
aria-hidden
|
aria-hidden
|
||||||
>
|
>
|
||||||
|
|||||||
@ -146,7 +146,7 @@ export default function AutomationsPage() {
|
|||||||
if (automations === null) {
|
if (automations === null) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-64 items-center justify-center">
|
<div className="flex h-64 items-center justify-center">
|
||||||
<Loader2 className="h-6 w-6 animate-spin text-violet-500" />
|
<Loader2 className="h-6 w-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ export default function AutomationsPage() {
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push("/automations/new")}
|
onClick={() => router.push("/automations/new")}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700"
|
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
Create Automation
|
Create Automation
|
||||||
@ -182,9 +182,9 @@ export default function AutomationsPage() {
|
|||||||
<button
|
<button
|
||||||
key={slug}
|
key={slug}
|
||||||
onClick={() => startFromTemplate(slug)}
|
onClick={() => startFromTemplate(slug)}
|
||||||
className="group flex flex-col items-start rounded-xl border border-slate-800 bg-slate-900 p-4 text-left transition-colors hover:border-violet-500/50 hover:bg-slate-900/80"
|
className="group flex flex-col items-start rounded-xl border border-slate-800 bg-slate-900 p-4 text-left transition-colors hover:border-primary/50 hover:bg-slate-900/80"
|
||||||
>
|
>
|
||||||
<div className="mb-3 flex h-9 w-9 items-center justify-center rounded-lg bg-violet-500/10 text-violet-400 group-hover:bg-violet-500/15">
|
<div className="mb-3 flex h-9 w-9 items-center justify-center rounded-lg bg-primary/10 text-primary group-hover:bg-primary/15">
|
||||||
<Icon className="h-5 w-5" />
|
<Icon className="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-semibold text-white">{t.name}</div>
|
<div className="text-sm font-semibold text-white">{t.name}</div>
|
||||||
@ -198,8 +198,8 @@ export default function AutomationsPage() {
|
|||||||
|
|
||||||
{automations.length === 0 ? (
|
{automations.length === 0 ? (
|
||||||
<div className="flex h-48 flex-col items-center justify-center rounded-xl border border-dashed border-slate-800 bg-slate-900/40">
|
<div className="flex h-48 flex-col items-center justify-center rounded-xl border border-dashed border-slate-800 bg-slate-900/40">
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-violet-500/10">
|
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10">
|
||||||
<Zap className="h-6 w-6 text-violet-500" />
|
<Zap className="h-6 w-6 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-3 text-sm font-medium text-white">No automations yet</p>
|
<p className="mt-3 text-sm font-medium text-white">No automations yet</p>
|
||||||
<p className="mt-1 text-xs text-slate-400">
|
<p className="mt-1 text-xs text-slate-400">
|
||||||
@ -275,10 +275,10 @@ function AutomationCard({
|
|||||||
<li className="rounded-xl border border-slate-800 bg-slate-900 transition-colors hover:border-slate-700">
|
<li className="rounded-xl border border-slate-800 bg-slate-900 transition-colors hover:border-slate-700">
|
||||||
<div className="flex items-center gap-4 p-4">
|
<div className="flex items-center gap-4 p-4">
|
||||||
<div
|
<div
|
||||||
className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-violet-500/10"
|
className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-primary/10"
|
||||||
aria-hidden
|
aria-hidden
|
||||||
>
|
>
|
||||||
<Zap className="h-5 w-5 text-violet-400" />
|
<Zap className="h-5 w-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@ -292,8 +292,8 @@ function AutomationCard({
|
|||||||
</span>
|
</span>
|
||||||
{automation.is_active && (
|
{automation.is_active && (
|
||||||
<span className="relative flex h-2 w-2" aria-label="active">
|
<span className="relative flex h-2 w-2" aria-label="active">
|
||||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-violet-400 opacity-75" />
|
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" />
|
||||||
<span className="relative inline-flex h-2 w-2 rounded-full bg-violet-500" />
|
<span className="relative inline-flex h-2 w-2 rounded-full bg-primary" />
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -246,7 +246,7 @@ export default function BroadcastDetailPage() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-64 items-center justify-center">
|
<div className="flex h-64 items-center justify-center">
|
||||||
<Loader2 className="h-6 w-6 animate-spin text-violet-500" />
|
<Loader2 className="h-6 w-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -265,7 +265,7 @@ export default function BroadcastDetailPage() {
|
|||||||
const status = getBroadcastStatus(broadcast.status);
|
const status = getBroadcastStatus(broadcast.status);
|
||||||
|
|
||||||
const funnelSteps: FunnelStep[] = [
|
const funnelSteps: FunnelStep[] = [
|
||||||
{ label: 'Sent', value: broadcast.sent_count, color: 'bg-violet-500' },
|
{ label: 'Sent', value: broadcast.sent_count, color: 'bg-primary' },
|
||||||
{ label: 'Delivered', value: broadcast.delivered_count, color: 'bg-teal-500' },
|
{ label: 'Delivered', value: broadcast.delivered_count, color: 'bg-teal-500' },
|
||||||
{ label: 'Read', value: broadcast.read_count, color: 'bg-blue-500' },
|
{ label: 'Read', value: broadcast.read_count, color: 'bg-blue-500' },
|
||||||
{ label: 'Replied', value: broadcast.replied_count, color: 'bg-indigo-500' },
|
{ label: 'Replied', value: broadcast.replied_count, color: 'bg-indigo-500' },
|
||||||
@ -361,7 +361,7 @@ export default function BroadcastDetailPage() {
|
|||||||
value={broadcast.sent_count}
|
value={broadcast.sent_count}
|
||||||
total={broadcast.total_recipients}
|
total={broadcast.total_recipients}
|
||||||
icon={<Send className="h-4 w-4" />}
|
icon={<Send className="h-4 w-4" />}
|
||||||
color="bg-violet-500/10 text-violet-400"
|
color="bg-primary/10 text-primary"
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
label="Delivered"
|
label="Delivered"
|
||||||
@ -423,7 +423,7 @@ export default function BroadcastDetailPage() {
|
|||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => setStatusFilter('all')}
|
onClick={() => setStatusFilter('all')}
|
||||||
className={
|
className={
|
||||||
statusFilter === 'all' ? 'text-violet-400' : 'text-slate-300'
|
statusFilter === 'all' ? 'text-primary' : 'text-slate-300'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
All statuses
|
All statuses
|
||||||
@ -434,7 +434,7 @@ export default function BroadcastDetailPage() {
|
|||||||
onClick={() => setStatusFilter(s)}
|
onClick={() => setStatusFilter(s)}
|
||||||
className={
|
className={
|
||||||
statusFilter === s
|
statusFilter === s
|
||||||
? 'text-violet-400'
|
? 'text-primary'
|
||||||
: 'text-slate-300'
|
: 'text-slate-300'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -140,9 +140,9 @@ export default function NewBroadcastPage() {
|
|||||||
<div
|
<div
|
||||||
className={`flex h-8 w-8 items-center justify-center rounded-full text-xs font-medium transition-all ${
|
className={`flex h-8 w-8 items-center justify-center rounded-full text-xs font-medium transition-all ${
|
||||||
isCompleted
|
isCompleted
|
||||||
? 'bg-violet-500 text-white'
|
? 'bg-primary text-primary-foreground'
|
||||||
: isActive
|
: isActive
|
||||||
? 'border-2 border-violet-500 bg-violet-500/10 text-violet-400'
|
? 'border-2 border-primary bg-primary/10 text-primary'
|
||||||
: 'border border-slate-700 bg-slate-800 text-slate-500'
|
: 'border border-slate-700 bg-slate-800 text-slate-500'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -150,7 +150,7 @@ export default function NewBroadcastPage() {
|
|||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
className={`hidden text-sm font-medium sm:block ${
|
className={`hidden text-sm font-medium sm:block ${
|
||||||
isActive ? 'text-white' : isCompleted ? 'text-violet-400' : 'text-slate-500'
|
isActive ? 'text-white' : isCompleted ? 'text-primary' : 'text-slate-500'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{step.label}
|
{step.label}
|
||||||
@ -159,7 +159,7 @@ export default function NewBroadcastPage() {
|
|||||||
{index < steps.length - 1 && (
|
{index < steps.length - 1 && (
|
||||||
<div
|
<div
|
||||||
className={`mx-3 h-px flex-1 ${
|
className={`mx-3 h-px flex-1 ${
|
||||||
index < currentStep ? 'bg-violet-500' : 'bg-slate-800'
|
index < currentStep ? 'bg-primary' : 'bg-slate-800'
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -35,7 +35,7 @@ function RateCell({
|
|||||||
}: {
|
}: {
|
||||||
value: number;
|
value: number;
|
||||||
total: number;
|
total: number;
|
||||||
/** Tailwind bg class for the fill, e.g. "bg-violet-500" */
|
/** Tailwind bg class for the fill, e.g. "bg-primary" */
|
||||||
color: string;
|
color: string;
|
||||||
}) {
|
}) {
|
||||||
const pct = percent(value, total);
|
const pct = percent(value, total);
|
||||||
@ -128,7 +128,7 @@ export default function BroadcastsPage() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-64 items-center justify-center">
|
<div className="flex h-64 items-center justify-center">
|
||||||
<Loader2 className="h-6 w-6 animate-spin text-violet-500" />
|
<Loader2 className="h-6 w-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -154,7 +154,7 @@ export default function BroadcastsPage() {
|
|||||||
aria-label="Broadcast in progress"
|
aria-label="Broadcast in progress"
|
||||||
className="broadcast-indeterminate fixed inset-x-0 top-0 z-40 h-0.5 overflow-hidden bg-slate-800"
|
className="broadcast-indeterminate fixed inset-x-0 top-0 z-40 h-0.5 overflow-hidden bg-slate-800"
|
||||||
>
|
>
|
||||||
<div className="broadcast-indeterminate-bar h-0.5 bg-violet-500" />
|
<div className="broadcast-indeterminate-bar h-0.5 bg-primary" />
|
||||||
<style jsx>{`
|
<style jsx>{`
|
||||||
.broadcast-indeterminate-bar {
|
.broadcast-indeterminate-bar {
|
||||||
width: 33%;
|
width: 33%;
|
||||||
@ -183,7 +183,7 @@ export default function BroadcastsPage() {
|
|||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push('/broadcasts/new')}
|
onClick={() => router.push('/broadcasts/new')}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700"
|
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
New Broadcast
|
New Broadcast
|
||||||
@ -199,7 +199,7 @@ export default function BroadcastsPage() {
|
|||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => router.push('/broadcasts/new')}
|
onClick={() => router.push('/broadcasts/new')}
|
||||||
className="mt-4 bg-violet-600 text-white hover:bg-violet-700"
|
className="mt-4 bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
New Broadcast
|
New Broadcast
|
||||||
@ -243,7 +243,7 @@ export default function BroadcastsPage() {
|
|||||||
<RateCell
|
<RateCell
|
||||||
value={broadcast.delivered_count}
|
value={broadcast.delivered_count}
|
||||||
total={broadcast.total_recipients}
|
total={broadcast.total_recipients}
|
||||||
color="bg-violet-500"
|
color="bg-primary"
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="hidden lg:table-cell">
|
<TableCell className="hidden lg:table-cell">
|
||||||
|
|||||||
@ -226,7 +226,7 @@ export default function ContactsPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={openAddForm}
|
onClick={openAddForm}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
>
|
>
|
||||||
<Plus className="size-4" />
|
<Plus className="size-4" />
|
||||||
Add Contact
|
Add Contact
|
||||||
@ -269,7 +269,7 @@ export default function ContactsPage() {
|
|||||||
<TableRow className="border-slate-800">
|
<TableRow className="border-slate-800">
|
||||||
<TableCell colSpan={7} className="text-center py-12">
|
<TableCell colSpan={7} className="text-center py-12">
|
||||||
<div className="flex flex-col items-center gap-2">
|
<div className="flex flex-col items-center gap-2">
|
||||||
<Loader2 className="size-6 animate-spin text-violet-500" />
|
<Loader2 className="size-6 animate-spin text-primary" />
|
||||||
<p className="text-sm text-slate-500">Loading contacts...</p>
|
<p className="text-sm text-slate-500">Loading contacts...</p>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@ -306,7 +306,7 @@ const EVENT_COLOR: Record<string, string> = {
|
|||||||
started: "text-emerald-300",
|
started: "text-emerald-300",
|
||||||
node_entered: "text-slate-300",
|
node_entered: "text-slate-300",
|
||||||
message_sent: "text-sky-300",
|
message_sent: "text-sky-300",
|
||||||
reply_received: "text-violet-300",
|
reply_received: "text-primary",
|
||||||
fallback_fired: "text-amber-300",
|
fallback_fired: "text-amber-300",
|
||||||
handoff: "text-amber-300",
|
handoff: "text-amber-300",
|
||||||
timeout: "text-slate-500",
|
timeout: "text-slate-500",
|
||||||
|
|||||||
@ -302,7 +302,7 @@ export default function PipelinesPage() {
|
|||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
className="inline-flex items-center gap-2 rounded-lg border border-slate-700 bg-slate-900 px-3 py-2 text-sm text-white hover:bg-slate-800 transition-colors data-[popup-open]:bg-slate-800"
|
className="inline-flex items-center gap-2 rounded-lg border border-slate-700 bg-slate-900 px-3 py-2 text-sm text-white hover:bg-slate-800 transition-colors data-[popup-open]:bg-slate-800"
|
||||||
>
|
>
|
||||||
<GitBranch className="h-4 w-4 text-violet-500" />
|
<GitBranch className="h-4 w-4 text-primary" />
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
{selectedPipeline?.name ?? "Select Pipeline"}
|
{selectedPipeline?.name ?? "Select Pipeline"}
|
||||||
</span>
|
</span>
|
||||||
@ -323,7 +323,7 @@ export default function PipelinesPage() {
|
|||||||
onClick={() => setSelectedPipelineId(p.id)}
|
onClick={() => setSelectedPipelineId(p.id)}
|
||||||
className={
|
className={
|
||||||
p.id === selectedPipelineId
|
p.id === selectedPipelineId
|
||||||
? "text-violet-400"
|
? "text-primary"
|
||||||
: "text-slate-300"
|
: "text-slate-300"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -357,7 +357,7 @@ export default function PipelinesPage() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => handleAddDeal()}
|
onClick={() => handleAddDeal()}
|
||||||
disabled={!selectedPipelineId || stages.length === 0}
|
disabled={!selectedPipelineId || stages.length === 0}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700"
|
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
<Plus className="mr-1 h-4 w-4" />
|
<Plus className="mr-1 h-4 w-4" />
|
||||||
Add Deal
|
Add Deal
|
||||||
@ -377,7 +377,7 @@ export default function PipelinesPage() {
|
|||||||
</p>
|
</p>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setNewPipelineOpen(true)}
|
onClick={() => setNewPipelineOpen(true)}
|
||||||
className="mt-4 bg-violet-600 text-white hover:bg-violet-700"
|
className="mt-4 bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
<Plus className="mr-1 h-4 w-4" />
|
<Plus className="mr-1 h-4 w-4" />
|
||||||
Create Pipeline
|
Create Pipeline
|
||||||
@ -428,7 +428,7 @@ export default function PipelinesPage() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleCreatePipeline}
|
onClick={handleCreatePipeline}
|
||||||
disabled={creating || !newPipelineName.trim()}
|
disabled={creating || !newPipelineName.trim()}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700"
|
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{creating ? "Creating..." : "Create Pipeline"}
|
{creating ? "Creating..." : "Create Pipeline"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export default function Icon() {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
background: "#7c3aed", // violet-600 (Hostinger-aligned purple)
|
background: "#7c3aed", // primary (Hostinger-aligned purple)
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -77,17 +77,17 @@ interface StepMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const STEP_META: Record<AutomationStepType, StepMeta> = {
|
const STEP_META: Record<AutomationStepType, StepMeta> = {
|
||||||
send_message: { label: "Send Message", icon: MessageSquare, border: "border-l-violet-500" },
|
send_message: { label: "Send Message", icon: MessageSquare, border: "border-l-primary" },
|
||||||
send_template: { label: "Send Template", icon: FileText, border: "border-l-violet-500" },
|
send_template: { label: "Send Template", icon: FileText, border: "border-l-primary" },
|
||||||
add_tag: { label: "Add Tag", icon: Tag, border: "border-l-violet-500" },
|
add_tag: { label: "Add Tag", icon: Tag, border: "border-l-primary" },
|
||||||
remove_tag: { label: "Remove Tag", icon: TagIcon, border: "border-l-violet-500" },
|
remove_tag: { label: "Remove Tag", icon: TagIcon, border: "border-l-primary" },
|
||||||
assign_conversation: { label: "Assign Conversation", icon: UserCheck, border: "border-l-violet-500" },
|
assign_conversation: { label: "Assign Conversation", icon: UserCheck, border: "border-l-primary" },
|
||||||
update_contact_field: { label: "Update Contact Field", icon: PencilLine, border: "border-l-violet-500" },
|
update_contact_field: { label: "Update Contact Field", icon: PencilLine, border: "border-l-primary" },
|
||||||
create_deal: { label: "Create Deal", icon: Briefcase, border: "border-l-violet-500" },
|
create_deal: { label: "Create Deal", icon: Briefcase, border: "border-l-primary" },
|
||||||
wait: { label: "Wait", icon: Hourglass, border: "border-l-slate-500" },
|
wait: { label: "Wait", icon: Hourglass, border: "border-l-slate-500" },
|
||||||
condition: { label: "Condition (If/Else)", icon: GitBranch, border: "border-l-amber-500" },
|
condition: { label: "Condition (If/Else)", icon: GitBranch, border: "border-l-amber-500" },
|
||||||
send_webhook: { label: "Send Webhook", icon: Webhook, border: "border-l-violet-500" },
|
send_webhook: { label: "Send Webhook", icon: Webhook, border: "border-l-primary" },
|
||||||
close_conversation: { label: "Close Conversation", icon: CircleSlash, border: "border-l-violet-500" },
|
close_conversation: { label: "Close Conversation", icon: CircleSlash, border: "border-l-primary" },
|
||||||
}
|
}
|
||||||
|
|
||||||
const ADDABLE_STEPS: AutomationStepType[] = [
|
const ADDABLE_STEPS: AutomationStepType[] = [
|
||||||
@ -275,7 +275,7 @@ export function AutomationBuilder({ initial }: { initial: BuilderInitial }) {
|
|||||||
<Button
|
<Button
|
||||||
onClick={save}
|
onClick={save}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700"
|
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{saving ? <Loader2 className="h-4 w-4 animate-spin" /> : null}
|
{saving ? <Loader2 className="h-4 w-4 animate-spin" /> : null}
|
||||||
{isEditing ? "Save" : "Save Draft"}
|
{isEditing ? "Save" : "Save Draft"}
|
||||||
@ -356,7 +356,7 @@ function TriggerCard({
|
|||||||
<select
|
<select
|
||||||
value={type}
|
value={type}
|
||||||
onChange={(e) => onTypeChange(e.target.value as AutomationTriggerType)}
|
onChange={(e) => onTypeChange(e.target.value as AutomationTriggerType)}
|
||||||
className="w-full rounded-md border border-slate-700 bg-slate-800 px-2 py-1.5 text-sm text-white focus:border-violet-500 focus:outline-none"
|
className="w-full rounded-md border border-slate-700 bg-slate-800 px-2 py-1.5 text-sm text-white focus:border-primary focus:outline-none"
|
||||||
>
|
>
|
||||||
{TRIGGER_OPTIONS.map((o) => (
|
{TRIGGER_OPTIONS.map((o) => (
|
||||||
<option key={o.value} value={o.value}>
|
<option key={o.value} value={o.value}>
|
||||||
@ -637,7 +637,7 @@ function ConditionBranches({
|
|||||||
// cram each branch to ~170px which is too narrow for the nested
|
// cram each branch to ~170px which is too narrow for the nested
|
||||||
// cards. Two-column grid returns on sm+.
|
// cards. Two-column grid returns on sm+.
|
||||||
<div className="mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2">
|
<div className="mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||||
<BranchColumn label="Yes" color="text-violet-400">
|
<BranchColumn label="Yes" color="text-primary">
|
||||||
<StepList {...props} steps={yes} parentPath={yesPath} />
|
<StepList {...props} steps={yes} parentPath={yesPath} />
|
||||||
</BranchColumn>
|
</BranchColumn>
|
||||||
<BranchColumn label="No" color="text-rose-400">
|
<BranchColumn label="No" color="text-rose-400">
|
||||||
@ -670,7 +670,7 @@ function AddButton({ onPick }: { onPick: (t: AutomationStepType) => void }) {
|
|||||||
<div className="h-4 w-[2px] bg-slate-700" aria-hidden />
|
<div className="h-4 w-[2px] bg-slate-700" aria-hidden />
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
className="flex h-8 w-8 items-center justify-center rounded-full border-2 border-dashed border-slate-700 bg-slate-950 text-slate-400 transition-colors hover:border-violet-500 hover:bg-violet-500/10 hover:text-violet-400 data-[popup-open]:border-violet-500 data-[popup-open]:bg-violet-500/20 data-[popup-open]:text-violet-400"
|
className="flex h-8 w-8 items-center justify-center rounded-full border-2 border-dashed border-slate-700 bg-slate-950 text-slate-400 transition-colors hover:border-primary hover:bg-primary/10 hover:text-primary data-[popup-open]:border-primary data-[popup-open]:bg-primary/20 data-[popup-open]:text-primary"
|
||||||
aria-label="Add step"
|
aria-label="Add step"
|
||||||
>
|
>
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export function Step1ChooseTemplate({ selectedTemplate, onSelect, onNext, onBack
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-64 items-center justify-center">
|
<div className="flex h-64 items-center justify-center">
|
||||||
<Loader2 className="h-6 w-6 animate-spin text-violet-500" />
|
<Loader2 className="h-6 w-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ export function Step1ChooseTemplate({ selectedTemplate, onSelect, onNext, onBack
|
|||||||
onClick={() => onSelect(template)}
|
onClick={() => onSelect(template)}
|
||||||
className={`flex flex-col gap-3 rounded-xl border p-4 text-left transition-all ${
|
className={`flex flex-col gap-3 rounded-xl border p-4 text-left transition-all ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-violet-500 bg-violet-500/5 ring-1 ring-violet-500/30'
|
? 'border-primary bg-primary/5 ring-1 ring-primary/30'
|
||||||
: 'border-slate-800 bg-slate-900/50 hover:border-slate-700 hover:bg-slate-900'
|
: 'border-slate-800 bg-slate-900/50 hover:border-slate-700 hover:bg-slate-900'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -123,7 +123,7 @@ export function Step1ChooseTemplate({ selectedTemplate, onSelect, onNext, onBack
|
|||||||
<Button
|
<Button
|
||||||
onClick={onNext}
|
onClick={onNext}
|
||||||
disabled={!selectedTemplate}
|
disabled={!selectedTemplate}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700 disabled:opacity-50"
|
className="bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
<ArrowRight className="h-4 w-4" />
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
|||||||
@ -279,14 +279,14 @@ export function Step2SelectAudience({
|
|||||||
}
|
}
|
||||||
className={`flex items-start gap-3 rounded-xl border p-4 text-left transition-all ${
|
className={`flex items-start gap-3 rounded-xl border p-4 text-left transition-all ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-violet-500 bg-violet-500/5 ring-1 ring-violet-500/30'
|
? 'border-primary bg-primary/5 ring-1 ring-primary/30'
|
||||||
: 'border-slate-800 bg-slate-900/50 hover:border-slate-700'
|
: 'border-slate-800 bg-slate-900/50 hover:border-slate-700'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`flex h-9 w-9 shrink-0 items-center justify-center rounded-lg ${
|
className={`flex h-9 w-9 shrink-0 items-center justify-center rounded-lg ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'bg-violet-500/10 text-violet-400'
|
? 'bg-primary/10 text-primary'
|
||||||
: 'bg-slate-800 text-slate-400'
|
: 'bg-slate-800 text-slate-400'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -307,7 +307,7 @@ export function Step2SelectAudience({
|
|||||||
<div className="rounded-xl border border-slate-800 bg-slate-900/50 p-4">
|
<div className="rounded-xl border border-slate-800 bg-slate-900/50 p-4">
|
||||||
<p className="mb-3 text-sm font-medium text-white">Select Tags</p>
|
<p className="mb-3 text-sm font-medium text-white">Select Tags</p>
|
||||||
{loadingTags ? (
|
{loadingTags ? (
|
||||||
<Loader2 className="h-5 w-5 animate-spin text-violet-500" />
|
<Loader2 className="h-5 w-5 animate-spin text-primary" />
|
||||||
) : tags.length === 0 ? (
|
) : tags.length === 0 ? (
|
||||||
<p className="text-xs text-slate-400">
|
<p className="text-xs text-slate-400">
|
||||||
No tags found. Create tags in Settings.
|
No tags found. Create tags in Settings.
|
||||||
@ -322,7 +322,7 @@ export function Step2SelectAudience({
|
|||||||
onClick={() => toggleTag(tag.id)}
|
onClick={() => toggleTag(tag.id)}
|
||||||
className={`inline-flex items-center rounded-full border px-3 py-1 text-xs font-medium transition-all ${
|
className={`inline-flex items-center rounded-full border px-3 py-1 text-xs font-medium transition-all ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-violet-500/30 bg-violet-500/10 text-violet-400'
|
? 'border-primary/30 bg-primary/10 text-primary'
|
||||||
: 'border-slate-700 bg-slate-800 text-slate-300 hover:border-slate-600'
|
: 'border-slate-700 bg-slate-800 text-slate-300 hover:border-slate-600'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -343,7 +343,7 @@ export function Step2SelectAudience({
|
|||||||
<div className="space-y-3 rounded-xl border border-slate-800 bg-slate-900/50 p-4">
|
<div className="space-y-3 rounded-xl border border-slate-800 bg-slate-900/50 p-4">
|
||||||
<p className="text-sm font-medium text-white">Custom Field Filter</p>
|
<p className="text-sm font-medium text-white">Custom Field Filter</p>
|
||||||
{loadingFields ? (
|
{loadingFields ? (
|
||||||
<Loader2 className="h-5 w-5 animate-spin text-violet-500" />
|
<Loader2 className="h-5 w-5 animate-spin text-primary" />
|
||||||
) : customFields.length === 0 ? (
|
) : customFields.length === 0 ? (
|
||||||
<p className="text-xs text-slate-400">
|
<p className="text-xs text-slate-400">
|
||||||
No custom fields defined. Create one in Settings → Custom Fields.
|
No custom fields defined. Create one in Settings → Custom Fields.
|
||||||
@ -353,7 +353,7 @@ export function Step2SelectAudience({
|
|||||||
<select
|
<select
|
||||||
value={audience.customField?.fieldId ?? ''}
|
value={audience.customField?.fieldId ?? ''}
|
||||||
onChange={(e) => updateCustomField({ fieldId: e.target.value })}
|
onChange={(e) => updateCustomField({ fieldId: e.target.value })}
|
||||||
className="h-9 rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500"
|
className="h-9 rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-primary focus:ring-1 focus:ring-primary"
|
||||||
>
|
>
|
||||||
<option value="">Select field…</option>
|
<option value="">Select field…</option>
|
||||||
{customFields.map((f) => (
|
{customFields.map((f) => (
|
||||||
@ -369,7 +369,7 @@ export function Step2SelectAudience({
|
|||||||
operator: e.target.value as CustomFieldOperator,
|
operator: e.target.value as CustomFieldOperator,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
className="h-9 rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500"
|
className="h-9 rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-primary focus:ring-1 focus:ring-primary"
|
||||||
>
|
>
|
||||||
{OPERATOR_OPTIONS.map((op) => (
|
{OPERATOR_OPTIONS.map((op) => (
|
||||||
<option key={op.value} value={op.value}>
|
<option key={op.value} value={op.value}>
|
||||||
@ -382,7 +382,7 @@ export function Step2SelectAudience({
|
|||||||
value={audience.customField?.value ?? ''}
|
value={audience.customField?.value ?? ''}
|
||||||
onChange={(e) => updateCustomField({ value: e.target.value })}
|
onChange={(e) => updateCustomField({ value: e.target.value })}
|
||||||
placeholder="Value"
|
placeholder="Value"
|
||||||
className="h-9 rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none placeholder:text-slate-500 focus:border-violet-500 focus:ring-1 focus:ring-violet-500"
|
className="h-9 rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none placeholder:text-slate-500 focus:border-primary focus:ring-1 focus:ring-primary"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -431,12 +431,12 @@ export function Step2SelectAudience({
|
|||||||
<p className="mb-2 text-sm font-medium text-white">Audience Summary</p>
|
<p className="mb-2 text-sm font-medium text-white">Audience Summary</p>
|
||||||
{loadingCount ? (
|
{loadingCount ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Loader2 className="h-4 w-4 animate-spin text-violet-500" />
|
<Loader2 className="h-4 w-4 animate-spin text-primary" />
|
||||||
<span className="text-xs text-slate-400">Calculating…</span>
|
<span className="text-xs text-slate-400">Calculating…</span>
|
||||||
</div>
|
</div>
|
||||||
) : estimatedCount !== null ? (
|
) : estimatedCount !== null ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Users className="h-4 w-4 text-violet-400" />
|
<Users className="h-4 w-4 text-primary" />
|
||||||
<span className="text-sm text-white">
|
<span className="text-sm text-white">
|
||||||
{estimatedCount.toLocaleString()}
|
{estimatedCount.toLocaleString()}
|
||||||
</span>
|
</span>
|
||||||
@ -461,7 +461,7 @@ export function Step2SelectAudience({
|
|||||||
<Button
|
<Button
|
||||||
onClick={onNext}
|
onClick={onNext}
|
||||||
disabled={!isValid}
|
disabled={!isValid}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700 disabled:opacity-50"
|
className="bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
<ArrowRight className="h-4 w-4" />
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
|||||||
@ -211,7 +211,7 @@ export function Step3Personalize({
|
|||||||
className="rounded-xl border border-slate-800 bg-slate-900/50 p-4"
|
className="rounded-xl border border-slate-800 bg-slate-900/50 p-4"
|
||||||
>
|
>
|
||||||
<div className="mb-3 flex items-center gap-2">
|
<div className="mb-3 flex items-center gap-2">
|
||||||
<span className="inline-flex items-center rounded-md bg-violet-500/10 px-2 py-0.5 text-xs font-mono font-medium text-violet-400">
|
<span className="inline-flex items-center rounded-md bg-primary/10 px-2 py-0.5 text-xs font-mono font-medium text-primary">
|
||||||
{placeholder}
|
{placeholder}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -313,16 +313,16 @@ export function Step3Personalize({
|
|||||||
sees approximately what the recipient will see. */}
|
sees approximately what the recipient will see. */}
|
||||||
<div className="rounded-xl border border-slate-800 bg-slate-900/50 p-4">
|
<div className="rounded-xl border border-slate-800 bg-slate-900/50 p-4">
|
||||||
<div className="mb-3 flex items-center gap-2">
|
<div className="mb-3 flex items-center gap-2">
|
||||||
<Eye className="h-4 w-4 text-violet-400" />
|
<Eye className="h-4 w-4 text-primary" />
|
||||||
<p className="text-sm font-medium text-white">Live Preview</p>
|
<p className="text-sm font-medium text-white">Live Preview</p>
|
||||||
<span className="text-xs text-slate-500">({previewLabel})</span>
|
<span className="text-xs text-slate-500">({previewLabel})</span>
|
||||||
{loadingPreview && (
|
{loadingPreview && (
|
||||||
<Loader2 className="h-3.5 w-3.5 animate-spin text-violet-500" />
|
<Loader2 className="h-3.5 w-3.5 animate-spin text-primary" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="rounded-lg bg-[#0e1a12] p-3">
|
<div className="rounded-lg bg-[#0e1a12] p-3">
|
||||||
<div className="ml-auto max-w-[85%] rounded-lg bg-violet-700/30 px-3 py-2 shadow-sm">
|
<div className="ml-auto max-w-[85%] rounded-lg bg-primary/30 px-3 py-2 shadow-sm">
|
||||||
<p className="whitespace-pre-wrap text-sm text-violet-50">
|
<p className="whitespace-pre-wrap text-sm text-primary">
|
||||||
{previewText}
|
{previewText}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -351,7 +351,7 @@ export function Step3Personalize({
|
|||||||
<Button
|
<Button
|
||||||
onClick={onNext}
|
onClick={onNext}
|
||||||
disabled={unmappedKeys.length > 0}
|
disabled={unmappedKeys.length > 0}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700 disabled:opacity-50"
|
className="bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Next
|
Next
|
||||||
<ArrowRight className="h-4 w-4" />
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
|||||||
@ -126,10 +126,10 @@ export function Step4ScheduleSend({
|
|||||||
<p className="text-xs text-slate-400">Estimated Reach</p>
|
<p className="text-xs text-slate-400">Estimated Reach</p>
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
{loadingReach ? (
|
{loadingReach ? (
|
||||||
<Loader2 className="h-3 w-3 animate-spin text-violet-500" />
|
<Loader2 className="h-3 w-3 animate-spin text-primary" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Users className="h-3.5 w-3.5 text-violet-400" />
|
<Users className="h-3.5 w-3.5 text-primary" />
|
||||||
<p className="font-medium text-white">{estimatedReach.toLocaleString()}</p>
|
<p className="font-medium text-white">{estimatedReach.toLocaleString()}</p>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -144,17 +144,17 @@ export function Step4ScheduleSend({
|
|||||||
|
|
||||||
{/* Processing overlay */}
|
{/* Processing overlay */}
|
||||||
{isProcessing && (
|
{isProcessing && (
|
||||||
<div className="rounded-xl border border-violet-500/20 bg-violet-500/5 p-4">
|
<div className="rounded-xl border border-primary/20 bg-primary/5 p-4">
|
||||||
<div className="mb-2 flex items-center justify-between">
|
<div className="mb-2 flex items-center justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Loader2 className="h-4 w-4 animate-spin text-violet-500" />
|
<Loader2 className="h-4 w-4 animate-spin text-primary" />
|
||||||
<p className="text-sm font-medium text-white">Sending broadcast...</p>
|
<p className="text-sm font-medium text-white">Sending broadcast...</p>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs font-medium text-violet-400">{progress}%</span>
|
<span className="text-xs font-medium text-primary">{progress}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-1.5 w-full rounded-full bg-slate-800">
|
<div className="h-1.5 w-full rounded-full bg-slate-800">
|
||||||
<div
|
<div
|
||||||
className="h-1.5 rounded-full bg-violet-500 transition-all duration-300"
|
className="h-1.5 rounded-full bg-primary transition-all duration-300"
|
||||||
style={{ width: `${progress}%` }}
|
style={{ width: `${progress}%` }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -190,7 +190,7 @@ export function Step4ScheduleSend({
|
|||||||
render={
|
render={
|
||||||
<Button
|
<Button
|
||||||
disabled={!name.trim() || isProcessing}
|
disabled={!name.trim() || isProcessing}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700 disabled:opacity-50"
|
className="bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@ -221,7 +221,7 @@ export function Step4ScheduleSend({
|
|||||||
setShowConfirm(false);
|
setShowConfirm(false);
|
||||||
onSend();
|
onSend();
|
||||||
}}
|
}}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700"
|
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
<Send className="h-4 w-4" />
|
<Send className="h-4 w-4" />
|
||||||
Confirm & Send
|
Confirm & Send
|
||||||
|
|||||||
@ -331,7 +331,7 @@ export function ContactDetailView({
|
|||||||
>
|
>
|
||||||
{loading || !contact ? (
|
{loading || !contact ? (
|
||||||
<div className="flex items-center justify-center h-full">
|
<div className="flex items-center justify-center h-full">
|
||||||
<Loader2 className="size-6 animate-spin text-violet-500" />
|
<Loader2 className="size-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
@ -339,7 +339,7 @@ export function ContactDetailView({
|
|||||||
<SheetHeader className="p-4 border-b border-slate-700/50">
|
<SheetHeader className="p-4 border-b border-slate-700/50">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Avatar className="size-12 bg-slate-800 border border-slate-700">
|
<Avatar className="size-12 bg-slate-800 border border-slate-700">
|
||||||
<AvatarFallback className="bg-violet-500/10 text-violet-400 text-sm font-medium">
|
<AvatarFallback className="bg-primary/10 text-primary text-sm font-medium">
|
||||||
{getInitials(contact.name)}
|
{getInitials(contact.name)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
@ -353,12 +353,12 @@ export function ContactDetailView({
|
|||||||
<div className="flex flex-wrap items-center gap-3 mt-1.5 text-xs text-slate-400">
|
<div className="flex flex-wrap items-center gap-3 mt-1.5 text-xs text-slate-400">
|
||||||
<button
|
<button
|
||||||
onClick={copyPhone}
|
onClick={copyPhone}
|
||||||
className="flex items-center gap-1 hover:text-violet-400 transition-colors cursor-pointer"
|
className="flex items-center gap-1 hover:text-primary transition-colors cursor-pointer"
|
||||||
>
|
>
|
||||||
<Phone className="size-3" />
|
<Phone className="size-3" />
|
||||||
{contact.phone}
|
{contact.phone}
|
||||||
{copiedPhone ? (
|
{copiedPhone ? (
|
||||||
<Check className="size-3 text-violet-400" />
|
<Check className="size-3 text-primary" />
|
||||||
) : (
|
) : (
|
||||||
<Copy className="size-3" />
|
<Copy className="size-3" />
|
||||||
)}
|
)}
|
||||||
@ -385,31 +385,31 @@ export function ContactDetailView({
|
|||||||
<TabsList className="bg-slate-800/50 border-b border-slate-700 mx-4 mt-3">
|
<TabsList className="bg-slate-800/50 border-b border-slate-700 mx-4 mt-3">
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="details"
|
value="details"
|
||||||
className="data-active:bg-slate-800 data-active:text-violet-400 text-slate-400"
|
className="data-active:bg-slate-800 data-active:text-primary text-slate-400"
|
||||||
>
|
>
|
||||||
Details
|
Details
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="tags"
|
value="tags"
|
||||||
className="data-active:bg-slate-800 data-active:text-violet-400 text-slate-400"
|
className="data-active:bg-slate-800 data-active:text-primary text-slate-400"
|
||||||
>
|
>
|
||||||
Tags
|
Tags
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="notes"
|
value="notes"
|
||||||
className="data-active:bg-slate-800 data-active:text-violet-400 text-slate-400"
|
className="data-active:bg-slate-800 data-active:text-primary text-slate-400"
|
||||||
>
|
>
|
||||||
Notes
|
Notes
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="custom"
|
value="custom"
|
||||||
className="data-active:bg-slate-800 data-active:text-violet-400 text-slate-400"
|
className="data-active:bg-slate-800 data-active:text-primary text-slate-400"
|
||||||
>
|
>
|
||||||
Custom Fields
|
Custom Fields
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger
|
<TabsTrigger
|
||||||
value="deals"
|
value="deals"
|
||||||
className="data-active:bg-slate-800 data-active:text-violet-400 text-slate-400"
|
className="data-active:bg-slate-800 data-active:text-primary text-slate-400"
|
||||||
>
|
>
|
||||||
Deals
|
Deals
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
@ -455,7 +455,7 @@ export function ContactDetailView({
|
|||||||
<Button
|
<Button
|
||||||
onClick={saveDetails}
|
onClick={saveDetails}
|
||||||
disabled={savingDetails}
|
disabled={savingDetails}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white w-full"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground w-full"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
{savingDetails ? (
|
{savingDetails ? (
|
||||||
@ -489,7 +489,7 @@ export function ContactDetailView({
|
|||||||
disabled={savingTags}
|
disabled={savingTags}
|
||||||
className={`inline-flex items-center rounded-full px-3 py-1 text-xs font-medium transition-all cursor-pointer ${
|
className={`inline-flex items-center rounded-full px-3 py-1 text-xs font-medium transition-all cursor-pointer ${
|
||||||
selected
|
selected
|
||||||
? 'ring-2 ring-violet-500 ring-offset-1 ring-offset-slate-900'
|
? 'ring-2 ring-primary ring-offset-1 ring-offset-slate-900'
|
||||||
: 'opacity-50 hover:opacity-80'
|
: 'opacity-50 hover:opacity-80'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
@ -519,7 +519,7 @@ export function ContactDetailView({
|
|||||||
<Button
|
<Button
|
||||||
onClick={addNote}
|
onClick={addNote}
|
||||||
disabled={!newNote.trim() || savingNote}
|
disabled={!newNote.trim() || savingNote}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
{savingNote ? (
|
{savingNote ? (
|
||||||
@ -605,7 +605,7 @@ export function ContactDetailView({
|
|||||||
<Button
|
<Button
|
||||||
onClick={saveCustomFields}
|
onClick={saveCustomFields}
|
||||||
disabled={savingCustom}
|
disabled={savingCustom}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white w-full"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground w-full"
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
{savingCustom ? (
|
{savingCustom ? (
|
||||||
@ -623,7 +623,7 @@ export function ContactDetailView({
|
|||||||
<TabsContent value="deals" className="flex-1 overflow-y-auto px-4 py-3">
|
<TabsContent value="deals" className="flex-1 overflow-y-auto px-4 py-3">
|
||||||
{loadingDeals ? (
|
{loadingDeals ? (
|
||||||
<div className="flex items-center justify-center py-8">
|
<div className="flex items-center justify-center py-8">
|
||||||
<Loader2 className="size-5 animate-spin text-violet-500" />
|
<Loader2 className="size-5 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
) : deals.length === 0 ? (
|
) : deals.length === 0 ? (
|
||||||
<p className="text-xs text-slate-500">No deals yet</p>
|
<p className="text-xs text-slate-500">No deals yet</p>
|
||||||
@ -663,7 +663,7 @@ export function ContactDetailView({
|
|||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
deal.status === 'won'
|
deal.status === 'won'
|
||||||
? 'text-violet-400'
|
? 'text-primary'
|
||||||
: 'text-red-400'
|
: 'text-red-400'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -245,7 +245,7 @@ export function ContactForm({
|
|||||||
onClick={() => toggleTag(tag.id)}
|
onClick={() => toggleTag(tag.id)}
|
||||||
className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium transition-colors cursor-pointer ${
|
className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium transition-colors cursor-pointer ${
|
||||||
selected
|
selected
|
||||||
? 'ring-2 ring-violet-500 ring-offset-1 ring-offset-slate-900'
|
? 'ring-2 ring-primary ring-offset-1 ring-offset-slate-900'
|
||||||
: 'opacity-60 hover:opacity-100'
|
: 'opacity-60 hover:opacity-100'
|
||||||
}`}
|
}`}
|
||||||
style={{
|
style={{
|
||||||
@ -274,7 +274,7 @@ export function ContactForm({
|
|||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
>
|
>
|
||||||
{saving && <Loader2 className="size-4 animate-spin" />}
|
{saving && <Loader2 className="size-4 animate-spin" />}
|
||||||
{isEdit ? 'Update' : 'Create'}
|
{isEdit ? 'Update' : 'Create'}
|
||||||
|
|||||||
@ -196,11 +196,11 @@ export function ImportModal({ open, onOpenChange, onImported }: ImportModalProps
|
|||||||
{/* Upload area */}
|
{/* Upload area */}
|
||||||
<div
|
<div
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
className="flex flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-slate-700 p-6 cursor-pointer hover:border-violet-500/50 transition-colors"
|
className="flex flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-slate-700 p-6 cursor-pointer hover:border-primary/50 transition-colors"
|
||||||
>
|
>
|
||||||
{file ? (
|
{file ? (
|
||||||
<>
|
<>
|
||||||
<FileText className="size-8 text-violet-400" />
|
<FileText className="size-8 text-primary" />
|
||||||
<p className="text-sm text-slate-300">{file.name}</p>
|
<p className="text-sm text-slate-300">{file.name}</p>
|
||||||
<p className="text-xs text-slate-500">
|
<p className="text-xs text-slate-500">
|
||||||
{parsedRows.length} row{parsedRows.length !== 1 ? 's' : ''} detected
|
{parsedRows.length} row{parsedRows.length !== 1 ? 's' : ''} detected
|
||||||
@ -269,7 +269,7 @@ export function ImportModal({ open, onOpenChange, onImported }: ImportModalProps
|
|||||||
<p className="text-sm font-medium text-white">Import Complete</p>
|
<p className="text-sm font-medium text-white">Import Complete</p>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{result.imported > 0 && (
|
{result.imported > 0 && (
|
||||||
<div className="flex items-center gap-1.5 text-violet-400 text-sm">
|
<div className="flex items-center gap-1.5 text-primary text-sm">
|
||||||
<CheckCircle className="size-4" />
|
<CheckCircle className="size-4" />
|
||||||
{result.imported} imported
|
{result.imported} imported
|
||||||
</div>
|
</div>
|
||||||
@ -299,7 +299,7 @@ export function ImportModal({ open, onOpenChange, onImported }: ImportModalProps
|
|||||||
type="button"
|
type="button"
|
||||||
disabled={parsedRows.length === 0 || importing}
|
disabled={parsedRows.length === 0 || importing}
|
||||||
onClick={handleImport}
|
onClick={handleImport}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
>
|
>
|
||||||
{importing && <Loader2 className="size-4 animate-spin" />}
|
{importing && <Loader2 className="size-4 animate-spin" />}
|
||||||
Import {parsedRows.length > 0 ? `${parsedRows.length} Contacts` : ''}
|
Import {parsedRows.length > 0 ? `${parsedRows.length} Contacts` : ''}
|
||||||
|
|||||||
@ -32,8 +32,8 @@ interface KindTheme {
|
|||||||
|
|
||||||
const KIND_THEME: Record<ActivityKind, KindTheme> = {
|
const KIND_THEME: Record<ActivityKind, KindTheme> = {
|
||||||
message: { icon: MessageSquare, badge: 'bg-blue-500/10 text-blue-400' },
|
message: { icon: MessageSquare, badge: 'bg-blue-500/10 text-blue-400' },
|
||||||
contact: { icon: UserPlus, badge: 'bg-violet-500/10 text-violet-400' },
|
contact: { icon: UserPlus, badge: 'bg-primary/10 text-primary' },
|
||||||
deal: { icon: Briefcase, badge: 'bg-violet-500/10 text-violet-400' },
|
deal: { icon: Briefcase, badge: 'bg-primary/10 text-primary' },
|
||||||
broadcast: { icon: Radio, badge: 'bg-amber-500/10 text-amber-400' },
|
broadcast: { icon: Radio, badge: 'bg-amber-500/10 text-amber-400' },
|
||||||
automation: { icon: Zap, badge: 'bg-rose-500/10 text-rose-400' },
|
automation: { icon: Zap, badge: 'bg-rose-500/10 text-rose-400' },
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ export function ActivityFeed({ items, loading }: ActivityFeedProps) {
|
|||||||
<h2 className="text-sm font-semibold text-white">Recent Activity</h2>
|
<h2 className="text-sm font-semibold text-white">Recent Activity</h2>
|
||||||
<Link
|
<Link
|
||||||
href="/inbox"
|
href="/inbox"
|
||||||
className="text-xs font-medium text-violet-400 hover:text-violet-300"
|
className="text-xs font-medium text-primary hover:text-primary/80"
|
||||||
>
|
>
|
||||||
View all →
|
View all →
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@ -289,8 +289,8 @@ function LineSvg({
|
|||||||
<span className="inline-block h-1.5 w-1.5 rounded-full bg-blue-500" />
|
<span className="inline-block h-1.5 w-1.5 rounded-full bg-blue-500" />
|
||||||
{hovered.incoming} incoming
|
{hovered.incoming} incoming
|
||||||
</span>
|
</span>
|
||||||
<span className="flex items-center gap-1.5 text-violet-300">
|
<span className="flex items-center gap-1.5 text-primary">
|
||||||
<span className="inline-block h-1.5 w-1.5 rounded-full bg-violet-500" />
|
<span className="inline-block h-1.5 w-1.5 rounded-full bg-primary" />
|
||||||
{hovered.outgoing} outgoing
|
{hovered.outgoing} outgoing
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export function MetricCard({ title, value, icon: Icon, delta, subtitle }: Metric
|
|||||||
function DeltaRow({ sign, label }: { sign: number; label: string }) {
|
function DeltaRow({ sign, label }: { sign: number; label: string }) {
|
||||||
const tone =
|
const tone =
|
||||||
sign > 0
|
sign > 0
|
||||||
? 'text-violet-400'
|
? 'text-primary'
|
||||||
: sign < 0
|
: sign < 0
|
||||||
? 'text-red-400'
|
? 'text-red-400'
|
||||||
: 'text-slate-500'
|
: 'text-slate-500'
|
||||||
|
|||||||
@ -16,10 +16,10 @@ interface Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ACTIONS: Action[] = [
|
const ACTIONS: Action[] = [
|
||||||
{ label: 'New Contact', href: '/contacts', icon: UserPlus, tint: 'text-violet-400' },
|
{ label: 'New Contact', href: '/contacts', icon: UserPlus, tint: 'text-primary' },
|
||||||
{ label: 'New Deal', href: '/pipelines', icon: Briefcase, tint: 'text-blue-400' },
|
{ label: 'New Deal', href: '/pipelines', icon: Briefcase, tint: 'text-blue-400' },
|
||||||
{ label: 'New Broadcast', href: '/broadcasts/new', icon: Radio, tint: 'text-amber-400' },
|
{ label: 'New Broadcast', href: '/broadcasts/new', icon: Radio, tint: 'text-amber-400' },
|
||||||
{ label: 'New Automation', href: '/automations/new', icon: Zap, tint: 'text-violet-400' },
|
{ label: 'New Automation', href: '/automations/new', icon: Zap, tint: 'text-primary' },
|
||||||
]
|
]
|
||||||
|
|
||||||
export function QuickActions() {
|
export function QuickActions() {
|
||||||
|
|||||||
@ -123,7 +123,7 @@ const NODE_META: Record<
|
|||||||
send_buttons: {
|
send_buttons: {
|
||||||
label: "Send buttons",
|
label: "Send buttons",
|
||||||
icon: ListChecks,
|
icon: ListChecks,
|
||||||
color: "text-violet-400",
|
color: "text-primary",
|
||||||
},
|
},
|
||||||
send_list: {
|
send_list: {
|
||||||
label: "Send list",
|
label: "Send list",
|
||||||
|
|||||||
@ -156,7 +156,7 @@ export function ContactSidebar({ contact }: ContactSidebarProps) {
|
|||||||
<Phone className="h-4 w-4 text-slate-500" />
|
<Phone className="h-4 w-4 text-slate-500" />
|
||||||
<span className="flex-1 text-left">{contact.phone}</span>
|
<span className="flex-1 text-left">{contact.phone}</span>
|
||||||
{copied ? (
|
{copied ? (
|
||||||
<Check className="h-3 w-3 text-violet-400" />
|
<Check className="h-3 w-3 text-primary" />
|
||||||
) : (
|
) : (
|
||||||
<Copy className="h-3 w-3 text-slate-600" />
|
<Copy className="h-3 w-3 text-slate-600" />
|
||||||
)}
|
)}
|
||||||
@ -259,11 +259,11 @@ export function ContactSidebar({ contact }: ContactSidebarProps) {
|
|||||||
onChange={(e) => setNewNote(e.target.value)}
|
onChange={(e) => setNewNote(e.target.value)}
|
||||||
placeholder="Add a note..."
|
placeholder="Add a note..."
|
||||||
rows={2}
|
rows={2}
|
||||||
className="flex-1 resize-none rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 text-xs text-white placeholder-slate-500 outline-none focus:border-violet-500/50"
|
className="flex-1 resize-none rounded-lg border border-slate-700 bg-slate-800 px-3 py-2 text-xs text-white placeholder-slate-500 outline-none focus:border-primary/50"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-auto bg-violet-600 px-2 hover:bg-violet-500"
|
className="h-auto bg-primary px-2 hover:bg-primary/90"
|
||||||
onClick={handleAddNote}
|
onClick={handleAddNote}
|
||||||
disabled={!newNote.trim() || addingNote}
|
disabled={!newNote.trim() || addingNote}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -31,7 +31,7 @@ interface ConversationListProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const STATUS_COLORS: Record<ConversationStatus, string> = {
|
const STATUS_COLORS: Record<ConversationStatus, string> = {
|
||||||
open: "bg-violet-500",
|
open: "bg-primary",
|
||||||
pending: "bg-amber-500",
|
pending: "bg-amber-500",
|
||||||
closed: "bg-slate-500",
|
closed: "bg-slate-500",
|
||||||
};
|
};
|
||||||
@ -156,7 +156,7 @@ export function ConversationList({
|
|||||||
value={search}
|
value={search}
|
||||||
onChange={handleSearchChange}
|
onChange={handleSearchChange}
|
||||||
placeholder="Search conversations..."
|
placeholder="Search conversations..."
|
||||||
className="border-slate-700 bg-slate-800 pl-9 text-sm text-white placeholder-slate-500 focus:border-violet-500/50"
|
className="border-slate-700 bg-slate-800 pl-9 text-sm text-white placeholder-slate-500 focus:border-primary/50"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ export function ConversationList({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"text-sm",
|
"text-sm",
|
||||||
filter === opt.value
|
filter === opt.value
|
||||||
? "text-violet-400"
|
? "text-primary"
|
||||||
: "text-slate-300"
|
: "text-slate-300"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@ -191,7 +191,7 @@ export function ConversationList({
|
|||||||
<ScrollArea className="flex-1">
|
<ScrollArea className="flex-1">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<div className="h-5 w-5 animate-spin rounded-full border-2 border-violet-500 border-t-transparent" />
|
<div className="h-5 w-5 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||||
</div>
|
</div>
|
||||||
) : filtered.length === 0 ? (
|
) : filtered.length === 0 ? (
|
||||||
<div className="px-4 py-12 text-center">
|
<div className="px-4 py-12 text-center">
|
||||||
@ -244,7 +244,7 @@ function ConversationItem({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-full items-start gap-3 px-3 py-3 text-left transition-colors hover:bg-slate-800/50",
|
"flex w-full items-start gap-3 px-3 py-3 text-left transition-colors hover:bg-slate-800/50",
|
||||||
isActive && "border-l-2 border-violet-500 bg-slate-800/70"
|
isActive && "border-l-2 border-primary bg-slate-800/70"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{/* Avatar */}
|
{/* Avatar */}
|
||||||
@ -274,7 +274,7 @@ function ConversationItem({
|
|||||||
</p>
|
</p>
|
||||||
<div className="flex shrink-0 items-center gap-1.5">
|
<div className="flex shrink-0 items-center gap-1.5">
|
||||||
{conversation.unread_count > 0 && (
|
{conversation.unread_count > 0 && (
|
||||||
<span className="flex h-4 min-w-4 items-center justify-center rounded-full bg-violet-500 px-1 text-[10px] font-bold text-white">
|
<span className="flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[10px] font-bold text-primary-foreground">
|
||||||
{conversation.unread_count}
|
{conversation.unread_count}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -103,7 +103,7 @@ function MediaImage({ url, alt }: { url: string; alt: string }) {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex h-40 w-60 items-center justify-center rounded-lg bg-slate-700">
|
<div className="flex h-40 w-60 items-center justify-center rounded-lg bg-slate-700">
|
||||||
<div className="h-5 w-5 animate-spin rounded-full border-2 border-violet-500 border-t-transparent" />
|
<div className="h-5 w-5 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ function MessageContent({ message }: { message: Message }) {
|
|||||||
case "template":
|
case "template":
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<span className="mb-1 inline-flex items-center gap-1 rounded bg-violet-500/20 px-1.5 py-0.5 text-[10px] font-medium text-violet-400">
|
<span className="mb-1 inline-flex items-center gap-1 rounded bg-primary/20 px-1.5 py-0.5 text-[10px] font-medium text-primary">
|
||||||
<LayoutTemplate className="h-3 w-3" />
|
<LayoutTemplate className="h-3 w-3" />
|
||||||
Template
|
Template
|
||||||
</span>
|
</span>
|
||||||
@ -286,7 +286,7 @@ export function MessageBubble({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"relative rounded-2xl px-3 py-2",
|
"relative rounded-2xl px-3 py-2",
|
||||||
isAgent
|
isAgent
|
||||||
? "rounded-br-md bg-violet-600 text-white"
|
? "rounded-br-md bg-primary text-primary-foreground"
|
||||||
: "rounded-bl-md bg-slate-800 text-slate-100",
|
: "rounded-bl-md bg-slate-800 text-slate-100",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -128,14 +128,14 @@ export function MessageComposer({
|
|||||||
disabled={sessionExpired}
|
disabled={sessionExpired}
|
||||||
rows={1}
|
rows={1}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex-1 resize-none rounded-xl border border-slate-700 bg-slate-800 px-4 py-2.5 text-sm text-white placeholder-slate-500 outline-none transition-colors focus:border-violet-500/50",
|
"flex-1 resize-none rounded-xl border border-slate-700 bg-slate-800 px-4 py-2.5 text-sm text-white placeholder-slate-500 outline-none transition-colors focus:border-primary/50",
|
||||||
sessionExpired && "cursor-not-allowed opacity-50"
|
sessionExpired && "cursor-not-allowed opacity-50"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-9 w-9 shrink-0 bg-violet-600 p-0 hover:bg-violet-500 disabled:opacity-40"
|
className="h-9 w-9 shrink-0 bg-primary p-0 hover:bg-primary/90 disabled:opacity-40"
|
||||||
disabled={!text.trim() || sessionExpired || sending}
|
disabled={!text.trim() || sessionExpired || sending}
|
||||||
onClick={handleSend}
|
onClick={handleSend}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export function MessageReactions({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[11px] leading-none transition-colors",
|
"inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-[11px] leading-none transition-colors",
|
||||||
g.byCurrentUser
|
g.byCurrentUser
|
||||||
? "border-violet-500/60 bg-violet-500/15 text-violet-200 hover:bg-violet-500/25"
|
? "border-primary/60 bg-primary/15 text-primary hover:bg-primary/25"
|
||||||
: "border-slate-700 bg-slate-800/80 text-slate-200 hover:bg-slate-700",
|
: "border-slate-700 bg-slate-800/80 text-slate-200 hover:bg-slate-700",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -114,7 +114,7 @@ function groupMessagesByDate(messages: Message[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const STATUS_OPTIONS: { label: string; value: ConversationStatus; color: string }[] = [
|
const STATUS_OPTIONS: { label: string; value: ConversationStatus; color: string }[] = [
|
||||||
{ label: "Open", value: "open", color: "text-violet-400" },
|
{ label: "Open", value: "open", color: "text-primary" },
|
||||||
{ label: "Pending", value: "pending", color: "text-amber-400" },
|
{ label: "Pending", value: "pending", color: "text-amber-400" },
|
||||||
{ label: "Closed", value: "closed", color: "text-slate-400" },
|
{ label: "Closed", value: "closed", color: "text-slate-400" },
|
||||||
];
|
];
|
||||||
@ -734,7 +734,7 @@ export function MessageThread({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
className={cn(
|
className={cn(
|
||||||
"ml-1 hidden gap-1 border-slate-700 text-[10px] sm:inline-flex sm:ml-2",
|
"ml-1 hidden gap-1 border-slate-700 text-[10px] sm:inline-flex sm:ml-2",
|
||||||
sessionInfo.expired ? "text-red-400" : "text-violet-400"
|
sessionInfo.expired ? "text-red-400" : "text-primary"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Clock className="h-3 w-3" />
|
<Clock className="h-3 w-3" />
|
||||||
@ -795,7 +795,7 @@ export function MessageThread({
|
|||||||
<DropdownMenuTrigger
|
<DropdownMenuTrigger
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center h-7 gap-1 px-2 text-xs rounded-md hover:bg-slate-800",
|
"inline-flex items-center justify-center h-7 gap-1 px-2 text-xs rounded-md hover:bg-slate-800",
|
||||||
assignedAgentId ? "text-violet-400" : "text-slate-400"
|
assignedAgentId ? "text-primary" : "text-slate-400"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<UserPlus className="h-3 w-3" />
|
<UserPlus className="h-3 w-3" />
|
||||||
@ -819,7 +819,7 @@ export function MessageThread({
|
|||||||
onClick={() => handleAssignChange(p.user_id)}
|
onClick={() => handleAssignChange(p.user_id)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-sm",
|
"text-sm",
|
||||||
isSelected ? "text-violet-400" : "text-slate-300"
|
isSelected ? "text-primary" : "text-slate-300"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="flex-1">
|
<span className="flex-1">
|
||||||
@ -851,7 +851,7 @@ export function MessageThread({
|
|||||||
<div ref={scrollRef} className="flex-1 overflow-y-auto px-4 py-4">
|
<div ref={scrollRef} className="flex-1 overflow-y-auto px-4 py-4">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<div className="h-5 w-5 animate-spin rounded-full border-2 border-violet-500 border-t-transparent" />
|
<div className="h-5 w-5 animate-spin rounded-full border-2 border-primary border-t-transparent" />
|
||||||
</div>
|
</div>
|
||||||
) : messages.length === 0 ? (
|
) : messages.length === 0 ? (
|
||||||
<div className="flex flex-col items-center justify-center py-12">
|
<div className="flex flex-col items-center justify-center py-12">
|
||||||
|
|||||||
@ -25,14 +25,14 @@ export function ReplyQuote({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex items-start gap-2 border-l-2 border-violet-400 px-2 py-1",
|
"flex items-start gap-2 border-l-2 border-primary px-2 py-1",
|
||||||
isChip
|
isChip
|
||||||
? "rounded-md bg-slate-800/80"
|
? "rounded-md bg-slate-800/80"
|
||||||
: "mb-1.5 rounded-md bg-black/20",
|
: "mb-1.5 rounded-md bg-black/20",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="truncate text-[11px] font-medium text-violet-300">
|
<div className="truncate text-[11px] font-medium text-primary">
|
||||||
{authorLabel}
|
{authorLabel}
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate text-xs text-slate-200/80">{preview}</div>
|
<div className="truncate text-xs text-slate-200/80">{preview}</div>
|
||||||
|
|||||||
@ -137,7 +137,7 @@ export function TemplatePicker({
|
|||||||
<DialogContent className="border-slate-700 bg-slate-900 sm:max-w-lg">
|
<DialogContent className="border-slate-700 bg-slate-900 sm:max-w-lg">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2 text-white">
|
<DialogTitle className="flex items-center gap-2 text-white">
|
||||||
<LayoutTemplate className="h-4 w-4 text-violet-400" />
|
<LayoutTemplate className="h-4 w-4 text-primary" />
|
||||||
{selected ? selected.name : "Send template"}
|
{selected ? selected.name : "Send template"}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription className="text-slate-400">
|
<DialogDescription className="text-slate-400">
|
||||||
@ -151,7 +151,7 @@ export function TemplatePicker({
|
|||||||
<div className="max-h-[60vh] space-y-2 overflow-y-auto">
|
<div className="max-h-[60vh] space-y-2 overflow-y-auto">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="flex items-center justify-center py-8">
|
<div className="flex items-center justify-center py-8">
|
||||||
<Loader2 className="h-5 w-5 animate-spin text-violet-400" />
|
<Loader2 className="h-5 w-5 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
) : templates.length === 0 ? (
|
) : templates.length === 0 ? (
|
||||||
<div className="rounded-md border border-slate-800 bg-slate-950/50 p-6 text-center">
|
<div className="rounded-md border border-slate-800 bg-slate-950/50 p-6 text-center">
|
||||||
@ -167,7 +167,7 @@ export function TemplatePicker({
|
|||||||
key={t.id}
|
key={t.id}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => pickTemplate(t)}
|
onClick={() => pickTemplate(t)}
|
||||||
className="w-full rounded-md border border-slate-800 bg-slate-950/50 p-3 text-left transition-colors hover:border-violet-500/40 hover:bg-slate-900"
|
className="w-full rounded-md border border-slate-800 bg-slate-950/50 p-3 text-left transition-colors hover:border-primary/40 hover:bg-slate-900"
|
||||||
>
|
>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
@ -175,7 +175,7 @@ export function TemplatePicker({
|
|||||||
<p className="truncate text-sm font-medium text-white">
|
<p className="truncate text-sm font-medium text-white">
|
||||||
{t.name}
|
{t.name}
|
||||||
</p>
|
</p>
|
||||||
<Badge className="border border-violet-600/30 bg-violet-600/20 text-[10px] text-violet-400">
|
<Badge className="border border-primary/30 bg-primary/20 text-[10px] text-primary">
|
||||||
{t.category}
|
{t.category}
|
||||||
</Badge>
|
</Badge>
|
||||||
{t.language && (
|
{t.language && (
|
||||||
@ -242,7 +242,7 @@ export function TemplatePicker({
|
|||||||
<Button
|
<Button
|
||||||
disabled={!canConfirm}
|
disabled={!canConfirm}
|
||||||
onClick={confirm}
|
onClick={confirm}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700 disabled:opacity-50"
|
className="bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
Send template
|
Send template
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -65,7 +65,7 @@ export function DealCard({ deal, stage, onEdit, isOverlay }: DealCardProps) {
|
|||||||
{deal.title}
|
{deal.title}
|
||||||
</h4>
|
</h4>
|
||||||
{deal.status === "won" && (
|
{deal.status === "won" && (
|
||||||
<span className="inline-flex shrink-0 items-center gap-1 rounded-full bg-violet-500/15 px-2 py-0.5 text-[10px] font-semibold text-violet-400">
|
<span className="inline-flex shrink-0 items-center gap-1 rounded-full bg-primary/15 px-2 py-0.5 text-[10px] font-semibold text-primary">
|
||||||
<Check className="h-3 w-3" />
|
<Check className="h-3 w-3" />
|
||||||
Won
|
Won
|
||||||
</span>
|
</span>
|
||||||
@ -87,7 +87,7 @@ export function DealCard({ deal, stage, onEdit, isOverlay }: DealCardProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 flex items-center justify-between">
|
<div className="mt-2 flex items-center justify-between">
|
||||||
<span className="text-sm font-bold text-violet-400">
|
<span className="text-sm font-bold text-primary">
|
||||||
{formatCurrency(deal.value, deal.currency)}
|
{formatCurrency(deal.value, deal.currency)}
|
||||||
</span>
|
</span>
|
||||||
{deal.expected_close_date && (
|
{deal.expected_close_date && (
|
||||||
@ -102,7 +102,7 @@ export function DealCard({ deal, stage, onEdit, isOverlay }: DealCardProps) {
|
|||||||
<div className="mt-2 flex items-center justify-end">
|
<div className="mt-2 flex items-center justify-end">
|
||||||
<span
|
<span
|
||||||
title={assigneeLabel}
|
title={assigneeLabel}
|
||||||
className="flex h-5 w-5 items-center justify-center rounded-full bg-violet-500/15 text-[10px] font-semibold text-violet-400"
|
className="flex h-5 w-5 items-center justify-center rounded-full bg-primary/15 text-[10px] font-semibold text-primary"
|
||||||
>
|
>
|
||||||
{initials(assigneeLabel)}
|
{initials(assigneeLabel)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -264,7 +264,7 @@ export function DealForm({
|
|||||||
<select
|
<select
|
||||||
value={contactId}
|
value={contactId}
|
||||||
onChange={(e) => setContactId(e.target.value)}
|
onChange={(e) => setContactId(e.target.value)}
|
||||||
className="h-9 w-full rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-violet-500 focus:ring-1 focus:ring-violet-500"
|
className="h-9 w-full rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-primary focus:ring-1 focus:ring-primary"
|
||||||
>
|
>
|
||||||
<option value="">Select a contact</option>
|
<option value="">Select a contact</option>
|
||||||
{contacts.map((c) => (
|
{contacts.map((c) => (
|
||||||
@ -277,7 +277,7 @@ export function DealForm({
|
|||||||
{linkedConversation && (
|
{linkedConversation && (
|
||||||
<Link
|
<Link
|
||||||
href="/inbox"
|
href="/inbox"
|
||||||
className="mt-1 inline-flex items-center gap-1.5 self-start rounded-md bg-violet-500/10 px-2 py-1 text-xs text-violet-400 hover:bg-violet-500/20"
|
className="mt-1 inline-flex items-center gap-1.5 self-start rounded-md bg-primary/10 px-2 py-1 text-xs text-primary hover:bg-primary/20"
|
||||||
>
|
>
|
||||||
<MessageSquare className="h-3 w-3" />
|
<MessageSquare className="h-3 w-3" />
|
||||||
Link to Conversation
|
Link to Conversation
|
||||||
@ -304,7 +304,7 @@ export function DealForm({
|
|||||||
<select
|
<select
|
||||||
value={currency}
|
value={currency}
|
||||||
onChange={(e) => setCurrency(e.target.value)}
|
onChange={(e) => setCurrency(e.target.value)}
|
||||||
className="h-9 w-full rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-violet-500"
|
className="h-9 w-full rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-primary"
|
||||||
>
|
>
|
||||||
<option value="USD">USD</option>
|
<option value="USD">USD</option>
|
||||||
<option value="EUR">EUR</option>
|
<option value="EUR">EUR</option>
|
||||||
@ -328,7 +328,7 @@ export function DealForm({
|
|||||||
<select
|
<select
|
||||||
value={stageId}
|
value={stageId}
|
||||||
onChange={(e) => setStageId(e.target.value)}
|
onChange={(e) => setStageId(e.target.value)}
|
||||||
className="h-9 w-full rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-violet-500"
|
className="h-9 w-full rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-primary"
|
||||||
>
|
>
|
||||||
{stages.map((s) => (
|
{stages.map((s) => (
|
||||||
<option key={s.id} value={s.id}>
|
<option key={s.id} value={s.id}>
|
||||||
@ -343,7 +343,7 @@ export function DealForm({
|
|||||||
<select
|
<select
|
||||||
value={assignedTo}
|
value={assignedTo}
|
||||||
onChange={(e) => setAssignedTo(e.target.value)}
|
onChange={(e) => setAssignedTo(e.target.value)}
|
||||||
className="h-9 w-full rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-violet-500"
|
className="h-9 w-full rounded-lg border border-slate-700 bg-slate-800 px-2.5 text-sm text-white outline-none focus:border-primary"
|
||||||
>
|
>
|
||||||
<option value="">Unassigned</option>
|
<option value="">Unassigned</option>
|
||||||
{profiles.map((p) => (
|
{profiles.map((p) => (
|
||||||
@ -374,7 +374,7 @@ export function DealForm({
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => handleStatusChange("won")}
|
onClick={() => handleStatusChange("won")}
|
||||||
disabled={!!statusAction || deal.status === "won"}
|
disabled={!!statusAction || deal.status === "won"}
|
||||||
className="flex-1 bg-violet-600 text-white hover:bg-violet-700 disabled:opacity-50"
|
className="flex-1 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{statusAction === "won" ? (
|
{statusAction === "won" ? (
|
||||||
<Loader2 className="h-4 w-4 animate-spin" />
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
@ -428,7 +428,7 @@ export function DealForm({
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={saving || !title.trim() || !contactId || !stageId}
|
disabled={saving || !title.trim() || !contactId || !stageId}
|
||||||
className="flex-1 bg-violet-600 text-white hover:bg-violet-700"
|
className="flex-1 bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{saving ? "Saving..." : deal ? "Save Changes" : "Create Deal"}
|
{saving ? "Saving..." : deal ? "Save Changes" : "Create Deal"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -107,7 +107,7 @@ export function PipelineAnalytics({ stages, deals }: PipelineAnalyticsProps) {
|
|||||||
tooltip="Count of every deal in this pipeline that isn't marked as Lost. Won deals are still included."
|
tooltip="Count of every deal in this pipeline that isn't marked as Lost. Won deals are still included."
|
||||||
/>
|
/>
|
||||||
<Metric
|
<Metric
|
||||||
icon={<DollarSign className="h-4 w-4 text-violet-400" />}
|
icon={<DollarSign className="h-4 w-4 text-primary" />}
|
||||||
label="Pipeline Value"
|
label="Pipeline Value"
|
||||||
value={formatCurrency(stats.totalValue)}
|
value={formatCurrency(stats.totalValue)}
|
||||||
tooltip="Sum of the dollar values of all deals in this pipeline, excluding deals marked as Lost."
|
tooltip="Sum of the dollar values of all deals in this pipeline, excluding deals marked as Lost."
|
||||||
@ -125,7 +125,7 @@ export function PipelineAnalytics({ stages, deals }: PipelineAnalyticsProps) {
|
|||||||
tooltip="Expected revenue: each open deal's value × its stage probability. First stage ≈ 10%, stages progress up to 90%, Won = 100%. Lost deals are excluded."
|
tooltip="Expected revenue: each open deal's value × its stage probability. First stage ≈ 10%, stages progress up to 90%, Won = 100%. Lost deals are excluded."
|
||||||
/>
|
/>
|
||||||
<Metric
|
<Metric
|
||||||
icon={<Trophy className="h-4 w-4 text-violet-400" />}
|
icon={<Trophy className="h-4 w-4 text-primary" />}
|
||||||
label="Won This Month"
|
label="Won This Month"
|
||||||
value={String(stats.wonThisMonth)}
|
value={String(stats.wonThisMonth)}
|
||||||
tooltip="Deals marked as Won since the first day of the current month."
|
tooltip="Deals marked as Won since the first day of the current month."
|
||||||
|
|||||||
@ -206,7 +206,7 @@ function StageColumn({
|
|||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
className={`mt-3 flex flex-1 flex-col gap-2 rounded-lg transition-all ${
|
className={`mt-3 flex flex-1 flex-col gap-2 rounded-lg transition-all ${
|
||||||
isOver
|
isOver
|
||||||
? "bg-violet-500/5 outline outline-2 outline-dashed outline-violet-400 outline-offset-2"
|
? "bg-primary/5 outline outline-2 outline-dashed outline-primary outline-offset-2"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -349,7 +349,7 @@ export function PipelineSettings({
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={saving || !name.trim()}
|
disabled={saving || !name.trim()}
|
||||||
className="bg-violet-600 text-white hover:bg-violet-700"
|
className="bg-primary text-primary-foreground hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{saving ? "Saving..." : "Save Changes"}
|
{saving ? "Saving..." : "Save Changes"}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -84,7 +84,7 @@ export function PasswordForm() {
|
|||||||
<Card className="bg-slate-900/40 border-slate-800">
|
<Card className="bg-slate-900/40 border-slate-800">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2 text-white">
|
<CardTitle className="flex items-center gap-2 text-white">
|
||||||
<KeyRound className="size-4 text-violet-400" />
|
<KeyRound className="size-4 text-primary" />
|
||||||
Password
|
Password
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="text-slate-400">
|
<CardDescription className="text-slate-400">
|
||||||
|
|||||||
@ -227,7 +227,7 @@ export function ProfileForm() {
|
|||||||
{currentAvatar ? (
|
{currentAvatar ? (
|
||||||
<AvatarImage src={currentAvatar} alt={fullName || 'Avatar'} />
|
<AvatarImage src={currentAvatar} alt={fullName || 'Avatar'} />
|
||||||
) : null}
|
) : null}
|
||||||
<AvatarFallback className="bg-violet-500/10 text-base text-violet-400">
|
<AvatarFallback className="bg-primary/10 text-base text-primary">
|
||||||
{initial}
|
{initial}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|||||||
@ -52,7 +52,7 @@ export function SessionsCard() {
|
|||||||
<Card className="bg-slate-900/40 border-slate-800">
|
<Card className="bg-slate-900/40 border-slate-800">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="flex items-center gap-2 text-white">
|
<CardTitle className="flex items-center gap-2 text-white">
|
||||||
<LogOut className="size-4 text-violet-400" />
|
<LogOut className="size-4 text-primary" />
|
||||||
Active sessions
|
Active sessions
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="text-slate-400">
|
<CardDescription className="text-slate-400">
|
||||||
|
|||||||
@ -142,7 +142,7 @@ export function TagManager() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<Loader2 className="size-6 animate-spin text-violet-500" />
|
<Loader2 className="size-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ export function TagManager() {
|
|||||||
setSelectedColor(PRESET_COLORS[3].value);
|
setSelectedColor(PRESET_COLORS[3].value);
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
}}
|
}}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
>
|
>
|
||||||
<Plus className="size-4" />
|
<Plus className="size-4" />
|
||||||
New Tag
|
New Tag
|
||||||
@ -281,7 +281,7 @@ export function TagManager() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleCreate}
|
onClick={handleCreate}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
>
|
>
|
||||||
{saving ? (
|
{saving ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -40,7 +40,7 @@ const categoryColors: Record<string, string> = {
|
|||||||
const statusColors: Record<string, string> = {
|
const statusColors: Record<string, string> = {
|
||||||
Draft: 'bg-slate-600/20 text-slate-400 border-slate-600/30',
|
Draft: 'bg-slate-600/20 text-slate-400 border-slate-600/30',
|
||||||
Pending: 'bg-yellow-600/20 text-yellow-400 border-yellow-600/30',
|
Pending: 'bg-yellow-600/20 text-yellow-400 border-yellow-600/30',
|
||||||
Approved: 'bg-violet-600/20 text-violet-400 border-violet-600/30',
|
Approved: 'bg-primary/20 text-primary border-primary/30',
|
||||||
Rejected: 'bg-red-600/20 text-red-400 border-red-600/30',
|
Rejected: 'bg-red-600/20 text-red-400 border-red-600/30',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -246,7 +246,7 @@ export function TemplateManager() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<Loader2 className="size-6 animate-spin text-violet-500" />
|
<Loader2 className="size-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -280,7 +280,7 @@ export function TemplateManager() {
|
|||||||
setForm(emptyForm);
|
setForm(emptyForm);
|
||||||
setDialogOpen(true);
|
setDialogOpen(true);
|
||||||
}}
|
}}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
>
|
>
|
||||||
<Plus className="size-4" />
|
<Plus className="size-4" />
|
||||||
New Template
|
New Template
|
||||||
@ -456,7 +456,7 @@ export function TemplateManager() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
>
|
>
|
||||||
{saving ? (
|
{saving ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -265,7 +265,7 @@ export function WhatsAppConfig() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<Loader2 className="size-6 animate-spin text-violet-500" />
|
<Loader2 className="size-6 animate-spin text-primary" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -315,7 +315,7 @@ export function WhatsAppConfig() {
|
|||||||
<Alert className="bg-slate-900 border-slate-700">
|
<Alert className="bg-slate-900 border-slate-700">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{connectionStatus === 'connected' ? (
|
{connectionStatus === 'connected' ? (
|
||||||
<CheckCircle2 className="size-4 text-violet-500" />
|
<CheckCircle2 className="size-4 text-primary" />
|
||||||
) : (
|
) : (
|
||||||
<XCircle className="size-4 text-red-500" />
|
<XCircle className="size-4 text-red-500" />
|
||||||
)}
|
)}
|
||||||
@ -444,7 +444,7 @@ export function WhatsAppConfig() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="bg-violet-600 hover:bg-violet-700 text-white"
|
className="bg-primary hover:bg-primary/90 text-primary-foreground"
|
||||||
>
|
>
|
||||||
{saving ? (
|
{saving ? (
|
||||||
<>
|
<>
|
||||||
@ -510,13 +510,13 @@ export function WhatsAppConfig() {
|
|||||||
<AccordionItem className="border-slate-700">
|
<AccordionItem className="border-slate-700">
|
||||||
<AccordionTrigger className="text-slate-300 hover:text-white hover:no-underline">
|
<AccordionTrigger className="text-slate-300 hover:text-white hover:no-underline">
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span className="flex size-5 items-center justify-center rounded-full bg-violet-600 text-xs font-bold text-white">1</span>
|
<span className="flex size-5 items-center justify-center rounded-full bg-primary text-xs font-bold text-primary-foreground">1</span>
|
||||||
Create a Meta App
|
Create a Meta App
|
||||||
</span>
|
</span>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent className="text-slate-400">
|
<AccordionContent className="text-slate-400">
|
||||||
<ol className="list-decimal list-inside space-y-1 text-sm">
|
<ol className="list-decimal list-inside space-y-1 text-sm">
|
||||||
<li>Go to <span className="text-violet-400">developers.facebook.com</span></li>
|
<li>Go to <span className="text-primary">developers.facebook.com</span></li>
|
||||||
<li>Click "My Apps" and then "Create App"</li>
|
<li>Click "My Apps" and then "Create App"</li>
|
||||||
<li>Select "Business" as the app type</li>
|
<li>Select "Business" as the app type</li>
|
||||||
<li>Fill in app details and create</li>
|
<li>Fill in app details and create</li>
|
||||||
@ -527,7 +527,7 @@ export function WhatsAppConfig() {
|
|||||||
<AccordionItem className="border-slate-700">
|
<AccordionItem className="border-slate-700">
|
||||||
<AccordionTrigger className="text-slate-300 hover:text-white hover:no-underline">
|
<AccordionTrigger className="text-slate-300 hover:text-white hover:no-underline">
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span className="flex size-5 items-center justify-center rounded-full bg-violet-600 text-xs font-bold text-white">2</span>
|
<span className="flex size-5 items-center justify-center rounded-full bg-primary text-xs font-bold text-primary-foreground">2</span>
|
||||||
Add WhatsApp Product
|
Add WhatsApp Product
|
||||||
</span>
|
</span>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
@ -543,7 +543,7 @@ export function WhatsAppConfig() {
|
|||||||
<AccordionItem className="border-slate-700">
|
<AccordionItem className="border-slate-700">
|
||||||
<AccordionTrigger className="text-slate-300 hover:text-white hover:no-underline">
|
<AccordionTrigger className="text-slate-300 hover:text-white hover:no-underline">
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span className="flex size-5 items-center justify-center rounded-full bg-violet-600 text-xs font-bold text-white">3</span>
|
<span className="flex size-5 items-center justify-center rounded-full bg-primary text-xs font-bold text-primary-foreground">3</span>
|
||||||
Get API Credentials
|
Get API Credentials
|
||||||
</span>
|
</span>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
@ -560,7 +560,7 @@ export function WhatsAppConfig() {
|
|||||||
<AccordionItem className="border-slate-700">
|
<AccordionItem className="border-slate-700">
|
||||||
<AccordionTrigger className="text-slate-300 hover:text-white hover:no-underline">
|
<AccordionTrigger className="text-slate-300 hover:text-white hover:no-underline">
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span className="flex size-5 items-center justify-center rounded-full bg-violet-600 text-xs font-bold text-white">4</span>
|
<span className="flex size-5 items-center justify-center rounded-full bg-primary text-xs font-bold text-primary-foreground">4</span>
|
||||||
Configure Webhooks
|
Configure Webhooks
|
||||||
</span>
|
</span>
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
@ -581,7 +581,7 @@ export function WhatsAppConfig() {
|
|||||||
href="https://developers.facebook.com/docs/whatsapp/cloud-api/get-started"
|
href="https://developers.facebook.com/docs/whatsapp/cloud-api/get-started"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="inline-flex items-center gap-1.5 text-sm text-violet-400 hover:text-violet-300 transition-colors"
|
className="inline-flex items-center gap-1.5 text-sm text-primary hover:text-primary/80 transition-colors"
|
||||||
>
|
>
|
||||||
<ExternalLink className="size-3.5" />
|
<ExternalLink className="size-3.5" />
|
||||||
Meta WhatsApp API Documentation
|
Meta WhatsApp API Documentation
|
||||||
|
|||||||
@ -5,7 +5,8 @@ import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
|
|||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
// Root: emerald when checked, slate when unchecked. Matches app theme.
|
// Root: primary token when checked (responds to the active color theme),
|
||||||
|
// slate when unchecked.
|
||||||
function Switch({
|
function Switch({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
@ -15,9 +16,9 @@ function Switch({
|
|||||||
data-slot="switch"
|
data-slot="switch"
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors",
|
"inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors",
|
||||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-950",
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-slate-950",
|
||||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
"data-[checked]:bg-violet-500 data-[unchecked]:bg-slate-700",
|
"data-[checked]:bg-primary data-[unchecked]:bg-slate-700",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ export const TRIGGER_META: Record<AutomationTriggerType, TriggerMeta> = {
|
|||||||
},
|
},
|
||||||
new_contact_created: {
|
new_contact_created: {
|
||||||
label: 'New Contact',
|
label: 'New Contact',
|
||||||
pillClass: 'border-violet-500/30 bg-violet-500/10 text-violet-300',
|
pillClass: 'border-primary/30 bg-primary/10 text-primary',
|
||||||
},
|
},
|
||||||
conversation_assigned: {
|
conversation_assigned: {
|
||||||
label: 'Conversation Assigned',
|
label: 'Conversation Assigned',
|
||||||
|
|||||||
@ -26,10 +26,13 @@ describe("getBroadcastStatus", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("each variant has the dark-theme class triple", () => {
|
it("each variant has the dark-theme class triple", () => {
|
||||||
|
// Accept both fixed-shade Tailwind names (bg-red-500/10) and
|
||||||
|
// token-backed names without a shade number (bg-primary/10) since
|
||||||
|
// the brand-accent statuses now ride the active color theme.
|
||||||
for (const v of Object.values(broadcastStatusConfig)) {
|
for (const v of Object.values(broadcastStatusConfig)) {
|
||||||
expect(v.classes).toMatch(/bg-[a-z]+-\d+\/10/);
|
expect(v.classes).toMatch(/bg-[a-z]+(-\d+)?\/10/);
|
||||||
expect(v.classes).toMatch(/text-[a-z]+-\d+/);
|
expect(v.classes).toMatch(/text-[a-z]+(-\d+)?/);
|
||||||
expect(v.classes).toMatch(/border-[a-z]+-\d+\/20/);
|
expect(v.classes).toMatch(/border-[a-z]+(-\d+)?\/20/);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export const broadcastStatusConfig: Record<BroadcastStatus, StatusDisplay> = {
|
|||||||
},
|
},
|
||||||
sent: {
|
sent: {
|
||||||
label: "Sent",
|
label: "Sent",
|
||||||
classes: "bg-violet-500/10 text-violet-400 border-violet-500/20",
|
classes: "bg-primary/10 text-primary border-primary/20",
|
||||||
},
|
},
|
||||||
failed: {
|
failed: {
|
||||||
label: "Failed",
|
label: "Failed",
|
||||||
@ -55,11 +55,11 @@ export const recipientStatusConfig: Record<RecipientStatus, StatusDisplay> = {
|
|||||||
},
|
},
|
||||||
delivered: {
|
delivered: {
|
||||||
label: "Delivered",
|
label: "Delivered",
|
||||||
classes: "bg-violet-500/10 text-violet-400 border-violet-500/20",
|
classes: "bg-primary/10 text-primary border-primary/20",
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
label: "Read",
|
label: "Read",
|
||||||
classes: "bg-violet-500/10 text-violet-300 border-violet-500/20",
|
classes: "bg-primary/10 text-primary border-primary/20",
|
||||||
},
|
},
|
||||||
replied: {
|
replied: {
|
||||||
label: "Replied",
|
label: "Replied",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user