Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesMinisitesMinisites — Code Map

Minisites — Code Map

App root

  • apps/minisites/astro.config.mjs - output: 'server', dual adapter (Vercel / Node).
  • apps/minisites/vercel.json - Vercel rewrites & domains.
  • apps/minisites/package.json - Astro 5 + minimal section components.

Pages

  • apps/minisites/src/pages/[...slug].astro - multi-page catch-all router. Resolves the host, fetches publishedContent, resolves the URL slug to a page (findPage), composes that page’s sections, and renders the lead popup. Unknown/disabled slug returns 404.
  • apps/minisites/src/pages/404.astro - fallback when the host resolves but the site isn’t published.
  • apps/minisites/src/pages/courses/ - per-course detail landing pages (/courses/[id]).
  • apps/minisites/src/pages/robots.txt.ts - robots dynamic endpoint.

Layout

  • apps/minisites/src/layouts/modern.astro - outer shell. Premium editorial token layer (charcoal + copper + cream). Injects theme CSS variables (incl. headingFontFamily, cornerStyle, sectionDensity — all three now wired into --font-display/--radius-*/--section-pad as of FIT-287), normalizes legacy primary colors to the copper default, renders the floating pill top nav (page links vs in-page anchors; CTA prefers joinUrl when present), locale + direction, SEO tags, hero preload, comprehensive dark footer, floating WhatsApp button, and (FIT-287) the inline <script> block that sets html.js and runs the one IntersectionObserver-based scroll-reveal system for .reveal/.reveal-fade elements — there is no separate reveal.ts file.

Section components

  • apps/minisites/src/components/sections/Hero.astro
  • apps/minisites/src/components/sections/About.astro
  • apps/minisites/src/components/sections/Classes.astro
  • apps/minisites/src/components/sections/Schedule.astro
  • apps/minisites/src/components/sections/Pricing.astro - card anatomy since FIT-287: highlight/spots-left/sold-out badges, intro-aware price block, three-branch CTA (joinUrl present-and-open / present-and-sold-out / absent). Reads platformData.plans[].introPriceInCents/introDiscountPercent/introDurationCycles/seatsLeft/soldOut and platformData.joinUrl, all optional.
  • apps/minisites/src/components/sections/Courses.astro
  • apps/minisites/src/components/sections/Trainers.astro
  • apps/minisites/src/components/sections/Contact.astro
  • apps/minisites/src/components/sections/Gallery.astro
  • apps/minisites/src/components/sections/Testimonials.astro
  • apps/minisites/src/components/sections/FAQ.astro - renders as a native <details> accordion since FIT-287 (no JS).
  • apps/minisites/src/components/sections/Social.astro - manual social channels + curated post thumbnails.
  • apps/minisites/src/components/sections/Events.astro - auto-populated dated events from platformData.events[]; lead-capture CTA.

All 14 section components plus apps/minisites/src/pages/courses/[id].astro (the per-course detail page) got the FIT-287 token/radius/type-scale sweep (var(--radius-card), var(--font-display), the desaturated image filter, .reveal/.reveal-fade classes) — a reskin, not a re-architecture; prop contracts and hasDataMap behavior are unchanged.

Other components

  • apps/minisites/src/components/LeadPopup.astro - timed lead-capture modal (delay + exit intent, localStorage dismissal, posts to /leads/organization/:orgId).

Lib

  • apps/minisites/src/lib/i18n.ts - resolveLocale(input) normalizes draft/org locale to one of he | en | ru, falling back to en (not he). mt(locale, key) flat lookup (locale → en → raw key). mtp(locale, baseKey, count) (FIT-287) — CLDR-plural-aware lookup on top of mt, used by the pricing section’s “spot(s) left” / “first N payments” copy.
  • apps/minisites/src/lib/pages.ts - multi-page resolution: getPages (shims legacy sections into a home page), findPage (slug -> enabled page), getNavLinks (page links for multi-page, empty for single-page). Mirrors the shared getMinisitePages/findPageBySlug shim so the Astro build stays decoupled from @taikan/shared.

Shared

  • libs/shared/src/lib/minisite.ts - Zod schemas and helpers: MinisiteContent (with pages[], leadPopup), MinisitePage, MinisiteSection, MinisiteTheme, section data schemas (incl. SocialSectionData, EventsSectionData, and PricingSectionData which since FIT-287 carries ctaMode: 'lead' | 'purchase', default 'lead'), MinisiteEventInput, MinisitePlatformData (since FIT-287: optional per-plan introPriceInCents/introDiscountPercent/introDurationCycles/seatsLeft/soldOut and a top-level optional joinUrl), getMinisitePages/findPageBySlug (back-compat shim), createDefaultPages/createDefaultSections.
  • libs/shared/src/lib/constants/feature-flags.ts - FeatureFlags.MINISITE_PLAN_PURCHASE ('minisite-plan-purchase', FIT-287) gates joinUrl emission. The intro/seat fields are ungatedFeatureFlags.PLAN_INTRO_PRICING was deleted (2026-08-19, as if permanently true) and no constant for it remains.
  • libs/shared/src/lib/schemas/plan-group.schema.ts - (FIT-289) showCapToMembers on the group create/update/response schemas — the per-group owner toggle that decides whether a MEMBER-facing payload carries seatsLeft alongside soldOut.

