Appearance
Frontend redesign → current dev architecture map
Read-only map for the supplied frontend worktree. No repository files were changed.
- Current dev:
/home/kirbysmashyeet/.treehouse/BloxClips-frontend-207dad/5/BloxClips-frontend,7c3f0079f7a4847ff698c508470757ca81e64b36(origin/dev), clean at inspection. - Main snapshot:
ad33ecb44f208d91e69aae215471e70941111483. - Supplied comparison base:
3e376417a90e400eda3b36ee3da340053182fab2. - The main-side source references below refer to that immutable commit; the dev references point at the supplied checkout.
Architecture at dev
Route groups and shared shell
Route groups do not appear in public URLs. Current public pages are under app/(marketing) and product pages under app/(content-rewards); /invoice/[token] is a standalone route and reports use app/(reports). Main's app/(site) would therefore collide by URL with the existing marketing pages if added alongside it.
| URL / surface | Current dev route and screen |
|---|---|
/, /contact, /book-call, /campaigns, /campaigns/[slug], /creator-guides, /creator-guides/[slug], /services, /privacy, /terms | app/(marketing)/...; marketing layout wraps MarketingQueryProvider and Footer (app/(marketing)/layout.tsx:5-13). Pages that use the marketing shell render Navbar themselves (home at app/(marketing)/page.tsx:4-12,76-94; contact at app/(marketing)/contact/page.tsx:3,8-13); home composition is app/(marketing)/page.tsx:63-97. |
/login, /onboarding, /verify-email | app/(content-rewards)/{login,onboarding,verify-email}/page.tsx → surfaces/content-rewards/screens/auth/*. Route-group theme is app/(content-rewards)/layout.tsx:1-5. |
/dashboard, /dashboard/campaigns, /dashboard/campaigns/[campaignId], /dashboard/submit, /dashboard/history, /dashboard/payout, /dashboard/payouts/method, /dashboard/payouts/tax, /dashboard/payment, /dashboard/profile, /dashboard/support | app/(content-rewards)/dashboard/**/page.tsx → surfaces/content-rewards/screens/**. Dashboard shell is app/(content-rewards)/dashboard/layout.tsx:13-82. |
/dashboard/admin/** | Route wrappers under app/(content-rewards)/dashboard/admin/**; shell at app/(content-rewards)/dashboard/admin/layout.tsx:1-7, with AdminNavigation and AdminAccessBoundary. Includes campaigns/funding, payouts, submissions, support, clipper groups, analytics, users, staff, tax, audit, etc. |
/invoice/[token] | app/invoice/[token]/page.tsx:1-9 → public invoice screen; noindex and no-referrer metadata. |
/share/report/[token] | app/(reports)/share/report/[token]/page.tsx; report group layout is independent (app/(reports)/layout.tsx:1-5). |
Shared root is app/layout.tsx:11,134-158: force-dynamic, global font/metadata/JSON-LD, AudienceThemeProvider, and shared CookieConsent, Telemetry, and ToastViewport. The marketing provider is separate (app/(marketing)/layout.tsx:7-12); its React Query defaults are in surfaces/marketing/providers/MarketingQueryProvider.tsx:6-22. app/globals.css imports Tailwind and shared toast/review styles; the content-rewards group imports surfaces/content-rewards/styles/tokens.css and sets its theme wrapper (app/(content-rewards)/layout.tsx:1-5).
Telemetry is a material shared behavior: shared/components/common/Telemetry.tsx:8-25 suppresses Analytics and Speed Insights for private report URLs. Main's root uses direct Vercel components (main app/layout.tsx:3-4,189-196), so replacing the root wholesale would drop that exclusion as well as dev's toast viewport and shared telemetry wrapper. Root also owns metadata and provider state used by product routes; keep redesign CSS scoped to the marketing group where possible.
The audience switch is context-only in dev and defaults to clippers (surfaces/marketing/providers/AudienceTheme.tsx:5-23). The home page reads ?audience= and strips it from history (app/(marketing)/page.tsx:63-74). Dashboard light/dark selection is separate: app/(content-rewards)/dashboard/layout.tsx:14-16,29-47 and surfaces/content-rewards/hooks/useDashboardSession.ts:10-21,55-58.
Authentication, Whop, API client, and RBAC
- Dashboard auth is a cookie-backed backend session, not a Next.js API route:
useDashboardSessioncalls${NEXT_PUBLIC_API_URL}/api/auth/mewithcredentials: "include", routes 403 to restricted login, and redirects unauthenticated users to/login?returnTo=...(surfaces/content-rewards/hooks/useDashboardSession.ts:9-53). Logout and TOS acceptance call backend endpoints at lines 60-75. - Provider URLs are constructed against
NEXT_PUBLIC_API_URLfor Google, Discord, or Whop (features/auth/api/login.ts:1-10);LoginScreen.tsx:35-37passes all three. Whop sign-in is therefore a backend auth-provider redirect.@whop/embedded-components-react-jsand the vanilla package are used insurfaces/content-rewards/screens/support/SupportScreen.tsx:7-44for support chat UI; no general Whop embedded-app router is evident in the dev route tree. - Whop environment display is browser configuration:
.env.example:9-21setsNEXT_PUBLIC_WHOP_ENVIRONMENT=sandbox;surfaces/content-rewards/lib/whopEnvironment.ts:1-9treats only literalliveas live, otherwise sandbox.WhopEnvironmentBadgeis included inDashboardHeader(surfaces/content-rewards/components/shell/DashboardHeader.tsx:7,33). Server-only Whop credentials for the local live-count route are documented separately at.env.example:26-39; do not expose those withNEXT_PUBLIC_. - Product and admin API clients mostly call the backend origin directly, defaulting to
http://localhost:3001.adminFetchdefaults to cookie credentials, retries one 429, and blocks client mutations while capability verification is stale (surfaces/content-rewards/lib/adminFetch.ts:57-81). Ordinary feature APIs commonly usefetch(..., { credentials: "include" })(for examplefeatures/campaigns/api/campaigns.ts:13-19). - Admin route/capability checks are a UI boundary, with backend authorization still required. Capabilities refresh in
surfaces/content-rewards/context/AdminAccessContext.tsx:22-69; exact route patterns are enumerated insurfaces/content-rewards/lib/adminRouteAccess.ts:9-23;AdminAccessBoundarydenies unknown routes and renders only after access resolves (.../AdminAccessBoundary.tsx:28-35). Funding create/reconcile buttons receiveINVOICE_CREATE/INVOICE_UPDATEaccess from the route wrapper (app/(content-rewards)/dashboard/admin/campaigns/[campaignId]/funding/page.tsx:7-19).
The Next app's own API routes are currently limited to /api/contact, /api/live/clipper-count, and Roblox game/thumbnail proxy routes (find app/api -type f). Funding, payout, submissions, support, and campaign management are backend API calls; these frontend files do not implement their server-side authorization or financial transitions.
MVP product surfaces to preserve
| Feature | Dev route / main implementation | API contract or notable boundary | Existing regression coverage |
|---|---|---|---|
| Creator campaign list/details; private campaign option | /dashboard/campaigns and /dashboard/campaigns/[campaignId] → CampaignsScreen, CampaignDetailScreen; admin editor at /dashboard/admin/campaigns/**. Admin checkbox says a private campaign is available only to active members of its Clipper Groups (surfaces/content-rewards/screens/admin/campaign-management/components/AdminCampaignManagementPlatformsSection.tsx:43-48); management API includes updateAdminCampaignPrivacy (features/admin/campaign-management/api/adminCampaignManagement.ts:26-30). | Creator list requests /api/campaigns?status=all with credentials (features/campaigns/api/campaigns.ts:13-19). UI contains no separate private-route family; campaign visibility/membership filtering must remain a backend guarantee. | campaign-budget-canonical, campaign-budget-display; admin-access. |
| Sponsor funding + public invoice | Admin /dashboard/admin/campaigns/[campaignId]/funding → CampaignFundingScreen; public /invoice/[token] → PublicCampaignInvoiceScreen. Route wrapper and capability props: app/(content-rewards)/dashboard/admin/campaigns/[campaignId]/funding/page.tsx:7-19; invoice wrapper: app/invoice/[token]/page.tsx:1-9. | Admin client endpoints: /api/admin/campaigns/:id/invoices GET/POST, /:invoiceId/reconcile, and /:invoiceId/client-link (features/admin/campaign-management/api/campaignFunding.ts:16-43). Public invoice fetch is /api/campaign-invoices/:token with credentials omitted, no-store, and no referrer (.../publicCampaignInvoice.ts:3-17). Keep the canonical funding model in backend contracts; this UI is a client of it. | campaign-funding-page, campaign-invoice-api, campaign-budget-canonical, campaign-budget-display. |
| Creator earnings / admin payouts | /dashboard/payout, /dashboard/payouts/method, /dashboard/payouts/tax; admin /dashboard/admin/payouts and detail. Creator screen calls /api/payout-finance/submissions and /api/payout-finance/status (features/payouts/hooks/usePayoutOverview.ts:20-21); shared client features/payouts/financialContracts.ts:62+. | UI reports Whop transfer status and simulation classification; actual eligibility, ledger and execution are backend-owned. Admin mutations use financialFetch and explicit request keys in screen code. | payout-financial-errors, payout-maturity, payout-review-navigation. |
| Submission create/history/review | Creator /dashboard/submit, /dashboard/history; admin /dashboard/admin/submissions. Submission modal posts /api/submissions (features/submissions/components/SubmitVideoModal.tsx:241,293); list client uses cookie credentials and no-store (features/submissions/api/submissions.ts:10-41). | Route/UI separation is thin; moderation and submission invariants are not enforced by hiding UI alone. | manual-submission, submission-review, admin-submissions-query. |
| Analytics / reports | Creator /dashboard overview; staff admin overview; /dashboard/admin/clipper-groups/[groupId] has ClipperGroupAnalyticsPanel; public tokenized sponsor report route. | Group analytics endpoint is /api/admin/clipper-groups/:id/analytics?days=...&platform=... (features/admin/clipper-groups/api/adminClipperGroups.ts:106-115). Shared telemetry excludes private report URLs (shared/components/common/Telemetry.tsx:8-25). | overview-sparklines, clipper-group-analytics, admin-review-analytics, public-report-api. |
| Support | Creator /dashboard/support; admin support route currently redirects to the creator support path (app/(content-rewards)/dashboard/admin/support/page.tsx:1-5); SupportScreen embeds Whop chat UI. | Support API client hits /api/support-chat; creator calls use cookie credentials, staff reads/resolves go through adminFetch (features/support/api/supportChat.ts:32-58,77-105). | support-resolution. |
| Clipper groups + RPM | Admin list/detail pages at /dashboard/admin/clipper-groups[/id]; screen owns roster, analytics and rates. | features/admin/clipper-groups/api/adminClipperGroups.ts:22-27,106-115,117+ centralizes /api/admin/clipper-groups; rate endpoints/versioned RPM are further below. Route is gated by CLIPPER_GROUP_MANAGEMENT (surfaces/content-rewards/lib/adminRouteAccess.ts:17). | clipper-group-rates, clipper-group-analytics. |
Relevant available tests are listed in package.json:5-9; the regular command is npm test, and npm run test:charts is a separate chart check. The tests above are present at the dev SHA. There is no contact form/API regression test in tests/ (rg found no contact/BookCall tests).
Main → dev mapping and conflicts
The main snapshot replaces the old direct marketing route files with app/(site) and app-local _components; current dev has already organized those concepts under app/(marketing) plus surfaces/marketing. Avoid adding the whole (site) group beside (marketing): the route URLs overlap.
| Main changed source | Current dev equivalent / candidate destination | Conflict or gap |
|---|---|---|
app/(site)/page.tsx (ConceptCPage, JSON-LD, main:1-68) and app/(site)/_components/Home.tsx | Existing / at app/(marketing)/page.tsx:63-97; reusable components under surfaces/marketing/components. | Same URL but different page composition and audience/content behavior. Port to the current route file, not a second route group. Preserve dev's root provider tree and home audience query behavior unless intentionally redesigned. |
app/(site)/_components/Nav.tsx, Footer.tsx, Hero.tsx, Stats.tsx, CreatorStats.tsx, Process.tsx, CreatorSteps.tsx, Why.tsx, Faq.tsx, FinalCta.tsx, LogoWall.tsx, ClipWindow.tsx, Roblox.tsx, CaseCards.tsx, CampaignArt.tsx, Counter.tsx, Sales.tsx | Closest current modules are surfaces/marketing/components/Navbar.tsx, Footer.tsx, Hero.tsx, BrandStats.tsx, HowItWorks.tsx, WhyBloxClips.tsx, FAQAccordion.tsx, ActiveCampaignsStrip.tsx, RobloxLiveStat.tsx, TweetEmbed.tsx; home assembles several at app/(marketing)/page.tsx:4-12,76-94. | Names mostly differ and main uses a new page composition; treat these as component-level porting targets, not exact file renames. Current marketing layout injects a global Footer. Main pages often render Nav/Footer themselves, so direct page copy risks duplicate footer/nav. |
app/(site)/_components/InquiryForm.tsx (main:14-16,40-62,82-99) and app/(site)/contact/page.tsx (InquiryForm at main:8,26,111) | /contact is app/(marketing)/contact/page.tsx:8-13,31-38; form implementation is surfaces/marketing/components/BookCallForm.tsx:7-10,28-34,84-104. | Main form imports @/app/components/InvisibleTurnstile (main InquiryForm:5); current equivalent is @/shared/components/common/InvisibleTurnstile (dev BookCallForm:5). Both snapshots include /api/contact and its blob is identical (cefd3b337b9901c408b67ffddafc74370debcf1a). Payload shape matches app/api/contact/route.ts:8-15,134-172; endpoint verifies Turnstile and sends via Resend (:39-58,102-131). |
app/(site)/campaigns/page.tsx, campaigns/[slug]/page.tsx, app/(site)/_components/cases.ts | Same URL routes at app/(marketing)/campaigns/page.tsx:1-8,16-20 and app/(marketing)/campaigns/[slug]/page.tsx:1-19,37-43; dev data at surfaces/marketing/case-studies/campaignData.ts. | Same URLs with different components/data. The pinned main tree has no app/api/live/clipper-count/route.ts, while dev's LiveCreatorNetworkStat calls that path (surfaces/marketing/hooks/useClipperCount.ts:15; route app/api/live/clipper-count/route.ts). If retaining that dev stat while porting the main route tree, preserve or replace its endpoint. |
app/(site)/creator-guides/page.tsx, [slug]/page.tsx, app/lib/content.ts | Same URL pages at app/(marketing)/creator-guides/page.tsx and [slug]/page.tsx; current guide records at features/creator-guides/guideData.ts (slug page imports it at dev:5). | Same URLs. Preserve current feature data imports and metadata/JSON-LD structure as appropriate; main's app/lib/content.ts has no exact same path module in dev. |
app/(site)/services/page.tsx, privacy/page.tsx, terms/page.tsx | Existing matching public routes under app/(marketing). | Same URL collisions; replace current route implementation, do not add parallel routes. Main terms/privacy are long, content-heavy pages and should be separately reviewed for source accuracy. |
app/(site)/creators/page.tsx, creators/apply/page.tsx, creators/start/page.tsx, _components/{Proof,Apply,Start,StartTransition,SectionHeadServer}.tsx, _content.ts, _progress.ts | No dev /creators route. Candidate route paths under current group: app/(marketing)/creators/page.tsx, /creators/apply/page.tsx, /creators/start/page.tsx; no current screen/action equivalent. | New route and feature. Do not confuse /onboarding with this public application/lessons funnel; onboarding is authenticated product setup. |
app/(site)/creators/_actions.ts (submitApplication, main:1-50) | No corresponding dev action or /api/creator-application route. Candidate action could remain server-only under a new creators route/feature if desired. | Sends form answers directly to ROBLOX_GAMING_UGC_DISCORD_WEBHOOK_URL; absent env returns {ok:true, delivered:false}, and network errors also return ok (main:20-49). This behavior is not persistence or delivery confirmation. The env name is absent from dev .env.example; no matching dev test. Validate input and intended destination at the primary/security review. |
app/(site)/careers/page.tsx, CareersRoles.tsx, app/(site)/clipping/page.tsx | No current dev /careers or /clipping page. Candidate new routes: app/(marketing)/careers/page.tsx, app/(marketing)/clipping/page.tsx. | New public URLs and content. The main homepage also references /clipping in structured data (main app/(site)/page.tsx:45-50). |
app/(site)/layout.tsx, site.css, creators/creators.css, app/globals.css, app/layout.tsx | Current public layout is app/(marketing)/layout.tsx:5-13; root is app/layout.tsx:11,134-158; global design tokens are in app/globals.css; product tokens are separately scoped in surfaces/content-rewards/styles/tokens.css. | Main route layout installs StartTransitionProvider, Rubik/Space Mono, and .cc-root scoping (main app/(site)/layout.tsx:1-7,23-43). Main modifies root layout and global CSS too. Port styles into the marketing scope; do not replace product root/provider wiring blindly. Check shared telemetry/private-report exclusion, cookies, toast, and AudienceThemeProvider. |
app/components/AudienceTheme.tsx, CookieConsent.tsx, PlatformIcons.tsx, Reveal.tsx, SmoothScroll.tsx | Audience theme is already surfaces/marketing/providers/AudienceTheme.tsx; cookie, icons, telemetry, toasts and other shared primitives live under shared/components/common; dev root has no main Reveal/SmoothScroll equivalent. | Main moves/modifies shared behavior. Reveal and smooth scrolling are optional new experience code; CookieConsent/AudienceTheme are existing shared integrations and need merge-level comparison. |
app/book-call/page.tsx deleted by main | Dev still exposes /book-call at app/(marketing)/book-call/page.tsx. | If main navigation or links continue pointing to /book-call, the destination would be missing after an equivalent port unless the route is retained/redirected. |
app/api/contact/route.ts | Exact same route blob exists in main and dev: both resolve to Git blob cefd3b337b9901c408b67ffddafc74370debcf1a. Dev source is app/api/contact/route.ts:1-177; main form submits to /api/contact (app/(site)/_components/InquiryForm.tsx:14,82-99). | No endpoint absence in pinned main. Main's @/app/components/InvisibleTurnstile exists at main but was moved in dev to shared/components/common/InvisibleTurnstile; update only that import when porting. .env.example:41-47 documents Turnstile/Resend variables; lines 42-43 contain a stale note saying the main contact form posts to API origin, while dev BookCallForm posts to /api/contact (surfaces/marketing/components/BookCallForm.tsx:7,93-104). |
app/(site)/[...rest]/page.tsx, not-found.tsx, _seo.ts | Dev has shared sitemap/robots and page metadata helpers spread over current route modules; there is no (marketing) catch-all equivalent in find app. | New public fallback semantics and SEO strategy. Review route ordering and metadata rather than copying as an independent root route. |
Legal copy drift to preserve and reconcile
The two legal trees differ materially. The current dev wording below reflects the Whop-only model visible in the dev frontend; do not replace it with the pinned main wording without product/legal confirmation. This is a content comparison, not a legal opinion.
- Whop identity and payout data: dev Privacy Policy lists Whop among provider links (
app/(marketing)/privacy/page.tsx:13-21), says the app may collect a Whop profile identity and verified email for Whop login (:88-95), and says a linked Whop identity is the payout destination (:106-112). Sources include linking the Whop payout identity and OAuth through Whop (:122-130). Sections 8–9 identify Whop for login and creator payouts, and say payouts go from BloxClips's Whop business balance to the creator's linked Whop balance (:208-249). Main Privacy Policy drops these Whop assertions and instead lists PayPal email, Stripe status, USDT wallet data, and Stripe/PayPal/NOWPayments as payout providers (main app/(site)/privacy/page.tsx:124-132,228-270). Preserve the Whop statements if they reflect the actual current product; do not reintroduce the legacy rails from main. - Payout rail and external withdrawal boundary: dev Terms §7.8 says creator payouts go from BloxClips's Whop business balance to the linked Whop balance, requires a linked Whop identity, and says there are no alternative payout rails, method elections, or processor surcharges; withdrawals from Whop are governed by Whop (
app/(marketing)/terms/page.tsx:209-215). Main Terms §7.8 instead offers PayPal, USDT/TRC-20 via NOWPayments, and Stripe, describes a 2.5% Stripe surcharge, PayPal Goods and Services, and irreversible crypto transfers (main app/(site)/terms/page.tsx:234-246). These are incompatible payment models. - Maturity and payout gate: dev Terms §7.9 says eligible earnings mature 120 hours after views are observed and only mature earnings form payout batches, with a $100 minimum plus holds, verification, and tax eligibility (
app/(marketing)/terms/page.tsx:217-220). Main §7.9 adds a cooldown and explicit manual administrative approval, plus a generic verified payout method (main app/(site)/terms/page.tsx:248-251). Do not carry the main-only operational requirements into a port without checking current product behavior. - Internal dev inconsistency in the fee amount: dev Terms §7.7 states a 7% platform fee (
app/(marketing)/terms/page.tsx:198-206), while the creator payout UI says “The platform fee is 5% of gross earnings” (surfaces/content-rewards/screens/payouts/PayoutOverviewScreen.tsx:43-47). Both main and dev legal terms include a 7% passage, but the UI disagrees. Resolve this independently before publishing legal copy; neither number is proven by this frontend comparison. - Sponsor campaign fee mechanics: dev Terms §8.5 says the campaign platform fee is a percentage of the campaign budget and gives a 10% example with budget burn/gross-to-net mechanics (
app/(marketing)/terms/page.tsx:261-270). The frontend funding UI instead consumes an explicit invoicefeeHandlingvalue and renders it (surfaces/content-rewards/screens/admin/campaign-funding/CampaignFundingScreen.tsx:127-129,144-152); the backend contract is authoritative for whether/how a client fee is charged. Review this specific legal claim against the current invoice fee policy before retaining or publishing either branch's text.
Main Terms also changes product actors from “Clipper” to “Creator,” but the provider and payout-rail assertions above are the material technical drift. The copy, payment facts, tax details, and effective date still need owner/legal validation.
Environment and regression checklist
Current documented keys: NEXT_PUBLIC_API_URL, NEXT_PUBLIC_WHOP_ENVIRONMENT, NEXT_PUBLIC_DISCORD_OAUTH_ENABLED, optional NEXT_PUBLIC_TURNSTILE_SITE_KEY, optional public NEXT_PUBLIC_WHOP_COMPANY_ID, server-only WHOP_API_BASE_URL, WHOP_COMPANY_ROUTE, WHOP_API_KEY / alias, clipper-count cache/fallback, TURNSTILE_SECRET_KEY, RESEND_API_KEY, RESEND_FROM_EMAIL, and CONTACT_FORM_TO_EMAIL (.env.example:9-47). Main creator action adds an undocumented ROBLOX_GAMING_UGC_DISCORD_WEBHOOK_URL.
Useful current dev checks by surface: tests/campaign-funding-page.test.tsx, campaign-invoice-api.test.tsx, campaign-budget-canonical.test.tsx, campaign-budget-display.test.tsx, payout-financial-errors.test.tsx, payout-maturity.test.tsx, payout-review-navigation.test.mjs, manual-submission.test.tsx, submission-review.test.tsx, admin-submissions-query.test.tsx, overview-sparklines.test.tsx, clipper-group-analytics.test.tsx, clipper-group-rates.test.tsx, support-resolution.test.tsx, admin-access.test.tsx, whop-environment.test.tsx, and marketing-query-dedup.test.tsx. Scripts in package.json:5-9: dev, dev:permissions, build, start, lint, test, test:charts.
This map does not establish backend access behavior, Whop provider semantics, or financial correctness; those require the primary agent's backend/security judgment and corresponding backend evidence.