Commit Graph

14 Commits

Author SHA1 Message Date
Arnas Donauskas
4aa329b527
feat(theme): add light/dark mode toggle (#243)
Introduce a light/dark MODE axis orthogonal to the existing accent
themes. Neutral surface tokens now live in html[data-mode="light|dark"]
blocks; accent tokens (--primary*, --ring, --chart-1, --sidebar-primary*)
stay in html[data-theme] blocks, so any accent works in either mode.

- globals.css: split neutrals (mode) from accent (theme); add light
  palette; dark values unchanged (dark stays the default).
- lib/themes.ts: Mode type, DEFAULT_MODE=dark, MODE_STORAGE_KEY, isMode.
- use-theme.tsx: provider owns mode/setMode/toggleMode + cross-tab sync.
- layout.tsx: boot script applies persisted mode before first paint
  (no flash); colorScheme "dark light".
- mode-toggle.tsx (header sun/moon) + themed-toaster.tsx (mode-aware).
- appearance-panel.tsx: light/dark control + updated copy.
- Tokenize hardcoded neutral colors (slate/white/gray) -> semantic
  tokens across the app so every surface flips with mode. Flow canvas
  and message-bubble timestamps made mode-aware too.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 17:29:46 +03:00
Arnas Donauskas
7c373c8e0a
fix(theme): suppress hydration warning on root html data-theme (#207)
The `theme-boot` beforeInteractive script in layout.tsx rewrites
`data-theme` on <html> from localStorage before React hydrates, so for
any non-default theme the client DOM intentionally differs from the
server-rendered DEFAULT_THEME ("violet"). React then logged a hydration
mismatch on <html> on every load where the saved theme differed.

Add suppressHydrationWarning to the root <html>. It's scoped to that
element's own attributes (child mismatches still surface) and preserves
the documented localStorage-only/device-scoped theme design — unlike a
cookie-based SSR approach, which would change that behavior.

Verified in-browser (Next 16.2.6 / Turbopack): with a non-default theme
in localStorage, the reproduced "+ data-theme=violet / - data-theme=cobalt"
hydration error is gone; removing the prop brings it back (negative
control). typecheck, lint (0 errors), 370 tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 13:46:23 +03:00
Arnas Donauskas
2fbeeee3ac
Revert "feat(i18n): re-attempt next-intl with hardened artifact tracing" 2026-05-31 21:05:34 +03:00
Arnas Donauskas
8124640d80
Revert "Merge pull request #183 from ArnasDon/revert/i18n-pr-182"
This reverts commit 872f751c07, reversing
changes made to 5e8076cee9.
2026-05-31 20:56:54 +03:00
Arnas Donauskas
a30f04b5a4
Revert "Merge pull request #182 from ArnasDon/feat/i18n-next-intl"
This reverts commit 5e8076cee9, reversing
changes made to 962e4fb27d.
2026-05-31 20:42:14 +03:00
Arnas Donauskas
8a8758a487
feat(i18n): scaffold next-intl with [locale] routing + extract auth/layout
Introduces an internationalization layer so wacrm can ship in multiple
languages — initially en (canonical), es, lt (untranslated mirrors of
en, ready for translators).

Foundation
- next-intl ^4.13 (Next 16 + React 19 compatible)
- src/i18n/{routing,navigation,request}.ts — locale config, locale-aware
  Link/router/redirect, server-side message loader
- next.config.ts wired with `createNextIntlPlugin`
- src/app/* moved under src/app/[locale]/ (excluding /api and globals.css)
- [locale]/layout.tsx is now the root layout: <html lang={locale}>,
  hasLocale guard, setRequestLocale, NextIntlClientProvider,
  generateStaticParams for build-time prerendering of all locales
- middleware.ts composes next-intl's createMiddleware with the existing
  Supabase auth check: intl middleware runs first to handle locale
  prefix; auth redirects retain the active locale (e.g. signed-out user
  on /es/dashboard → /es/login); intl cookies (NEXT_LOCALE) are carried
  onto auth-issued responses so the sticky preference survives

Strings extracted (~80 keys across en/es/lt.json)
- common, nav (sidebar + header), roles (Owner/Admin/Agent/Viewer chips)
- auth.login, auth.signup, auth.forgotPassword (all forms + success states)
- join (all four UI states: loading, fail, accept, sign-up CTA + conflict modal)
- localeSwitcher

UI
- New `<LocaleSwitcher />` in the header — globe icon + locale dropdown
  using next-intl's useLocale + locale-aware useRouter

Out of scope for this PR (intentional — keeps diff reviewable)
- Dashboard feature pages (dashboard, inbox, contacts, pipelines,
  broadcasts, automations, flows, settings) and their components/
  namespaces still have hardcoded English; they continue to render
  fine because next/link redirects through the locale middleware
- Shared UI a11y strings (dialog close, etc.)
- Translating es.json / lt.json — they currently mirror en.json so the
  app runs in any locale without crashes (English fallback)
- Renaming middleware.ts → proxy.ts per Next 16 deprecation

Verification
- npm run build → 62 static pages prerendered across en/es/lt
- npm run typecheck → clean
- npm run test → 366 passing
- npm run lint → 0 errors (18 pre-existing warnings untouched)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-31 20:17:49 +03:00
Arnas Donauskas
54a969c56d
feat(settings): color theme picker (5 themes)
Adds a new "Appearance" tab in Settings with 5 dark color themes
(Violet, Emerald, Cobalt, Amber, Rose). Pick a theme → applied
immediately and persisted to localStorage. Boot script in the root
layout replays the pick before first paint so there's no
flash-of-default on subsequent loads.

Implementation, ordered the way it's read:

1. `globals.css` — 5 `html[data-theme="..."]` blocks extracted
   verbatim from the design handoff at project/Color Themes.html.
   `:root` mirrors Violet so SSR / first-paint renders correctly
   even before the boot script runs. Three new tokens
   (`--primary-hover`, `--primary-soft`, `--primary-soft-2`) added
   to all themes — used by tinted-primary surfaces.

2. `src/lib/themes.ts` — single source of truth for the catalog
   (id, name, tagline, swatch). Adding a theme = append a CSS
   block + append an entry here.

3. `src/hooks/use-theme.tsx` — ThemeProvider client component. Reads
   what the boot script already applied, syncs across tabs via the
   `storage` event, persists to localStorage. Falls back to a no-op
   setter when used outside the provider.

4. `src/app/layout.tsx` — inline `beforeInteractive` script that
   reads localStorage and sets `documentElement.dataset.theme`
   before React hydrates. Knowledge of valid theme IDs comes from
   the THEME_IDS constant so adding a theme doesn't silently break
   the boot path. Wraps the tree in ThemeProvider.

5. `src/components/settings/appearance-panel.tsx` — picker UI. 5
   cards, swatch + name + tagline + a 4-tone preview strip. Active
   card carries a primary-tinted ring + "Active" chip.

6. `src/app/(dashboard)/settings/page.tsx` — new "appearance" tab
   alongside profile/whatsapp/templates/tags.

7. Key-surface tokenization — replaced hard-coded `violet-*` with
   `primary` tokens on the high-visibility surfaces so the theme
   switch feels real: sidebar logo + active nav + unread dot +
   avatar fallback, header avatar, dashboard-shell loading
   spinner, Flows header icon + entry-node picker icon + entry
   badge + flash ring, Flows list page (template card hover,
   FlowCard icon), Flows editor + runs "back" links, Settings tabs
   active state.

NOT touched (intentional follow-ups):
- Per-node-type colors in flow-builder.tsx NODE_META — those are a
  deliberate per-type rainbow, not a brand accent.
- The inbox/contacts/pipelines surfaces — many `violet-*` usages
  there. Sweep is a contained follow-up; the user explicitly
  scoped this PR to "tokens + tab + key surfaces".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 16:48:40 +03:00
Arnas Donauskas
15a05eb280
Extract marketing site to ArnasDon/wacrm-site
Forks of this repo no longer inherit the wacrm.tech landing page
and docs — those move to the dedicated marketing-site repo at
https://github.com/ArnasDon/wacrm-site (deployed to wacrm.tech).

Changes here:
- Replace src/app/page.tsx with a redirect to /dashboard.
  Middleware already handles the unauth → /login bounce, so this
  works for both auth states.
- Drop src/app/{docs,opengraph-image.tsx,robots.ts,sitemap.ts}.
- Drop src/components/{landing,docs,seo} and src/lib/{seo,docs}.
- Drop the top-level docs/ markdown — moved to wacrm-site/docs.
- Slim src/app/layout.tsx metadata down to product chrome
  (no public OG cards, robots: noindex).
- Drop `marked` from dependencies; refresh package-lock.
- Refocus README on self-hosting; point readers at wacrm.tech/docs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 12:21:13 +03:00
Arnas Donauskas
fe14d4894f
fix(landing): drop hardcoded shortcut-icon pointing at removed favicon
layout.tsx still had `icons.shortcut: ["/favicon.ico"]`, which kept
emitting the default Next.js favicon link in <head> even after the
file was deleted. icon.tsx is the single source now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 08:59:24 +03:00
Arnas Donauskas
15e4b5366d
feat(seo): full SEO pass (reapply with OG-image build fix)
Re-applies the original SEO work from PR #47 that was reverted in
PR #54 because its opengraph-image.tsx crashed `next build` during
static prerender of /opengraph-image. Satori (next/og's renderer)
errored with:

  Error: Expected <div> to have explicit "display: flex",
         "display: contents", or "display: none" if it has more
         than one child node.
  Failed to load dynamic font for ✓ . Status: 400

Root cause in the OG image JSX:
- Several <div>s held text + a sibling element (e.g. "Run your
  WhatsApp business from <span>one inbox.</span>") without an
  explicit display value. Satori requires it whenever there is
  more than one child.
- The ✓ Unicode glyph isn't covered by the default font Satori
  loads, so next/og tried to download a dynamic font and failed.

Fix:
- Every <div> with more than one child now has display: flex.
- Multi-span headline sits in a flex-wrap row of two <span>s.
- ✓ replaced with plain text dot separators in the feature row.

Verified end-to-end: `npm run build` now completes with 27/27
pages prerendered, /opengraph-image among them.

All the other SEO-pass content is identical to the originally
merged-and-reverted work: metadataBase + title template, OpenGraph
+ Twitter Cards defaults, JSON-LD (WebSite + Organization +
SoftwareApplication + FAQPage), sitemap.ts, robots.ts, noindex on
auth + dashboard route-group layouts, shared faq-data + site-config
modules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 12:25:59 +03:00
Arnas Donauskas
adf78bb2ee
Revert "feat(seo): full SEO pass — metadata, structured data, sitemap, robots" 2026-04-19 12:15:42 +03:00
Arnas Donauskas
c2587f7466
feat(seo): full SEO pass — metadata, structured data, sitemap, robots
Brings the site from bare-bones metadata to production-ready SEO.
Covers technical crawlability, rich-result eligibility, social
sharing, and duplicate-content prevention.

Added:
- src/lib/seo/site-config.ts — single source of truth for base URL,
  brand, description, keywords. Referenced by every consumer so a
  domain / rename is a one-file edit.
- src/lib/seo/faq-data.ts — FAQ Q&A extracted from the component so
  the UI and the FAQPage JSON-LD render the same content.
- src/lib/seo/structured-data.ts — builders for WebSite,
  Organization, SoftwareApplication, and FAQPage JSON-LD nodes.
  FAQPage is eligible for Google's FAQ rich result; SoftwareApplication
  with a free Offer can earn a "Free" badge on SERP.
- src/components/seo/json-ld.tsx — emits one <script
  type="application/ld+json"> per node (Google's preferred shape).
- src/app/opengraph-image.tsx — Next auto-generates a 1200×630 PNG
  for the landing page's OG / Twitter previews.
- src/app/sitemap.ts — lists only public, indexable pages.
- src/app/robots.ts — Allow /, Disallow every app / auth route,
  references the sitemap.

Updated:
- src/app/layout.tsx — metadataBase, title template (%s — WaCRM),
  default OpenGraph + Twitter Cards (inherited everywhere), canonical,
  icons config, viewport (themeColor + colorScheme=dark for browser
  chrome on mobile).
- src/app/page.tsx — injects the 4 JSON-LD blocks, sets explicit
  canonical for /, overrides the title.
- src/app/(auth)/layout.tsx — noindex+nofollow for login / signup /
  forgot-password (no SEO value; would compete with landing page).
- src/app/(dashboard)/layout.tsx — split into thin server layout
  (exports noindex metadata) + DashboardShell client component. App
  routes won't be indexed even if a URL leaks.
- src/components/landing/faq.tsx — consumes the shared FAQ data
  module (no more drift between UI and JSON-LD).

Verified end-to-end against the running dev server:
- GET / HTML contains: canonical, description, keywords, robots,
  og:title/url/image/type, twitter:card/title, theme-color, + 4
  JSON-LD blocks (WebSite, Organization, SoftwareApplication,
  FAQPage).
- GET /opengraph-image → 200 OK, content-type image/png.
- GET /login HTML contains <meta name="robots" content="noindex,
  nofollow, nocache">.
- GET /sitemap.xml serves valid XML with /.
- GET /robots.txt serves Allow /, Disallow all app + auth paths,
  sitemap reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 11:17:48 +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
c70bfe0139
Initial commit from Create Next App 2026-04-16 17:52:39 +03:00