# BLACK HEART DANCE HOUSE — Project Context ## About Landing page for "BLACK HEART DANCE HOUSE" — a pole dance school in Minsk, Belarus. Instagram: @blackheartdancehouse Content language: Russian ## Tech Stack - **Next.js 16** (App Router, TypeScript, Turbopack) - **Tailwind CSS v4** (dark mode only, gold/black theme) - **lucide-react** for icons - **better-sqlite3** for SQLite database - **Fonts**: Inter (body) + Oswald (headings) via `next/font` - **Hosting**: Vercel (planned) ## Code Style - Function declarations for components (not arrow functions) - PascalCase for component files, camelCase for utils - `@/` path alias for imports - `next/image` with `unoptimized` for PNGs that need transparency preserved - Header nav uses `lg:` breakpoint (1024px) for desktop/mobile switch (9 nav links + CTA need the space) ## Project Structure ``` src/ ├── app/ │ ├── layout.tsx # Root layout, fonts, metadata │ ├── page.tsx # Landing: Hero → [OpenDay] → About → Team → Classes → MasterClasses → Schedule → Pricing → News → FAQ → Contact │ ├── globals.css # Tailwind imports │ ├── styles/ │ │ ├── theme.css # Theme variables, semantic classes │ │ └── animations.css # Keyframes, scroll reveal, modal animations │ ├── admin/ │ │ ├── page.tsx # Dashboard with 13 section cards │ │ ├── login/ # Password auth │ │ ├── layout.tsx # Sidebar nav shell (14 items) │ │ ├── _components/ # SectionEditor, FormField, ArrayEditor, NotifyToggle │ │ ├── meta/ # SEO editor │ │ ├── hero/ # Hero editor │ │ ├── about/ # About editor │ │ ├── team/ # Team list + [id] editor │ │ ├── classes/ # Classes editor with icon picker │ │ ├── master-classes/ # MC editor with registrations + notification toggles │ │ ├── open-day/ # Open Day event editor (settings + grid + bookings) │ │ ├── schedule/ # Schedule editor │ │ ├── bookings/ # Group booking management with notification toggles │ │ ├── pricing/ # Pricing editor │ │ ├── faq/ # FAQ editor │ │ ├── news/ # News editor │ │ └── contact/ # Contact editor │ └── api/ │ ├── auth/login/ # POST login │ ├── logout/ # POST logout │ ├── admin/ │ │ ├── sections/[key]/ # GET/PUT section data │ │ ├── team/ # CRUD team members │ │ ├── team/[id]/ # GET/PUT/DELETE single member │ │ ├── team/reorder/ # PUT reorder │ │ ├── upload/ # POST file upload (whitelisted folders) │ │ ├── mc-registrations/ # CRUD registrations + notification toggle │ │ ├── group-bookings/ # CRUD group bookings + notification toggle │ │ ├── open-day/ # CRUD events │ │ ├── open-day/classes/ # CRUD event classes │ │ ├── open-day/bookings/ # CRUD event bookings + notification toggle │ │ └── validate-instagram/ # GET check username │ ├── master-class-register/ # POST public MC signup │ ├── group-booking/ # POST public group booking │ └── open-day-register/ # POST public Open Day booking ├── components/ │ ├── layout/ │ │ ├── Header.tsx # Sticky nav, mobile menu, booking modal ("use client") │ │ └── Footer.tsx │ ├── sections/ │ │ ├── Hero.tsx # Hero with animated logo, floating hearts │ │ ├── About.tsx # About with stats (trainers, classes, locations) │ │ ├── Team.tsx # Carousel + profile view │ │ ├── Classes.tsx # Showcase layout with icon selector │ │ ├── MasterClasses.tsx # Cards with signup modal │ │ ├── OpenDay.tsx # Open Day schedule grid + booking (conditional) │ │ ├── Schedule.tsx # Day/group views with filters │ │ ├── Pricing.tsx # Tabs: prices, rental, rules │ │ ├── News.tsx # Featured + compact articles │ │ ├── FAQ.tsx # Accordion with show more │ │ └── Contact.tsx # Info + Yandex Maps iframe │ └── ui/ │ ├── Button.tsx │ ├── SectionHeading.tsx │ ├── BookingModal.tsx # Booking form → Instagram DM + DB save │ ├── MasterClassSignupModal.tsx # MC registration form → API │ ├── OpenDaySignupModal.tsx # Open Day class booking → API │ ├── NewsModal.tsx # News detail popup │ ├── Reveal.tsx # Intersection Observer scroll reveal │ ├── BackToTop.tsx │ └── ... ├── data/ │ └── content.ts # Fallback Russian text (DB takes priority) ├── lib/ │ ├── constants.ts # BRAND constants, NAV_LINKS │ ├── config.ts # UI_CONFIG (thresholds, counts) │ ├── db.ts # SQLite DB, 6 migrations, CRUD for all tables │ ├── auth.ts # Token signing (Node.js) │ ├── auth-edge.ts # Token verification (Edge/Web Crypto) │ ├── content.ts # getContent() — DB with fallback │ └── openDay.ts # getActiveOpenDay() — server-side Open Day loader ├── proxy.ts # Middleware: auth guard for /admin/* └── types/ ├── index.ts ├── content.ts # SiteContent, TeamMember, ClassItem, MasterClassItem, etc. └── navigation.ts ``` ## Brand / Styling - **Accent**: gold (`#c9a96e` / `hsl(37, 42%, 61%)`) - **Background**: `#050505` – `#0a0a0a` (dark only) - **Surface**: `#171717` dark cards - Logo: transparent PNG heart with gold glow, uses `unoptimized` ## Content Data - Primary source: SQLite database (`db/blackheart.db`) - Fallback: `src/data/content.ts` (auto-seeds DB on first access) - Admin panel edits go to DB, site reads from DB via `getContent()` - 12 team members with photos, Instagram links, bios, victories, education - 6 class types (Exotic Pole Dance, Pole Dance, Body Plastic, etc.) - Master classes with date/time slots and public registration - 2 addresses in Minsk, Yandex Maps embed with markers - Contact: phone, Instagram (no email) ## Admin Panel - Password-based auth with HMAC-SHA256 signed JWT (24h TTL) - Cookie: `bh-admin-token` (httpOnly, secure in prod) - Auto-save with 800ms debounce on all section editors - Team members: drag-reorder, photo upload, rich bio (experience, victories, education) - Master classes: slots, registration viewer with notification tracking (confirm + reminder), trainer/style autocomplete - Group bookings: saved to DB from BookingModal, admin page at `/admin/bookings` with notification toggles - Open Day: event settings (date, pricing, discount rules, min bookings), schedule grid (halls × time slots), per-class booking with auto-cancel threshold, public section after Hero - Shared `NotifyToggle` component (`src/app/admin/_components/NotifyToggle.tsx`) used across MC registrations, group bookings, and Open Day bookings - File upload: whitelisted folders (`team`, `master-classes`, `news`, `classes`), max 5MB, image types only ## Security Notes - **CSRF protection**: Double-submit cookie pattern. Login sets `bh-csrf-token` cookie (JS-readable). All admin fetch calls use `adminFetch()` from `src/lib/csrf.ts` which sends the token as `X-CSRF-Token` header. Middleware (`proxy.ts`) validates header matches cookie on POST/PUT/DELETE to `/api/admin/*`. **Always use `adminFetch()` instead of `fetch()` for admin API calls.** - File upload validates: MIME type, file extension, whitelisted folder (no path traversal) - API routes validate: input types, string lengths, numeric IDs - Public MC registration: length-limited but **no rate limiting yet** (add before production) ## Upcoming Features - **Rate limiting** on public endpoints (`/api/master-class-register`, `/api/group-booking`, `/api/open-day-register`) - **DB backup mechanism** — automated/manual backup of `db/blackheart.db` with rotation ## AST Index - **Always use the AST index** at `memory/ast-index.md` when searching for components, props, hooks, types, or styles - Contains: component tree, all exports, props, hooks, client/server status, CSS classes, keyframes - Update the index when adding/removing/renaming files or exports ## Database Migrations - **Never drop/recreate the database** — admin data (photos, edits, registrations) lives there - Schema changes go through versioned migrations in `src/lib/db.ts` (`migrations` array) - Add a new entry with the next version number; never modify existing migrations - Migrations run automatically on server start via `runMigrations()` and are tracked in the `_migrations` table - Use `CREATE TABLE IF NOT EXISTS` and column-existence checks (`PRAGMA table_info`) for safety ## Git - Remote: Gitea at `git.dolgolyov-family.by` - User: diana.dolgolyova - Branch: main