# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Laravel 12 + Vue 3 authentication application. Backend is served via Laravel Valet at `https://coloid-auth.test`. Frontend uses Inertia.js (no separate API for web — Vue pages are server-driven). Sanctum provides API token auth for programmatic access.

## Commands

### Development

```bash
composer dev          # Start Laravel serve + queue worker + Vite dev server (concurrently)
composer dev:ssr      # Same with Inertia SSR server
npm run dev           # Vite dev server only
```

### Build

```bash
npm run build         # Production frontend build
npm run build:dev     # Development build
npm run build:ssr     # SSR build
```

### Code Quality

```bash
npm run format        # Prettier (resources/)
npm run format:check  # Check formatting without modifying
npm run lint          # ESLint auto-fix
vendor/bin/pint       # PHP code style (PSR-12)
```

### Tests

```bash
composer test                         # Clear config cache + run full Pest suite
vendor/bin/pest                       # Run all tests
vendor/bin/pest tests/Feature/Auth/   # Run a specific directory
vendor/bin/pest --filter LoginTest    # Run a single test class
```

### Laravel Artisan (Valet)

```bash
valet php artisan migrate
valet php artisan migrate --seed      # First install
valet php artisan config:clear
valet php artisan route:clear
valet php artisan view:clear
valet php artisan wayfinder:generate  # Regenerate TypeScript route/action helpers
```

## Architecture

### Inertia.js bridge

There is no separate REST API for web routes. Laravel controllers return `Inertia::render('PageName', $props)` and Vue pages receive props directly. The `@inertiajs/vue3` package handles page transitions. Route names are available in TypeScript via auto-generated Wayfinder helpers (`resources/js/wayfinder/`). Run `valet php artisan wayfinder:generate` after adding/renaming routes.

### Authentication layers

- **Web (Fortify)**: Registration, login, 2FA (TOTP), email verification, password reset. Configured in `config/fortify.php`. Custom controllers in `app/Http/Controllers/Auth/`.
- **API (Sanctum)**: Personal access tokens. Routes in `routes/api.php`. Token management UI in `app/Http/Controllers/Api/`.

### Billing (Cashier + Stripe)

`laravel/cashier` v16 handles the CoLo-ID Pro subscription (1 year, user-chosen auto-renewal).
Stripe — not app code or a cron — owns renewal billing; the app only reads the resulting state.

- Keys live in `.env`: `STRIPE_KEY`, `STRIPE_SECRET`, `STRIPE_WEBHOOK_SECRET`,
  `STRIPE_PRO_PRICE_ID`, `CASHIER_CURRENCY=eur`. The Product/Price (1-year interval) is created by
  hand in the Stripe Dashboard, not in code.
- `STRIPE_SECRET` is a **restricted key**. It needs write on Customers, Checkout Sessions and
  Customer portal, plus read on Payment Methods — nothing else. `SUBSCRIPTIONS.md` §1
  ("Permissions de la clé API Stripe") has the ticklist and the traced API calls behind it; update
  it if a change adds a Stripe call, rather than widening the key.
- The price id is read via `config('services.stripe.pro_price')`, never `env()` directly.
- `stripe/*` is excluded from CSRF in `bootstrap/app.php` — Stripe signs its own webhooks.
- Cashier already registers `POST stripe/webhook` (`cashier.webhook`). A custom webhook controller
  must either replace it or set `CASHIER_WEBHOOK_ROUTE=false`.
- `SUBSCRIPTIONS.md` is the implementation plan and the running status of the 8 steps. Read it
  before touching billing, and update its status table when a step lands.

### Routes split

| File | Scope |
|---|---|
| `routes/web.php` | Public pages (welcome, dashboard) |
| `routes/auth.php` | Auth flows (login, register, 2FA, etc.) |
| `routes/settings.php` | Authenticated user settings |
| `routes/api.php` | API token endpoints |

### Frontend structure (`resources/js/`)

- `pages/` — Inertia page components (maps 1:1 to routes)
- `layouts/` — `AppLayout.vue` (authenticated), `AuthLayout.vue` (guest)
- `components/ui/` — shadcn-vue components (treat as vendor — ESLint ignores this folder)
- `components/*.vue` — feature-specific components
- `composables/` — shared Vue logic (`useAppearance`, `useInitials`)
- `types/` — TypeScript type definitions
- `wayfinder/` — auto-generated, do not edit manually

### Backend structure (`app/`)

- `Http/Controllers/Auth/` — Auth flows
- `Http/Controllers/Settings/` — Profile, password, 2FA management
- `Http/Controllers/Api/` — Token CRUD
- `Http/Requests/` — Form validation (always use these, not inline validation)
- `Http/Resources/` — API response formatting (`AuthResource`, `TokenResource`)
- `Models/` — `User`, `UserProfile`
- `Enums/` — Domain types (`UserPlan`, `Gender`, `Status`, practice-related enums)
- `Observers/UserObserver.php` — Model lifecycle hooks
- `Events/DeleteUser.php` + `Listeners/RevokeUserTokens.php` — event-driven cleanup

### AGENTS.md note

`AGENTS.md` in the repo root contains additional code style guidelines (import ordering, Vue component patterns, form handling, error display). Read it when adding new pages or components.

## Key Conventions

- All Vue components use `<script setup lang="ts">`
- Props: `defineProps<{ ... }>()`
- Form submissions: use Inertia `useForm()` — access errors via `form.errors`, loading state via `form.processing`
- Form errors displayed via `<InputError>` component; status messages via `<Alert>`
- Path alias `@/*` → `resources/js/*`
- Import order: Vue core → Inertia → UI components → layouts → internal → utilities
- Tailwind v4 (no `tailwind.config.js` — configured via CSS)
- shadcn-vue components imported as named: `import { Button } from '@/components/ui/button'`

## Changelog

`CHANGELOG.md` follows [Keep a Changelog](https://keepachangelog.com/). Add user-visible changes
to the `## [Unreleased]` section as part of the change itself — not in a separate pass. Group them
under `Added` / `Changed` / `Fixed` / `Removed`, and use `Known issues` for things left broken on
purpose. Cutting a release means renaming `Unreleased` to the new version and bumping `version` in
`package.json` to match. Pure refactors, formatting and test-only changes do not need an entry.

## Local Services

- App: `https://coloid-auth.test` (Valet)
- Mailpit (email preview): `http://localhost:8025/`
- Database: SQLite (`database/database.sqlite`)

## Wayfinder (vite.config.ts)

The Wayfinder Vite plugin auto-runs `valet php artisan wayfinder:generate` on file changes. If switching between Valet and Herd, update the `command` in `vite.config.ts`:

```ts
command: 'valet php artisan wayfinder:generate', // Valet
command: 'herd php artisan wayfinder:generate',  // Herd
command: 'php artisan wayfinder:generate',        // Staging/Prod
```
