Initial commit: monorepo Naturcalabacera reservas (apps/api + apps/web + packages/shared)
This commit is contained in:
50
architecture/db_schema.sql
Normal file
50
architecture/db_schema.sql
Normal file
@@ -0,0 +1,50 @@
|
||||
-- Protocolo E.T.A.P.A - Fase de Arquitectura - DB Schema
|
||||
-- Tabla: reservations
|
||||
|
||||
create type reservation_origin as enum ('Teneriffa2000', 'Naturcalabacera');
|
||||
|
||||
create table public.reservations (
|
||||
id uuid default gen_random_uuid() primary key,
|
||||
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
|
||||
start_date date not null,
|
||||
end_date date not null,
|
||||
client_name text not null,
|
||||
origin reservation_origin not null,
|
||||
invoice_number text,
|
||||
adults_count integer default 0,
|
||||
children_count integer default 0,
|
||||
has_cleaning boolean default false,
|
||||
has_pool_heating boolean default false,
|
||||
has_flies_products boolean default false,
|
||||
has_flies_products boolean default false,
|
||||
government_registration text,
|
||||
observations text
|
||||
);
|
||||
|
||||
-- Enable RLS
|
||||
alter table public.reservations enable row level security;
|
||||
|
||||
-- Policies (DEV MODE: PUBLIC ACCESS)
|
||||
-- Acceso Select: Permitir a todos (anon y authenticated)
|
||||
create policy "Enable read access for all users"
|
||||
on public.reservations for select
|
||||
to anon, authenticated
|
||||
using (true);
|
||||
|
||||
-- Acceso Insert: Permitir a todos
|
||||
create policy "Enable insert for all users"
|
||||
on public.reservations for insert
|
||||
to anon, authenticated
|
||||
with check (true);
|
||||
|
||||
-- Acceso Update: Permitir a todos
|
||||
create policy "Enable update for all users"
|
||||
on public.reservations for update
|
||||
to anon, authenticated
|
||||
using (true);
|
||||
|
||||
-- Acceso Delete: Permitir a todos
|
||||
create policy "Enable delete for all users"
|
||||
on public.reservations for delete
|
||||
to anon, authenticated
|
||||
using (true);
|
||||
48
architecture/frontend_components.md
Normal file
48
architecture/frontend_components.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# SOP: Arquitectura de Componentes Frontend
|
||||
|
||||
## Estructura General
|
||||
La aplicación es una SPA (Single Page Application) centrada en el Dashboard.
|
||||
|
||||
### 1. Layout Principal (`DashboardLayout`)
|
||||
- Contenedor full-screen.
|
||||
- Posible Sidebar o Header minimalista para controles globales (Logout, Filtros rápidos).
|
||||
- **Responsabilidad**: Estructura base y responsive container.
|
||||
|
||||
### 2. Calendario Interactivo (`CalendarView`)
|
||||
- **Librería**: `react-big-calendar`.
|
||||
- **Props**: `reservations` (Array), `onSelectEvent`, `onSelectSlot`.
|
||||
- **Renderizado**:
|
||||
- Personalización de `eventPropGetter` para aplicar colores (Teneriffa/Natur).
|
||||
- Componente custom para el evento (`EventComponent`) que muestra solo el nombre del cliente.
|
||||
- **Comportamiento**:
|
||||
- Click en evento -> Abrir `ReservationModal` (Modo Edición).
|
||||
- Click en slot vacío -> Abrir `ReservationModal` (Modo Creación) con fechas pre-rellenadas.
|
||||
|
||||
### 3. Modal de Reserva (`ReservationModal`)
|
||||
- **UI**: Dialog/Modal centrado (Desktop) o Bottom Sheet (Móvil).
|
||||
- **Estado**: Controlado por `isOpen`, `mode` (create/edit), `data`.
|
||||
- **Contenido**:
|
||||
- Título ("Nueva Reserva" / "Editar Reserva").
|
||||
- Formulario (`ReservationForm`).
|
||||
- Botones de acción (Guardar, Eliminar, Cancelar).
|
||||
|
||||
### 4. Formulario de Reserva (`ReservationForm`)
|
||||
- **Librería**: `react-hook-form`.
|
||||
- **Campos**:
|
||||
- Origen (Select).
|
||||
- Cliente (Input Text).
|
||||
- Fechas (Date Range Picker).
|
||||
- Adultos / Niños (Number Inputs).
|
||||
- Extras (Checkboxes).
|
||||
- Registro Gubernamental (Text Input) -> **CRÍTICO PARA WEBHOOK**.
|
||||
- **Lógica Visual**:
|
||||
- Mostrar totales calculados en tiempo real:
|
||||
- `Total Personas = Adultos + Niños`
|
||||
- `Total Días = End - Start`
|
||||
|
||||
## Diseño Visual (Tailwind)
|
||||
- **Colores**:
|
||||
- Teneriffa2000: `bg-blue-400`
|
||||
- Naturcalabacera: `bg-yellow-400`
|
||||
- Días libres: `bg-gray-50`
|
||||
- **Tipografía**: Sans-serif, limpia.
|
||||
34
architecture/logic_hooks.md
Normal file
34
architecture/logic_hooks.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# SOP: Lógica y Hooks
|
||||
|
||||
## Gestión de Estado (Hooks)
|
||||
|
||||
### `useReservations`
|
||||
- **Propósito**: Abstracción de Supabase para CRUD de reservas.
|
||||
- **Estado**: Lista de reservas (`data`), `loading`, `error`.
|
||||
- **Métodos**:
|
||||
- `fetchReservations()`: `Select * from reservations`.
|
||||
- `createReservation(data)`: Insert.
|
||||
- `updateReservation(id, data)`: Update. Detección de cambio en `government_registration` para payload de Webhook.
|
||||
- `deleteReservation(id)`: Delete.
|
||||
- **Realtime**: Suscripción a cambios en tabla `reservations` para actualizar vista automáticamente.
|
||||
|
||||
### `useFormCalculation` (dentro del Form)
|
||||
- **Propósito**: Cálculos derivados instantáneos.
|
||||
- **Input**: Valores actuales del form (watch).
|
||||
- **Output**: `totalPeople`, `totalDays`.
|
||||
|
||||
## Lógica de Webhook (Simulación en Cliente)
|
||||
> **Nota**: Idealmente esto se hace con Database Triggers, pero por simplicidad inicial y control, lo gestionaremos en la capa de servicio del cliente (`useReservations`) antes de enviar a Supabase, o inmediatamente después del éxito.
|
||||
|
||||
**Regla de Disparo**:
|
||||
1. Capturar valor previo de `government_registration` (en Update).
|
||||
2. Si `previos_value` es EMPTY/NULL Y `new_value` NO es EMPTY:
|
||||
-> Ejecutar `triggerRegistrationWebhook(reservation)`.
|
||||
|
||||
**Endpoint Mock**:
|
||||
- URL: (Definir en .env)
|
||||
- Method: POST
|
||||
- Body: Definido en `gemini.md`.
|
||||
|
||||
## Tipos (TypeScript)
|
||||
Definir interfaces estrictas en `src/types/supabase.ts` (o generado) y `src/types/app.ts`.
|
||||
2
architecture/migrations/001_add_observations.sql
Normal file
2
architecture/migrations/001_add_observations.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
-- Migration: Add observations column
|
||||
alter table public.reservations add column observations text;
|
||||
62
architecture/migrations/002_add_contract_storage.sql
Normal file
62
architecture/migrations/002_add_contract_storage.sql
Normal file
@@ -0,0 +1,62 @@
|
||||
-- Migration 002: Add contract_url column + create storage buckets for contracts
|
||||
-- Schema: natur_reservas
|
||||
-- Run this in the Supabase SQL editor
|
||||
|
||||
-- ── 1. Add contract_url column ────────────────────────────────────────────────
|
||||
ALTER TABLE natur_reservas.reservations
|
||||
ADD COLUMN IF NOT EXISTS contract_url text;
|
||||
|
||||
-- ── 2. Create storage buckets (one per property) ─────────────────────────────
|
||||
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
|
||||
VALUES
|
||||
(
|
||||
'contratos-los-dragos',
|
||||
'contratos-los-dragos',
|
||||
true,
|
||||
10485760, -- 10 MB
|
||||
ARRAY['application/pdf', 'image/jpeg', 'image/png']
|
||||
),
|
||||
(
|
||||
'contratos-la-esquinita',
|
||||
'contratos-la-esquinita',
|
||||
true,
|
||||
10485760,
|
||||
ARRAY['application/pdf', 'image/jpeg', 'image/png']
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- ── 3. Storage RLS policies ───────────────────────────────────────────────────
|
||||
|
||||
-- contratos-los-dragos
|
||||
CREATE POLICY "contratos-los-dragos: select"
|
||||
ON storage.objects FOR SELECT TO anon, authenticated
|
||||
USING (bucket_id = 'contratos-los-dragos');
|
||||
|
||||
CREATE POLICY "contratos-los-dragos: insert"
|
||||
ON storage.objects FOR INSERT TO anon, authenticated
|
||||
WITH CHECK (bucket_id = 'contratos-los-dragos');
|
||||
|
||||
CREATE POLICY "contratos-los-dragos: update"
|
||||
ON storage.objects FOR UPDATE TO anon, authenticated
|
||||
USING (bucket_id = 'contratos-los-dragos');
|
||||
|
||||
CREATE POLICY "contratos-los-dragos: delete"
|
||||
ON storage.objects FOR DELETE TO anon, authenticated
|
||||
USING (bucket_id = 'contratos-los-dragos');
|
||||
|
||||
-- contratos-la-esquinita
|
||||
CREATE POLICY "contratos-la-esquinita: select"
|
||||
ON storage.objects FOR SELECT TO anon, authenticated
|
||||
USING (bucket_id = 'contratos-la-esquinita');
|
||||
|
||||
CREATE POLICY "contratos-la-esquinita: insert"
|
||||
ON storage.objects FOR INSERT TO anon, authenticated
|
||||
WITH CHECK (bucket_id = 'contratos-la-esquinita');
|
||||
|
||||
CREATE POLICY "contratos-la-esquinita: update"
|
||||
ON storage.objects FOR UPDATE TO anon, authenticated
|
||||
USING (bucket_id = 'contratos-la-esquinita');
|
||||
|
||||
CREATE POLICY "contratos-la-esquinita: delete"
|
||||
ON storage.objects FOR DELETE TO anon, authenticated
|
||||
USING (bucket_id = 'contratos-la-esquinita');
|
||||
54
architecture/migrations/003_contracts_bucket_and_table.sql
Normal file
54
architecture/migrations/003_contracts_bucket_and_table.sql
Normal file
@@ -0,0 +1,54 @@
|
||||
-- Migration 003: Create contracts storage bucket + reservation_contracts table
|
||||
-- Schema: natur_reservas
|
||||
-- Run in Supabase SQL editor
|
||||
|
||||
-- ── 1. Storage bucket (privado, usa signed URLs) ──────────────────────────────
|
||||
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
|
||||
VALUES (
|
||||
'contracts',
|
||||
'contracts',
|
||||
false,
|
||||
10485760,
|
||||
ARRAY['application/pdf', 'image/jpeg', 'image/png']
|
||||
)
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- Storage RLS policies
|
||||
CREATE POLICY "contracts: select"
|
||||
ON storage.objects FOR SELECT TO anon, authenticated
|
||||
USING (bucket_id = 'contracts');
|
||||
|
||||
CREATE POLICY "contracts: insert"
|
||||
ON storage.objects FOR INSERT TO anon, authenticated
|
||||
WITH CHECK (bucket_id = 'contracts');
|
||||
|
||||
CREATE POLICY "contracts: update"
|
||||
ON storage.objects FOR UPDATE TO anon, authenticated
|
||||
USING (bucket_id = 'contracts');
|
||||
|
||||
CREATE POLICY "contracts: delete"
|
||||
ON storage.objects FOR DELETE TO anon, authenticated
|
||||
USING (bucket_id = 'contracts');
|
||||
|
||||
-- ── 2. Tabla reservation_contracts ────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS natur_reservas.reservation_contracts (
|
||||
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
|
||||
created_at timestamp with time zone DEFAULT timezone('utc', now()) NOT NULL,
|
||||
reservation_id uuid NOT NULL REFERENCES natur_reservas.reservations(id) ON DELETE CASCADE,
|
||||
file_path text NOT NULL,
|
||||
filename text NOT NULL,
|
||||
mime_type text NOT NULL,
|
||||
size_bytes integer NOT NULL,
|
||||
uploaded_by uuid REFERENCES auth.users(id)
|
||||
);
|
||||
|
||||
ALTER TABLE natur_reservas.reservation_contracts ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
CREATE POLICY "rc: select" ON natur_reservas.reservation_contracts
|
||||
FOR SELECT TO anon, authenticated USING (true);
|
||||
|
||||
CREATE POLICY "rc: insert" ON natur_reservas.reservation_contracts
|
||||
FOR INSERT TO anon, authenticated WITH CHECK (true);
|
||||
|
||||
CREATE POLICY "rc: delete" ON natur_reservas.reservation_contracts
|
||||
FOR DELETE TO anon, authenticated USING (true);
|
||||
Reference in New Issue
Block a user