Skip to content

Static-data audit: wire remaining fakes to Supabase (assistant, reminders) - #14

Merged
NewWorldVenture merged 14 commits into
mainfrom
claude/funny-darwin-gkmptm
Jun 20, 2026
Merged

NewWorldVenture merged 14 commits into
mainfrom
claude/funny-darwin-gkmptm

Conversation

@NewWorldVenture

Copy link
Copy Markdown
Owner

Summary

Static-data audit across every page/module. The app is overwhelmingly Supabase-wired already; this removes the remaining production-facing fakes.

Fixed

Assistant module (main offenders):

  • Hardcoded "Medication Reminder: 1" and "72°F Partly Cloudy" glance tiles → real Reminders Due and Active Meds counts from Supabase.
  • Recent-activity timestamps were hardcoded (9:16 AM, Yesterday) → real created_at via fmtRelative.
  • "Smart Suggestions" showed fabricated personalized insights ("Emma has a science project due tomorrow…") → honest generic prompt starters ("Try Asking"); removed dead "View All" button.
  • Time-aware greeting instead of fixed "Good morning".

Reminders module: relabeled a static template list from "AI Suggestions / AI Suggested Reminders" → honest "Quick Add / Common Reminders" (they're quick-add templates, not AI output).

Audit result

All feature modules (contacts, messages, photos, recipes, todos, reminders, shopping, etc.) and dashboards query Supabase. Remaining mock/sample/placeholder matches are input placeholders, example prompts, config constants, design tokens, or legitimate empty states — not fake data.

Honest note

This is a static + targeted-runtime audit (typecheck, lint, build, 80 unit tests — all green). I can't run live RLS/E2E/route-crawl here without a deployed Supabase, so I won't claim a blanket runtime "PASS"; production-facing static data has been removed and wired.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ


Generated by Claude Code

claude added 14 commits June 20, 2026 02:39
QA pass across the app. The codebase is largely Supabase-wired already;
this removes the remaining fabricated/static content and one real bug,
and adds a runnable unit-test suite.

Marketing (visual-mocks.tsx, pricing-content.tsx):
- Remove fabricated testimonials (fake names + 5-star ratings) and the
  unsubstantiated "As seen in Forbes/TechCrunch/..." press row.
- Replace with honest, benefit-driven copy backed by real product facts.
- Drop fake fallback counts ("10,000+ families") in favour of real,
  number-free copy when there is no data; remove now-unused Rating.

Dashboard (dashboard/page.tsx):
- Remove the hardcoded fake weather widget (72°F / Partly Cloudy).
- Make AI Assistant suggestions fully data-driven (derived from real
  open tasks, meal plans, upcoming events, birthdays) instead of static.
- Fix "Tasks Due" rendering the literal string "Task": now resolves and
  shows real chore titles via a typed lookup.

Security (.env.example):
- Replace committed live secrets (Stripe sk_live, Supabase service-role,
  Stripe webhook secret, Google client secret) with placeholders.
  NOTE: these values are in git history and must be rotated.

Tests:
- Add vitest config + unit tests for marketing count formatters and role
  helpers (8 tests) guarding the "no fabricated numbers" and access-gating
  behaviour. npm test now runs green.

Verified: typecheck, lint, build, and tests all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
The app had three conflicting pricing models. Only one product is actually
wired to Stripe (STRIPE_PRICE_FAMILY_MONTHLY/ANNUAL → checkout → webhook),
so everything is now aligned to that single source of truth.

lib/constants/plans.ts — rewritten as the canonical source:
- Slugs now match exactly what the webhook writes: free | family |
  family_annual (removed the stale free/family/family_plus model whose
  env vars NEXT_PUBLIC_STRIPE_PRICE_* never existed).
- Real prices: $9.99/mo, $95.99/yr; exports planMonthlyCents()/planName().

Admin (page.tsx, reports/page.tsx):
- Replaced two divergent, hardcoded price tables (plus=$19, family=$9)
  with the shared helpers. Fixes a real bug: annual subscriptions
  (slug 'family_annual') matched no plan id and were counted as $0 MRR.
- Updated plan-colour maps to the real plan names.

Marketing pricing page:
- Removed three advertised tiers (Plus $6.99 / Family $12.99 / Family+
  $19.99) that could NOT be purchased — checkout only supports the one
  Family product. Replaced with the real plan and an accurate monthly/
  annual toggle ($9.99/mo vs $8.00/mo billed annually, computed savings).
- Dropped the unverifiable "14-day refund guarantee" (→ honest "cancel
  anytime", which the Stripe portal actually supports) and the
  "iOS & Android Apps" claim (mobile is roadmap; it's a PWA).

Tests:
- tests/plans.test.ts (6) guards the source of truth: annual is non-zero
  MRR, free/unknown/null = $0, labels resolve, legacy slugs are gone.

Verified: typecheck, lint, build (56/56 pages), and 14 unit tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
…/delete)

The module had a complete storage helper (lib/storage/documents.ts) that was
never used. Now wired end to end:
- Upload: real file input + drag/drop → uploadFamilyDocument() into the private
  family folder, then records storage_path, size_bytes, mime_type. Rolls back
  the uploaded object if the DB insert fails (no orphans).
- Download: getDocumentSignedUrl() opens a short-lived signed URL.
- Delete: removes the storage object before deleting the row.

Removed fabricated metrics (the real issue here):
- "Total Documents" no longer floors to 24; "Folders" shows real count;
  "Shared" is now the real count of family-shared docs; "Storage Used"
  shows real bytes ('0 MB' when empty, not '1.2 GB').
- Storage donut no longer defaults to 24%; per-category breakdown uses real
  bytes (top 5) instead of a hardcoded example; sidebar Used/Free are real.

Removed dead UI: non-functional tab bar, Filter/More buttons, two no-op
"View all" links, and placeholder Quick Actions / "Ask AI" — replaced with
real actions and links to the AI assistant. fmtSize now handles GB.

Verified: typecheck, lint, build, 14 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
New /dashboard/inbox: paste a forwarded email, text, school notice, or
flyer text and AI extracts every actionable item, then writes confirmed
items straight to the family's Supabase data.

- /api/ai/import (two-phase): phase 1 parses text via getProvider() +
  AI_TOOLS into proposed actions with human summaries; phase 2 executes
  the user-confirmed items through runAction() (RLS-scoped to the family).
  Reuses the existing AI tool/action bridge — events, chores, reminders,
  grocery items, and meal plans. Rate-limited; relative dates resolved
  against today.
- inbox-module: paste box, one-click "Organize with AI", a checklist of
  proposed items (toggle/select), and "Add N items". Real loading/empty
  states and example prompts.
- Added "Magic Import" to the app nav.

Verified: typecheck, lint, build, 14 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
New /dashboard/scan: photograph or upload a school flyer, team schedule,
invitation, or PDF and Claude vision extracts the events; confirmed events
are written to calendar_events (RLS-scoped to the family).

- /api/ai/flyer (two-phase): phase 1 sends the image/PDF to Claude as a
  vision/document block and parses a strict JSON event list (resolving
  partial dates against today, normalizing to ISO + all-day handling);
  phase 2 inserts the confirmed events with title/time/location/category.
  Rate-limited, size-capped (~5 MB), images + PDF supported.
- scan-module: camera/upload with drag-drop, live preview, "Reading flyer…"
  state, and a toggle list of detected events before adding.
- Added "Scan Flyer" to the app nav; cross-links to Magic Import.

Verified: typecheck, lint, build, 14 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
New /display: a full-bleed, no-shell kiosk view for a kitchen tablet —
competes with Skylight without any hardware. Live, auto-refreshing family
overview pulled straight from Supabase (RLS-scoped):
- Family name + member avatars, live clock/date, birthday banner.
- Today's schedule (large, with assignee avatars), chores due today (with
  who + submitted state), today's planned meals, and grocery item count.
- AutoRefresh re-pulls server data every 60s; live client clock ticks each
  second. Exit button returns to the dashboard.
- Added "Kitchen Display" to the app nav. Route is protected by middleware
  and renders outside the dashboard shell for true kiosk display.

Verified: typecheck, lint, build, 14 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
…ine)

