Files
agenticSystem/docs/01-builder-fields.md
Jordan Diaz 6881d64a08 ajustes
2026-04-25 10:27:51 +00:00

445 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Builder Fields — Campos editables del index-base.tpl
Este documento define los campos editables que el usuario rellena desde el panel del builder de Acai. Cubre el atributo `data-field-type` con todos sus tipos (`textfield`, `headfield`, `textbox`, `wysiwyg`, `link`, `upload`, `uploadMulti`, `list`, `multiv2`, `checkbox`, `colorpicker`), la regla `data-field-label` → nombre de variable, los atributos Acai (`c-if`, `c-else`, `c-for`, `c-class`, `c-hidden`, `c-required`), el tag `<set>`, la inclusión de módulos, los formularios `c-form` y los componentes built-in. Léelo antes de crear o modificar cualquier `index-base.tpl`.
## Reglas de nomenclatura de variables
El atributo `data-field-label` se convierte automáticamente en el nombre de variable Twig: se ponen minúsculas y se eliminan espacios y caracteres especiales.
| Label | Variable resultante |
|-------|---------------------|
| `Categoría Noticia` | `categoranoticia` |
| `Color Principal` | `colorprincipal` |
| `Título Producto` | `ttuloproducto` |
Reglas obligatorias:
- Todo elemento con `data-field-type` DEBE incluir también `data-field-label`.
- Sin `data-field-label`, el builder genera 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".
- En `index-base.tpl` evita clases Tailwind con valores arbitrarios (`text-[44px]`, `font-['Cinzel']`, `leading-[1.1]`) — pueden romper el parseo. Muévelas a `style.css`.
## Tipos de campo (`data-field-type`)
| Tipo | Elemento HTML | Devuelve |
|------|---------------|----------|
| `textfield` | `<p>` | String |
| `headfield` | `<h1>``<h6>` | String + variable extra `_tag` con la etiqueta elegida |
| `textbox` | `<div>` | String multilínea |
| `wysiwyg` | `<div class="wysiwyg">` | String HTML |
| `link` | `<a>` | URL string (ya incluye barras) |
| `upload` | `<img>` | **Array** de `{urlPath, info1, info2, info3, info4}` |
| `uploadMulti` | `<li>` | Itera sobre archivos subidos |
| `list` (fijo) | `<div data-list-options="...">` | Valor seleccionado |
| `list` (tabla) | `<div data-list-table="...">` | `num` del registro |
| `multiv2` | `<li>` wrapper | Array de objetos repetibles |
| `checkbox` | `<div>` o `<input>` | `1` o `0` (número) |
| `colorpicker` | `<div>` | Hex color string |
### textfield
```html
<p data-field-type="textfield" data-field-label="Título">
Elemento editable
</p>
```
### headfield
Genera 2 variables: la estándar y `_tag` con la etiqueta elegida (h1…h6).
```html
<{{ titulo_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
</{{ titulo_tag | default('h2') }}>
```
### textbox
```html
<div data-field-type="textbox" data-field-label="Descripción">
Texto largo editable
</div>
```
### wysiwyg
Editor de texto enriquecido. Acceder con `| raw` para no escapar el HTML.
```html
<div class="wysiwyg" data-field-type="wysiwyg" data-field-label="Contenido Enriquecido">
<p>Texto con <strong>estilos</strong> editables</p>
</div>
```
### link
El campo `enlace` de Acai ya incluye las barras necesarias — nunca añadas barras extra.
```html
<a data-field-type="link" data-field-label="Enlace Principal" href="#">
Haz clic aquí
</a>
```
### upload
Devuelve un array. Acceso en Twig: `{{ imagen[0].urlPath }}`.
```html
<div class="p-1/6 relative">
<img class="absolute top-0 left-0 w-full h-full object-cover lazyload"
data-field-type="upload"
data-field-label="Imagen Principal"
data-lazy="true"
data-field-info1="titulo"
data-field-width="1400"
alt="">
</div>
```
Atributos disponibles:
- `data-lazy="true"` — carga perezosa
- `data-field-width="1400"` — ancho máximo sugerido
- `data-field-info1="titulo"` — campo de información adicional (típicamente alt)
### uploadMulti
Itera sobre todas las imágenes subidas. Variable iteradora: `uploadMulti`.
```html
<li data-field-type="uploadMulti" data-field-label="Galería" data-field-info1="titulo">
<div class="relative min-h-screen">
<img class="absolute top-0 left-0 w-full h-full object-cover lazyload"
data-src="{{ uploadMulti.urlPath | imagec(2100) }}"
alt="{{ uploadMulti.info1 }}">
</div>
</li>
```
### list (opciones fijas)
```html
<div data-field-type="list"
data-field-label="Color Producto"
data-list-options="Rojo,Azul,|Verde,3|Amarillo">
</div>
```
Formato `data-list-options`:
- `opcion1,opcion2` → la opción es etiqueta y valor a la vez
- `|valor3,etiqueta3` → separa valor de etiqueta con `|`
### list (tabla)
Selecciona un registro de otra tabla. Devuelve el `num`.
```html
<div data-field-type="list"
data-field-label="Noticia Destacada"
data-list-table="noticias"
data-list-value="num"
data-list-label="titulo">
{{ record.titulo }}
</div>
```
- `data-list-table` — nombre de tabla **sin prefijo `cms_`**
- `data-list-value` — campo a usar como valor (normalmente `num`)
- `data-list-label` — campo a mostrar como label
### multiv2 — Campos repetibles
Crea grupos de campos repetibles. La variable resultante es un array de objetos.
```html
<ul>
<li data-field-type="multiv2" data-field-label="Productos">
<div data-field-type="textfield" data-field-label="Nombre">
Nombre del producto
</div>
<div data-field-type="textbox" data-field-label="Descripción">
Descripción del producto
</div>
<div class="p-1/6 relative">
<img class="absolute top-0 left-0 w-full h-full object-cover lazyload"
data-field-type="upload"
data-field-label="Imagen"
data-lazy="true"
data-field-width="800"
alt="">
</div>
</li>
</ul>
```
Uso en Twig:
```twig
{% for record in productos %}
<div class="producto">
<h3>{{ record.nombre }}</h3>
<p>{{ record.descripcion }}</p>
<img src="{{ record.imagen[0].urlPath }}" alt="">
</div>
{% endfor %}
```
### checkbox
Devuelve `1` o `0` (número), nunca `true`/`false`.
### colorpicker
Devuelve un string hexadecimal (`#ff0000`). Almacenado en config-vars (no en `builder_custom`).
## Atributos Acai
### `c-if` — Renderizado condicional
Usa `=` (un solo igual) para comparaciones, no `==`.
```html
<div c-if="subtitle">{{ subtitle }}</div>
<div c-if="layout = 'grid'">Grid layout</div>
```
### `c-else`
Va inmediatamente después del elemento `c-if`.
```html
<div c-if="image">
<img src="{{ image[0].urlPath }}">
</div>
<div c-else>
<p>No image available</p>
</div>
```
### `c-for` — Iteración sobre array
```html
<div c-for="item in record.features">
<h3>{{ item.title }}</h3>
</div>
```
### `c-for` — Iteración sobre tabla de BD
```html
<ul>
<li c-for="producto in productos"
c-where="'visible=1'"
c-order="'num desc'"
c-limit="10">
{{ producto.title }}
</li>
</ul>
```
Parámetros opcionales: `c-where` (string SQL), `c-order` (string de orden), `c-limit` (entero).
Equivalente Twig:
```twig
{% for producto in 'productos' | get('visible=1','num desc',10) %}
<li>{{ producto.title }}</li>
{% endfor %}
```
Variables del loop: `loop.index` (1-based), `loop.index is odd`, `loop.index is even`.
### `c-class` — Clases CSS condicionales
```html
<!-- Simple -->
<div c-class="{ 'text-center': alineacion == '1', 'text-right': alineacion == '2' }">
<!-- Múltiples condiciones -->
<div c-class="{
'flex-row-reverse': orden == '1',
'cursor-pointer click-a-child': record.enlace_anchor,
'rounded-xl': radioborde == '4'
}">
<!-- Con loop -->
<div c-class="{
'md:order-1': loop.index is odd,
'md:pl-6': loop.index is even
}">
<!-- Combinado con clases estáticas -->
<div class="flex items-center" c-class="{ 'justify-center': centrado }">
```
### `c-hidden` — Variables ocultas
Elemento que NO se renderiza pero SÍ declara variables builder. Patrón típico para colores y opciones de configuración.
```html
<div c-hidden="true">
<input data-field-type="textfield" data-field-label="Color de fondo" value="">
<div data-field-type="list"
data-field-label="Color titulo resaltado"
data-list-options="|Main color,1|Main color light,2|Main color dark"></div>
</div>
```
### `c-required` — Validación condicional
```html
<input type="text" name="telefono"
c-required="'2' not in camposquitar"
placeholder="Teléfono">
```
## Tag `<set>` — Definir variables
```html
<!-- Obtener configuración de la BD -->
<set :tienda="'configuracion_tienda' | get('num != 0')[0]"></set>
<!-- Construir URLs dinámicas -->
<set :logo="tienda.logo.0.urlPath
? 'https://' ~ server.HTTP_HOST ~ tienda.logo.0.urlPath
: 'https://' ~ server.HTTP_HOST ~ '/template/estandar/images/logo.png'">
</set>
<!-- Twig set para expresiones complejas -->
{% set gracias = 'apartados' | get('num = 20').0 %}
```
## Incluir módulos
Para incluir un módulo dentro de otro módulo o dentro de una sección general, usa el `moduleId` como etiqueta HTML:
```html
<module_id :param1="value1" :param2="'string value'"></module_id>
```
Ejemplos:
```html
<header_menu :showLogo="true" :menuItems="items"></header_menu>
<product_card :product="selectedProduct" :showPrice="true"></product_card>
```
El módulo hijo recibe los parámetros como variables en su contexto.
## Formularios — `c-form`
Maneja automáticamente validación, almacenamiento en BD y envío de emails.
```html
<c-form
class="max-w-2xl mx-auto p-6 bg-white rounded-lg shadow"
tableName="'solicitudes'"
mailRecord="['correos', 'CONTACTO']"
sendTo="'contacto@empresa.com'"
sendToClient="'email'"
captcha="true"
honeypot="true"
messageOK="'¡Gracias! Te contactaremos pronto'"
messageKO="'Por favor, completa todos los campos'"
redirect="'/gracias'"
attachFiles="true">
<input name="nombre" type="text" required class="w-full p-2 border rounded">
<input name="email" type="email" required class="w-full p-2 border rounded">
<textarea name="mensaje" required class="w-full p-2 border rounded" rows="5"></textarea>
<label class="flex items-center">
<input name="acepto_politica" type="checkbox" class="mr-2" required>
<span>Acepto la política de privacidad</span>
</label>
<button type="submit" class="bg-teal-500 text-white px-6 py-2 rounded">Enviar</button>
<captcha/>
</c-form>
```
### Atributos `c-form`
| Atributo | Descripción |
|----------|-------------|
| `tableName="'tabla'"` | Tabla destino (sin `cms_`) |
| `mailRecord="['correos', 'ID']"` | Template de email en tabla `correos` |
| `sendTo="'email@dominio.com'"` | Destinatarios (separados por coma) |
| `sendToClient="'campo_email'"` | Campo del formulario con email del cliente para auto-reply |
| `captcha="true"` | Activa Google reCAPTCHA |
| `honeypot="true"` | Campo oculto anti-spam |
| `messageOK="'texto'"` | Mensaje al enviar correctamente |
| `messageKO="'texto'"` | Mensaje al fallar validación |
| `redirect="'/ruta/'"` | Redirección tras envío correcto |
| `attachFiles="true"` | Adjuntar archivos al email |
| `showImages="true"` | Mostrar thumbnails en email |
| `emailMode="'twig'"` | Email en formato Twig |
| `header="'<div>...'"` | HTML cabecera del email |
| `footer="'<div>...'"` | HTML footer del email |
| `styles="'body { ... }'"` | CSS del email |
Para formularios estándar (contacto, postulación), prefiere `c-form` antes que crear lógica custom de POST/hook. Solo crea una tabla propia si necesitas gestionar esos registros desde el admin.
## Componentes built-in
### Carousel — `c-tns-wrapper`
```html
<div class="c-tns-wrapper"
data-responsive='{"0":1,"768":2,"1024":3}'
data-speed="400"
data-nav="true"
data-autoplay-timeout="3000">
<div c-for="slide in record.slides">
<img src="{{ slide.image[0].urlPath }}">
</div>
</div>
```
### Lightbox
```html
<a href="{{ image[0].urlPath }}" class="glightbox" data-gallery="gallery1">
<img src="{{ image[0].urlPath | imagec(400) }}">
</a>
```
### Breadcrumb
```html
<breadCrumb/>
<breadCrumb class="bg-gray-200 p-3 rounded" c-prevlinks="null"></breadCrumb>
```
### Animate On Scroll (AOS)
```html
<div data-aos="fade-up" data-aos-delay="200" data-aos-duration="800">
Contenido animado
</div>
```
Valores comunes: `fade-up`, `fade-down`, `fade-left`, `fade-right`, `zoom-in`, `zoom-in-up`, `fade-up-right`, `fade-up-left`. Tras cambios dinámicos en JS: `AOS.refresh()`.
### Lazy loading
```html
<img class="lazyload" data-src="{{ image[0].urlPath }}">
<!-- O en builder field: -->
<img data-field-type="upload" data-field-label="Imagen" data-lazy="true">
```
## Reglas críticas
1. Todo `data-field-type` exige `data-field-label`.
2. `data-field-label` se transforma a variable: minúsculas, sin espacios ni caracteres especiales.
3. Campos `upload` retornan **arrays** — usa `imagen[0].urlPath`, nunca `imagen`.
4. Variables dentro de `multiv2` son propiedades del objeto iterado (`record.nombre`).
5. `c-if` usa `=` (un igual). `{% if %}` usa `==` (doble igual).
6. `c-for` con tabla: nombre **sin prefijo `cms_`**.
7. `enlace` ya incluye las barras — no añadas slashes extra.
8. Checkbox guarda `1` o `0` (número), nunca `true`/`false`.
9. Evita Tailwind arbitrary-value en `index-base.tpl` — muévelos a `style.css`.
10. `script.js` y `style.css` son estáticos: NO uses sintaxis Twig dentro. Pasa valores dinámicos vía `data-*`.