# 10 — API: schema di dominio Leggi `CLAUDE.md`. ## Obiettivo Tabelle di dominio dell'MVP (solo schema + migrazioni, niente route). ## Cosa fare - `installer_profiles`: user_id PK/FK, business_name, vat_number, phone, address, `location geography(Point,4326)`, `service_radius_km`, certifications text[], status (`pending|approved|rejected`), approved_at, notes. Indice GIST su location. (Drizzle: custom type per geography.) - `quote_requests`: id, user_id null (anche anonimi), email, name, phone, province, configurator_input jsonb, configurator_output jsonb, status (`new|contacted|converted|closed`), created_at. - `quotes`: id, quote_request_id null FK, user_id FK, number (progressivo leggibile), title, status (`draft|sent|accepted|rejected|expired`), amount_cents, pdf_document_id null, valid_until, created_at. - `tickets` + `ticket_messages`: ticket con user_id, subject, status (`open|in_progress|waiting_customer|closed`), priority; messaggi con author_id, body, created_at. - `blog_categories`, `blog_posts`: slug unique, title, excerpt, content_md, cover_path, status (`draft|published`), published_at, seo_title, seo_description, `search_vector tsvector` generata (configurazione `italian`, con `unaccent`) + indice GIN. - `documents`: id, title, category, file_path, mime_type, size_bytes, visibility (`public|customer|installer|admin`), product_ref null, created_at. - `contact_requests`, `showroom_bookings` (data/ora richiesta, stato), `consents` (user_id o email, tipo, versione testo, accepted_at, ip). - `webhook_events`: id (id evento Medusa, PK per idempotenza), event, payload jsonb, processed_at, error. ## Criteri di accettazione - Migrazione applicata senza errori; indici GIST e GIN presenti. - Una query di prova su `blog_posts.search_vector` con `websearch_to_tsquery('italian', ...)` funziona. - I tipi delle tabelle sono esportati da `apps/api` (e i DTO pubblici in `packages/shared`). - `CLAUDE.md` aggiornato (ERD testuale sintetico).