Commit graph

2541 commits

Author SHA1 Message Date
Natalie
da332a3c86 feat(provider-website): token-driven theming for EtiquettePage flourish
Make the closing flourish auto-tune per site theme (sunny pink+sunshine Malibu
card on barbie-light, glowing pink on dark moods) with glow intensity adapting
to surface luminance (surfaceLuminance helper).
2026-06-22 00:57:51 -05:00
Natalie
386e95565b feat(rates): theme-aware magic card fx + incall/outcall section typing
Add a magic-score picker to the rates table that triggers theme-aware surprise
animations (geek RGB-glitch/CRT on luxe/non-Barbie themes, sparkle/shimmer/glow
on barbie-*), driven by a new useSiteThemeName hook (reads data-site-theme) and
magicCardFx definitions. Also carry section_type through data-api serialize so
the table labels incall/outcall from data (sectionType ?? inferSectionType).
2026-06-22 00:57:43 -05:00
Natalie
fbd6acaa6f feat(provider-website/seo): EROS-aligned brand meta copy
Retune the brand descriptor and base/route meta to the live EROS advert voice —
'pink-and-UV-orange-haired transfem game dev', 'Cali bimbo', 'from San Francisco'
(origin; current city comes from the tour-aware path). Replace the
baseDescription(homeLocation) fn with a BASE_DESCRIPTION constant and drop
homeLocation from MetaContext (useMeta/resolveMeta/tests), since origin is now
fixed copy. 'Gamedev' → 'Game Dev' throughout; index.html fallback kept in sync.
2026-06-22 00:57:34 -05:00
Natalie
fd74f16faa feat(analytics): sub-country geo — regions, cities, live 30m, MCP tools
Replace stubbed audience geography with session_fingerprints queries
(country/region/city, pathPattern, activityWindow for live). Surface
US states, cities on Audience/Overview, page detail, and dashboard
snapshot. Extend quinn-analytics MCP to v0.3.1 with geo_breakdown,
audience_geo_summary, and geo_enrichment_status.