The notifications table was only ever displayed; nothing generated smart
alerts. New deterministic engine surfaces who-needs-to-know items from real
upcoming family data and writes them to notifications (per-member, mapped to
the affected member's user account):
- lib/server/notifications.ts: scans next 24–48h of calendar events,
  chores due, school + sports events, reminders, and 14-day document
  expiries; targets the right member (or whole family / managers for docs)
  and dedupes against notifications already created for each item.
  Rule-based on purpose — alerts must be trustworthy, not hallucinated.
- POST /api/notifications/generate: user-triggered scan for the active
  family (RLS-scoped).
- GET /api/cron/notifications: runs the engine for every family
  (service role); scheduled twice daily in vercel.json.
- Notifications module: "Scan for updates" button; realtime list already
  shows them and marks read/delete.

Verified: typecheck, lint, build, 14 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
Surfaces the new AI features from the main dashboard: Magic Import, Scan
Flyer, Daily Briefing, AI Assistant, and Kitchen Display.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
A real, admin-only Marketing module built on the existing FamilyOS data
(families, subscriptions, billing, profiles, support tickets, blog) plus
new marketing tables. Adapted honestly to this schema — no donor/business/
fundraising entities are invented, and provider-dependent channels show
setup states instead of fake metrics.

Schema (migration 0013_marketing.sql): marketing_segments, _campaigns,
_email_campaigns, _content_items, _seo_pages, _seo_keywords,
_aeo_questions, _settings, _audit_logs — with FKs, indexes, check
constraints, unique constraints, updated_at triggers, and RLS enabled with
no policies (admin/service-role only, matching support_tickets/super_admins).
Types added to lib/database.types.ts.

Routes (all under the super-admin-gated /admin shell, force-dynamic):
- dashboard: real metrics (customers, new, lapsed, est. MRR/LTV, segments,
  campaigns, email open rate, leads), lifecycle breakdown, and deterministic
  recommended next actions.
- customers: normalized marketing-customer view derived live from families+
  subscriptions+members+profiles (lifecycle, est. LTV), with filter/search.
- segments: dynamic segment builder with live preview counts (pure,
  tested engine) + create/archive.
- campaigns: list / new / [id] with status workflow and audience sizing.
- email: drafts + provider (Resend) setup state — never fakes sends.
- content: pipeline + published blog integration.
- seo: first-party signals (pages, sitemap, robots) + keyword tracking;
  AI ideas clearly labeled, no invented rankings/volume.
- aeo: question/answer opportunities + readiness scoring.
- analytics: acquisition, channel mix, email perf, est. revenue — real data.
- audit: marketing_audit_logs.
- settings: provider status + editable settings.

Access control: every server action re-verifies super admin
(requireMarketingAdmin) and writes marketing_audit_logs. Added "Marketing"
to the admin nav.

Tests: 7 new (segment engine + customer summary); server-only stubbed for
vitest. Typecheck, lint, build (13 marketing routes), and 21 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
…kmptm

# Conflicts:
#	lib/constants/navigation.ts
#	vitest.config.ts
New /admin/marketing/assistant + POST /api/admin/marketing/ai. Uses the
existing provider-agnostic AI layer to generate marketing work grounded in
real customer/segment/campaign data:
- Tasks: analyze customers, next best actions, campaign plan, SEO plan,
  AEO plan, content calendar, and email/SMS/ad/landing copy drafts.
- Context is built from getMarketingCustomers + summarizeCustomers + live
  segments/campaigns; prompt forbids fabricated metrics/rankings/testimonials.
- Admin-only (requireMarketingAdmin); every generation is audit-logged.
- Client page with task picker, input, copy-to-clipboard, loading/error states.
- Added "AI Assistant" to the marketing sub-nav.

Verified: typecheck, lint, build, 21 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
… funnels, landing pages, forms)

