← All entries

Saturday, May 2, 2026

Day 30 — feature-complete for first-barn onboarding

Brand assets, analytics, the auth split-panel redesign, marketing-site additions, guardian parity across web and mobile, a fully enriched client dashboard, and self-serve subscription management. Today the platform crossed into 'ready'.

3 min read

Daily entry

Day 30of building EquinePilot

Currently Day 77 · founder build log

Milestones reached

  • PostHog analytics
  • Auth redesign
  • Guardian parity
  • Self-serve subscriptions
  • Comparison table
  • Testimonials pipeline

The day I crossed the line from "shipping features" to "this is ready for a real barn."

Brand assets. EP mark and Wordmark extracted to @equinepilot/ui as typed SVG components. icon.svg favicon added to both apps. Found a fun bug along the way: the middleware matcher was intercepting favicon requests and redirecting unauthenticated browsers to /login — so the login page itself displayed a broken icon. Excluded icon.svg and apple-icon.png from the matcher.

Analytics. PostHog wired into the StableSync root layout. Auto page-view tracking; users identified with userId, role, barnId, barnSlug; barns grouped so PostHog dashboards filter per barn. Opt-in via NEXT_PUBLIC_POSTHOG_KEY — if absent the provider is a no-op and nothing breaks.

Then /admin/analytics, which is the surface I'll actually look at more often: a DB-driven platform dashboard. Barns by status (trialing/active/suspended), users by role, monthly new-signup trend, mobile-active barn count, per-feature adoption rates, and a per-barn feature matrix covering scheduling, bookings, billing, waivers, shows, horse health, training log, and mobile. No PostHog dependency — it's just SQL — so it's useful before PostHog has data.

Auth layout redesign. Login page rebuilt as a split-panel: left side Paddock Green with EP mark + branding + feature bullets + tagline, right side linen with the form in a white card. Forgot-password and reset-password inherit the same design through a shared (auth)/layout.tsx. Mobile collapses cleanly to a top logo strip with the form below.

Marketing-site additions. Competitor comparison table between Features and Suite — 8 rows where the "Other barn software" column is all ✗ and the EquinePilot column is all ✓. Plus a testimonials pipeline end-to-end: a /feedback form on equinepilot.com that submits a pending row to POST /api/testimonials; an /admin/testimonials UI inside StableSync (cookie auth via timingSafeEqual) for approve/reject; the homepage TestimonialsSection fetches approved rows at render time and is hidden entirely when there are none.

Guardian / responsible-party parity. This was the most painful bug-hunt of the day. Seven web client pages — calendar, shows, horses, bookings, lessons, credits, notifications — were each querying clients only by userId. A guardian (responsible party) has no clients row of her own; her ward's record links to her via responsiblePartyUserId. So a guardian logging in saw empty screens on every tab. Updated all seven web pages and six mobile API routes to OR on responsiblePartyUserId. Also added isHorseOwner to resolveClientContext() so the "My Horse" nav link hides itself for non-horse-owning clients (and guardians of non-owners). The kind of polish you don't notice if it works and definitely notice if it doesn't.

Client dashboard enriched. Next upcoming lesson promoted to a Paddock Green hero card with date, time, level, lesson type, and a 24-hour cancel link. An Upcoming Shows section. A Barn Calendar section for non-show events. A quick-nav grid linking everything else. Went from 4 sleepy summary cards to a real information hub.

Self-serve subscription management. POST /api/billing/portal-session creates a Stripe Customer Portal session for active barns. A ManageSubscriptionButton on the subscription page replaces what was previously an "email support" placeholder. Managers can now upgrade, downgrade, switch billing cycle, update payment method, view invoice history, or cancel — all via Stripe's hosted portal, no contact-support step. (Bonus: removed the gate that was hiding the button when stripeSubscriptionId was null, because locally seeded active barns weren't seeing it.)

Test coverage crossed 321 tests today. Three new test files: billing-portal.test.ts, admin-testimonials.test.ts, client-context.test.ts.

Day 30. Platform is feature-complete for first-barn onboarding.