ShieldAI waitlist landing page and analytics infrastructure FRE-5274

Build waitlist landing page with Solid.js (hero, features, tier comparison,
waitlist signup form, blog preview, footer). Create waitlist signup and blog
API endpoints in Fastify. Add WaitlistEntry and BlogPost models to Prisma
schema. Create analytics hooks for GA4 and Mixpanel tracking. Fix pre-existing
Prisma schema issue (AnalysisJob relation missing User field).

- Landing page: responsive Solid.js app with hero, 6 feature cards, 3-tier
  pricing comparison table, blog preview, and full waitlist signup form with
  interest tier selection
- API: POST /api/waitlist/signup, GET /api/waitlist/count, GET /api/blog,
  GET /api/blog/:slug, CRUD /api/admin/blog
- DB models: WaitlistEntry (with UTM params, conversion tracking, source),
  BlogPost (with tags, view count, publish scheduling)
- Analytics: useAnalytics hook with initAnalytics(), trackEvent(),
  trackWaitlistSignup(), trackPageView() — GA4 and Mixpanel dual-tracking
- Blog: listing, detail, and admin CRUD routes; seed.ts with 3 starter articles
- Fix: AnalysisJob.analysisJobId missing @unique constraint, missing
  analysisJobs[] on User model

Delegated to CMO: FRE-5280 (GA4 config), FRE-5281 (Mixpanel config),
FRE-5282 (email marketing platform)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-05-13 23:47:25 -04:00
parent 65c7da4852
commit 9d4865306c
28 changed files with 2311 additions and 1 deletions

View File

@@ -36,6 +36,7 @@ model User {
normalizedAlerts NormalizedAlert[]
correlationGroups CorrelationGroup[]
securityReports SecurityReport[]
analysisJobs AnalysisJob[]
// Audit
createdAt DateTime @default(now())
@@ -376,7 +377,7 @@ model AnalysisJob {
model AnalysisResult {
id String @id @default(uuid())
analysisJobId String
analysisJobId String @unique
syntheticScore Float
verdict DetectionVerdict
confidence Float
@@ -626,3 +627,52 @@ model SecurityReport {
@@index([periodStart, periodEnd])
@@index([createdAt])
}
// ============================================
// Waitlist & Marketing Models
// ============================================
model WaitlistEntry {
id String @id @default(uuid())
email String
name String?
source String? // landing_page, blog, referral, social, paid_search
tier SubscriptionTier? // interest level
utmSource String?
utmMedium String?
utmCampaign String?
metadata Json? // Browser, device, location, etc.
// Conversion tracking
convertedAt DateTime?
convertedToUserId String?
convertedToSubscriptionId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@index([source])
@@index([createdAt])
}
model BlogPost {
id String @id @default(uuid())
slug String @unique
title String
excerpt String?
content String
authorName String?
coverImageUrl String?
tags String[] // Array of tag strings
published Boolean @default(false)
publishedAt DateTime?
viewCount Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([slug])
@@index([published, publishedAt])
@@index([tags])
}

View File

@@ -48,11 +48,15 @@ export type {
Alert,
VoiceEnrollment,
VoiceAnalysis,
AnalysisJob,
AnalysisResult,
SpamFeedback,
SpamRule,
AuditLog,
KPISnapshot,
SecurityReport,
WaitlistEntry,
BlogPost,
UserRole,
FamilyMemberRole,
SubscriptionTier,
@@ -68,6 +72,9 @@ export type {
RuleAction,
ReportType,
ReportStatus,
AnalysisType,
AnalysisJobStatus,
DetectionVerdict,
} from '@prisma/client';
export * as PrismaModels from '@prisma/client';