Adds the remaining marketing surfaces on top of the module, all Supabase-backed
and admin-only. Migration 0014_marketing_channels.sql adds 9 tables
(marketing_sms_campaigns, _social_posts, _ad_campaigns, _automation_workflows,
_automation_runs, _funnels, _landing_pages, _forms, _form_submissions) with
FKs, indexes, check constraints, updated_at triggers, and RLS-on-no-policies.
Types added to lib/database.types.ts.

Pages (under the super-admin-gated marketing shell, force-dynamic):
- SMS: drafts + Twilio setup state + consent/opt-out guidance (no fake sends).
- Social: multi-platform post planner/scheduler (no fabricated engagement).
- Ads: paid-campaign planner with budget/spend tracking (manual/imported,
  never invented).
- Automation: trigger → ordered-action workflow builder with status controls.
- Funnels: step-by-step funnel definitions.
- Landing pages: draft/publish landing page records with view/conversion fields.
- Forms: lead-capture form builder; submissions persist and can feed
  segments/automations.

Server actions for each are admin-guarded (requireMarketingAdmin) and
audit-logged. Sub-nav updated with all new sections.

Verified: typecheck, lint, build (all marketing routes), 21 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
…kmptm

# Conflicts:
#	app/(app)/admin/marketing/actions.ts
#	app/(app)/admin/marketing/layout.tsx
#	app/(app)/admin/marketing/page.tsx
#	lib/constants/navigation.ts
Static-data audit pass across all pages/modules. The app is overwhelmingly
Supabase-wired already; this removes the last production-facing fakes:

Assistant module (the main offenders):
- Replaced hardcoded "Medication Reminder: 1" and "72°F Partly Cloudy"
  glance tiles with real "Reminders Due" and "Active Meds" counts queried
  from Supabase (reminders, medications).
- Recent-activity timestamps were hardcoded ('9:16 AM', 'Yesterday') — now
  use the real created_at via fmtRelative.
- "Smart Suggestions" presented fabricated personalized insights about the
  family ("Emma has a science project due tomorrow…"). Replaced with honest,
  generic prompt starters ("Try Asking"); removed the dead "View All" button.
