Commit Graph

78 Commits

Author SHA1 Message Date
tyler d892b3f64f feat(selfhost): complete Supabase removal migration
Deploy to route.crispygoat.com / deploy (push) Failing after 9s
Phase 1 — Pattern library
- Add src/lib/api.ts (typed PostgREST client, anon-key)
- Add src/lib/svc-fetch.ts (service-role fetch + PostgREST client)
- Add src/lib/db-types.ts (Database generic, RowOf helper)
- Add src/lib/auth-admin.ts (better-auth admin wrappers: createUser, listUsers, removeUser, requestPasswordReset, validateSession)

Phase 2 — Server action migration
- Migrate all 67 server actions off @supabase/supabase-js to svcApi/svcRpc
- Replace service.auth.admin.* with authAdmin.* (better-auth)
- Replace publicApi.auth.* with authAdmin.* (better-auth)
- Add typed single() null guards + nullable column fallbacks

Phase 3 — Delete mock data + debug routes
- Remove if(useMockData) branches from 7 files (-226 lines)
- Delete src/lib/mock-data.ts (no longer referenced)
- Delete 7 debug API routes (debug-admin-users, debug-auth, debug-cookie, debug-env, debug-get-admin-users, debug-hello, debug-me)

Phase 4 — Hard cut
- Delete src/lib/supabase.ts (orphan — no importers)
- Delete src/app/api/supabase + src/app/api/supabase-test routes
- Remove @supabase/ssr + @supabase/supabase-js from package.json
- Rename env vars: NEXT_PUBLIC_SUPABASE_URL → NEXT_PUBLIC_API_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY → NEXT_PUBLIC_API_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY → POSTGREST_SERVICE_KEY

Phase 5 — Verify
- npx tsc --noEmit: 0 errors
- npm run lint: 0 new errors from migration (104 pre-existing errors unrelated)
- npm run build: same failure mode as main worktree (PostgREST-dependent prerender), no regression

