Add Umami analytics and broaden beta copy for Android.

Optional NEXT_PUBLIC_UMAMI_* env vars load tracking when set; marketing pages now mention iOS TestFlight and Google Play internal testing.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 17:30:03 -04:00
co-authored by Cursor
parent d6040e2a09
commit bbdef0faa6
8 changed files with 83 additions and 22 deletions
+4
View File
@@ -16,3 +16,7 @@
# Password required to view the site (login page at /login).
# Leave empty/unset to disable the gate.
SITE_PASSWORD=""
# Umami analytics (optional). Omit NEXT_PUBLIC_UMAMI_WEBSITE_ID to disable tracking.
# NEXT_PUBLIC_UMAMI_WEBSITE_ID="your-website-uuid"
# NEXT_PUBLIC_UMAMI_SCRIPT_URL="https://cloud.umami.is/script.js"
+13 -4
View File
@@ -1,6 +1,6 @@
# Medscribe Web
Marketing homepage for [Medscribe](../README.md) — a privacy-first, on-device medical companion for iOS.
Marketing homepage for [Medscribe](../README.md) — a privacy-first, on-device medical companion for iOS and Android.
Built with [create-t3-app](https://create.t3.gg/) (Next.js + TypeScript + Tailwind). Single-page site; no database or API routes.
@@ -15,6 +15,15 @@ bun run dev
Open [http://localhost:3000](http://localhost:3000).
### Analytics (optional)
Set in `.env` (see `.env.example`):
- `NEXT_PUBLIC_UMAMI_WEBSITE_ID` — website UUID from your Umami dashboard
- `NEXT_PUBLIC_UMAMI_SCRIPT_URL` — optional; defaults to `https://cloud.umami.is/script.js` (use your self-hosted URL if needed)
Leave the website ID unset to disable tracking (default for local dev).
## Build
```bash
@@ -26,10 +35,10 @@ Screenshots live in `public/screenshots/`. They are wrapped in an iPhone-style f
### Capturing raw screenshots for the site
Export **in-app content only** from the simulator or device — no simulator chrome or macOS capture toolbar. A normal in-app iOS status bar/tab bar is fine.
Export **in-app content only** from the simulator or device — no simulator chrome or macOS capture toolbar. A normal in-app status bar/tab bar is fine.
1. Run the app on **iPhone 17 Pro** (or 16 Pro) simulator or device.
2. Capture each screen (⌘S in Simulator saves to Desktop).
1. Run the app on **iPhone 17 Pro** (or 16 Pro) simulator or device, or on an **Android** emulator/device.
2. Capture each screen (⌘S in Simulator saves to Desktop; `adb exec-out screencap -p` on Android).
3. Drop PNGs into `public/screenshots/` and update paths in `src/app/page.tsx` / `src/components/feature-showcase.tsx`.
The frame dimensions assume tall iPhone screenshots and are controlled in `src/components/phone-mockup.tsx`. Use `captureMode="simulator"` only for old captures that already include device chrome.
@@ -9,6 +9,7 @@ import { MedscribeLogo } from "~/components/medscribe-logo";
import { PhoneMockup } from "~/components/phone-mockup";
import { PrintButton } from "~/components/print-button";
import { SectionLabel } from "~/components/section-label";
import { BETA_FOOTER_NOTE, BETA_SUMMARY } from "~/lib/beta";
import { Badge } from "~/components/ui/badge";
import {
Card,
@@ -326,9 +327,7 @@ export default function ExecutiveSummaryPage() {
{/* Beta */}
<section className="flex flex-col gap-5 rounded-xl border border-border-soft bg-surface p-6 sm:flex-row sm:items-center sm:justify-between sm:p-8">
<p className="max-w-xl text-sm leading-relaxed text-muted-foreground">
A beta is available on iOS through TestFlight. Everything runs
privately on the device, so no health data leaves the phone. If
you&apos;d like to try it, reach out.
{BETA_SUMMARY} If you&apos;d like to try it, reach out.
</p>
<PrintButton size="lg" className="shrink-0" />
</section>
@@ -459,9 +458,7 @@ export default function ExecutiveSummaryPage() {
<footer className="mt-auto flex items-center justify-between border-t border-teal-900/10 pt-1.5 text-[10pt] text-slate-500">
<p>Sean O&apos;Connor</p>
<p className="text-slate-400">
A beta is available on iOS through TestFlight.
</p>
<p className="text-slate-400">{BETA_FOOTER_NOTE}</p>
</footer>
</article>
</>
+11 -9
View File
@@ -13,6 +13,11 @@ import {
import { FeatureShowcase } from "~/components/feature-showcase";
import { PhoneMockup } from "~/components/phone-mockup";
import { SectionLabel } from "~/components/section-label";
import {
BETA_CTA_BODY,
BETA_CTA_TITLE,
BETA_INVITE_MAILTO,
} from "~/lib/beta";
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";
import {
@@ -119,8 +124,9 @@ export default function HomePage() {
</div>
<div className="animate-fade-in-up-delay-2 flex flex-wrap gap-2">
<Badge variant="success">On TestFlight</Badge>
<Badge variant="outline">iOS</Badge>
<Badge variant="success">Beta available</Badge>
<Badge variant="outline">iOS · TestFlight</Badge>
<Badge variant="outline">Android · Play internal</Badge>
<Badge variant="outline">Emergency-ready</Badge>
<Badge variant="outline">No cloud AI</Badge>
</div>
@@ -319,11 +325,9 @@ export default function HomePage() {
<Card className="overflow-hidden border-primary-button/20 bg-primary-button text-white">
<CardHeader className="gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="space-y-2">
<CardTitle className="text-white">Try Medscribe on iOS</CardTitle>
<CardTitle className="text-white">{BETA_CTA_TITLE}</CardTitle>
<CardDescription className="text-white/75">
The app is live on TestFlight. Reach out for an invite if
you&apos;re managing medications, visits, or emergency context for
someone you love.
{BETA_CTA_BODY}
</CardDescription>
</div>
<Button
@@ -332,9 +336,7 @@ export default function HomePage() {
variant="outline"
className="border-white/30 bg-white text-primary-button hover:bg-white/90"
>
<a href="mailto:sean@soconnor.dev?subject=Medscribe%20TestFlight%20invite%20request&body=Hi%20Sean%2C%0D%0A%0D%0AI%27d%20like%20a%20TestFlight%20invite%20to%20try%20Medscribe.%20I%27m%20managing%20medications%2C%20visits%2C%20or%20emergency%20context%20for%20someone%20I%20care%20for.%0D%0A%0D%0AThanks%21">
Request an invite
</a>
<a href={BETA_INVITE_MAILTO}>Request an invite</a>
</Button>
</CardHeader>
</Card>
+6 -1
View File
@@ -3,6 +3,8 @@ import "~/styles/globals.css";
import { type Metadata } from "next";
import { Instrument_Sans, Playfair_Display } from "next/font/google";
import { UmamiAnalytics } from "~/components/umami-analytics";
const instrumentSans = Instrument_Sans({
subsets: ["latin"],
variable: "--font-instrument-sans",
@@ -42,7 +44,10 @@ export default function RootLayout({
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en" className={`${instrumentSans.variable} ${playfair.variable}`}>
<body className="min-h-screen">{children}</body>
<body className="min-h-screen">
{children}
<UmamiAnalytics />
</body>
</html>
);
}
+25
View File
@@ -0,0 +1,25 @@
import Script from "next/script";
import { env } from "~/env";
const DEFAULT_UMAMI_SCRIPT_URL = "https://cloud.umami.is/script.js";
/**
* Privacy-friendly page analytics via Umami. Enabled when
* `NEXT_PUBLIC_UMAMI_WEBSITE_ID` is set in `.env`.
*/
export function UmamiAnalytics() {
const websiteId = env.NEXT_PUBLIC_UMAMI_WEBSITE_ID;
if (!websiteId) return null;
const src = env.NEXT_PUBLIC_UMAMI_SCRIPT_URL ?? DEFAULT_UMAMI_SCRIPT_URL;
return (
<Script
defer
src={src}
data-website-id={websiteId}
strategy="afterInteractive"
/>
);
}
+6 -2
View File
@@ -18,7 +18,10 @@ export const env = createEnv({
* `NEXT_PUBLIC_`.
*/
client: {
// NEXT_PUBLIC_CLIENTVAR: z.string(),
/** Umami website ID — omit to disable analytics. */
NEXT_PUBLIC_UMAMI_WEBSITE_ID: z.string().optional(),
/** Umami script URL (defaults to Umami Cloud). */
NEXT_PUBLIC_UMAMI_SCRIPT_URL: z.string().url().optional(),
},
/**
@@ -28,7 +31,8 @@ export const env = createEnv({
runtimeEnv: {
NODE_ENV: process.env.NODE_ENV,
SITE_PASSWORD: process.env.SITE_PASSWORD,
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
NEXT_PUBLIC_UMAMI_WEBSITE_ID: process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID,
NEXT_PUBLIC_UMAMI_SCRIPT_URL: process.env.NEXT_PUBLIC_UMAMI_SCRIPT_URL,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
+15
View File
@@ -0,0 +1,15 @@
/** Shared beta / early-access copy for the marketing site. */
export const BETA_SUMMARY =
"A beta is available on iOS (TestFlight) and Android (Google Play internal testing). Everything runs privately on the device, so no health data leaves the phone.";
export const BETA_CTA_TITLE = "Try Medscribe";
export const BETA_CTA_BODY =
"The app is in beta on iOS and Android. Reach out for an invite if you're managing medications, visits, or emergency context for someone you love.";
export const BETA_INVITE_MAILTO =
"mailto:sean@soconnor.dev?subject=Medscribe%20beta%20invite%20request&body=Hi%20Sean%2C%0D%0A%0D%0AI%27d%20like%20a%20beta%20invite%20to%20try%20Medscribe%20(iOS%20or%20Android).%20I%27m%20managing%20medications%2C%20visits%2C%20or%20emergency%20context%20for%20someone%20I%20care%20for.%0D%0A%0D%0AThanks%21";
export const BETA_FOOTER_NOTE =
"Beta on iOS (TestFlight) and Android (Google Play internal testing).";