From 65295c9fe15b4d4f6db912057f3c259e32514cd8 Mon Sep 17 00:00:00 2001 From: Dmielgo Date: Wed, 16 Sep 2026 10:51:32 +0100 Subject: [PATCH 1/2] docs: elimina menciones a Docker (workflow local descontinuado) El desarrollo local via Docker ya no se usa -- todo el trabajo es via Forge/MCP. Mantener las menciones a localhost:8080, docker exec y .docker/ confundia sobre el workflow real. Se deja el campo de respuesta 'dockerUrl' de generate_image tal cual (es el nombre real que devuelve la API hoy, no una instruccion de workflow). --- CLAUDE.md | 35 +++++------------------------------ docs/builder-fields.md | 2 +- docs/hooks-and-api.md | 8 +++++--- 3 files changed, 11 insertions(+), 34 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8007f15..72c0556 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,9 +4,9 @@ This is an Acai CMS website project. Follow these instructions when working with ## Environment -- The site runs in Docker, typically at **http://localhost:8080** -- You can make HTTP requests to test pages, APIs, or form submissions -- If you need to inspect the live site, use browser tools (Playwright MCP) or HTTP requests to localhost:8080 +- The site runs on Acai Forge — get the live editable URL with the `get_web_url` MCP tool (never assume a fixed domain) +- You can make HTTP requests to test pages, APIs, or form submissions against that URL +- If you need to inspect the live site, use browser tools (Playwright MCP) or HTTP requests to the Forge URL ## Project Structure @@ -28,13 +28,7 @@ This is an Acai CMS website project. Follow these instructions when working with │ ├── data/schema/ # Database table schemas (JSON) │ ├── lib/plugins/ # CMS plugins │ └── uploads/ # Uploaded media files -├── .acai # Project config (domain, tokens, DB credentials) -├── .docker/ -│ ├── .env # Docker environment (DB credentials) -│ ├── docker-compose.yml -│ ├── tunnel-url.txt # Public tunnel URL (if active) -│ └── bore-db-url.txt # Database tunnel URL (if active) -└── database.sql # Database dump +└── .acai # Project config (domain, tokens, DB credentials) ``` ## Key Concepts @@ -89,28 +83,9 @@ Rule of thumb: See [docs/hooks-and-api.md](docs/hooks-and-api.md) for usage. -## Database Access - -When the site is running in Docker, you can connect to the database: - -- **Host:** `127.0.0.1` -- **Port:** Check `.docker/docker-compose.yml` for the mapped port (usually 3307+) -- **Credentials:** Read from `.docker/.env`: - - `DB_USERNAME` - - `DB_PASSWORD` - - `DB_DATABASE` - -```bash -docker exec -it dw--db mysql -u root -p -``` - -**Important:** Table names in CmsApi/Twig do NOT use the `cms_` prefix. The primary key is always `num`, never `id`. - ## Acai Core (web-base) -The project workspace contains only the **customization layer** (modules, hooks, schemas, uploads). The CMS core (routing, rendering engine, admin panel, APIs) lives in a separate directory called **web-base** that is mounted as a Docker volume. - -The web-base path can be obtained via: `GET http://localhost:9090/api/web-base-path` +The project workspace contains only the **customization layer** (modules, hooks, schemas, uploads). The CMS core (routing, rendering engine, admin panel, APIs) lives in a separate, shared directory called **web-base**. Do NOT modify web-base files — they are shared across all projects. diff --git a/docs/builder-fields.md b/docs/builder-fields.md index 4bf7192..12ae3c6 100644 --- a/docs/builder-fields.md +++ b/docs/builder-fields.md @@ -471,7 +471,7 @@ El nombre del campo de imagen viene de `builder.json` → `vars.NOMBRE.relations - `tableName`: `"builder_custom"` (siempre, sin prefijo cms_) - `recordId`: el `recordNum` del paso 1 (ej: `"778"`) - `fieldName`: el campo de relations del builder.json (ej: `"image1"`) - - `imageUrl`: URL completa accesible desde Docker + - `imageUrl`: URL completa y públicamente accesible 3. `reorder_record_uploads` si es necesario — pasar array de upload IDs en el orden deseado 4. `list_record_uploads` para verificar diff --git a/docs/hooks-and-api.md b/docs/hooks-and-api.md index 3e94aec..a1f0399 100644 --- a/docs/hooks-and-api.md +++ b/docs/hooks-and-api.md @@ -56,13 +56,15 @@ return [ ### Testing Hooks -El Docker debe estar corriendo. Hacer curl al endpoint del hook: +Usa la URL editable de Forge del proyecto (`get_web_url`) — no hace falta ningún servidor local. Haz +`curl` al endpoint del hook contra esa URL: ```bash -curl http://localhost:8080/hooks/example_hook/ +curl https:///hooks/example_hook/ ``` -No usar X-Hooks-Token en desarrollo local. +Para diagnosticar sin depender del enrutado HTTP directo (que en Forge puede dar 404 — ver más abajo), +usa el patrón vía filtro `| hook` desde un módulo/página temporal. ### Cómo Llamar Hooks From eefce8e81f6e933b4119e6e62835698bc22ff974 Mon Sep 17 00:00:00 2001 From: Acai Forge Date: Sat, 19 Sep 2026 20:44:51 +0000 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20fuera=20el=20docs/=20muerto=20y=20C?= =?UTF-8?q?LAUDE.md=20enlaza=20a=20los=2013=20can=C3=B3nicos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El docs/ de este repo era el set antiguo sin numerar: copy_scaffold lo omite desde 2026-06-11 y copia la canónica de agenticSystem/docs en cada web, así que ningún cambio aquí llegaba a nadie (los PRs #1 y #3 lo editaban). Se sustituye por un README que señala la fuente. CLAUDE.md apuntaba a esos 12 nombres, que no existen en ninguna web: ahora enlaza a los 13 canónicos numerados. Nota de hooks por HTTP (404 a propósito sin X-Hooks-Token → call_hook). --- CLAUDE.md | 40 +- docs/README.md | 11 + docs/builder-fields.md | 481 ----------------------- docs/css-js-conventions.md | 261 ------------- docs/hooks-and-api.md | 390 ------------------- docs/mcp-tools-reference.md | 118 ------ docs/modular-system.md | 105 ----- docs/module-creation-guide.md | 100 ----- docs/pages-and-records.md | 104 ----- docs/production-patterns.md | 262 ------------- docs/quick-reference.md | 85 ----- docs/twig-filters.md | 209 ---------- docs/vue-builder-examples.md | 695 ---------------------------------- docs/vue-builder-rules.md | 484 ----------------------- 14 files changed, 33 insertions(+), 3312 deletions(-) create mode 100644 docs/README.md delete mode 100644 docs/builder-fields.md delete mode 100644 docs/css-js-conventions.md delete mode 100644 docs/hooks-and-api.md delete mode 100644 docs/mcp-tools-reference.md delete mode 100644 docs/modular-system.md delete mode 100644 docs/module-creation-guide.md delete mode 100644 docs/pages-and-records.md delete mode 100644 docs/production-patterns.md delete mode 100644 docs/quick-reference.md delete mode 100644 docs/twig-filters.md delete mode 100644 docs/vue-builder-examples.md delete mode 100644 docs/vue-builder-rules.md diff --git a/CLAUDE.md b/CLAUDE.md index 72c0556..a14a447 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -40,7 +40,7 @@ Visual components that the site builder uses. Each module is a self-contained un - Each module instance gets a unique `section_id` variable for anchors/scoping - Use `interno` variable to detect CMS editor mode vs public view -See [docs/modular-system.md](docs/modular-system.md) for detailed rules. +See [docs/03-modules-and-sections.md](docs/03-modules-and-sections.md) for detailed rules. ### Pages Every record with an `enlace` field is a page. Pages are either **Builder** (modular) or **Standard**: @@ -50,7 +50,7 @@ Every record with an `enlace` field is a page. Pages are either **Builder** (mod **Critical**: Never change `enlace` or `controlador` of existing pages unless explicitly asked. -See [docs/pages-and-records.md](docs/pages-and-records.md) for full details. +See [docs/04-pages-and-records.md](docs/04-pages-and-records.md) for full details. ### General Sections Database-backed templates (headers, footers, record views) that use the `thisrecord` variable to access record fields. They use the same Twig + Acai attribute engine as modules. @@ -58,7 +58,7 @@ Database-backed templates (headers, footers, record views) that use the `thisrec - Upload fields return arrays: `thisrecord.image[0].urlPath` - Foreign keys use `_num` suffix: `category_num` -See [docs/modular-system.md](docs/modular-system.md) for details. +See [docs/03-modules-and-sections.md](docs/03-modules-and-sections.md) for details. ### Hooks (`hooks/`) PHP files that execute server-side logic. Triggered by: @@ -80,8 +80,9 @@ Rule of thumb: - If the logic is only used by one module, prefer that module's `hook.php` - If the logic will be reused by several modules/pages, create a global hook in `hooks/` - Return arrays from hooks; do not use `echo json_encode(...)` or `exit` +- Calling a hook over plain HTTP (`curl https:///hooks/x/`) returns **404 on purpose**: the endpoint requires the `X-Hooks-Token` header. From the agent use the `call_hook` MCP tool; the site's own JS (`CmsApi.hook`) already sends the token -See [docs/hooks-and-api.md](docs/hooks-and-api.md) for usage. +See [docs/06-hooks-and-cmsapi.md](docs/06-hooks-and-cmsapi.md) for usage. ## Acai Core (web-base) @@ -110,25 +111,28 @@ Do NOT modify web-base files — they are shared across all projects. This project has MCP tools for managing modules, records, media, and more. **Before starting any task, consult the tools reference for the correct workflow.** -See [docs/mcp-tools-reference.md](docs/mcp-tools-reference.md) for the complete list of available tools and step-by-step workflows. +See [docs/09-mcp-tools-reference.md](docs/09-mcp-tools-reference.md) for the complete list of available tools and step-by-step workflows, and [docs/11a-decision-table.md](docs/11a-decision-table.md) to route a request to the right tool. Key workflows: -- **Create module**: Read [docs/module-creation-guide.md](docs/module-creation-guide.md) first → create files with `acai-write` / refine with `acai-line-replace` → automatic compile on `index-base.tpl` → `add_module_to_record` (returns sectionId) → `set_module_config_vars` (returns uploadFields) → images via uploadFields. Use `compile_module` only if you need a manual recompile without editing the file. +- **Create module**: Read [docs/03-modules-and-sections.md](docs/03-modules-and-sections.md) first → create files with `acai-write` / refine with `acai-line-replace` → automatic compile on `index-base.tpl` → `add_module_to_record` (returns sectionId) → `set_module_config_vars` (returns uploadFields) → images via uploadFields. Use `compile_module` only if you need a manual recompile without editing the file. - **Edit module**: `acai-view` → `acai-line-replace` (or `acai-write` for full rewrites) → automatic compile on `index-base.tpl` - **Add images**: use `uploadFields` from `set_module_config_vars` response → `upload_record_image` - **Generate images**: `generate_image` → `upload_record_image` with returned URL ## Documentation -- [docs/modular-system.md](docs/modular-system.md) — Modules, general sections, global variables -- [docs/builder-fields.md](docs/builder-fields.md) — Builder field types, Acai attributes, c-form, components -- [docs/twig-filters.md](docs/twig-filters.md) — Twig filters reference (get, hook, module, queryDB, etc.) -- [docs/hooks-and-api.md](docs/hooks-and-api.md) — PHP hooks, CmsApi, CocoDB, record creation -- [docs/css-js-conventions.md](docs/css-js-conventions.md) — CSS/JS/Vue 3, Tailwind, BEM, native components -- [docs/quick-reference.md](docs/quick-reference.md) — Cheat sheet: domain rules, field types, filters -- [docs/production-patterns.md](docs/production-patterns.md) — Real production patterns (header, zigzag, FAQ, forms) -- [docs/vue-builder-rules.md](docs/vue-builder-rules.md) — CMS-VUE rules (tabs, colorpicker, components) -- [docs/vue-builder-examples.md](docs/vue-builder-examples.md) — Vue builder examples (Banner Slideshow, etc.) -- [docs/pages-and-records.md](docs/pages-and-records.md) — Page types (Builder vs Standard), sections, visibility, critical rules -- [docs/module-creation-guide.md](docs/module-creation-guide.md) — Module creation workflow, style reference, field types -- [docs/mcp-tools-reference.md](docs/mcp-tools-reference.md) — MCP tools reference, available tools, workflows +The `docs/` folder holds the 13 canonical Acai docs (numbered; the same set the Forge agent uses). They are refreshed automatically from the canonical source — do not edit them here. + +- [docs/01-builder-fields.md](docs/01-builder-fields.md) — Builder field types (`data-field-*`), tabs, conditional visibility, colors/colorpicker, components +- [docs/02-twig.md](docs/02-twig.md) — Twig filters reference (get, hook, queryDB, translate, module, set…) and `{% if %}` vs `c-if` +- [docs/03-modules-and-sections.md](docs/03-modules-and-sections.md) — Modules, `index-base.tpl`, general sections (`custom-{table}`), module creation workflow, uploads +- [docs/04-pages-and-records.md](docs/04-pages-and-records.md) — Page types (Builder vs Standard), controller, `enlace`, records with `num`, visibility, critical rules +- [docs/05-tables-and-fields.md](docs/05-tables-and-fields.md) — Creating and changing tables and fields with the MCP tools, `regenerate_enlaces` +- [docs/06-hooks-and-cmsapi.md](docs/06-hooks-and-cmsapi.md) — PHP hooks (global and per module), `CmsApi`/`CocoDB`, middleware, `X-Hooks-Token`/`call_hook`, record creation +- [docs/07-css-js-conventions.md](docs/07-css-js-conventions.md) — Tailwind, BEM scoped CSS, `data-*` for dynamic values, `CmsApi.hook` from JS, Vue 3, native components +- [docs/08-layout-and-libraries.md](docs/08-layout-and-libraries.md) — Header/footer/global CSS/global JS via `get_layout_field`/`set_layout_field`, global libraries +- [docs/09-mcp-tools-reference.md](docs/09-mcp-tools-reference.md) — Full MCP tools inventory by category and canonical workflows +- [docs/10-production-patterns.md](docs/10-production-patterns.md) — Real production snippets (header with menu, FAQ, zigzag, forms, listings, structured data) +- [docs/11a-decision-table.md](docs/11a-decision-table.md) — "User intent → MCP tool" routing table +- [docs/11b-rules-cheat-sheet.md](docs/11b-rules-cheat-sheet.md) — Non-negotiable rules cheat sheet (`cms_`, `num`, `_num`, upload arrays, `c-if`), field types +- [docs/12-glossary.md](docs/12-glossary.md) — Short definitions of Acai terms (sectionId, recordNum, apartados, builder_custom, enlace, controlador…) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..d904903 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,11 @@ +# Los docs no viven aquí + +La documentación canónica de Acai (los 13 `.md` numerados que reciben todas las webs y +que usa el agente de Forge) vive en el repo `agenticSystem`, carpeta `docs/` +(`gitea.cocosolution.com/superadmin/agenticSystem`). + +`copy_scaffold` (Forge, `server/scaffold.py`) omite el `docs/` de este repo a propósito y +copia la canónica en cada web. Cualquier cambio de documentación va allí; un PR aquí no +llega a ninguna web ni al agente. + +Este repo solo aporta `CLAUDE.md`, `.claude/` y `.githooks/`. diff --git a/docs/builder-fields.md b/docs/builder-fields.md deleted file mode 100644 index 12ae3c6..0000000 --- a/docs/builder-fields.md +++ /dev/null @@ -1,481 +0,0 @@ -# Builder Fields & Acai Attributes - -## Nombres de variables - -El atributo `data-field-label` se convierte a variable removiendo espacios y caracteres especiales (minúsculas). - -Reglas obligatorias: -- Todo elemento editable con `data-field-type` debe incluir también `data-field-label` -- Si falta `data-field-label`, el builder puede generar variables temporales o incorrectas y el módulo queda mal configurado -- Usa labels descriptivos y estables; no dejes labels vacíos ni genéricos como "Campo" o "Texto" - -Evita también en `index-base.tpl` las clases Tailwind con valores arbitrarios como `text-[44px]`, `font-['Cinzel']` o `leading-[1.1]`. En este stack pueden romper el parseo/compilación del template. Muévelas a `style.css`. - -| Label | Variable | -|-------|----------| -| Categoría Noticia | `categoranoticia` | -| Color Principal | `colorprincipal` | -| Título Producto | `ttuloproducto` | - ---- - -## Field Types (`data-field-type`) - -| Type | Element | Returns | -|------|---------|---------| -| `textfield` | `