Net: 158 files changed, +1640 / -1903 lines (-263 net)
2026-06-05 20:17:02 +00:00
tyler e7ac495831 docs: explicit step to clean up test brands after restore
The captured production data still includes 3 test brands (Sunrise,
Green Valley, Orchard) created during the migration work. Promote the
note in 'Notes' to a fully reproducible post-restore step with the
exact DELETE statement and verification query.
2026-06-05 17:54:41 +00:00
tyler c9b1c49f53 selfhost: route brand assets through /storage/ rewrite
Download 3 Tuxedo brand logos from Supabase Storage to local MinIO and
point the DB at portable /storage/... paths. Add a Next.js rewrite in
next.config.ts that proxies /storage/* to the configured MinIO endpoint.

Why: Next.js's image optimizer refuses to fetch upstream images whose
hostname resolves to a private IP. Local MinIO lives on 127.0.0.1, so
direct URLs (e.g. http://localhost:9000/...) get blocked with
"upstream image resolved to private ip [::1,127.0.0.1]".

The rewrite keeps URLs same-origin in the browser, so the optimizer's
private-IP check is bypassed. For production, set STORAGE_PUBLIC_URL to
the public MinIO endpoint and the same DB values work without change.

Also fix the same pattern in 3 client-rendered components that
hardcoded publicUrl(BUCKETS.BRAND_LOGOS, ...) for fallback assets:
- TuxedoVideoHero (hero video + Olathe Sweet dark logo)
- TimeTrackingFieldClient (field UI logo)
- tuxedo/about (about page logo)

email-service.ts keeps publicUrl() because Resend fetches URLs
server-side from its own network.

Documented the workflow and gotchas in docs/SUPABASE_DUMP_GUIDE.md.
2026-06-05 17:51:48 +00:00
tyler cbc8958b55 update dump guide with real lessons from 2026-06-05 capture
- aws-1 pooler (not aws-0)
- pg_dump 17 required for Supabase (PG 17.6 server)
- extensions schema + stubs needed for local PG 16
- auth.users stub for RLS policy references
- Re-apply is idempotent (errors are 'already exists')
2026-06-05 17:31:31 +00:00
tyler be226d3430 real Supabase schema + data loaded; cleanup synthesized placeholder
Major changes:
- Drop synthesized base schema (supabase/synthesized/000_base_schema.sql) —
  real pg_dump from Supabase is now the source of truth
- Switch better-auth from better-auth/minimal to better-auth (full)
- Add localhost:9000 (MinIO) to next.config images allowlist
- Add .data/, bin/ to gitignore (local runtime artifacts)
- Ignore supabase/captured/ (regenerate from Supabase as needed)

DB state after dump:
- 65 real tables (vs 70 synthesized)
- 252 functions
- 5 brands: Tuxedo Corn, Indian River Direct + 3 test brands
- 3 admin users, 1 communication template, 7 brand features

Pooler URL: aws-1-us-east-1.pooler.supabase.com:5432
  user: postgres.wnzkhezyhnfzhkhiflrp
  (NOT aws-0 — that one doesn't know this project)
2026-06-05 17:30:30 +00:00
tyler b433c79677 supabase dump & restore guide (runbook for after spend cap removed)
Covers:
- Connection test (direct vs pooler, IPv4 vs IPv6)
- pg_dump commands (schema-only and data-only)
- Restore steps to local DB
- Verification queries
- Schema-per-brand restructure (future work)
2026-06-05 16:45:37 +00:00
tyler a266203c07 synthesized base schema for local dev (60 base tables)
Temporary schema derived from migration column references. Provides
workable local structure BEFORE the real Supabase pg_dump arrives.

When brother removes the Supabase spend cap:
1. pg_dump the real Supabase schema to supabase/captured_schema.sql
2. pg_dump the data to supabase/captured_data.sql
3. Drop everything in public schema
4. Apply the captured schema + data
5. Drop the synthesized file

The synthesized schema is NOT for production.
2026-06-05 16:44:41 +00:00
tyler 3f4145e800 fix(sitemap): render at request time to avoid build-time DB dependency 2026-06-05 15:36:45 +00:00
tyler 553bfed070 fix(supabase): don't force mock mode for non-supabase.co URLs (PostgREST compat) 2026-06-05 15:24:17 +00:00
tyler 452eef7606 feat(deploy): bring up Docker stack + apply migrations in Gitea workflow 2026-06-05 15:22:38 +00:00
tyler c20538ef9f Add MinIO storage + replace Supabase Storage with S3 SDK
- New: src/lib/storage.ts — S3-compatible client, uploadFile/deleteFile/listFiles/publicUrl helpers, bucket constants
- New: docker-compose adds minio + minio_init services
- Replaced Supabase fetch PUTs in:
  - src/actions/brand-settings.ts (3 uploaders)
  - src/actions/products/upload-image.ts
  - src/actions/communications/import-contacts.ts
  - src/app/api/water-photo-upload/route.ts
- Replaced hardcoded Supabase URLs in:
  - src/components/storefront/TuxedoVideoHero.tsx
  - src/components/time-tracking/TimeTrackingFieldClient.tsx
  - src/app/tuxedo/about/page.tsx
  - src/lib/email-service.ts (4 occurrences)
- Added @aws-sdk/client-s3 + @aws-sdk/s3-request-presigner
- .env.example adds MinIO + storage vars
- Migration cleanup: deleted BUNDLE_018_042, 4 XXX drafts, 087/145/099-contact storage migrations
- Migration patches: 006 STATIC→STABLE, 135 param reordering
- Preflight: added pgcrypto extension, removed storage stub
- Verified: MinIO upload/list/delete round-trip works against local instance
2026-06-05 15:17:21 +00:00
tyler 66d8cdf9b2 Add self-hosted Postgres docker-compose
- docker-compose.yml: Postgres 16 Alpine with named volume, healthcheck
- .env.example: POSTGRES_* and DATABASE_URL template
- .gitignore: exclude db_data/ volume

Starting fresh, no data migration. App still wired to Supabase;
DB is ready for migrations to be applied.
2026-06-05 15:12:53 +00:00
tyler e41ce98b74 Fix workflow: use actual secrets, fix env file writing 2026-06-05 15:12:53 +00:00
tyler 85db68ed70 Load .env.production from server before build 2026-06-05 15:12:53 +00:00
tyler 4eb6b173e0 Use npm install instead of npm ci (no lockfile) 2026-06-05 15:12:53 +00:00
tyler 2635c0a99d Remove npm cache from workflow (local runner) 2026-06-05 15:12:53 +00:00
tyler 3a9c6e3934 Add Gitea Actions deploy workflow 2026-06-05 15:12:53 +00:00
tyler d9dee2c926 Add rocket emoji to tagline 2026-06-05 15:12:53 +00:00
tyler a15f2b7dcf docs(plan): Supabase → self-hosted migration implementation plan 2026-06-05 15:05:56 +00:00
tyler 2565c18cdd docs(spec): self-review pass — fix commit count and migration patch count 2026-06-05 14:50:06 +00:00
tyler e6a97ba9ab docs(spec): Supabase → self-hosted migration design 2026-06-05 14:48:18 +00:00
tyler 9374e63ae6 docs(memory): record Codex review round 2 pass 2026-06-03 16:40:03 +00:00
tyler 0245aa29cc fix(buyer/billing/comms/a11y): Codex review pass round 2
Tuxedo buyer path (subagent 2):
- src/app/tuxedo/page.tsx: remove duplicate CinematicShowcase render
- src/components/storefront/CinematicShowcase.tsx: wire up useCart, Add to Cart button, brand-aware fulfillment
- src/app/tuxedo/stops/TuxedoStopsList.tsx: improved empty state with calendar icon + CTAs
- src/app/cart/CartClient.tsx: guard against empty cart checkout; 'Cart is Empty' state

Billing reconciliation (subagent 3):
- src/actions/billing/billing-overview.ts: NEW — single source of truth
- src/app/admin/settings/billing/page.tsx: use getBillingOverview
- src/app/admin/settings/billing/BillingClientPage.tsx: rewritten to consume BillingOverview (status pill, addons state, removable flags, derived invoice amounts, usage footer)
- src/app/admin/page.tsx: use getBillingOverview (aligns dashboard with billing)
- src/components/admin/DashboardClient.tsx: 'Active Products' now reads from getBillingOverview
- supabase/migrations/203_plan_usage_active_products.sql: get_brand_plan_info counts products as active=true AND deleted_at IS NULL

Harvest Reach dedup + audience preview (manual, subagent 4 didn't complete):
- src/components/admin/CommunicationsPage.tsx: add initialTab prop
- src/app/admin/communications/compose/page.tsx: now renders with initialTab='compose' (single compose experience, no duplicate edit panel)
- src/components/admin/HarvestReach/CampaignComposerPage.tsx: always-visible audience preview panel (count + sample emails), loads via previewCampaignAudience action

Layout/content consistency + a11y sweep (subagent 5):
- src/components/layout/SiteHeader.tsx: Admin link only shows for authenticated admin users
- src/components/Providers.tsx: suppress public SiteHeader/Footer for /admin, /cart, /checkout, /wholesale, /water (fixes duplicate headers)
- src/app/contact/ContactClientPage.tsx: Phone/Email now use tel:/mailto: links; dynamic year
- src/app/blog/page.tsx, changelog, privacy-policy, roadmap, security, terms-and-conditions, waitlist: dynamic year
- src/app/admin/wholesale/WholesaleClient.tsx: proper htmlFor/id, type=email/tel, autoComplete
- src/app/admin/settings/ai/AIClient.tsx: proper htmlFor/id, required + aria-required
- src/app/admin/settings/integrations/IntegrationsClient.tsx: only mask secret fields; add required + aria-required + CredentialField.required type
- src/app/admin/water-log/headgates/HeadgatesManager.tsx: htmlFor/id, aria-required
- src/components/admin/CreateUserModal.tsx: htmlFor/id, required, aria-required, aria-describedby, autoComplete
- src/app/admin/me/AdminMeClient.tsx, products/import, sales/import, water-log/settings, login, brands, tuxedo: a11y polish (ids/required/aria)
2026-06-03 16:39:19 +00:00
tyler 03ae372509 fix(home): resilient homepage animations + anchors + counters
Subagent 1 (Codex review pass):

LandingPageClient.tsx
- Guard GSAP .scroll-reveal loop to prevent 'missing target' warnings when no targets exist

brands/page.tsx
- Comment cleanup for selector scoping

tuxedo/page.tsx
- Add pageScopeRef + use as explicit GSAP context scope (fixes 'Invalid scope' warnings)
- Reduced-motion guard for counter animations
- Comments noting scope is now resilient

HeroSection.tsx
- Wrap sections in containerRef for proper GSAP context
- Reduced-motion support (final states shown immediately, no heavy anims)
- Counter animation: switched from textContent tween (unreliable, left values at 0) to proxy object pattern (val -> display)
- Added IO + rAF fallback if GSAP is unavailable
- Added section ids: #features, #stats, #reviews (matches nav anchors)
- Reduced blank scroll region (300vh -> 140vh on story section)
- Removed duplicate inline footer (LandingPageWrapper <Footer /> now sole source)

LandingPageWrapper.tsx
- Anchor comments noting ids added in HeroSection

Docs + scripts:
- docs/ADMIN_FUNCTIONALITY_CHECKLIST.md (admin app coverage notes)
- docs/pricing-assessment.md (pricing model notes)
- scripts/apply-admin-create-stop.js, check-stop-fns*.js, verify-stop-fns.js (admin stop creation tooling)
2026-06-03 15:36:40 +00:00
tyler 84ad60b4b0 feat(admin): comprehensive functionality pass across admin apps (Codex review)
- Restore admin order creation (top blocker):
  - New createAdminOrder server action wrapping create_order_with_items RPC with proper getAdminUser + can_manage_orders + brand scoping.
  - AdminOrdersPanel now supports ?new=true with working modal form (customer, stop/ship, dynamic items with fulfillment/price/qty, live total).
  - Fixed dashboard "New Order" and "Create your first order" links; added /admin/orders/new redirect.
  - Success flow: toast + redirect to list.

- Product edit reliability and admin flows hardened.

- Form a11y + validation foundation (cross-cutting):
  - AdminInput now generates stable ids, sets htmlFor, forwards required/aria-required/aria-describedby to children.
  - All admin forms benefit (labels programmatic, required semantics real).

- Integrations: non-secret fields (email, name, phone for Resend/Twilio) no longer masked as password by default. Only isSecret fields use type=password + toggle.

- Advanced/AI settings pages now expose real content (cards + links to actual config) instead of pure redirects.

- Permission hardening example: water-log admin creates now enforce getAdminUser + can_manage_water_log + service key.

- Systematic coverage of admin "apps": orders, products, stops, communications, wholesale, water-log, time-tracking, route-trace, settings (billing/integrations/ai/apps/etc), import, users, reports, etc. via exploration + targeted fixes. Empty states, toasts, scoping, quick actions improved.

- Updated MEMORY.md with full pass summary, test instructions, and remaining items from Codex review.

Non-destructive focus during pass; used dev auth for verification. Core blockers from review now addressed for testing.

See session plan.md for detailed execution guide.
2026-06-03 15:29:03 +00:00
tyler ba94d755fa chore: improve Supabase migrations via CLI after login and fix compatibility issues
- Update supabase/push-migrations.js:
  - Detect modern Supabase CLI link state (supabase/.temp/project-ref)
  - Prefer `supabase db query --linked --file` (works in this env where direct postgres connections fail with ENOTFOUND/network unreachable)
  - Updated docs/comments for `supabase login` + `supabase link --project-ref wnzkhezyhnfzhkhiflrp` workflow

- Add MEMORY.md capturing the Supabase login/link session, tooling changes, migration patches applied, and gotchas

- Patch migrations for current remote schema (column drift, syntax, idempotency, pre-existing tables):
  - 091_brand_plan_tier.sql: fix extra paren in get_brand_plan_info
  - 145_create_product_images_bucket.sql: allowed_mime_types + DROP POLICY IF EXISTS + safer bucket insert
  - 148_public_stops_rpc.sql: quote reserved "time" column in RETURNS TABLE + SELECT
  - 200_production_features.sql: add ALTER TABLE for user_activity_logs (originated in 036) so policies/indexes validate
  - 201_seed_data.sql: trim demo seeds with outdated column lists (products/stops/etc.); keep only compatible brands insert

- Include 202_fix_admin_create_stop.sql and related stop creation updates (src/actions/stops/create-stop.ts, 147_admin_create_stop_rpcs.sql, ADMIN_CREATE_STOP_FIX.sql)

- Update CLAUDE.md with pointer to MEMORY.md for recent migration work

Applied via the new flow (after supabase login + link): 084, 091, 142–148, 200–202 etc.

Refs: Supabase CLI now linked; use `node supabase/push-migrations.js <prefix>` or `npm run migrate:one NNN`
2026-06-03 15:11:42 +00:00
tyler f155bf6f5c Fix CRI-17: wire handleSubmitEntry to entry form, remove dead state + unused prop
WaterFieldClient.tsx:
- Wrap entry-form section in <form onSubmit={handleSubmitEntry}>
  (handler was defined but never bound to a form or button — submit
  button did nothing). Submit button already had type="submit".
- Remove unused selectedRole state + its two setSelectedRole calls
  (state was set at role-selection but never read).

wholesale/login/page.tsx:
- Remove unused 'name' prop from BrandLogo (was passed in but
  never referenced inside the component).

Fixes CRI-17.
2026-06-03 02:13:44 +00:00
tyler 1fe5ffee8d Refactor: move public storefront stop data to server-side + parallel agent work
Server-side / caching refactor (Grok):
- New RPC get_public_stops_for_brand (migration 148) for public storefront stops
- New server action getPublicStopsForBrand with revalidate=300 + tags
- Add revalidateTag invalidation to createStopsBatch + publishStop
- Convert /tuxedo/stops and /indian-river-direct/stops to Server Components
- Extract TuxedoStopsList + IndianRiverStopsList as client islands (GSAP only)
- Removes supabase-js from browser bundle on those routes
- Both pages now statically prerendered (5m ISR)

Parallel agent changes also staged:
- AI provider model list refresh (claude-sonnet-4-5, etc.)
- ESLint directive patches for react-hooks/set-state-in-effect
- Admin + storefront + checkout + cart updates
- New admin_create_stop_rpcs migration (147)
- Misc fixes across ~90 files

Build verified: typecheck clean, lint clean on new files, production build succeeds.
2026-06-03 02:04:21 +00:00
tyler 57da01c786 fix: enable Tasks and Workers tabs from URL params
Sidebar Tasks link points to /admin/time-tracking?tab=tasks
Sidebar Workers & PINs link points to /admin/time-tracking
Now TimeTrackingAdminPanel reads ?tab= URL param to set initial tab
2026-06-02 16:53:39 +00:00
tyler 056ea8f502 refactor: reorganize admin sidebar menu
New order:
- Dashboard, Orders, Stops & Routes, Products, Communications
- Settings section:
  - General
  - Workers & PINs
  - Tasks
  - Users & Permissions
  - Integrations
  - Billing

Removed: Route Trace, Time Tracking, Add-ons, AI, Shipping, Square Sync (moved to Integrations or removed)
Added icons for users and shield
2026-06-02 16:42:52 +00:00
tyler 12947b88ba Make CreateUserModal mobile responsive
- GlassModal: Add responsive padding, max-height with scroll, flex layout
- CreateUserModal: 2-col layout for name/phone on tablet+, tighter mobile spacing, full-width buttons stacked on mobile
2026-06-02 16:34:21 +00:00
tyler 2c95ce9a19 fix: remove duplicate headers and unused theme toggle
- Providers now excludes login page from SiteHeader/SiteFooter
- Removed ThemeToggle from SiteHeader (not being used)
- Login page uses fixed viewport height to prevent scrolling
- DemoMode also uses fixed viewport height
2026-06-02 16:31:40 +00:00
tyler 1bf8f32525 feat: simplify SiteHeader and add glass effect
- Matching landing page design aesthetic (warm cream)
- Added glass/blur backdrop effect to header
- Removed Cart button (not needed for this B2B platform)
- Links now use uppercase + letter-spacing for refined look
- Shows brand name when on brand routes
- Uses Route Commerce logo + font styling
2026-06-02 16:22:37 +00:00
tyler 6feac9bb9a feat: redesign login page to match landing page
- Matched landing page design aesthetic (warm cream tones, organic backgrounds)
- Added header with Route Commerce logo and back link
- Added footer with logo and minimal links
- Added security trust badges section:
  - 256-bit SSL
  - SOC 2 compliance
  - Powered by Supabase
- Clean typography with Cormorant Garamond + Plus Jakarta Sans
- Subtle animations on page load
- Demo mode also styled to match
2026-06-02 16:16:17 +00:00
tyler c5509f53f9 feat: add Header with Sign In to landing page
- Wrapped LandingPageClient with LandingPageWrapper
- Header now shows Sign In button next to Get Started
- Mobile menu also includes Sign In option
- Fixed Plus Jakarta Sans font import
2026-06-02 16:07:43 +00:00
tyler f55f0551f9 clean: minimal, modern footer design
- Redesigned landing page footer with cleaner layout
- Added subtle tagline 'Fresh produce, delivered fresh'
- Links now use uppercase + letter-spacing for refined look
- Better spacing (py-12 vs py-8) and gap management
- Official page links instead of hash anchors
- Copyright on separate line for better hierarchy
- Consistent with overall minimal aesthetic
2026-06-02 16:00:37 +00:00
tyler 47ac74329f fix: counter animations and add footer with legal links
- Fixed counter-animate to use gsap.fromTo with proper textContent interpolation
- Added footer section with logo, privacy/terms/security/contact links
- Added copyright notice with dynamic year
2026-06-02 15:53:28 +00:00
tyler 7a9d74504b fix: resolve ParallaxLayer/FadeOnScroll JSX issues, remove large black section 2026-06-02 15:44:27 +00:00
tyler 2db3375f87 feat: Apple-style scroll-driven landing page
- Add ScrollAnimations component (ScrollReveal, ParallaxLayer, FadeOnScroll)
- Add CinematicShowcase for scroll-driven product reveals
- Update Tuxedo page with cinematic product showcase and parallax layers
- Update main HeroSection with same premium scroll animations
- Add scroll progress bar, staggered reveals, counter animations
- Apply consistent Apple-style UX across landing and Tuxedo pages

Features:
- Sections pin and reveal content progressively on scroll
- Parallax depth effects on decorative elements
- Counter animations triggered by scroll
- Smooth hover states and transitions
- GSAP + Framer Motion for smooth animations
2026-06-02 15:41:48 +00:00
tyler bf4dc09e65 fix: restore full landing page with FeaturesAndStats and TestimonialsAndCTA 2026-06-02 15:29:39 +00:00
tyler 344c8741da feat: cinematic scroll-driven landing pages with GSAP ScrollTrigger
- Enhanced HeroSection with pinned scroll animations, parallax layers,
  sticky storytelling sections, and scroll progress indicator
- Rewrote TuxedoVideoHero with GSAP scroll-driven parallax video scaling,
  floating decorative orbs, and animated content reveals
- Added scroll-triggered reveals (.reveal-text, .reveal-scale, .reveal-slide)
  to Tuxedo Corn index page with counter animations
- Applied Apple-style hero sequences with smooth scrub animations
- Integrated scroll timeline with progress bar showing scroll position
2026-06-02 15:23:44 +00:00
tyler 72089d7c19 feat(storefront): add stops listing pages for both brands
- Create /tuxedo/stops page showing all Tuxedo Corn pickup stops
- Create /indian-river-direct/stops page showing all Indian River Direct stops
- Each page features: upcoming/past stop separation, date badges, GSAP animations
- Link to individual stop detail pages
- Orange accent for IRD, emerald accent for Tuxedo
2026-06-02 15:17:29 +00:00
tyler 6ec4d78404 refactor(brands): compact side-by-side card layout
- Switch from stacked vertical cards to 2-column grid
- Reduce card padding and font sizes for compact display
- Add colored accent bar at top of each card
- Shrink header, hero, and footer sections
- Both brands now fit above the fold without scrolling
2026-06-02 15:11:10 +00:00
tyler 71e0aa6f3b feat(landing): enhance hero with vibrant colorful design
- Add gradient background with warm peachy/golden tones
- Add 4 large colorful decorative blobs (coral, mint, amber, lavender)
- Add colorful accent dots scattered around hero
- Enhance grid pattern with blue-tinted color
- Add colorful gradient divider before CTAs
- Give trust indicator cards unique gradient backgrounds (mint, amber, coral)
- Enhance floating stat cards with vibrant gradients (pink, teal, amber)
- Update pulse glow animation colors to match theme

Adds energy and visual depth while maintaining professional appearance
2026-06-02 15:06:02 +00:00
tyler 4095c62011 refactor(admin): compact UI styling for data-dense admin interface
- Reduce table cell padding (px-5 py-4 → px-3 py-2) across OrderTableBody, ProductTableBody, StopTableBody, AdminTable
- Narrow sidebar (w-60 → w-56) with smaller nav items
- Reduce page/card padding in admin-design-system.css
- Smaller default buttons (md → sm) in AdminButton
- Smaller page headers (text-2xl → text-xl, w-12 → w-10 icon)
- Tighter cards and filter tabs

Makes admin UI feel like professional B2B tool at 100% zoom
2026-06-02 14:55:42 +00:00
tyler f53f72e5a2 feat(admin): add Compose and Settings tabs to Harvest Reach communications
- Add Compose tab that displays CampaignComposerPage with 4-step wizard
- Add Settings tab linking to existing /admin/communications/settings
- Complete the 8-tab navigation structure for Harvest Reach module

Tabs: Campaigns, Compose, Segments, Analytics, Templates, Contacts, Logs, Settings
2026-06-02 14:49:13 +00:00
tyler 12f9b2da99 admin: polish route trace page to match design system 2026-06-02 14:36:22 +00:00
tyler d031a9783c Polish pass: micro-interactions on admin and storefront pages
Admin design system:
- AdminCard: hover shadow elevation
- AdminTable: enhanced empty state, row hover
- AdminBadge: tracking-wide typography
- AdminButton: active scale press feedback
- AdminFormElements: input transitions
- AdminFilterBar: search input polish
- PageHeader: icon hover scale, chevron separator
- AdminToggle: glow effect, thumb shadow
- AdminEmptyState: icon hover, spacing
- AdminPagination: active solid bg, ellipsis

Storefronts (tuxedo + indian-river-direct):
- Button hover lifts (-translate-y-0.5)
- Shadow elevations on hover
- Button press feedback
- Consistent timing (duration-200)
- Product card image zoom
- FAQ empty state fix
2026-06-02 14:29:27 +00:00
tyler 8dee63fef2 feat: wire up real Supabase data to analytics dashboard
- Create src/actions/analytics.ts with real data fetching:
  - getAnalyticsMetrics() - revenue, orders, customers, AOV
  - getRevenueChart() - daily revenue data
  - getTopProducts() - product performance from reports RPC
  - getRecentOrders() - live orders from orders table
  - getCustomerGrowth() - contact growth stats
  - getConversionFunnel() - computed from order data

- Rewrite AnalyticsDashboard.tsx to:
  - Fetch real data from Supabase via brand-scoped RPCs
  - Show loading states and error handling
  - Display actual metrics from the database
  - Remove all mock data

- Add src/app/admin/analytics/page.tsx server component

All analytics now pull from real Supabase data instead of mock values.
2026-06-02 06:38:44 +00:00
tyler bcbf7be84f fix: lazy-initialize Stripe client to prevent build-time errors
Stripe SDK was being initialized at module load time, which failed
during Next.js build when STRIPE_SECRET_KEY wasn't set in the build
environment. Changed to lazy initialization pattern with proxy object
that creates the client on first access.

Fixes: Error: Neither apiKey nor config.authenticator provided
2026-06-02 06:30:47 +00:00