Schema

  • libs/db/src/lib/schema/minisites.ts - minisite_content table + new minisite_events table (dated promotable events/workshops).
  • libs/db/drizzle/0066_lying_siren.sql - migration creating minisite_events.

Editor (dashboard surface, apps/web)

Under apps/web/src/app/[lang]/(protected)/dashboard/minisite/:

  • page.tsx - tabbed editor: Overview, Pages, Events, Branding, SEO. Overview tab (FIT-287) shows a “Plan purchase: live / blocked - join link disabled / blocked - payments not active” status row whenever the published pricing section’s ctaMode === 'purchase' — reads GET /organizations/:id/join-link + GET /organizations/:orgId/payment-config client-side, mirroring MinisitesService.publishedPricingCtaMode/the joinUrl gate (the API doesn’t expose that private helper, so the client re-derives it off publishedContent).
  • components/page-manager.tsx - page manager (enable/disable, reorder, nav label/visibility).
  • components/section-manager.tsx - per-page section manager; section ops are page-scoped via the ?page= query.
  • components/events-manager.tsx - events CRUD UI.
  • components/theme-editor.tsx - theme controls (colors, fonts, button/corner style, density) plus the lead-popup editor. cornerStyle/sectionDensity/headingFontFamily controls were already here pre-FIT-287; the renderer just didn’t read them until now.
  • components/lead-popup-editor.tsx - lead-popup config editor.
  • components/section-editors/pricing-editor.tsx - (FIT-287) ctaMode radio (lead/purchase) + non-blocking readiness hints (join-link enabled? payments active?) shown only in purchase mode; per-plan Intro/Cap: n chips, ungated, driven by what each plan carries.
  • components/section-editors/social-editor.tsx, components/section-editors/events-editor.tsx - section editors (alongside the existing per-type editors).

API endpoints used

Public (consumed by the Astro app):

  • GET /minisites/resolve?host=<host> - resolver consumed by the catch-all; platformData includes events[], the per-plan intro/seat fields (FIT-287, ungated) and a top-level joinUrl (gated on minisite-plan-purchase).
  • POST /leads/organization/:orgId - contact form + lead-popup submissions.

Related, not part of this module but read by the FIT-287 purchase chain once joinUrl is emitted: GET /join/:token and the shop deep-link flow in apps/web (request-invite/acceptJoinLink in apps/api/src/memberships/memberships.service.ts).

Editor endpoints (dashboard surface, not consumed by the Astro app directly), in apps/api/src/organizations/minisites.controller.ts:

  • PATCH /minisites/:orgId/content - whole-blob multi-page save.
  • PATCH /minisites/:orgId/lead-popup - lead-popup config.
  • PATCH /minisites/:orgId/theme - theme tokens.
  • PATCH /minisites/:orgId/pages/reorder, /pages/:pageKind/toggle, /pages/:pageKind - page ops.
  • PATCH /minisites/:orgId/sections/reorder, /sections/:type/toggle, /sections/:type - section ops (page-scoped via ?page=, default home).
  • GET/POST /minisites/:orgId/events, PATCH/DELETE /minisites/:orgId/events/:eventId - events CRUD.
  • PATCH /minisites/:orgId/publish and /unpublish - publish workflow.

Cross-module dependency (FIT-287)

MinisitesService.getPlatformData (apps/api/src/organizations/minisites.service.ts) is not a self-contained module for the pricing DTO — it imports:

  • getSeatsTakenByPlan, loadGroupCaps, computeSeatFields from apps/api/src/plans/plan-capacity.util.ts (the plans module) — the same pure seat-computation helpers PlansService uses for dashboard listing and purchase enforcement (FIT-289), extracted so the two call sites can never disagree about a plan’s effective seats. PlansService.loadGroupCaps/seatFields are now thin delegates over these exports.
  • PaymentProviderConfigService.getActiveConfig from the payments module (added to OrganizationsModule’s imports — apps/api/src/organizations/organizations.module.ts) for the joinUrl gate’s “org can actually charge” condition.
  • EventTrackingService (global module, no explicit import needed) for both flag evaluations.