A mobile-first multi-step quiz app that tests musical ear. Users listen to audio clips, answer questions, receive a scored result, and leave contact details for a free first lesson.
Built with Next.js 16, React, Tailwind CSS v4, TypeScript.
src/
app/ # Next.js routing, layouts, API routes
[lang]/
layout.tsx # Root layout: fonts, metadata, html shell
page.tsx # Server entry: fetches dictionary, renders MainPage
api/
lead/route.ts # POST — sends lead email via Resend
sheet/route.ts # POST — writes booking to Google Sheets via Apps Script
types.ts # Shared request body types for API routes
globals.css # Tailwind import + @theme color/font tokens
proxy.ts # Locale detection & redirect (cookie → Accept-Language)
lib/
api.ts # Base ApiClient class (fetch wrapper with shared headers)
appApi.ts # AppApi extends ApiClient — submitLead, bookLesson
quizConfig.ts # Quiz questions, answers, audio sources
i18n/
config.ts # Locale list, Dictionary type, createTranslator()
server.ts # getDictionary() — server-only
schemas/
contactStep.ts # Zod schema for contact form
dictionaries/
en.json # English
ua.json # Ukrainian
pl.json # Polish
react/ # Pure React — no Next.js APIs
pages/
MainPage.tsx # Hero screen (server component)
FormPage.tsx # Multi-step form screen (client component)
components/ # Shared UI components
context/
FormContext.tsx # Form state: step, answers, navigation
LangContext.tsx # Active locale for client components
public/
audio/ # Quiz audio clips (.mp3)
images/ # Brand assets (logo.svg, soprano.svg)
- Server / client split —
MainPageis a server component for SEO. The form overlay is client-only, mounted on demand byFormTrigger. - i18n — route-based (
/en,/ua,/pl). Dictionary is fetched server-side and passed as a plain object to avoid crossing the server→client boundary with functions. - Colors & fonts — defined once in
globals.cssunder@theme. Usebg-accent,text-accent,font-bebas, etc. as Tailwind utilities everywhere. - API layer —
AppApiextendsApiClient. To add a new endpoint, add a method toAppApiand a type toapi/types.ts.
npm install
npm run devCopy .env.local.example to .env.local and fill in the required variables:
APPS_SCRIPT_URL= # Google Apps Script web app URL
APPS_SCRIPT_SECRET= # Shared secret validated by the script
RESEND_API_KEY= # Resend API key for email delivery
CRM_EMAIL= # Address that receives lead notifications
| Command | Description |
|---|---|
npm run dev |
Start dev server with hot reload |
npm run build |
Production build |
npm run i18n:extract |
Scan t() calls and sync all dictionary files |