Builder multi-proveedor de servicios (tour / A&B / transportacion). Python stdlib + SQLite + vanilla JS SPA. Hereda filosofia del Hub. Secretos y datos (catalogo.db, secret.key, uploads/) excluidos via .gitignore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
98 lines
6.6 KiB
Markdown
98 lines
6.6 KiB
Markdown
# 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).
|