Repository navigation
Marketing: AI assistant + channels & builders (SMS, social, ads, automation, funnels, landing pages, forms) - #13
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
|
Deployment failed with the following error: Learn More: https://vercel.link/3Fpeeb1 |
…kmptm # Conflicts: # app/(app)/admin/marketing/actions.ts # app/(app)/admin/marketing/layout.tsx # app/(app)/admin/marketing/page.tsx # lib/constants/navigation.ts
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 3, 2026
Conflict detection only warned on the Moments page, but Home is where families glance. The Home "Get ready" banner now shows the same amber "Overlaps Sam's recital (+N)" chip when the most imminent moment double-books — reusing findOverlaps over the calendar rows. Birthdays (synthetic) never clash. Updated the Opportunity Register (#13) and Scorecard (Home now lists double-booking warnings). Verified: tsc clean, eslint clean on changed files, 1542 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
#13) FamilyWall's headline offline behavior, delivered read-side without a service-worker rearchitecture: - lib/offline/cache.ts: localStorage read-cache — per family+table+query keys (djb2 of deps), 7-day TTL, 200-row cap, quota-safe with oldest-first eviction, injectable storage. 4 tests. - useRealtimeQuery (every realtime module rides this): hydrates from the cache for instant paint, writes through on successful fetch, suppresses the error banner while offline (shows last-synced data quietly), and refetches automatically on the `online` event. - OfflineBanner in the app shell: amber "you're offline — showing your last-synced data" while down, brief green "back online — syncing" flash on reconnect. - Privacy: clearAllCache() wipes every cached row on sign-out. - v2 (offline mutations + conflict resolution) logged as owner-scoped.
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 12, 2026
Enriches leads/users one question at a time instead of a wall of fields, building on the identity stitch (#6) that links users to crm_contacts. - lib/marketing/progressive-profile.ts: pure engine — an ordered question set (role → priority → household → kids → interests) with next-question, completeness, and answer validation. 10 tests. - 0171_crm_contact_profile: structured per-contact profile (extra.skipped[] remembers dismissed questions). Owner-scoped RLS via crm_contacts.owner_id; updated_at trigger. - Server actions resolve/create the signed-in user's crm_contacts lead (service-role, identity-verified) and upsert answers; feeds segmentation. - ProfileNudge card on /dashboard/settings: one question at a time, choice saves on tap / multi on confirm, Skip + Dismiss, a completeness bar. - seed_crm_contact_profile.sql: 500 rows across varied completeness, in SEED_ALL.sql. PG16-verified: migration idempotent x2, RLS policy + trigger, seed 500 x2. tsc/eslint/build green; full suite 2433 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
Enriches leads/users one question at a time instead of a wall of fields, building on the identity stitch (#6) that links users to crm_contacts. - lib/marketing/progressive-profile.ts: pure engine — an ordered question set (role → priority → household → kids → interests) with next-question, completeness, and answer validation. 10 tests. - 0171_crm_contact_profile: structured per-contact profile (extra.skipped[] remembers dismissed questions). Owner-scoped RLS via crm_contacts.owner_id; updated_at trigger. - Server actions resolve/create the signed-in user's crm_contacts lead (service-role, identity-verified) and upsert answers; feeds segmentation. - ProfileNudge card on /dashboard/settings: one question at a time, choice saves on tap / multi on confirm, Skip + Dismiss, a completeness bar. - seed_crm_contact_profile.sql: 500 rows across varied completeness, in SEED_ALL.sql. PG16-verified: migration idempotent x2, RLS policy + trigger, seed 500 x2. tsc/eslint/build green; full suite 2433 passing. Claude-Session: https://claude.ai/code/session_01VJBpzPzPrBbMzYFSVCQaTM Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Jul 12, 2026
NewWorldVenture
pushed a commit
that referenced
this pull request
Oct 11, 2026
…tion receipts and delivery, one civil-time rule Coordinator 6103529789 items 1-4 and the verified follow-up findings on #1004. T1 (audit #18) app/api/vacations/ai/route.ts build: after generation the route claims the trip with a compare-and-set on vacations.updated_at as read before the model call (409 code trip_changed for the loser), re-reads the plan (an overtaken first build answers 409 has_plan), and fences every rebuild delete at write time on booked=false, the builder stamp and the updated_at the row had when the family asked. A row booked, un-stamped or edited during generation or the write phase survives; rollback restores exactly what the delete returned. Residual (needs SQL): a request that read the trip after another's claim while that one was still writing is not fenced; pinned in tests/a-trip-rebuild-keeps-what-changed-meanwhile.test.ts. T2 (audit #12) a repeated disruption returns no moves and the recorded summary; the action passes alreadyApplied/previouslyNotified and the receipt says "already on the itinerary" instead of an 18:00->20:00 move that never happened. T3 (audit #12/#1) notification delivery is tracked apart from the re-flow: a "[family notified]" stamp on the disruption note, written after notify() accepts; notify() is keyed per disruption (trip:disruption:note) with once, so a failed or interrupted notice is sent by the retry exactly once and the itinerary does not move again. T4 (audit #7/#8/#10/#13/#16) lib/time/civil.ts: one grammar and one rule (RFC 5545 3.3.5, the calendar's) for datetime-local inputs, reminders and school/sports windows: fractions read on the family clock, impossible dates refused, seconds kept, folds -> first instant everywhere, gaps -> offset before the gap, a midnight-gap day starts at the jump (America/Santiago, Asia/Beirut). fetchForecast treats a missing end_date as start_date. L-trips-1 (#18) buildPlan stamps the concierge marker; the 409 says whether a rebuild replaces anything and the client asks "add alongside?" otherwise. L-trips-2 (#17) disruption rollback deletes only created days that hold no item (day_id is ON DELETE CASCADE). L-trips-3 (#11) trip zones stored by canonical IANA name (create form, editor, findOrCreateVacation); abbreviations and offsets refused. L-trips-4 (#13) overview weather card reads only in-trip days for both its empty state and its advice. L-trips-5 (#22) a first concierge message creates its conversation only after the model answered. L-trust-1 (#2/#6) dryRunGate and execute.ts gate() share readSkipsTrustGate; the dry run passes sensitiveRead. L-trust-2 (#6) insights kinds school/sports/trips and household search's trip sources follow the same view rule; the "private to the adults" comments now say what is true. L-trust-3 (#2) the gate comment names which household rules can lift a private-read denial for the AI actor (role/everyone/AI policies, not per-member grants or delegations). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KP9rt5rVQ9jDMpZp2xBy3K
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
Completes the Marketing module with the AI Marketing Assistant and the remaining channels & builders, all Supabase-backed and admin-only.
AI Marketing Assistant
/admin/marketing/assistant+POST /api/admin/marketing/ai. Uses the existing provider-agnostic AI layer, grounded in live customer/segment/campaign data. Tasks: analyze customers, next best actions, campaign/SEO/AEO plans, content calendar, and email/SMS/ad/landing copy drafts. Admin-only and audit-logged; the prompt forbids fabricated metrics/rankings/testimonials.Channels & builders (migration
0014, 9 tables)All tables use RLS-enabled-no-policies (admin/service-role only), with FKs, indexes, check constraints, and
updated_attriggers. Every server action re-verifies super admin and writes the marketing audit log.Tests / gates
21 tests pass; typecheck, lint, and build (all marketing routes) green.
Before production use
Apply migrations
0013and0014. Provider-backed channels (email/SMS/ads/social) need their API keys; UIs degrade to honest setup states without them.🤖 Generated with Claude Code
https://claude.ai/code/session_01BVdGmgvtEjp4ZSqcES7ThJ
Generated by Claude Code