# Catálogo (borrador) — Contexto para Claude > Builder de catálogo multi-proveedor de **servicios** (no productos físicos). > Hereda la filosofía del Art4Hotel Hub. Proyecto independiente, no toca el Hub. ## Qué es Sistema interno para armar la base de datos de un catálogo de servicios de varios **touroperadores / proveedores**. Tres tipos de servicio: - **tour** · **ayb** (A&B / banquetes) · **transportacion** Objetivo final (fases siguientes): usar esta DB para **propuestas** y una **página web** de catálogo online optimizado. Por ahora el foco es el **builder** (captura de datos + fotos). ## ⚠️ Principio clave (del Hub): el servicio = fuente única de verdad Cada atributo de un servicio impacta hasta 3 funciones. Antes de agregar/cambiar uno, define su impacto en: 1. **Operación** — disponibilidad, horarios, capacidad, precio neto 2. **Propuesta / cotizador** — lo que ve el cliente, precio público, términos 3. **Página web** — catálogo online **Neto vs público**: `precio_neto` = lo que cobra el proveedor · `precio_publico` = lo que paga el cliente. El margen vive en medio y nunca se mezcla con lo que ve el cliente. ## Stack - Backend: Python 3 stdlib (`http.server` + `sqlite3`), **zero deps**. WAL + FK on. - Frontend: SPA vanilla JS (`index.html`), sin frameworks. - CRUD genérico vía dict `TABLES` en `server.py` (un solo handler POST/PUT/DELETE para todas las tablas). - Auth: usuario/contraseña (PBKDF2) + cookie de sesión firmada (HMAC). `needs_setup` en primer acceso. ## Acceso al server - Host: `claude@100.110.177.1` (Tailscale `iclaude`) / red local `192.168.50.46` - Path: `/mnt/iclaude/catalogo-borrador/` - Puerto: **4403** (4401 = Hub, 4402 = airbnb-pricing) - URL: `http://iclaude:4403` / `http://192.168.50.46:4403` - Servicio: `sudo systemctl restart catalogo-borrador` - Deploy: `scp index.html server.py claude@100.110.177.1:/mnt/iclaude/catalogo-borrador/ && ssh claude@100.110.177.1 "sudo systemctl restart catalogo-borrador"` - Logs: `sudo journalctl -u catalogo-borrador -n 50 --no-pager` ## Modelo de datos (SQLite) - `proveedores` — quién ofrece (nombre, tipo_principal, contacto, comision_default, …) - `servicios` — **fuente única de verdad** (FK proveedor_id). Campos por sección de impacto: - 📋 Identidad: nombre, tipo, categoria, codigo, descripcion - ⚙️ Operación: horarios, capacidad_min/max, restricciones + `atributos` (JSON específico por tipo) - 💰 Comercial: precio_neto, precio_publico, moneda, unidad, tarifas_adicionales - 📄 Condiciones: terminos - 🌐 Publicación: mostrar_en_web - `usuarios` — auth ### Atributos flexibles por tipo (`servicios.atributos`, JSON) En vez de columnas rígidas, los extras específicos van en JSON. Sugeridos por tipo (en `index.html` → `ATRIBUTOS`): - tour: duracion, punto_encuentro, incluye, no_incluye, idioma - ayb: tipo_menu, min_personas, montaje, servicio_incluido, opciones_dieta - transportacion: tipo_vehiculo, pasajeros, ruta_zona, chofer, tiempo_espera ## Disponibilidad — modos (`servicios.modo_disponibilidad` + `horarios` JSON) Eje independiente del `tipo`. **`modo_disponibilidad`**: `salidas` | `rango` | `siempre` (24/7) | `por_evento`. Default sugerido por tipo (`MODO_DEFAULT`: tour→salidas, transportacion→siempre, ayb→por_evento), editable. El editor (`renderDisp`) cambia los controles según el modo: - **salidas**: editor semanal de días + varias salidas por día → `horarios`=`{"Lun":["08:00","14:00"],...}`. (`+ salida` agrega varias; `parseHor`/`fmtHorGroups`/`compactDias` para mostrar.) - **rango**: días + ventana → `horarios`=`{"dias":[...],"desde":"06:00","hasta":"22:00"}`. - **siempre** / **por_evento**: `horarios`={} (sin horario; la reserva es por fecha). `dispShort(s)` arma el texto de disponibilidad según el modo (usado en vista cliente). Compat: servicios viejos sin `modo` = `salidas` con su `horarios` día-keyed. ## Alimentos / Menú (opcional, flexible) `servicios.incluye_alimentos` (0/1) + `menu_detalle` (texto). Fotos del menú: se suben con `tipo=menu` (prefijo `menu_`) al mismo `uploads/servicio_{id}/`; se separan de las fotos del servicio por prefijo (`loadPhotos` excluye `menu_`, `loadMenuFotos` solo `menu_`). En `file-counts` el prefijo `menu` está excluido del `first_image` (no roba el thumbnail). En vista cliente se muestra el bloque "🍽️ Menú / Alimentos" (detalle + galería) solo si aplica. Pensado para tours que incluyen comida como plus; opcional porque depende del operador. ## Campos "reserva-lista" (`servicios`) `ubicacion` (lugar/punto de encuentro/dirección), `mapa_url` (link Google Maps), `checkin` (anti no-show, ej. "Llegar 15 min antes"), `anticipacion` (lead time, ej. "48 h"). Se ven en la **vista cliente** (bloques Disponibilidad / Dónde / Check-in). Cada modo + estos campos definen qué pedirá el futuro formulario de reserva (lead casi listo). ## Vistas (toggle en tab Servicios) - **✎ Builder** — grid de edición (muestra precio público + margen). - **👁 Vista cliente** — catálogo limpio agrupado por tipo, como lo vería un cliente. `openCliente()` abre detalle estilo público. **Oculta**: precio_neto, margen, comisión, notas internas, proveedor, código. ## Diseño / UI Formal y minimalista: tipografía **Inter**, paleta blanco/negro/gris (`--ink`,`--muted`,`--line`), acentos sobrios **mar** (`--sea` #1f4b54, primario/activos) y **arena** (`--sand`, detalles puntuales como A&B). Botón primario negro. Sin serif decorativo. ## Archivos / fotos - En `uploads/servicio_{id}/`, prefijo `foto_` (fotos) o `doc_` (documentos). - `/api/file-counts` elige `first_image` PREFIRIENDO `foto_` y excluyendo docs (mismo patrón que el Hub). ## Endpoints - `GET/POST/PUT/DELETE /api/{proveedores|servicios}` - `GET /api/servicios` (join proveedor_nombre) · `GET /api/proveedores` (con servicios_count) - `GET /api/dashboard` · `GET /api/file-counts` · `GET /api/files/{entidad}` - `POST /api/upload/{entidad}?tipo=foto&label=...` · `DELETE /api/files/{entidad}/{name}` ## Convenciones de edición - Editar LOCAL aquí (`/Users/claudeandrefg/Documents/catalogo-borrador/`) y desplegar con scp. - No instalar dependencias — stdlib + vanilla JS. Si una feature lo requiere, discutir antes. - Para agregar una tabla nueva: `CREATE TABLE` en `init_db()` + entrada en `TABLES` (CRUD automático). ## Pendientes / fases siguientes 1. (Hecho) Builder: proveedores + servicios con fotos y todos los campos. 2. Builder de **propuestas/catálogos** (selección de servicios → documento → PDF). Reusar patrón `catalogos` del Hub. 3. **Página web** pública de catálogo online optimizado (sync desde la DB).