- Time-aware greeting instead of a fixed "Good morning".

Reminders module: relabeled static template list from "AI Suggestions /
AI Suggested Reminders" to honest "Quick Add / Common Reminders" (they are
quick-add templates, not AI-generated insights).

Audit confirmed all feature modules (contacts, messages, photos, recipes,
todos, reminders, shopping, etc.) and dashboards query Supabase; remaining
matches for mock/sample/placeholder are input placeholders, example prompts,
config constants, design tokens, or legitimate empty states.

Verified: typecheck, lint, build, 80 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
@vercel

vercel Bot commented Jun 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
familyos Building Building Preview, Comment Jun 20, 2026 6:25pm

@NewWorldVenture
NewWorldVenture merged commit f4c5af1 into main Jun 20, 2026
1 of 2 checks passed
NewWorldVenture pushed a commit that referenced this pull request Jul 3, 2026
…/ Later

With 30-day birthdays plus events, the Moments page was one long undifferentiated
grid. Now it reads as a calm, scannable timeline with section headers + counts,
so "what needs me today" is instantly separable from "later".

- Pure lib lib/moments/grouping.ts (tested, 6 cases): momentBucket (whole-day
  distance → today/tomorrow/this-week/later) + groupMoments (non-empty buckets
  in fixed soonest-first order, input order preserved within each).
- MomentsView renders one grid per non-empty bucket under a labeled header.

Verified: tsc clean, eslint clean on changed files, 1548 tests, next build
exit 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qnr3AA4xBTeaJ4Lrm41o7J
NewWorldVenture pushed a commit that referenced this pull request Jul 12, 2026
The visitor-intelligence signals existed but there was no visible score. This
scores every CRM contact 0-100 with a fully itemized ledger — every point is
an attributable, named factor, never a black box.

- lib/marketing/contact-score.ts: pure scorer (site engagement, recency,
  conversions, demo, marketing/analytics consent, profile depth, lifecycle) →
  { score, band, factors[] }, capped at 100, banded cold/warm/hot/qualified.
  7 tests. Named distinctly from the existing ticket scoreLead.
- lib/marketing/contact-score-compute.ts: gathers the signals per contact
  (mkt_visitors, mkt_touchpoints conversions, mkt_consent_events, demo_email_uses,
  crm_contact_profile) and upserts; recompute one / all.
- 0172_crm_lead_scores: persisted score + band + factors ledger, indexed by
  score; admin/service-role only (RLS on, no public policy).
- Admin /admin/marketing/lead-scores (super-admin): ranked contacts, band chips,
  band stat tiles, expandable "why" ledger, and a Recompute action. Added to the
  marketing sub-nav.
- seed_crm_lead_scores.sql: 500 rows across all four bands with ledgers that sum
  exactly to the score, in SEED_ALL.sql.

PG16-verified: migration idempotent x2, RLS + index, seed 500 x2, ledger sums
to score (0 mismatches). tsc/eslint/build green; full suite 2440 passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VJBpzPzPrBbMzYFSVCQaTM
NewWorldVenture added a commit that referenced this pull request Jul 12, 2026
The visitor-intelligence signals existed but there was no visible score. This
scores every CRM contact 0-100 with a fully itemized ledger — every point is
an attributable, named factor, never a black box.

- lib/marketing/contact-score.ts: pure scorer (site engagement, recency,
  conversions, demo, marketing/analytics consent, profile depth, lifecycle) →
  { score, band, factors[] }, capped at 100, banded cold/warm/hot/qualified.
  7 tests. Named distinctly from the existing ticket scoreLead.
- lib/marketing/contact-score-compute.ts: gathers the signals per contact
  (mkt_visitors, mkt_touchpoints conversions, mkt_consent_events, demo_email_uses,
  crm_contact_profile) and upserts; recompute one / all.
- 0172_crm_lead_scores: persisted score + band + factors ledger, indexed by
  score; admin/service-role only (RLS on, no public policy).
- Admin /admin/marketing/lead-scores (super-admin): ranked contacts, band chips,
  band stat tiles, expandable "why" ledger, and a Recompute action. Added to the
  marketing sub-nav.
- seed_crm_lead_scores.sql: 500 rows across all four bands with ledgers that sum
  exactly to the score, in SEED_ALL.sql.

PG16-verified: migration idempotent x2, RLS + index, seed 500 x2, ledger sums
to score (0 mismatches). tsc/eslint/build green; full suite 2440 passing.


Claude-Session: https://claude.ai/code/session_01VJBpzPzPrBbMzYFSVCQaTM

Co-authored-by: Claude <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — f234895c Deployed Jun 20, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants