Commit Graph

6 Commits

Author SHA1 Message Date
Arnas Donauskas
cbcad71fda
feat(ui): front-end correctness pass from multi-user review (S1,S2,S6-S8,P8,P9)
PR C of the post-review fix series. Bundles six related front-end
correctness / portability fixes. No behavior change for happy-path
users — these are belt-and-braces against edge cases.

S1 — useCan exhaustiveness (`src/hooks/use-can.ts`)
- Add a `default` branch with `const _: never = action` so adding
  a new `CanAction` without a case fails the typecheck instead of
  silently returning `undefined` at runtime.

S2 + P8 — GatedButton helper (`src/components/ui/gated-button.tsx`)
- `<Button disabled title="...">` doesn't render a tooltip in
  Safari or older Firefox: those browsers don't fire mouse events
  on disabled controls, so the title never surfaces. Viewers on
  those browsers saw silently-disabled CTAs with no explanation.
- New `<GatedButton canAct={...} gateReason="...">` wraps the
  Button in a non-disabled `<span>` that owns the `title`. The
  span receives mouseover everywhere; the tooltip works.
- Also subsumes the duplicated `READ_ONLY_TITLE = "..."` constants
  across 5 list pages. `gateReason` is the verb phrase that
  completes "Read-only — your role can't <gateReason>".
- Migrated call sites: broadcasts / automations / flows / contacts
  / pipelines list pages + inbox composer (Send + Templates
  buttons). The composer's textarea keeps its inline title since
  the GatedButton pattern only applies to buttons.

S6 — Revert optimistic role-change on error (`members-tab.tsx`)
- Pre-fix: PATCH failure showed a toast and left the local
  member-row state with the new role. Next interaction
  (re-toggling to the same role) was a no-op via the
  `member.role === nextRole` guard at the top, so the row
  appeared stuck. Now we capture `previousRole` before the
  optimistic flip and revert on both 4xx and network failure.

S7 — Sidebar flag check matches settings (`sidebar.tsx`)
- Pre-fix: sidebar's `accountSharingEnabled` didn't guard on
  `profileLoading`, so the account-name strip briefly was hidden
  during the initial profile fetch then popped in. Settings page
  did this guard correctly. Now they match.

S8 — Referrer-Policy: no-referrer on /join (`join/layout.tsx`)
- The plaintext invite token lives in the URL path. Belt-and-
  braces against any future external resource on the join page
  leaking the token via the Referer header. Set via Next 16's
  `metadata` export → `<meta name="referrer" content="no-referrer">`.
  Also marks the route `noindex, nofollow` so a join URL can't end
  up in search results.

P9 — Client-side label length validation (`invite-member-dialog.tsx`)
- Server caps `label` at 80 chars and returns 400 on overflow.
  Add a `MAX_LABEL_LEN` constant on the client, point both the
  `<Input maxLength>` and the pre-submit check at it. Catches the
  paste-then-submit path where a paste lands an over-limit string
  in state before the keystroke-level cap engages.

Verification
- `npm run lint`      → 0 errors (same 19 pre-existing warnings)
- `npm run typecheck` → clean (the new `never` cast catches typos)
- `npm test`          → 366/366 pass
- `npm run build`     → succeeds

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 19:33:28 +03:00
Arnas Donauskas
32022c13fe
feat(theme): sweep remaining violet-* → primary across all surfaces
PR #132 (color themes) only tokenized the high-visibility chrome.
Inbox / contacts / pipelines / broadcasts / automations / auth /
settings sub-forms still had ~150 hard-coded `violet-*` classes
across ~32 files, so picking a non-violet theme felt half-broken —
sidebar and header changed colour, but message bubbles, deal cards,
and the broadcast wizard all stayed violet.

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

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

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

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 17:00:32 +03:00
Arnas Donauskas
77dfa4f165
chore: switch accent palette to Hostinger violet, drop emerald green
The legal brief on third-party trademark use says: avoid the WhatsApp
teal/green palette and any visual treatment that could read as a
WhatsApp/Meta product. Emerald-500 was used as the primary brand colour
across hero, CTAs, sidebar, charts, badges, and the favicon — close
enough in hue to WhatsApp's signature green to muddy the line.

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

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

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

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

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:57:12 +03:00
Arnas Donauskas
b7b362ae42
feat(automations): visual flow builder, engine, and logs viewer
Replaces the /automations placeholder with a full feature:
- Migration 006 adds automations, automation_steps, automation_logs,
  and automation_pending_executions with RLS matching existing tables.
- List page with active/inactive switch, quick-start templates,
  duplicate, delete, and View Logs actions.
- Full-screen vertical flow builder with Trigger + Action + Condition
  (Yes/No branches) + Wait cards. Inline edit forms, up/down reorder,
  dotted-grid canvas.
- Execution engine (src/lib/automations/engine.ts) dispatched from the
  WhatsApp webhook for new_message_received, new_contact_created, and
  keyword_match. Wait steps enqueue to automation_pending_executions;
  /api/automations/cron drains them behind AUTOMATION_CRON_SECRET.
- Logs viewer with per-step checkmarks and expandable detail.

Existing files touched only in webhook/route.ts (fire-and-forget
trigger dispatch) and types/index.ts (appended automation types).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 19:39:02 +03:00
Arnas Donauskas
658d070811
Initial scaffold: WhatsApp CRM with Next.js + Supabase
Complete WhatsApp CRM application built with Next.js 14 (App Router),
Supabase (PostgreSQL + Auth + Realtime), and shadcn/ui.

Features:
- Auth: Login, signup, forgot password with Supabase Auth
- Dashboard: Analytics overview with stats, recent activity
- Inbox: Real-time WhatsApp conversations with message threading
- Contacts: CRUD with tags, custom fields, notes, CSV import
- Broadcasts: 4-step wizard with audience selection, template sending
- Pipelines: Kanban board with deal management
- Settings: WhatsApp API config, templates, tags
- WhatsApp API: Webhook (verify + receive), send, broadcast, media proxy
- Database: 16 tables with RLS policies, indexes, realtime subscriptions
- Dark theme throughout with emerald accents

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 18:17:31 +03:00
Arnas Donauskas
de3deb32da
feat: initial commit 2026-04-16 17:53:27 +03:00