diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index bed9459bd..000000000 --- a/AGENTS.md +++ /dev/null @@ -1,31 +0,0 @@ -# Repository Guidelines - -## Project Structure & Module Organization -- `app/` defines Next.js routes and Server Actions; folder names mirror URLs and expose `page.tsx` or `route.ts` entrypoints. -- Shared UI lives in `components/`, domain hooks in `hooks/`, reusable utilities in `lib/`, and Tailwind tokens plus global CSS under `styles/`. -- Static assets sit in `public/`; data fixtures and CMS JSON in `data/`; D1 schema and migrations in `sql/`; automation scripts in `scripts/`. -- Tests mirror this layout inside `__tests__/` so suites stay close to the features they exercise. - -## Build, Test, and Development Commands -- `npm run dev` starts the local server; `npm run build` and `npm run start` produce and serve the production bundle. -- Quality gates: `npm run lint`, `npm run format:check`, and `npm run ci:typecheck` keep linting, formatting, and typing aligned. -- Testing relies on Vitest: `npm run test` for watch mode, `npm run test:run` for CI-stable runs, and `npm run test:coverage` for reports. -- OpenNext & data ops: `npm run pages:build` prepares the worker bundle, `npm run deploy:preview` / `npm run deploy:production` push through Wrangler, and `npm run db:migrate[:local]` or `npm run db:backup` manage D1 state. -- Agent support tools live under `npm run bmad:*` (refresh, list, validate) and should follow any manifest updates in `bmad/`. - -## Coding Style & Naming Conventions -- Ship TypeScript with strict typing; prefer explicit module exports over defaults for clean tree-shaking. -- Prettier and ESLint enforce 2-space indents, double quotes, and trailing commas; run `npm run format` before reviews. -- Files follow PascalCase for components, camelCase for hooks/utilities, and kebab-case for route folders to produce friendly URLs. - -## Testing Guidelines -- Place specs under the matching `__tests__/` subtree and name them `*.test.ts[x]`. -- Combine Vitest with Testing Library for component interactions and use `vitest.setup.ts` for shared providers. -- Add regression coverage for each bug fix and ensure watch mode passes before pushing. -- Gate merges with `npm run test:coverage`; flag low coverage in the PR description. - -## Commit & Pull Request Guidelines -- Follow the Conventional Commit format in history (`feat(scope): summary`, `fix(area): detail`) using present-tense voice. -- Bundle schema or fixture changes with their related code and note breaking behavior explicitly. -- Before opening a PR, run lint, typecheck, coverage, and `npm run pages:build`; attach output if CI is down. -- PRs should explain motivation, link issues or tickets, and include UI screenshots or recordings when visuals change. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..b30f8aa8d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,279 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +United Tattoo is a Next.js-based website for a tattoo studio in Fountain, CO. The application includes artist portfolios, booking systems, appointment management with CalDAV integration, and admin dashboards. + +**Stack:** +- Next.js 14 (App Router) with TypeScript +- Cloudflare D1 (SQLite) for database +- Cloudflare R2 for file storage +- NextAuth.js for authentication +- Deployed via OpenNext on Cloudflare Workers +- ShadCN UI components + Tailwind CSS +- Vitest for testing + +## Common Commands + +### Development +```bash +npm run dev # Start Next.js dev server (port 3000) +npm run dev:wrangler # Build and preview with OpenNext/Cloudflare +``` + +### Testing +```bash +npm run test # Run Vitest in watch mode +npm run test:ui # Run Vitest with UI +npm run test:run # Run tests once +npm run test:coverage # Run tests with coverage report +``` + +### Build & Deployment +```bash +npm run pages:build # Build with OpenNext for Cloudflare +npm run build # Standard Next.js build (standalone) +npm run preview # Preview OpenNext build locally +npm run deploy # Deploy to Cloudflare Pages +``` + +### CI Commands +```bash +npm run ci:lint # ESLint +npm run ci:typecheck # TypeScript type checking (noEmit) +npm run ci:test # Run tests with coverage +npm run ci:build # Build for production +npm run ci:budgets # Check bundle size budgets +``` + +### Database Management +```bash +# Local database +npm run db:migrate:local # Apply schema to local D1 +npm run db:studio:local # Show tables in local D1 + +# Preview (default) environment +npm run db:migrate # Apply schema to preview D1 +npm run db:migrate:latest:preview # Apply all migrations from sql/migrations/ +npm run db:studio # Show tables in preview D1 +npm run db:backup # Backup preview database + +# Production environment +npm run db:migrate:up:prod # Apply specific migration to production +npm run db:migrate:latest:prod # Apply all migrations to production +npm run db:backup:local # Backup local database + +# Direct Wrangler commands +wrangler d1 execute united-tattoo --local --command="SELECT * FROM artists" +wrangler d1 execute united-tattoo --file=./sql/schema.sql +``` + +### Code Quality +```bash +npm run lint # Run ESLint +npm run format # Format code with Prettier +npm run format:check # Check formatting without changing files +npm run security:audit # Run npm audit +``` + +## Architecture + +### Database Layer (`lib/db.ts`) + +The database layer provides type-safe functions for interacting with Cloudflare D1. Key patterns: + +- **Binding access**: `getDB(env)` retrieves D1 from Cloudflare bindings via OpenNext's global symbol +- **R2 access**: `getR2Bucket(env)` retrieves R2 bucket binding for file uploads +- **Namespace-style exports**: Use `db.artists.findMany()`, `db.portfolioImages.create()`, etc. +- **JSON fields**: `specialties` and `tags` are stored as JSON strings and parsed/stringified automatically + +Main tables: +- `users` - Authentication and user profiles with roles (SUPER_ADMIN, SHOP_ADMIN, ARTIST, CLIENT) +- `artists` - Artist profiles linked to users, includes slug for URLs +- `portfolio_images` - Artist portfolio work with tags and ordering +- `appointments` - Booking appointments with CalDAV sync support +- `flash_items` - Flash tattoo designs available for booking +- `site_settings` - Global site configuration +- `artist_calendars` - Nextcloud CalDAV calendar configuration per artist + +### Authentication (`lib/auth.ts`) + +NextAuth.js setup with role-based access control: + +- **Providers**: Credentials (email/password), optional Google/GitHub OAuth +- **Dev mode**: Any email/password combo creates a SUPER_ADMIN user for testing +- **Seed admin**: `nicholai@biohazardvfx.com` is hardcoded as admin +- **Session strategy**: JWT (no database adapter currently) +- **Helper functions**: + - `requireAuth(role?)` - Protect routes, throws if unauthorized + - `getArtistSession()` - Get artist profile for logged-in artist users + - `canEditArtist(userId, artistId)` - Check edit permissions + - `hasRole(userRole, requiredRole)` - Check role hierarchy + +### CalDAV Integration (`lib/calendar-sync.ts`, `lib/caldav-client.ts`) + +Bidirectional sync between database appointments and Nextcloud calendars: + +- **Push to calendar**: `syncAppointmentToCalendar()` - Called when creating/updating appointments +- **Pull from calendar**: `pullCalendarEventsToDatabase()` - Background sync to import calendar events +- **Availability checking**: `checkArtistAvailability()` - Real-time conflict detection using CalDAV +- **Per-artist calendars**: Each artist can have their own Nextcloud calendar configured in `artist_calendars` table + +Environment variables required: +- `NEXTCLOUD_BASE_URL` +- `NEXTCLOUD_USERNAME` +- `NEXTCLOUD_PASSWORD` +- `NEXTCLOUD_CALENDAR_BASE_PATH` (defaults to `/remote.php/dav/calendars`) + +### File Uploads (`lib/r2-upload.ts`, `lib/upload.ts`) + +- **R2 storage**: Files uploaded to Cloudflare R2 bucket +- **Image processing**: HEIC to JPEG conversion, resizing, AVIF format support +- **Public URLs**: Files served from R2 public URL +- **Upload API**: `/api/upload` handles multipart form data + +### Environment Configuration (`lib/env.ts`) + +Validates required environment variables at boot using Zod. Critical vars: +- Database: `DATABASE_URL`, `DIRECT_URL` (Supabase URLs, though using D1) +- Auth: `NEXTAUTH_URL`, `NEXTAUTH_SECRET` +- Storage: AWS/R2 credentials (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_BUCKET_NAME`, `AWS_ENDPOINT_URL`) +- CalDAV: Nextcloud credentials (optional) + +Note: The env validation expects Supabase URLs but actual runtime uses Cloudflare D1 via bindings. + +### API Routes + +All API routes follow Next.js App Router conventions (`app/api/*/route.ts`): + +**Public APIs:** +- `/api/artists` - List public artists with portfolio images +- `/api/artists/[id]` - Get single artist by ID or slug +- `/api/public/migrate` - Public migration endpoint (token-protected) + +**Protected APIs** (require authentication): +- `/api/artists/me` - Current artist's profile (ARTIST role) +- `/api/portfolio` - CRUD for portfolio images +- `/api/flash/[artistId]` - Manage flash tattoo items +- `/api/appointments` - Appointment management +- `/api/upload` - File upload to R2 +- `/api/admin/*` - Admin-only endpoints (stats, migrations, calendars) +- `/api/caldav/sync` - Trigger CalDAV sync +- `/api/caldav/availability` - Check artist availability + +### Frontend Structure + +**Pages:** +- `/` - Homepage (hero, artists, services, contact) +- `/artists` - Artist listing +- `/artists/[id]` - Individual artist portfolio (supports slug or ID) +- `/artists/[id]/book` - Book with specific artist +- `/book` - General booking page +- `/admin/*` - Admin dashboard (analytics, portfolio, calendar, artist management, uploads) +- `/artist-dashboard/*` - Artist-specific dashboard (profile, portfolio editing) +- `/auth/signin` - Login page + +**Data Sources:** +- `data/artists.ts` - Static artist data (may be legacy, check if still used vs database) + +### Routing Notes + +- **Middleware** (`middleware.ts`): Handles permanent redirects (e.g., `/artists/amari-rodriguez` → `/artists/amari-kyss`) +- **Dynamic routes**: Artist pages work with both database IDs and slugs +- **Authentication**: Admin and artist dashboard routes require appropriate roles + +### Testing + +- **Framework**: Vitest with React Testing Library +- **Config**: `vitest.config.ts` (check for any custom setup) +- Tests located alongside components or in `__tests__/` directories + +### Bundle Size Budgets + +Defined in `package.json` under `budgets` key: +- `TOTAL_STATIC_MAX_BYTES`: 3,000,000 (3MB) +- `MAX_ASSET_BYTES`: 1,500,000 (1.5MB) + +Checked by `scripts/budgets.mjs` during CI. + +## Development Workflow + +### Working with Migrations + +1. Create new migration file in `sql/migrations/` with format `YYYYMMDD_NNNN_description.sql` +2. For local testing: `npm run db:migrate:local` +3. For preview: `npm run db:migrate:latest:preview` +4. For production: `npm run db:migrate:latest:prod` + +Migrations are also tracked in `sql/migrations_up/` for Wrangler's built-in migration system. + +### Working with Artists + +Artists have both a user account and an artist profile: +1. User created in `users` table with role `ARTIST` +2. Artist profile in `artists` table linked via `user_id` +3. Slug auto-generated from name, handles duplicates with numeric suffix +4. Portfolio images in `portfolio_images` table +5. Flash items in `flash_items` table (optional, new feature) + +### Adding New Features + +When adding database tables: +1. Add to `sql/schema.sql` +2. Create migration file in `sql/migrations/` +3. Update TypeScript types in `types/database.ts` +4. Add CRUD functions to `lib/db.ts` +5. Create API routes if needed +6. Update this CLAUDE.md if it's a major architectural change + +### CI/CD Pipeline + +Located in `.gitea/workflows/`: +- `ci.yaml` - Main CI pipeline (lint, typecheck, test, build, budgets) +- `security.yaml` - Security audits +- `performance.yaml` - Performance checks +- `deploy.yaml` - Deployment to Cloudflare + +The CI enforces: +- ESLint passing +- TypeScript compilation (with `ignoreBuildErrors: true` for builds but strict for typecheck) +- Test coverage +- Bundle size budgets +- Migration dry-run (best-effort with local D1) + +### Known Configuration Quirks + +- **TypeScript errors ignored during build** (`next.config.mjs`): `typescript.ignoreBuildErrors: true` allows builds to succeed even with type errors. CI separately runs `tsc --noEmit` to catch them. +- **Images unoptimized**: Next.js Image optimization disabled for Cloudflare Workers compatibility +- **Standalone output**: Docker builds use `output: "standalone"` mode +- **Node.js compatibility**: Cloudflare Workers use `nodejs_compat` flag in `wrangler.toml` + +### Deployment + +Production URL: `https://united-tattoos.com` + +Deploy command: `npm run pages:build && wrangler deploy` + +The deployment process: +1. Build with OpenNext: `npm run pages:build` → outputs to `.vercel/output/static` +2. Deploy to Cloudflare: `wrangler pages deploy .vercel/output/static` + +### Docker Support + +Dockerfile included for self-hosting: +```bash +docker build -t united-tattoo:latest . +docker run --rm -p 3000:3000 -e PORT=3000 united-tattoo:latest +``` + +Uses Next.js standalone mode for minimal image size. + +## Important Notes + +- Always test database changes locally first with `--local` flag +- The migration token (`MIGRATE_TOKEN`) protects public migration endpoints +- CalDAV integration is optional; appointments work without it +- Role hierarchy: CLIENT < ARTIST < SHOP_ADMIN < SUPER_ADMIN +- Flash items feature may not exist in older database schemas (lib/db.ts tolerates missing table)