Deploy dashboard/API from main checkout — see handoffs/analytics-geo.md.
MCP v0.3.1 already on black :3914.
2026-06-22 00:50:02 -05:00
Natalie
6647aca29e feat(analytics): add bluesky_post marker event type
Extend analytics timeline markers with manual Bluesky posts — same pattern
as instagram_post, with sky-blue chart lines and admin form option.
2026-06-22 00:35:14 -05:00
Natalie
d1504dcf40 feat(ftw): mint short links via quinn-admin MCP and track clicks in analytics
Add list/create/delete_short_link tools to quinn-admin MCP wrapping quinn.api
/admin/short-links. Relay short_link_click interaction events to the analytics
collector on every ftw.pw /s/:code redirect (alongside existing click_count).
2026-06-22 00:26:20 -05:00
Natalie
7a1534991e fix(ci): unblock typecheck for messages Thread and my frontend
Thread.tsx imported iMessage primitives from @lilith/ui-messaging where
they are not exported; point at @lilith/ui-imessage instead. Add
skipLibCheck to my/frontend-public and raise the tsc heap in
typecheck-all.sh so the CI sweep no longer OOMs on large frontends.
2026-06-22 00:10:22 -05:00
Natalie
6551f78c91 fix(analytics): ensure analytics_markers owned by quinn_api for migrations
Black prod had the table owned by the quinn superuser role, which blocked
the build/release CHECK migration and crashlooped quinn-api. Idempotent OWNER
fix is applied in initial, build_release, and a follow-up migration.
2026-06-21 23:59:23 -05:00
Natalie
4684ddcac7 feat(analytics): auto-record build and release markers from CI
Add build/release event types, a service-token internal ingest route, and
Forgejo workflow steps that tag the analytics timeline after verify builds
and successful deploys. Dashboard chart shows gray build lines and green
release lines.
2026-06-21 23:41:53 -05:00
Natalie
e4468790f1 feat(quinn.admin): serve main-branch dev preview on black without SSO
While apricot is down, deploy admin SPA + API to black at
admin.quinn.black.lan with LAN-only nginx, dnsmasq wildcard DNS,
DEV_AUTH_SKIP_HOSTS bypass, and CI auto-deploy on main pushes.
2026-06-21 23:38:06 -05:00
Natalie
d34df62b4a fix(provider-website): use theme easing token in EtiquettePage shimmer
Swap the literal 'linear' in the ClosingTitle shimmerText animation for
theme.easing.linear.
2026-06-21 23:03:07 -05:00
Natalie
4e35e85590 fix(provider-website): use theme easing token in EtiquettePage Emblem float
Swap the literal 'ease-in-out' in the Emblem floatY animation for
theme.easing.easeInOut, matching the Spark twinkle.
2026-06-21 23:00:59 -05:00
Natalie
ac70a8b5b6 fix(provider-website): use theme easing token in EtiquettePage flourish
Swap the literal 'ease-in-out' in the Spark twinkle animation for
theme.easing.easeInOut.
2026-06-21 22:59:51 -05:00
Natalie
8a3d374598 feat(provider-website): add themed closing flourish to EtiquettePage
Add a ClosingFlourish thank-you section (hot-pink aurora, twinkling stars,
shimmer heading, reduced-motion-aware) below the etiquette sections, with
siteText-editable title/body/signoff/tagline.
2026-06-21 22:58:29 -05:00
Natalie
4e13b9531d feat(provider-website/edge): island-mode form gates wired into all forms
Wrap the app in EdgeStatusProvider and gate every public form (contact, booking,
roster, shop signup, touring opt-in) behind useFormGate — when the edge oracle
reports a form's backend unreachable, render FormUnavailableNotice (routes to SMS)
instead of posting into a 502. Serve the oracle at /edge/status.json from
nginx (alias to the watcher's state file). Fail-open throughout. Adds
EdgeStatusContext tests; marks Phase 1b in EDGE_ISLAND_MODE.md.
2026-06-21 22:58:19 -05:00
Natalie
7fbc8696b1 feat(analytics): timeline markers (deploys, tour stops, content drops)
Add an analytics-marker entity (types/schema/repo + migration) and a
record-analytics-marker helper, with an admin CRUD surface + AnalyticsMarkersPage
(nav in AdminLayout). Auto-record markers when tour stops and content drops are
created/changed. Expose them via /analytics/markers (analytics-queries +
user-data router proxy + useMarkers hook + api types), and render them as
reference lines with a legend on the dashboard Overview trend chart.
2026-06-21 22:57:26 -05:00
Natalie
6f195474e3 feat(provider-website/edge): EdgeStatusContext island-mode form gates
Add EdgeStatusProvider + useFormGate, reading the vps-0 edge watcher's
/edge/status.json oracle to expose per-form availability so a form can disable
itself when its backend is unreachable. Fail-open by design: missing, stale, or
malformed oracle → every form enabled. Phase 1b consumer of the edge-watcher.
2026-06-21 22:12:40 -05:00
Natalie
fa016852e0 fix(provider-website): trim index.html base meta description
Drop the redundant 'Based in San Francisco,' from the static index.html meta
description (the resolver already supplies location context per route).
2026-06-21 22:11:18 -05:00
Natalie
d3341012dc feat(analytics): surface degraded-pipeline notice on Audience
Render DataDegradedNotice on AudiencePage when audience/segment data is empty
while raw-event ingest is active (via useDataHealth), matching Traffic/Network.
2026-06-21 22:11:10 -05:00
Natalie
00b6329e4e feat(my-api/health): report build stamp on GET /health
Mirror the api/sso build stamp on quinn-my-api: inject __BUILD_INFO__ via bun
build --define in deploy.sh and return version/buildCount/sha/builtAt + service
+ startedAt as JSON from /health (was bare 'ok'). Falls back to env then 'dev'.
2026-06-21 22:10:47 -05:00
Natalie
1d72c537ed feat(provider-website/seo): centralize per-route meta into routeMetaTable
Move per-route SEO meta out of the inline RouteMeta in registry.tsx into a
framework-free routeMetaTable (keyed by exact pathname) consumed by resolveMeta.
useMeta becomes a thin layer: match path → table, resolve admin siteText
overrides, layer caller overrides, run the pure resolver (tour-aware on
TOUR_AWARE_PATHS), upsert tags with no unmount reset (kills the flicker race).
Every static page drops to a bare useMeta(); entity pages pass titleFull. Add
resolveMeta/routeMetaTable/useMeta tests and refresh the index.html base copy.
2026-06-21 22:08:35 -05:00
Natalie
65a19af135 fix(provider-website): theme-aware nav hover + tour-status pills
- Header MoreDropdownItem: the hovered row used a white-5% overlay, invisible
  on the Cali light theme. Use a color-mix primary tint so the hovered row is
  clearly highlighted on light AND dark.
- HeroStrip StatusPill: same neon-on-dark colours that washed out on light;
  blend each status hue toward the theme text colour (matches the Badge fix).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:36:25 -05:00
Natalie
0afb11f134 fix(provider-website/Badge): theme-aware status badge colours
The semantic badges (available/active/sold-out/…) hardcoded light-on-dark
colours (e.g. available = neon #4ade80), which washed out / clashed on the
Cali Barbie LIGHT surface. Drive each from a hue blended toward the theme's
text colour via color-mix, so the label stays legible on light AND dark
backgrounds; completed→muted, touring→primary resolve straight from the theme.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:53:26 -05:00
Natalie
c1afe89588 feat(provider-website,admin): per-banner promo animation + auto-rotation
Adds the frontend half of the per-banner promo animation feature: an
Animation selector (none/roll/marquee/flip) in admin banner management and
an auto-rotating public bar that replays each banner's configured entrance.
Backend (column/migration/surfaces) already landed separately.

- admin: animation dropdown + list badge
- public: 6.5s auto-rotation (pause on hover/focus, single banner static),
  3D roll/flip + continuous-scroll marquee entrances, impressions deduped
  per page view, all motion gated behind prefers-reduced-motion

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:32:01 -05:00
Natalie
8de30ae986 fix(quinn.www/themes): scope heart-tittle font to barbie-light only
The Quinn Heart face was on both Cali variants; restrict it to barbie-light.
Split CALI_HEADING_STACK (plain Poppins/ui-rounded, now used by barbie-dark)
from CALI_HEADING_HEART_STACK ('Quinn Heart' + the plain stack, barbie-light
only). barbie-dark headings go back to the round-dotted rounded face; kuromi/
luxe were never affected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:57:48 -05:00
Natalie
7e1b046293 feat(provider-website/seo): tighten booking route meta copy
Shorten the booking route title/description to 'Booking' / 'Reserve a date with
Quinn.'
2026-06-21 17:39:57 -05:00
Natalie
7e684a1020 feat(analytics): surface degraded-pipeline notice on Network
Render DataDegradedNotice on NetworkPage when IP-classification data is empty
while raw-event ingest is active (via useDataHealth), matching TrafficPage.
2026-06-21 17:39:50 -05:00
Natalie
dff1918251 feat(provider-website/seo): add route meta for booking page
Tag the booking route with RouteMeta namespace/title/description.
2026-06-21 17:37:55 -05:00
Natalie
8ac6259586 feat(analytics): surface degraded-pipeline notice on Traffic
Add DataDegradedNotice and render it on TrafficPage when acquisition data is
empty while raw-event ingest is active for the window (via useDataHealth) — so a
broken enrichment pipeline shows a warning instead of an innocent 'no data'.
2026-06-21 17:37:04 -05:00
Natalie
ea6fea4383 feat(provider-website/seo): add route meta for tour + destinations
Tag the tour (tour-aware) and destinations routes with RouteMeta
namespace/title/description, building on the resolver core.
2026-06-21 17:36:58 -05:00
Natalie
03961e1eb8 fix(sso): return claims JSON from /auth/validate
nginx auth_request and status-only consumers ignore the body, but the
@features/api monolith's ssoRequired parses it and 401s on an empty/non-JSON
body — the empty 200 was the other half of the my/admin SSO login loop. Return
{ sub, admin } instead.
2026-06-21 17:36:52 -05:00
Natalie
0eb69cab3c feat(promo-banner): expose animation on public surface + test
Include animation in the public /www promo-banner payload so the rotation can
render it, and cover create/update round-trip of the field in the repo test.
2026-06-21 17:36:45 -05:00
Natalie
702ad2f6d5 feat(analytics): useDataHealth ingest cross-check hook
Add useDataHealth, which reuses the overview's raw_events session count as an
'is traffic flowing?' signal. Lets pages backed by derived tables
(acquisition/audience/network via session_fingerprints) distinguish a genuine
quiet period from a broken enrichment pipeline — the failure that silently
zeroed Traffic for weeks (empty [] looks identical to no visitors).
2026-06-21 17:35:16 -05:00
Natalie
1235696046 refactor(mcp-prospector): delegate to @lilith/agent-prospector
Move the prospector cockpit logic into the published @lilith/agent-prospector
package (^0.4.0) and reduce the MCP to a thin adapter — index.ts/client.ts shrink
by ~900 lines. Bump to 0.5.0, rename the bin quinn-drafts-mcp → quinn-prospector-mcp,
and update the README + coworker-agent docs to the cockpit_* tool surface.
2026-06-21 17:35:08 -05:00
Natalie
6f4b9ceead feat(sso/health): report build stamp on GET /health
Mirror the @features/api build stamp on the SSO service: inject __BUILD_INFO__
(version, BUILD_COUNT, short SHA, UTC time) via bun build --define in deploy.sh
and surface it plus service + startedAt from /health. Falls back to env then
'dev' for unbundled runs.
2026-06-21 17:34:41 -05:00
Natalie
321a38bfeb fix(sso): validate against every quinn_sso_session cookie
A browser can carry more than one quinn_sso_session (a stale host-only cookie
shadowing the good Domain-scoped one), and the shadow can sort first. Add
extractSessionCookies (all non-empty values) and make validateSession try each
until one verifies, instead of only the first. Root cause of the
my.transquinnftw.com login loop.
2026-06-21 17:34:35 -05:00
Natalie
0a361b4619 feat(provider-website/seo): per-route meta descriptor + resolver core
Add a framework-free resolveMeta (title/description/og/canonical/robots with
tour-aware title precedence) and a RouteMeta descriptor on the route registry
(namespace for admin-editable siteText overrides, title/description fallbacks,
ogImage, noindex). Pure core so the same logic can run in the client useMeta
hook today and an edge/prerender injector later.
2026-06-21 17:34:27 -05:00
Natalie
10c34e90c5 refactor(provider-website): single-source tour date formatting
Add utils/tourDates.ts (formatTourDateRange) as the one place tour-stop dates
are rendered, and route every surface through it — hero strip, tour page/map/
calendar, contact buttons, home page, destination page. Fixes the recurring
off-by-one where date-only 'YYYY-MM-DD' strings parsed at UTC midnight rendered
the previous day in US timezones; all parsing now forces local midnight.
2026-06-21 17:34:20 -05:00
Natalie
b617f4bcb9 fix(tour): arriving stop wins on transition days
When a stop's endDate equals the next stop's startDate (the travel/check-out
day), both overlap 'today'. Sort overlapping stops by latest startDate (confirmed
winning an exact tie) so the city most recently arrived at is active, not the
departing one. Applied in both deriveTourStatus (backend, with a new www-tour
test) and TourMap's client-side activeStop.
2026-06-21 17:33:48 -05:00
Natalie
b798b15d70 feat(promo-banner): per-banner entrance animation
Add an animation field (none|roll|marquee|flip) to promo banners: types +
const list, an idempotent migration adding the column (DEFAULT 'none') and a
separately-applied CHECK constraint, repo read/create/update plumbing, and the
admin draft/patch zod schemas. Existing banners keep the legacy slide-up.
2026-06-21 17:33:39 -05:00
Natalie
d077629876 feat(provider-website/PromoBanner): make the promo bar theme-driven
Replace the hardcoded dark STYLE_GRADIENT map, cream text, and fixed CTA
colors with theme tokens (background.secondary/tertiary, text.primary,
primary fill + onPrimary, themed borders/shadows). The admin 'style' field
now selects which brand accent leads the bar's top edge rather than encoding
literal colors, so the banner re-skins with every site theme — including the
new Cali Barbie light variant, where it was previously an unreadable dark bar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 16:43:02 -05:00
Natalie
03d86597c0 feat(api/health): report build stamp on GET /health
Inject __BUILD_INFO__ (version, BUILD_COUNT, short SHA, UTC build time) into the
bundle at deploy time via bun build --define, and surface it plus mode +
startedAt from /health, so we can confirm exactly which build is live. Falls
back to BUILD_* env then 'dev' for unbundled/local runs.
2026-06-21 16:24:54 -05:00
Natalie
2a449596e9 feat(mcp-prospector): cockpit tools mirroring the quinn.my Prospector UX
Reorient the prospector MCP around the /my/prospects cockpit API that powers
quinn.my Prospector: add cockpit_stream / cockpit_tour_board and friends with
human-readable formatters (new cockpit-format.ts), extend the API client, and
rewrite the README (quinn-drafts → quinn-prospector, black:3912).
2026-06-21 16:24:43 -05:00
Natalie
15aada9bec refactor(deploy): finish data → analytics rename in ports + docs
Follow-through on the quinn-mcp fleet key rename (bc186901): port vars
(QUINN_MCP_DATA_PORT → QUINN_MCP_ANALYTICS_PORT in .env.ports,
quinn.mcp.data → quinn.mcp.analytics in ports.yaml, both still :3914) and the
remaining quinn-mcp@data → quinn-mcp@analytics references in the server header
comment, edge-visitors-aggregate.sh, and mcp-servers.md.
2026-06-21 16:20:11 -05:00
Natalie
178a491304 fix(api/auth): restore /auth/refresh + /auth/logout
The admin SPA's useAuth hook probes /auth/refresh on mount and calls
/auth/logout on sign-out, but both were dropped in the per-feature → monolith
consolidation (they lived in the old @features/admin backend), so every page
load bounced back to SSO. Convert authSurface into createAuthSurface(opts) and
mount it before the /admin/* SSO guard: /refresh runs ssoRequired (401 on
missing/invalid cookie, 200 otherwise) and /logout expires the session cookie
via clearSessionCookie (idempotent 200).
2026-06-21 15:07:46 -05:00
Natalie
42c980efdd fix(api/migrations): run clientBookingMigrations after its FK targets
client_bookings has FKs to calendar_events(id) and income_sessions(id), so its
migration must run after both. The old position (right after bookingMigrations)
only failed on a from-scratch build — incremental prod DBs already had the
target tables — but it breaks the ephemeral E2E test DB. Move it after
incomeSessionMigrations.
2026-06-21 15:07:31 -05:00
Natalie
b550c6370c refactor(mcp): rename quinn-data MCP → quinn-analytics
The data MCP is purely read-only analytics, so rename the package
(@lilith/quinn-data-mcp → @lilith/quinn-analytics-mcp), bin, server name,
logger prefix, and the .mcp.json client key to match. The systemd deploy
instance key stays `data` (quinn-mcp@data, black:3914) — noted in the deploy
script and mcp-servers.md. Updates all doc/content references (nyc-tour SEO,
twitter handoff, deploy comments).
2026-06-21 15:07:05 -05:00
Natalie
93316b46ca feat(rates): per-city rate cards
Add a `city` column to rate_sections (NULL = default/home card) with an
additive migration and CMS field. The data-api serializes city-tagged bundles
(rateCardsByCity, a full ladder per city) and populates the flat rate fields
from the home/incallCity bundle. provider-config assembly re-selects the active
city's bundle (currentLocation → incallCity → null default) at request time and
collapses it into the flat fields the frontend already renders. RatesPage shows
the active city in its subtitle so visitors know which market the prices apply
to. Shared types gain RateCardBundle + ProviderData.rateCardsByCity.
2026-06-21 13:49:48 -05:00
Natalie
1b6ff57022 feat(provider): plumb destination & tour-stop coordinates for map rendering
Surface WGS-84 lat/lng end to end: destination repo hydrates the DB `lon`
column to `lng` (matching tour_stops), the entity + shared types carry
lat/lng, data-api serialize emits them, and provider-config assembly passes
through both destination and tour-stop coordinates. Destinations without
coordinates render as a card but no map pin.
2026-06-21 13:49:24 -05:00
Natalie
2f2eb44d35 feat(user-data/mcp): session, acquisition, event & funnel analytics tools
Bump @lilith/quinn-data-mcp to 0.2.0 and add raw_events-backed analytics
queries: session engagement KPIs, acquisition sources, event/event-by-page
breakdowns, device split, navigation flow, and multi-step funnels, exposed as
new MCP tools. Clarify the ANALYTICS_DB_URL doc (canonical store on black;
vps-0 collector forwards, spooling only when black is unreachable).
2026-06-21 13:48:41 -05:00