` | String | -| `headfield` | `

`-`

` | String + variable `_tag` con la etiqueta elegida | -| `textbox` | `
` | String multi-línea | -| `wysiwyg` | `
` | HTML string | -| `link` | `` | URL string (ya incluye barras) | -| `upload` | `` | **Array** de `{urlPath, info1, info2, info3, info4}` | -| `uploadMulti` | `
  • ` | Itera sobre archivos subidos | -| `list` (fijo) | `
    ` | Valor seleccionado | -| `list` (tabla) | `
    ` | `num` del registro | -| `multiv2` | `
  • ` wrapper | Array de objetos | - -### textfield - -```html -

    - Elemento editable -

    -``` - -### headfield - -Genera 2 variables: la estándar y otra con sufijo `_tag` con la etiqueta elegida por el usuario. - -```html -<{{ title_tag | default('h2') }} data-field-type="headfield" data-field-label="Título Sección" class="text-3xl font-bold"> - Título de la sección - -``` - -### textbox - -```html -
    - Texto largo editable -
    -``` - -### wysiwyg - -```html -
    -

    Texto con estilos editables

    -
    -``` - -### link - -```html -
    - Haz clic aquí - -``` - -### upload - -```html -
    - -
    -``` - -Atributos disponibles: -- `data-lazy="true"`: Carga perezosa -- `data-field-width="1400"`: Ancho máximo sugerido -- `data-field-info1="titulo"`: Campo de información adicional (usado como alt) - -Acceso en Twig: `{{ imagen[0].urlPath }}`, `{{ imagen[0].info1 }}` - -### uploadMulti - -Itera sobre todas las imágenes subidas: - -```html -
  • -
    - {{ uploadMulti.info1 }} -
    -
  • -``` - -### list (opciones fijas) - -```html -
    -
    -``` - -Formato de opciones: `opcion1,opcion2,|opcion3,valor3|opcion4` - -### list (tabla) - -```html -
    - {{ record.titulo }} -
    -``` - -- `data-list-table`: Nombre de tabla sin prefijo `cms_` -- `data-list-value`: Campo a usar como valor (generalmente `num`) -- `data-list-label`: Campo a mostrar como label - -### multiv2 — Campos repetibles - -```html -
      -
    • -
      - Nombre del producto -
      -
      - Descripción del producto -
      -
      - -
      -
    • -
    -``` - -Uso en Twig — las variables son propiedades del objeto iterado: - -```twig -{% for record in productos %} -
    -

    {{ record.nombre }}

    -

    {{ record.descripcion }}

    - -
    -{% endfor %} -``` - ---- - -## Acai Attributes - -### `c-if` — Renderizado condicional - -```html - -
    {{ subtitle }}
    - - -
    Grid layout
    -``` - -### `c-else` - -Debe ir inmediatamente después del elemento `c-if`: - -```html -
    - -
    -
    -

    No image available

    -
    -``` - -### `c-for` — Iteración sobre array - -```html -
    -

    {{ item.title }}

    -
    -``` - -### `c-for` — Iteración sobre tabla de BD - -```html -
      -
    • - {{ producto.title }} -
    • -
    -``` - -Parámetros opcionales: `c-where` (condición SQL), `c-order` (orden), `c-limit` (límite). - -Equivalente en Twig: -```twig -{% for producto in 'productos' | get('visible=1','num desc',10) %} -
  • {{ producto.title }}
  • -{% endfor %} -``` - -Dentro del loop: `loop.index` (1-based), `loop.index is odd`, `loop.index is even` - -### `c-class` — Clases CSS condicionales - -```html - -
    - - -
    - - -
    - - -
    -``` - -### `c-hidden` — Elementos ocultos - -Elemento que no se renderiza pero puede declarar variables builder: - -```html -
    - -
    -``` - -### `c-required` — Campos requeridos condicionales - -```html - -``` - ---- - -## Definiendo variables con `` - -```html - - - - - - - -{% set gracias = 'apartados' | get('num = 20').0 %} -``` - ---- - -## Incluyendo módulos - -Para incluir un módulo dentro de otro módulo o sección general, usa el ID del módulo como etiqueta HTML: - -```html - -``` - -Ejemplo: -```html - - -``` - -El módulo hijo recibe los parámetros como variables en su contexto. - ---- - -## Formularios (`c-form`) - -Manejo automático de validación, almacenamiento en BD y envío de emails. - -```html - -
    - - -
    -
    - - -
    -
    - - -
    -
    - -
    - - -
    -``` - -### Atributos de c-form - -| Atributo | Descripción | -|----------|-------------| -| `tableName="'table'"` | Tabla donde almacenar registros | -| `mailRecord="['correos', 'ID']"` | Template de email de la tabla `correos` | -| `sendTo="'email@domain.com'"` | Destinatarios (separados por coma) | -| `sendToClient="'campo_email'"` | Campo con email del cliente para auto-reply | -| `captcha="true"` | Google reCAPTCHA | -| `honeypot="true"` | Campo oculto anti-spam | -| `messageOK="'texto'"` | Mensaje de éxito | -| `messageKO="'texto'"` | Mensaje de error | -| `redirect="'/path/'"` | Redirección tras envío exitoso | -| `attachFiles="true"` | Adjuntar archivos al email | -| `showImages="true"` | Mostrar thumbnails en email | -| `emailMode="'twig'"` | Email en formato Twig | -| `header="'
    ...
    '"` | HTML cabecera del email | -| `footer="'
    ...
    '"` | HTML footer del email | -| `styles="'body { ... }'"` | CSS para el email | - ---- - -## Componentes Built-in - -### Carousel (`c-tns-wrapper`) - -```html -
    -
    - -
    -
    -``` - -### Lightbox - -```html - - - -``` - -### Breadcrumb - -```html - -``` - -### Animate On Scroll (AOS) - -```html -
    - Animated content -
    -``` - -### Lazy Loading - -```html - - - -``` - ---- - -## Puntos importantes - -1. **Nombres de variables:** `data-field-label` → sin espacios ni caracteres especiales, minúsculas -2. **Variables en multiv2:** Son propiedades del objeto iterado (`record.nombre`) -3. **Campos upload:** Retornan arrays, no strings (`imagen[0].urlPath`, no `imagen`) -4. **c-if usa `=` no `==`:** `c-if="layout = 'grid'"` (un solo igual) -5. **c-for tabla:** El nombre de tabla va sin prefijo `cms_` -6. **Enlace:** Ya incluye barras, no añadir extras -7. **Checkbox:** Valores `1` o `0`, no `true`/`false` - ---- - -## MCP Tools: Config Vars e Imágenes de Módulos - -### Regla importante: Siempre rellenar variables al añadir un módulo -Cuando se añade un módulo a una página (con `add_module_to_record`), este queda vacío y no muestra nada visible. **SIEMPRE** hay que llamar a `set_module_config_vars` inmediatamente después para rellenar las variables con contenido de ejemplo coherente con el contexto del sitio. Incluir: -- Textos (títulos, descripciones, pretítulos) con contenido relevante al sitio -- Valores de listas/selects con una opción válida -- Para variables multi (records), crear al menos 2-3 items de ejemplo -- Para variables de imagen (upload), usar `generate_image` o `upload_record_image` para que el módulo se vea completo - -Un módulo sin variables configuradas es invisible en la web. - -### Leer variables de un módulo -Antes de modificar cualquier módulo, usar `get_module_config_vars` para conocer el estado actual: -- **tableName**: tabla del registro padre (ej: `apartados`), SIN prefijo `cms_` -- **recordNum**: campo `num` del registro padre (ej: `2`) -- **sectionId**: el `section_id` de la instancia del módulo (ej: `6c6d8`) - -### Escribir variables de un módulo -Usar `set_module_config_vars` con los mismos tableName, recordNum y sectionId. Pasar todos los valores como strings. - -La respuesta incluye `configVars` con el `recordNum` del registro `builder_custom` creado/actualizado y `uploadFields` para imágenes. - -**Tipos de almacenamiento (manejado automáticamente):** -- `headfield`, `textfield`, `link`, `textbox`, `wysiwyg`, `upload` → se guardan en tabla `builder_custom` -- `list`, `checkbox`, `colorpicker` → se guardan directamente en el JSON config-vars (no en builder_custom) - -No necesitas preocuparte por esto — `set_module_config_vars` lo maneja internamente. Solo pasa los valores como strings. - -### Subir imágenes a un módulo -El nombre del campo de imagen viene de `builder.json` → `vars.NOMBRE.relations.builder_custom` (ej: `"image1"`). NO es el nombre de la variable (ej: NO `"imagenes"`). - -**Flujo correcto:** -1. `get_module_config_vars` → obtener el `recordNum` en builder_custom de la variable de imagen -2. `upload_record_image` con: - - `tableName`: `"builder_custom"` (siempre, sin prefijo cms_) - - `recordId`: el `recordNum` del paso 1 (ej: `"778"`) - - `fieldName`: el campo de relations del builder.json (ej: `"image1"`) - - `imageUrl`: URL completa y públicamente accesible -3. `reorder_record_uploads` si es necesario — pasar array de upload IDs en el orden deseado -4. `list_record_uploads` para verificar - -**Errores comunes a evitar:** -- NO usar el sectionId como recordId — usar el `num` de builder_custom -- NO usar el nombre de la variable como fieldName — usar el campo de relations del builder.json (ej: `image1`, no `imagenes`) -- NO poner prefijo `cms_` en tableName diff --git a/docs/css-js-conventions.md b/docs/css-js-conventions.md deleted file mode 100644 index 6a046ae..0000000 --- a/docs/css-js-conventions.md +++ /dev/null @@ -1,261 +0,0 @@ -# CSS & JavaScript Conventions - -## Estructura del módulo - -- Genera HTML + CSS + JS (o Vue 3 si es necesario) -- Define una clase raíz en kebab-case: `product-card`, `hero-section`, etc. -- Todo el CSS y JS scopeado bajo esa clase raíz - ---- - -## CSS - -### Tailwind First - -Usar TailwindCSS como método principal. Solo CSS custom cuando Tailwind no cubra el estilo o se necesiten estados complejos/transiciones específicas. - -```html -
    -

    Title

    -
    -``` - -### BEM para CSS Custom - -Cuando se necesite CSS personalizado, siempre scopeado bajo la clase raíz con BEM: - -```css -.hero-section { } -.hero-section__title { } -.hero-section__image { } -.hero-section--dark { } -``` - -Nunca usar clases globales sin prefijo de módulo. - -### CSS Variables del tema - -```css -var(--main-color) /* Color de marca primario */ -var(--main-color-light) /* Variante clara */ -var(--main-color-dark) /* Variante oscura */ -``` - -### Estilos inline con fallbacks - -Patrón para colores configurables por el usuario: - -```html -
    -

    -``` - -### Clases utilitarias de Acai - -| Clase | Descripción | -|-------|-------------| -| `transition3s` | Transición suave 0.3s | -| `click-a-child` | Hace el padre clickeable via primer `` hijo | -| `line-clamp2` / `line-clamp3` / `line-clamp5` | Truncar texto a N líneas | -| `filter-white` | Filtro CSS para hacer imágenes/iconos blancos | -| `lazyload` | Lazy loading (usar con `data-src`) | -| `text-shadow` | Sombra de texto para legibilidad sobre imágenes | -| `wysiwyg` | Wrapper para contenido de texto enriquecido | -| `bg-main-color` / `bg-main-color-light` / `bg-main-color-dark` | Fondos con color primario | -| `text-main-color` / `text-main-color-light` / `text-main-color-dark` | Texto con color primario | - ---- - -## JavaScript - -### Module Scripts (`script.js`) - -JavaScript scopeado al módulo usando `section_id`: - -This is the default and expected place for module JavaScript. -Do NOT embed ` -``` - -Siempre usar `'${'` y `'}'` como delimitadores Vue para evitar conflicto con Twig. - ---- - -## Variables Globales Disponibles - -| Variable | Descripción | Ejemplo | -|----------|-------------|---------| -| `section_id` | ID único por instancia del módulo | `

    ` | -| `server.HTTP_HOST` | Dominio actual | `https://{{ server.HTTP_HOST }}/path` | -| `loop.index` | Índice de iteración (1-based) en c-for/for | `{{ loop.index }}` | -| `loop.index is odd` | True en iteraciones impares | Layouts alternados | -| `loop.index is even` | True en iteraciones pares | Patrones zigzag | -| `interno` | True dentro del editor CMS | `c-class="{'editor-mode': interno}"` | - -### Patrón section_id - -Cada instancia de módulo recibe un `section_id` único. Usar para navigation anchor e IDs: - -```html -
    -
    - -
    -``` - ---- - -## Componentes Nativos - -### Carousel (`c-tns-wrapper`) - -```html -
    -
      -
    • - -
    • -
    -
    -``` - -| Atributo | Descripción | Ejemplo | -|----------|-------------|---------| -| `data-responsive` | Items por breakpoint | `"sm:2, md:3, lg:4"` | -| `data-autoplay-timeout` | Intervalo autoplay (ms) | `"5000"` | -| `data-mode` | Modo de transición | `"gallery"` o `"carousel"` | -| `data-speed` | Velocidad de transición (ms) | `"400"` | -| `data-nav` | Puntos de navegación | `"true"` | - -Dots de navegación custom: -```html -
    -
    -
    -
    -``` - -### Lightbox - -```html -
    - - -``` - -### Breadcrumb - -```html - -``` - -### AOS (Animate On Scroll) - -```html -
    Contenido
    -``` - -Valores comunes: `fade-up`, `fade-down`, `fade-left`, `fade-right`, `zoom-in`, `zoom-in-up`, `fade-up-right`, `fade-up-left` - -Después de cambios dinámicos: `AOS.refresh()` en JavaScript. - -### Lazy Loading - -```html - - - - - -``` - ---- - -## Buenas prácticas - -- HTML/Twig semántico -- Código limpio y organizado -- Evitar dependencias externas innecesarias -- Evitar estilos inline salvo casos justificados (colores dinámicos del usuario) -- No usar clases globales sin prefijo de módulo diff --git a/docs/hooks-and-api.md b/docs/hooks-and-api.md deleted file mode 100644 index a1f0399..0000000 --- a/docs/hooks-and-api.md +++ /dev/null @@ -1,390 +0,0 @@ -# Hooks & Server-Side API - -## Hooks - -Hooks son archivos PHP que ejecutan lógica server-side. Pueden existir en dos sitios: -- Hooks globales en `hooks/hooks..php` -- Hooks propios de módulo en `template/estandar/modulos//hook.php` - -### Tipos de hooks - -**1. Hook global** -- Archivo: `hooks/hooks..php` -- Endpoint: `/hooks//` -- Úsalo cuando la lógica se reutiliza entre módulos, páginas o formularios - -**2. Hook propio de módulo** -- Archivo: `template/estandar/modulos//hook.php` -- Endpoint: `/hooks//` -- Úsalo cuando la lógica pertenece solo a ese módulo - -Ejemplos: -- `hooks/hooks.buscar_barcos.php` -> `/hooks/buscar_barcos/` -- `template/estandar/modulos/hero_banner/hook.php` -> `/hooks/hero_banner/` - -Regla práctica: -- Si el hook solo sirve a un módulo, créalo dentro del módulo -- Si varias piezas del proyecto lo van a consumir, créalo como hook global - -## Reglas obligatorias para hooks - -- Un hook debe devolver datos con `return [...]` -- No uses `echo json_encode(...)` -- No uses `exit` -- Para leer parámetros, usa `$_REQUEST[...]` o las variables ya inyectadas por el sistema -- En hooks, usa `CmsApi::get()` o `CocoDB::get()` como primera opción -- No uses `CocoDB::getInstance()` salvo necesidad real muy excepcional -- No escribas SQL manual con `prepare()/bind_param()` salvo que no exista forma razonable de resolverlo con `CmsApi` o `CocoDB` - -### Estructura de un Hook - -```php - true, - "message" => "Valor procesado: " . $resultado, - "value" => $resultado -]; -?> -``` - -### Testing Hooks - -Usa la URL editable de Forge del proyecto (`get_web_url`) — no hace falta ningún servidor local. Haz -`curl` al endpoint del hook contra esa URL: - -```bash -curl https:///hooks/example_hook/ -``` - -Para diagnosticar sin depender del enrutado HTTP directo (que en Forge puede dar 404 — ver más abajo), -usa el patrón vía filtro `| hook` desde un módulo/página temporal. - -### Cómo Llamar Hooks - -La referencia siempre se hace con el endpoint `/hooks//`: -- Para hooks globales, `` es el nombre lógico del hook sin `hooks.` ni `.php` -- Para hooks de módulo, `` es el `module-id` de la carpeta del módulo - -**Desde HTML (recomendado para módulos):** -```html - -

    {{ myVar.message }}

    -``` - -**Desde Twig:** -```twig -{% set resultado = 'hooks/mimodulo/' | hook({param1: 100, param2: 'texto'}) %} -

    {{ resultado.message }}

    -``` - -**Desde JavaScript:** -```js -CmsApi.hook('/hooks/mimodulo/', {param1: 100, param2: 'texto'}, (data) => { - console.log(data.message); -}); -``` - -**Ejemplo real para hook de módulo:** -```js -// Módulo: template/estandar/modulos/buscadorapartados_hjd8s/ -CmsApi.hook('/hooks/buscadorapartados_hjd8s/', { termino: 'vela' }, (data) => { - console.log(data); -}); -``` - -**Desde otro Hook PHP:** -```php - 100, "param2" => "texto"]); -$mensaje = $result["message"]; -?> -``` - -**Desde c-form:** Los hooks se ejecutan automáticamente al enviar el formulario si están configurados. - ---- - -## CmsApi (PHP) - -API server-side para operaciones de base de datos. Disponible en todos los hooks. - -### Read — `CmsApi::get()` - -## IMPORTANTE : Las tablas y nombres de campos puedes extraerlas de los esquemas en cms/data/schemas/.ini.php - -```php -// Todos los registros -$products = CmsApi::get("productos"); - -// Con condición WHERE en string -$active = CmsApi::get("productos", "active=1"); - -// Con orden y límite -$latest = CmsApi::get("noticias", "", "fecha DESC", 5); - -// Con condición string -$activos = CmsApi::get("productos", "activo=1"); - -// Condición compleja -$caros = CmsApi::get("productos", "precio > 100"); - -// Múltiples condiciones (AND) -$resultados = CmsApi::get("productos", "activo = 1 AND stock > 0"); - -// Con operadores -$expensive = CmsApi::get("productos", "precio >= 100"); -$search = CmsApi::get("productos", "nombre LIKE '%keyword%'"); -$inList = CmsApi::get("productos", "categoria_num IN (1, 2, 3)"); - -#### Opciones de `get()` - -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `uploads` | bool | `true` | Incluir datos de upload fields | -| `relations` | bool/array | `true` | Resolver foreign keys. Array para limitar: `['category']` | -| `relationsDepth` | int | 2 | Profundidad de relaciones anidadas | -| `translates` | string | current lang | Código de idioma | -| `groupBy` | string | null | GROUP BY clause | -| `aggregates` | array | `[]` | Funciones de agregación | -| `onlyFields` | array | null | Seleccionar solo campos específicos | -| `debug` | bool | false | Mostrar SQL query | -| `redis` | bool | null | Forzar cache Redis | -| `redis_expire` | int | 60 | TTL de cache Redis (segundos) | - -// Con opciones -$datos = CmsApi::get("productos", "", "", "", [ - 'translates' => true, - 'uploads' => true, - 'relations' => true, - 'relationsDepth' => 2 -]); -``` - -### Insert — `CmsApi::insert()` - -## IMPORTANTE : Las tablas y nombres de campos puedes extraerlas de los esquemas en cms/data/schemas/.ini.php - -```php -// Un registro -CmsApi::insert('contacto', [ - ["nombre" => "John", "email" => "john@example.com", "mensaje" => "Hello"] -]); - -// Múltiples registros -CmsApi::insert('productos', [ - ["nombre" => "Producto A", "precio" => 100], - ["nombre" => "Producto B", "precio" => 200] -]); - -// Con retorno del último ID -CmsApi::insert('productos', - [["nombre" => "Nuevo", "precio" => 150]], - [], - ['return_last_id' => true] -); -``` - -#### Opciones de insert - -| Option | Description | -|--------|-------------| -| `forceNum` | Permite setear el campo `num` manualmente | -| `ignoreSchema` | Saltar validación de schema | -| `ignoreFields` | Array de campos a ignorar | - - -### Update — `CmsApi::update()` - -## IMPORTANTE : Las tablas y nombres de campos puedes extraerlas de los esquemas en cms/data/schemas/.ini.php - -```php -// Con condición string -CmsApi::update('productos', ["precio" => 150], "num=1"); - -// Con condición array -CmsApi::update('productos', - ["activo" => 1], - [["column" => "num", "operator" => "=", "value" => 1]] -); - -// Múltiples registros -CmsApi::update('productos', ["activo" => 0], "precio < 50"); -``` - -#### Opciones de update - -| Option | Description | -|--------|-------------| -| `forceNum` | Permite setear el campo `num` manualmente | -| `ignoreSchema` | Saltar validación de schema | -| `ignoreFields` | Array de campos a ignorar | - - -### Delete — `CmsApi::delete()` - -## IMPORTANTE : Las tablas y nombres de campos puedes extraerlas de los esquemas en cms/data/schemas/.ini.php - -```php -CmsApi::delete('productos', "num=5"); - -CmsApi::delete('productos', - [["column" => "activo", "operator" => "=", "value" => 0]] -); -``` - -### Reglas importantes - -- Nombres de tabla **sin** prefijo `cms_` -- Primary key siempre es `num`, nunca `id` -- Foreign keys: `categoria_num`, no `categoria_id` -- Upload fields: no se manejan via insert/update -- Operadores: `=`, `!=`, `>`, `>=`, `<`, `<=`, `LIKE`, `IN` - ---- - -## CmsApi (JavaScript — Client-Side) - -```js -// Llamar hook -CmsApi.hook('/hooks/module_id/', { param: 'value' }, function(response) { - // response es la salida del hook -}); - -// Leer registros (si está expuesto via hooks) -CmsApi.get('tableName', { where: conditions }, function(records) { - // records array -}); -``` - ---- - -## CocoDB - -Capa de abstracción de BD de bajo nivel usada internamente por CmsApi. Usar directamente desde hooks cuando necesites más control. - -Para búsquedas y lecturas habituales, prioriza: -1. `CmsApi::get()` -2. `CocoDB::get()` -3. SQL manual solo si de verdad no hay alternativa razonable - -### `CocoDB::get($table, $where, $order, $limit, $options)` -## Funcionalidad exactamente igual a CmsApi::get ( ver referencia CmsApi::get ) - - -### `CocoDB::insertRecords($table, $records, $functions, $options)` -## Funcionalidad exactamente igual a CmsApi::insert ( ver referencia CmsApi::insert ) - -### `CocoDB::updateRecords($table, $records, $where, $functions, $options)` -## Funcionalidad exactamente igual a CmsApi::update ( ver referencia CmsApi::update ) - - -### `CocoDB::deleteRecords($table, $where, $options)` -## Funcionalidad exactamente igual a CmsApi::delete ( ver referencia CmsApi::delete ) - ---- - -## Creación y Actualización de Registros - -### Flujo correcto - -1. Consultar el esquema de la tabla (leer `cms/data/schema/{tabla}.ini.php`) -2. Revisar los tipos de campo -3. Rellenar según el tipo de dato -4. Enviar con la estructura correcta - -### Tipos de campo y formato - -| Tipo | Formato | Ejemplo | -|------|---------|---------| -| **Text field** | String | `"Texto"` | -| **Text box** | String multilínea | `"Línea 1\nLínea 2"` | -| **Date/time** | `YYYY-MM-DD HH:mm:ss` | `"2025-12-03 10:30:00"` | -| **Wysiwyg** | String HTML | `"

    Texto

    "` | -| **List** | String o número | `"activo"` o `"1"` (num si es foreign key) | -| **Checkbox** | Número 1/0 | `1` o `0` | -| **Multivalores** | String JSON | `"[{\"producto\":\"1\"}]"` | -| **Upload** | **NO enviar** — usar `upload_record_image` después de crear el registro | - ---- - -## Table Schemas - -Los schemas están en `cms/data/schema/` como archivos `.ini.php`. Definen: -- Nombres y tipos de campo -- Reglas de validación -- Relaciones (foreign keys) -- Configuración de display - ---- - -## Ejemplos Prácticos - -### Hook de Cálculo de Precio - -```php - 10) { - $precioUnitario *= 0.85; // 15% descuento -} - -return [ - "success" => true, - "precioUnitario" => round($precioUnitario, 2), - "total" => round($precioUnitario * $cantidad, 2), - "descuento" => $tipo === 'mayoreo' ? 15 : 0 -]; -?> -``` - -```html - -

    Total: ${{ precio.total }}

    -``` - -En este ejemplo, el endpoint usa `calcular_precio` porque el archivo vive en: -`template/estandar/modulos/calcular_precio/hook.php` - -### Hook con Operaciones de BD - -```php - false, "message" => "Producto no encontrado"]; -} - -$total = $producto[0]['precio'] * $cantidad; - -// Crear venta -CmsApi::insert('ventas', [[ - "usuario_num" => $usuario_id, - "producto_num" => $producto_id, - "cantidad" => $cantidad, - "total" => $total, - "fecha" => date('Y-m-d H:i:s') -]], [], ['return_last_id' => true]); - -// Actualizar stock -$stock = CmsApi::get("stocks", "producto_num=" . $producto_id); -if (!empty($stock)) { - CmsApi::update('stocks', - ["cantidad" => $stock[0]['cantidad'] - $cantidad], - "producto_num=$producto_id" - ); -} - -return ["success" => true, "total" => $total]; -?> -``` diff --git a/docs/mcp-tools-reference.md b/docs/mcp-tools-reference.md deleted file mode 100644 index 3c71c11..0000000 --- a/docs/mcp-tools-reference.md +++ /dev/null @@ -1,118 +0,0 @@ -# MCP Tools Reference - -## Quick Reference - -| Tool | Categoría | Acción | -|------|-----------|--------| -| `compile_module` | Módulos | Recompilación manual de rescate cuando necesitas forzar la compilación sin editar el archivo | -| `check_module` | Módulos | Preview de cómo renderiza un módulo | -| `check_module_usage` | Módulos | Qué páginas usan un módulo | -| `acai-view` | Archivos | Lee un archivo del proyecto por tramos | -| `acai-glob` | Archivos | Busca rutas de archivos por patrón | -| `acai-grep` | Archivos | Busca texto en archivos del proyecto | -| `acai-line-replace` | Archivos | Reemplaza un bloque concreto en un archivo existente | -| `acai-write` | Archivos | Crea o reescribe un archivo completo. Antes de usarlo, lee la doc correspondiente según el tipo de archivo (`module-creation-guide`, `builder-fields`, `css-js-conventions`, `hooks-and-api`) | -| `acai-delete` | Archivos | Borra un archivo del proyecto | -| `list_page_modules` | Registros | Lista módulos de una página | -| `add_module_to_record` | Registros | Añade módulo a una página | -| `remove_module_from_record` | Registros | Elimina módulo de una página | -| `reorder_module` | Registros | Cambia posición de un módulo | -| `toggle_module_visibility` | Registros | Muestra/oculta módulo | -| `get_module_config_vars` | Registros | Lee variables de un módulo | -| `set_module_config_vars` | Registros | Escribe variables de un módulo | -| `list_table_records` | Registros | Buscar/listar registros con filtros | -| `get_record` | Registros | Obtener un registro por num | -| `create_or_update_record` | Registros | Crear o actualizar registros | -| `delete_table_records` | Registros | Eliminar registros (destructivo) | -| `upload_record_image` | Media | Subir imagen a campo de registro (desde URL) | -| `generate_image` | Media | Generar imagen con IA y guardar en uploads | -| `upload_image_to_assets` | Media | Subir imagen a /images/ del template | -| `list_record_uploads` | Media | Listar uploads de un campo | -| `replace_record_image` | Media | Reemplazar imagen existente | -| `delete_record_upload` | Media | Borrar upload | -| `reorder_record_uploads` | Media | Reordenar imágenes de un campo | -| `refresh_acai_token` | Auth | Renovar token JWT expirado | -| `navigate_browser` | Navegación | Navegar el browser del frontend a una URL | -| `save_project_styles` | Proyecto | Guardar resumen de estilos en docs/project-styles.md | -| `rollback_git` | Git | Recuperar cambios de git remoto | - -## Flujos de trabajo - -### Crear un módulo nuevo desde cero - -1. `acai-write` — Crea `index-base.tpl`, `style.css`, `script.js` y cualquier hook necesario con rutas relativas al proyecto -2. `acai-write` o `acai-line-replace` compilan automáticamente al tocar `index-base.tpl` -3. `add_module_to_record` — Añade el módulo a una página (tabla padre, ej: `apartados`) -4. `set_module_config_vars` — Rellena las variables con contenido (textos, colores, opciones). **OBLIGATORIO** — sin esto el módulo no muestra nada. Devuelve: - - `configVars`: mapa de variables → recordNums - - `uploadFields`: mapa de variables upload → `{ fieldName, recordNum }` — **usa estos directamente** para subir imágenes sin necesidad de leer builder.json - - Para vars multi con uploads: `uploadFields["varName.subVarName"]` es un array con `[{ index, fieldName, recordNum }]` -5. Para imágenes: `generate_image` o `upload_record_image` usando el `recordNum` y `fieldName` del `uploadFields` devuelto en el paso 4 -6. Verificar con `check_module` o recargando la página - -### Editar un módulo existente - -1. `get_module_config_vars` — Leer el estado actual del módulo (variables, recordNums) -2. `acai-view` — Leer solo el tramo de `index-base.tpl` que se va a modificar -3. `acai-line-replace` — Editar el bloque concreto. Usa `acai-write` solo si el archivo es nuevo o el cambio es masivo -4. La compilación es automática tras cada edición de `index-base.tpl` -5. Si cambias variables: `set_module_config_vars` para actualizar valores - -### Editar archivos del proyecto con bajo consumo de tokens - -1. `acai-view` — Leer el archivo o un rango de líneas -2. `acai-glob` — Encontrar archivos relevantes por ruta cuando no conoces el path exacto -3. `acai-grep` — Buscar texto o atributos concretos dentro de archivos del proyecto -4. `acai-line-replace` — Reemplazar el bloque exacto en archivos existentes -5. `acai-write` — Crear archivos nuevos o reescribirlos por completo si es necesario -6. `acai-delete` — Borrar archivos solo cuando sea explícitamente necesario - -Reglas: -- Usa siempre rutas relativas al proyecto -- No edites `index.tpl`, `index-twig.tpl` ni `builder.json` — son auto-generados -- Tras editar cualquier `index-base.tpl` con las file tools, la compilación se ejecuta automáticamente - -### Añadir/modificar imágenes de un módulo - -**Tras `set_module_config_vars`** (método recomendado — sin pasos extra): -1. El response de `set_module_config_vars` incluye `uploadFields` con los `recordNum` y `fieldName` de cada variable upload -2. `upload_record_image` con `tableName: "builder_custom"`, `recordId` y `fieldName` del `uploadFields` -3. Para uploads dentro de vars multi: `uploadFields["records.imagen"]` devuelve array con `{ index, fieldName, recordNum }` por cada record - -**Sin haber llamado a `set_module_config_vars`**: -1. `get_module_config_vars` — Obtener el `recordNum` de builder_custom -2. Leer `builder.json` del módulo para encontrar el `fieldName` real (ej: `image1`, NO el nombre de la variable) -3. `upload_record_image` con: - - `tableName`: `"builder_custom"` (siempre sin cms_) - - `recordId`: el recordNum del paso 1 - - `fieldName`: el campo de relations del builder.json (ej: `image1`) - - `imageUrl`: usa la URL recomendada por la tool que generó/subió la imagen. En Forge, si `generate_image` devuelve `uploadUrl` o `fullUrl`, priorízala frente a `dockerUrl` - -### Generar imagen con IA - -1. `generate_image` con prompt descriptivo + style (photographic, digital-art, minimalist...) -2. La imagen se guarda en `cms/uploads/generated/` y devuelve una URL local de preview (`dockerUrl`) y, cuando aplica, una URL recomendada para subida (`uploadUrl` / `fullUrl`) -3. Para `upload_record_image`, usa la URL recomendada por la tool. En Forge, prioriza `uploadUrl` o `fullUrl` si están presentes - -### Gestionar registros de una tabla - -1. `list_table_records` — Buscar registros con filtros (`where`, `order`, `limit`) -2. `get_record` — Obtener un registro completo por num -3. `create_or_update_record` — Crear o actualizar (la tabla sin prefijo `cms_`, PK es `num`) -4. `delete_table_records` — Eliminar por IDs - -### Explorar el sitio - -1. `list_page_modules` — Ver qué módulos tiene cada página -2. `get_module_config_vars` — Ver los datos de cada módulo -3. `check_module` — Preview de cómo renderiza - -## Reglas importantes para todas las tools - -1. **tableName** siempre SIN prefijo `cms_` (ej: `apartados`, no `cms_apartados`) -2. **Primary key** es siempre `num`, nunca `id` -3. **Uploads** son arrays — acceder con `[0].urlPath` -4. **fieldName de imágenes** viene de `builder.json` → `vars.NOMBRE.relations.builder_custom` (ej: `image1`), NO del nombre de la variable -5. **recordId para imágenes** es el `num` de `builder_custom`, NO el sectionId del módulo -6. Tras `set_module_config_vars`, TODAS las variables del módulo (incluyendo upload) reciben config-vars automáticamente -7. Si el token expira (error 403), usar `refresh_acai_token` diff --git a/docs/modular-system.md b/docs/modular-system.md deleted file mode 100644 index ecec0c3..0000000 --- a/docs/modular-system.md +++ /dev/null @@ -1,105 +0,0 @@ -# Acai Modular System - -## Modules - -Modules are the visual building blocks of Acai websites. Each module lives in `template/estandar/modulos//`. - -### File Structure - -``` -/ -├── index-base.tpl # Source template (EDIT THIS) -├── index.tpl # Compiled output (auto-generated, do NOT edit) -├── index-twig.tpl # Compiled Twig output (auto-generated, do NOT edit) -├── builder.json # Compiled builder vars (auto-generated, do NOT edit) -├── style.css # Module-scoped styles -└── script.js # Module JavaScript -``` - -### Template Syntax - -Templates use a hybrid of **Twig** and **Acai attributes**. The source file is always `index-base.tpl`. - -```html -
    -
    -

    - Title here -

    -

    - Description text -

    - - Call to action -
    -
    -``` - -### Including Modules from Other Modules - -```html - -``` - -Parameters are received as variables inside the included module. - -### Global Variables - -| Variable | Description | -|----------|-------------| -| `section_id` | Unique ID per module instance (use for anchors, JS scoping) | -| `interno` | `true` when viewing in CMS editor, `false` on public site | -| `server.HTTP_HOST` | Current domain | -| `loop.index` | 1-based iteration index (inside `c-for`) | -| `loop.index is odd` / `loop.index is even` | For alternating layouts | - - -## General Sections - -General sections are database-backed templates used for record views, headers, footers, and reusable layouts. They use the same template engine as modules. - -### Key Differences from Modules - -- Access record data via the `thisrecord` variable -- Upload fields return **arrays**: `thisrecord.image[0].urlPath` -- Additional upload metadata: `info1` (alt text), `info2`, `info3`, `info4` -- Foreign key fields use `_num` suffix: `thisrecord.category_num` -- Saved via `save_general_section()` (not `save_module()`) -- Parser type 2 = Twig (recommended), 0 = Acai legacy syntax - -### Example: Record Template - -```html -
    - {{ thisrecord.imagen[0].info1 }} -

    {{ thisrecord.nombre }}

    -

    {{ thisrecord.descripcion | raw }}

    - {{ thisrecord.precio }}€ -
    -``` - -### Variable Assignment - -Use `` tag to create variables from queries: - -```html - - -``` - - -## Repeatable Content (multiv2) - -The `multiv2` builder field type creates repeatable groups of fields: - -```html -
    -

    {{ item.title }}

    -

    {{ item.description }}

    - -
    -``` - -Access individual items: `record.items[0].title`, `record.items[1].image`, etc. diff --git a/docs/module-creation-guide.md b/docs/module-creation-guide.md deleted file mode 100644 index 0e23bb6..0000000 --- a/docs/module-creation-guide.md +++ /dev/null @@ -1,100 +0,0 @@ -# Module Creation Guide - -## Style Reference - -When creating new modules, you MUST match the visual style of the existing project. Follow these steps IN ORDER: - -### Step 1: Check for `docs/project-styles.md` -- If the file exists → read it and use it as your style reference. DONE — skip to module creation. -- If the file does NOT exist → continue to Step 2. - -### Step 2: Determine if exploration is needed -- Count modules in `template/estandar/modulos/` that have `builder.json` and do NOT start with `custom-` -- If 3+ qualifying modules exist → continue to Step 3 -- If fewer than 3 → skip exploration, create the module based on the user's description. The style will be defined as modules are created. - -### Step 3: Explore and GENERATE the style guide (MANDATORY) -- Read `index-base.tpl` and `style.css` of 3-4 representative modules (only those with `builder.json`, skip `custom-*`) -- **You MUST then call `save_project_styles`** with a markdown summary including: - - Primary/secondary/accent colors (hex values) - - Font families and sizes used - - Spacing scale (padding/margin patterns) - - Common Tailwind classes and custom CSS patterns - - Button styles, card styles, section layouts - - Any recurring design patterns (gradients, shadows, borders, etc.) -- This saves `docs/project-styles.md` which will be read by future module creation tasks — no re-exploration needed. - -**After creating a module:** if `docs/project-styles.md` does not exist yet and there are now 3+ modules, call `save_project_styles`. - -## Module Structure - -Each module lives in `template/estandar/modulos//` with: -- `index-base.tpl` — Twig template (source — EDIT THIS) -- `style.css` — Module styles -- `script.js` — Module JavaScript -- `builder.json` — Compiled builder vars (auto-generated, do NOT edit) -- `index.tpl` / `index-twig.tpl` — Compiled (auto-generated, do NOT edit) - -## Creating a Module — Full Workflow - -If the module needs JavaScript, you MUST read `docs/css-js-conventions.md` before writing `index-base.tpl` or `script.js`. -If the module needs server-side logic, dynamic data processing, form handling, or reusable backend behavior, you MUST read `docs/hooks-and-api.md` before creating `hook.php` or any global hook. -If the module will call hooks from Twig, also review `docs/twig-filters.md` for the `hook` filter syntax. -If `index-base.tpl` contains builder fields (`data-field-type`), you MUST review `docs/builder-fields.md` before writing the template. - -Hard rules for module files: -- `index-base.tpl` is for HTML/Twig only -- `script.js` is for module JavaScript -- `style.css` is for module CSS -- `hook.php` is for server-side logic -- Do NOT embed ` -``` - ---- - -## Ejemplo 2: [Módulo de texto genérico] - -```vue - - - -``` \ No newline at end of file diff --git a/docs/vue-builder-rules.md b/docs/vue-builder-rules.md deleted file mode 100644 index 846e5cc..0000000 --- a/docs/vue-builder-rules.md +++ /dev/null @@ -1,484 +0,0 @@ - -# Reglas CMS-VUE - -Aplica estas reglas ÚNICAMENTE cuando el usuario incluya "cms-vue" o "CMS-VUE" (en cualquier combinación de mayúsculas/minúsculas) en su mensaje. Ejemplos válidos: "dame el cms-vue", "cms-vue personalizado", "crea el CMS-VUE", "necesito el cms-vue de este módulo". Si el mensaje NO contiene "cms-vue", ignora completamente estas instrucciones. - ---- - -## 1. Estructura general: Tabs (`acai-vue-tabs`) - -- Analiza el HTML proporcionado para determinar cuántos tabs son necesarios y cómo nombrarlos. -- Tabs base comunes: **Configuración**, **Imágenes**, **Textos**, **Bloques** (records), **Enlaces**, **Colores**. -- Añade tabs adicionales si el módulo lo requiere (ej: "Formulario", "Video", "Overlay", "Slider", etc.). -- Si un tab solo tendría 1 campo, evalúa fusionarlo con otro tab relacionado. -- Cada tab tiene su propio `id`, `label`, `color` e `icon` (SVG inline). -- SVG dentro del template usan `:style="{ color: color }"` para heredar el color del tab. -- Textos descriptivos claros y orientados al usuario final del CMS. -- Usa `storage-key` único: `'nombre-modulo-tabs-' + (section_id || 'default')`. -- Siempre añade `:apply-theme-styles="true"`. -- **IMPORTANTE:** La prop para pasar los tabs es `:tabs` (NO `:tabs-config`). -- **IMPORTANTE:** Siempre añadir `v-if="data"` en el `` para evitar renderizar antes de que los datos estén listos. - -### Template de cada tab: -```html - -``` - ---- - -## 2. Colorpicker según contexto - -### 2.1 En tab "Colores" (campos generales de color de texto/fondo) -Siempre con SVG + título + descripción + colorpicker + textfield oculto: -```html -
    -
    -
    - ... -

    Nombre : Descripción.

    -
    -

    Nota : valor por defecto.

    -
    - -
    -
    - -
    -
    -
    -``` - -### 2.2 Colorpicker en otros tabs (ej: color del overlay en tab Imágenes) -Misma estructura con SVG + título + descripción + colorpicker + textfield oculto, pero usando el `color` del tab donde se encuentre. Se coloca junto a los campos relacionados (ver regla 10.2). - -### 2.3 Dentro de `` (sin icono ni descripción) -Se coloca debajo del campo al que corresponde: -- Nota con `mt-4` si campo anterior es `textfield` o `title`. -- Nota con `mt-3` si campo anterior es `textbox` o `wysiwyg`. -- Colorpicker siempre con `mt-1`. -```html -

    Nota : color por defecto (#hex).

    -
    - -
    -
    - -
    -``` - -### 2.4 Campos tipo `list` para colores -Se usan como `` con icono y nota. El componente detecta automáticamente si las opciones son colores y muestra el modo color selector con swatches. No llevan colorpicker ni textfield oculto. - -### 2.5 Extraer color por defecto -Del HTML: `style="color: {{ campo ? campo : '#HEX' }}"` → usar `#HEX`. Si no hay color, usar `#111827` (textos) o `transparent` (fondos). - ---- - -## 3. Campos: estructura en tabs - -### Fuera de records: -```html -
    -
    -
    - ... -

    Nombre : Descripción.

    -
    -

    Nota : info adicional.

    -
    - -
    -
    -
    -``` - -### Dentro de records: -```html -
    -
    - ... -

    Nombre : Descripción.

    -
    -
    - -
    -
    -``` - ---- - -## 4. Nombres de campos (`:field`) - -- Construir uniendo palabras del `data-field-label` en minúsculas sin espacios. -- Eliminar acentos: á→a, é→e, í→i, ó→o, ú→u, ñ→(eliminar). -- Ejemplos: `Color del título` → `colordeltitulo`, `Valoración` → `valoracion`, `Tamaño` → `tamao`. - ---- - -## 5. Upload de imágenes - -### General: -```html - -``` - -### En records: -```html - -``` - ---- - -## 6. Componentes y URLs - -Solo incluir los que se usen. Los componentes personalizados (tabs, selectv2) se cargan desde impulse; los estándar desde cocosolution: - -```javascript -// ── Componentes personalizados (impulse) ── -'acai-vue-tabs': httpVueLoader('https://impulse.webserver2.plandeweb.com/template/estandar/css/builder-acaivuetabsv2.vue?timestamp=' + new Date().getTime()), -'acai-vue-selectv2': httpVueLoader('https://impulse.webserver2.plandeweb.com/template/estandar/css/builder-acaivueselect-v2.vue?timestamp=' + new Date().getTime()), - -// ── Componentes estándar (cocosolution) ── -'acai-vue-colorpicker': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuecolorpicker.vue?timestamp=' + new Date().getTime()), -'acai-vue-upload': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivueupload.vue?timestamp=' + new Date().getTime()), -'acai-vue-records': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuerecords.vue?timestamp=' + new Date().getTime()), -'acai-vue-title': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuetitle.vue?timestamp=' + new Date().getTime()), -'acai-vue-wysiwyg': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuewysiwyg.vue?timestamp=' + new Date().getTime()), -'acai-vue-linkv2': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuelinkv2.vue?timestamp=' + new Date().getTime()), -'acai-vue-textbox': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuetextbox.vue?timestamp=' + new Date().getTime()), -'acai-vue-textfield': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuetextfield.vue?timestamp=' + new Date().getTime()), -'acai-vue-datepicker': httpVueLoader('https://cms.cocosolution.com/lib/plugins/builder_saas/tpl/componentes/builder-acaivuedatepicker.vue?timestamp=' + new Date().getTime()), -``` - -**IMPORTANTE:** `acai-vue-list` ha sido reemplazado por `acai-vue-selectv2` en todos los VUEs. NO usar `acai-vue-list` en nuevos VUEs. - ---- - -## 7. Mapeo HTML → Vue - -| `data-field-type` | Componente | -|---|---| -| `textfield` | `acai-vue-textfield` | -| `headfield` | `acai-vue-title` | -| `wysiwyg` | `acai-vue-wysiwyg` | -| `textbox` | `acai-vue-textbox` | -| `list` | `acai-vue-selectv2` | -| `upload` / `uploadMulti` | `acai-vue-upload` | -| `linkv2` | `acai-vue-linkv2` (siempre con `:show_text="true"`) | -| `multiv2` | `acai-vue-records` | -| `textfield` (usado como fecha) | `acai-vue-datepicker` + `acai-vue-textfield` oculto | - ---- - -## 8. Script base - -```javascript -module.exports = { - props: ["active", "section_id"], - data() { - return { - data: null, - builder: null, - idiomas: IDIOMAS, - tabsConfig: [ /* tabs */ ], - // iconos para toggles (solo si hay campos de 2 opciones con iconos) - // iconosNombreCampo: { '': '...', '1': '...' } - }; - }, - components: { /* solo los usados */ }, - mounted() { this.$emit("child-mounted"); }, - methods: { saveData() { this.$emit("save-data"); } }, -}; -``` - ---- - -## 9. Decisión de tabs según contenido HTML y contexto semántico - -### 9.1 Organización contextual (PRIORITARIA) - -**IMPORTANTE:** Primero analizar el **nombre del campo** para determinar su contexto semántico, independientemente del tipo. Un campo `list` llamado "tipo de imagen" debe ir en el tab **Imágenes**, no en Configuración. - -#### Keywords para tab Imágenes: -Campos que contengan: `imagen`, `photo`, `video`, `fondo`, `background`, `logo`, `icono`, `icon` - -**Ejemplos:** -- ✅ "tipo de imagen" (list) → **Imágenes** -- ✅ "video de fondo" (list) → **Imágenes** -- ✅ "logo principal" (upload) → **Imágenes** - -#### Keywords para tab Enlaces: -Campos que contengan: `enlace`, `link`, `boton`, `button`, `url`, `href` - -**Ejemplos:** -- ✅ "texto del botón" (textfield) → **Enlaces** -- ✅ "url externa" (textfield) → **Enlaces** -- ✅ "estilo del enlace" (list) → **Enlaces** - -#### Keywords para tab Textos: -Campos que contengan: `titulo`, `title`, `texto`, `text`, `descripcion`, `description`, `contenido`, `content`, `label`, `etiqueta` - -**Ejemplos:** -- ✅ "título principal" (headfield) → **Textos** -- ✅ "descripción corta" (textfield) → **Textos** - -### 9.2 Organización por tipo (fallback) - -Si el nombre del campo **no** coincide con ninguna keyword, usar el tipo: - -| Tipo | Tab | -|---|---| -| `headfield`, `textfield`, `textbox`, `wysiwyg` | Textos | -| `upload`, `image` | Imágenes | -| `linkv2` | Enlaces | -| `list`, `select` (sin contexto) | Configuración | -| `multiv2` (records) | Bloques | -| Otros campos de configuración | Configuración | - ---- - -## 10. Reglas especiales - -### 10.1 Selector imagen/video con v-show -Cuando el HTML tenga un campo `list` con opciones tipo `"|Imagen,1|Video"`: -- El selector "Tipo de fondo" va en el tab **Imágenes** como **primer campo** (encima de los uploads). -- Se renderiza como toggle con iconos (foto/vídeo) usando `acai-vue-selectv2` con `:toggle-icons`. -- Usa el icono `icon-tabler-photo-video`: -```html - -``` -- El upload de **imágenes** lleva: `v-show="data.tipodeimagen && data.tipodeimagen.newValues.builder_custom.value == ''"` (visible cuando es imagen o vacío). -- El upload de **video** lleva: `v-show="data.tipodeimagen && data.tipodeimagen.newValues.builder_custom.value == '1'"` (visible cuando es video). -- **NUNCA quitar estos `v-show`**, son esenciales para mostrar uno u otro según la selección. -- Iconos del toggle: -```javascript -iconosTipoImagen: { - '': '', - '1': '' -} -``` - -### 10.2 Grupo overlay (tipo + color + opacidad) -Cuando el HTML contenga campos de overlay (tipo de overlay, color del overlay, opacidad del overlay): -- Los tres campos van **juntos** en el tab **Imágenes**, **debajo** de la imagen/video sobre la que se aplica el overlay. -- El orden es: tipo de overlay → color del overlay (colorpicker) → opacidad del overlay. -- El **color del overlay NO va en el tab Colores**, va en Imágenes junto al resto del grupo overlay. -- El tipo de overlay (2 opciones: Sin degradado / Con degradado) se renderiza como toggle con iconos: -```javascript -iconosOverlay: { - '': '', - '1': '' -} -``` - -### 10.3 Campos que afectan al enlace -Los campos `list` que modifican propiedades del botón de enlace (radio borde, estilo, etc.) van en el tab **Enlaces**, debajo del campo `linkv2` al que afectan. NO van en Configuración ni en Imágenes. - -### 10.4 Tabs base: definición fija de id, label, color e icono -Los tabs base siempre usan la siguiente definición fija. Este es el orden por defecto; solo se incluyen los tabs que el módulo necesite. Tabs adicionales (ej: "Formulario", "Video") se crean con id, label, color e icono nuevos. - -```javascript -{ id: "configuracion", label: "Configuración", color: "#f59e0b", icon: '' }, - -{ id: "imagenes", label: "Imágenes", color: "#10b981", icon: '' }, - -{ id: "textos", label: "Textos", color: "#3b82f6", icon: '' }, - -{ id: "bloques", label: "Bloques", color: "#ec4899", icon: '' }, - -{ id: "enlaces", label: "Enlaces", color: "#ef4444", icon: '' }, - -{ id: "colores", label: "Colores", color: "#8b5cf6", icon: '' }, -``` - -### 10.5 Campos globales que afectan al multi van DENTRO del tab Bloques -Los campos `list` o `textfield` generales (no de records) que afectan visualmente a los elementos del multi (ej: radio de borde de los bloques, alineación del texto de los bloques, diseño del enlace de los bloques) deben colocarse **dentro del tab Bloques**, en la zona **superior**, ANTES del bloque descriptivo "Bloques del multi" y del ``. Estos campos NO van en Configuración ni en otros tabs, ya que pertenecen conceptualmente a los bloques. - -### 10.6 Bloque descriptivo "Bloques del multi" antes de acai-vue-records -Siempre añadir un bloque descriptivo con el icono `icon-tabler-stack-2` y el texto "Bloques del multi : Personaliza los bloques del multi." justo antes de ``: -```html - -
    -
    -
    - -

    Bloques del multi : Personaliza los bloques del multi.

    -
    -
    -
    -``` - -### 10.7 Slot de acai-vue-records: NO desestructurar `color` -El slot de `` NUNCA debe desestructurar `color`. Siempre usar: -```html -