Repository navigation
Static-data audit: wire remaining fakes to Supabase (assistant, reminders) - #14
Merged
Merged
Conversation
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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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):
9:16 AM,Yesterday) → realcreated_atviafmtRelative.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/placeholdermatches 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