Agent Template

Arquitecto de reservas de perfumes

Asistente en español para diseñar e implementar, por entregables, una aplicación de reservas de productos con catálogo, carrito, pagos, envíos y administración. Genera especificaciones y código orientativo sin presentar el prototipo como un comercio desplegado.

reservas perfumes comercio electrónico arquitectura backend
shido Nov 06, 2026 1 use

Preview Mode

This is a preview with sample data. The template uses placeholders like which will be replaced with actual agent data.

About This Template

Arquitecto de reservas de perfumes is a browser-executable AI agent template built on AgentOp. It runs entirely in the browser using Python (via Pyodide) and can be deployed without a server — just download the generated HTML file and open it locally or host it anywhere.

Topics reservas perfumes comercio electrónico arquitectura backend
Template Preview

Template Metadata

Slug
arquitecto-de-reservas-de-perfumes
Created By
shido
Created
Nov 06, 2026
Usage Count
1

Tags

reservas perfumes comercio electrónico arquitectura backend

Code Statistics

HTML Lines
49
CSS Lines
1
JS Lines
71
Python Lines
54

Source Code

<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ agent_name }}</title>
  <style>{{ css_code }}</style>
</head>
<body>
  <div class="app-shell">
    <header class="masthead">
      <div class="brand-mark" aria-hidden="true">✦</div>
      <div><p class="eyebrow">ESTUDIO DE IMPLEMENTACIÓN</p><h1>{{ agent_name }}</h1><p class="subtitle">{{ description }}</p></div>
    </header>
    <main class="workspace">
      <section class="panel brief-panel" aria-labelledby="brief-title">
        <p class="eyebrow">01 / CONFIGURAR</p>
        <h2 id="brief-title">Tu entregable de reservas</h2>
        <p class="helper">Diseña una parte concreta del sistema. La base de datos, la autenticación y los permisos pertenecen al servidor, no a esta página.</p>
        <form id="brief-form">
          <label for="deliverable">Tipo de entregable</label>
          <select id="deliverable" name="deliverable">
            <option value="Arquitectura y plan">Arquitectura y plan</option>
            <option value="Modelo de datos y migraciones">Modelo de datos y migraciones</option>
            <option value="API y autorización">API y autorización</option>
            <option value="Frontend y experiencia">Frontend y experiencia</option>
            <option value="Implementación de una función">Implementación de una función</option>
            <option value="Pruebas y auditoría">Pruebas y auditoría</option>
          </select>
          <label for="request">¿Qué necesitas construir?</label>
          <textarea id="request" rows="8" maxlength="6000" placeholder="Ej.: Diseña el checkout de perfumes con retiro, envío por km, transferencia y código único de reserva."></textarea>
          <p class="field-note">Indica el stack si ya lo elegiste; si no, se propondrá uno libre y adecuado.</p>
          <p id="form-error" class="error hidden" role="alert"></p>
          <div class="actions"><button id="generate-btn" type="submit">Generar entregable de reservas →</button><button id="clear-btn" class="secondary" type="button">Limpiar solicitud</button></div>
        </form>
        <div class="suggestions"><p class="eyebrow">PUNTOS DE PARTIDA</p><div class="chips"><button type="button" class="chip" data-example="0">Esquema de datos</button><button type="button" class="chip" data-example="1">Permisos de API</button><button type="button" class="chip" data-example="2">Checkout y envíos</button><button type="button" class="chip" data-example="3">Pruebas de seguridad</button></div></div>
      </section>
      <section class="panel output-panel" aria-labelledby="output-title">
        <div class="output-heading"><div><p class="eyebrow">02 / RESULTADO</p><h2 id="output-title">Plan de implementación</h2></div><button id="copy-btn" class="secondary" type="button" disabled>Copiar resultado</button></div>
        <p id="runtime-status" class="runtime-status" role="status">Inicializando el entorno de reservas…</p>
        <div id="results-container" aria-live="polite"><div id="empty-state" class="empty-state"><div class="empty-glyph" aria-hidden="true">✦</div><h3>Del concepto a una reserva verificable</h3><p>Selecciona un entregable para obtener una respuesta ajustada a estos requisitos:</p><ul><li>Catálogo público y gestión de productos con moneda propia.</li><li>Carrito, checkout único y reservas persistidas.</li><li>Envío por distancia, pagos y comprobantes por WhatsApp.</li><li>Permisos de servidor, administración y arrepentimiento.</li></ul></div><pre id="answer" class="answer hidden"></pre></div>
        <div id="loading-overlay" class="loading-overlay hidden" role="status" aria-live="polite"><span class="spinner" aria-hidden="true"></span><span>Preparando el entregable de reservas…</span></div>
      </section>
    </main>
    <footer>Guía de implementación · Las operaciones comerciales reales requieren un backend desplegado.</footer>
  </div>
  <script>{{ js_code }}</script>
</body>
</html>
:root{--bg:#f6f1ea;--paper:#fffdfa;--ink:#242522;--muted:#676b63;--accent:#32544a;--accent-hover:#244238;--line:#dedbd2;--soft:#e9efe8;--danger:#9e3838;--radius:20px;--shadow:0 18px 48px rgba(34,42,35,.07);--font:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font);line-height:1.55}.hidden{display:none!important}.app-shell{max-width:1500px;margin:auto;padding:40px 34px 22px}.masthead{display:flex;align-items:flex-start;gap:20px;margin:0 0 32px}.brand-mark{width:52px;height:52px;display:grid;place-items:center;background:var(--accent);color:#fff;border-radius:16px;font-size:29px;flex:none}.eyebrow{font-size:11px;font-weight:800;letter-spacing:.16em;color:var(--accent);margin:0 0 8px}h1,h2,h3,p{margin-top:0}h1{font-size:clamp(26px,3vw,40px);letter-spacing:-.045em;margin-bottom:5px}h2{font-size:23px;letter-spacing:-.035em;margin-bottom:12px}h3{font-size:21px;letter-spacing:-.025em}.subtitle,.helper,.field-note,footer{color:var(--muted)}.subtitle{max-width:850px;margin-bottom:0}.workspace{display:grid;grid-template-columns:minmax(350px,440px) minmax(0,1fr);gap:22px;align-items:start}.panel{position:relative;background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);padding:30px;box-shadow:var(--shadow)}.helper{font-size:14px;margin-bottom:28px}label{display:block;font-size:13px;font-weight:750;margin:20px 0 8px}select,textarea{width:100%;border:1px solid #c8d0c6;border-radius:12px;background:#fff;color:var(--ink);font:inherit;padding:13px 14px}textarea{resize:vertical;min-height:165px}.field-note{font-size:12px;margin:8px 0 0}.actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:22px}button{font:inherit;font-weight:700;cursor:pointer;border:1px solid var(--accent);background:var(--accent);color:white;border-radius:11px;padding:11px 16px;transition:background .18s,transform .18s}button:hover:not(:disabled){background:var(--accent-hover);transform:translateY(-1px)}button.secondary{background:transparent;color:var(--accent)}button.secondary:hover:not(:disabled),button.chip:hover:not(:disabled){background:var(--soft)}button:disabled{opacity:.48;cursor:not-allowed;transform:none}button:focus-visible,select:focus-visible,textarea:focus-visible{outline:3px solid #93bba5;outline-offset:3px}.error{color:var(--danger);background:#fff0ef;border-radius:10px;padding:10px 12px;margin:12px 0 0;font-size:13px}.suggestions{margin-top:35px;border-top:1px solid var(--line);padding-top:21px}.chips{display:flex;flex-wrap:wrap;gap:8px}.chip{background:#f5f8f3;color:var(--accent);border-color:#d4dfd2;padding:8px 11px;font-size:12px}.output-panel{min-height:590px}.output-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;border-bottom:1px solid var(--line);padding-bottom:16px}.output-heading h2{margin-bottom:0}.runtime-status{font-size:12px;color:var(--muted);margin:15px 0}.empty-state{padding:52px max(10px,7%) 45px}.empty-glyph{font-size:35px;color:var(--accent);margin-bottom:14px}.empty-state p,.empty-state li{color:var(--muted)}.empty-state ul{padding-left:20px}.empty-state li{margin-bottom:10px}.answer{white-space:pre-wrap;overflow-wrap:anywhere;font:14px/1.7 var(--font);margin:15px 0 0;max-height:70vh;overflow:auto}.loading-overlay{position:absolute;inset:0;z-index:2;background:rgba(255,253,250,.92);border-radius:var(--radius);display:flex;align-items:center;justify-content:center;gap:12px;color:var(--accent);font-weight:700;text-align:center;padding:18px}.spinner{width:24px;height:24px;border-radius:50%;border:3px solid #d6e2d8;border-top-color:var(--accent);animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}footer{text-align:center;font-size:12px;padding:26px 0}@media(max-width:1200px){.workspace{grid-template-columns:minmax(300px,390px) minmax(0,1fr)}.panel{padding:24px}}@media(max-width:768px){.app-shell{padding:25px 16px}.workspace{grid-template-columns:1fr}.panel{padding:22px}.output-panel{min-height:400px}.empty-state{padding:22px 5px}.output-heading{flex-wrap:wrap}}
(() => {
  const $ = id => document.getElementById(id);
  const form = $('brief-form');
  const request = $('request');
  const deliverable = $('deliverable');
  const error = $('form-error');
  const status = $('runtime-status');
  const generate = $('generate-btn');
  const clear = $('clear-btn');
  const copy = $('copy-btn');
  const overlay = $('loading-overlay');
  const answer = $('answer');
  const empty = $('empty-state');
  const examples = [
    'Diseña el esquema de base de datos para productos, carritos, reservas y tipos de cambio.',
    'Define los endpoints y permisos para clientes y administradores.',
    'Implementa el cálculo de envío por distancia y el cierre de una reserva multi-producto.',
    'Prepara pruebas para impedir que un cliente modifique reservas ajenas.'
  ];
  let ready = Boolean(window.pyodideReady && window.pyodide);
  let busy = false;
  const localProvider = () => window.PROVIDER === 'local' || window.PROVIDER === 'wllama' || Boolean(window.agentManager);
  const modelMissing = () => localProvider() && !(window.agentManager && window.agentManager.isLoaded);
  function setError(message) { error.textContent = message; error.classList.toggle('hidden', !message); }
  function updateState() {
    generate.disabled = busy || !ready || modelMissing();
    clear.disabled = busy;
    copy.disabled = busy || !answer.textContent;
    overlay.classList.toggle('hidden', !busy);
    status.textContent = busy ? 'Generando el entregable de reservas…' : !ready ? 'Inicializando el entorno de reservas…' : modelMissing() ? 'Carga un modelo desde la barra superior para generar el entregable.' : 'Entorno listo para diseñar tu aplicación de reservas.';
  }
  document.addEventListener('pyodide-ready', () => { ready = true; updateState(); });
  if (ready) updateState();
  window.setInterval(() => { if (!ready && window.pyodideReady && window.pyodide) ready = true; updateState(); }, 1200);
  request.addEventListener('input', () => { if (error.textContent) setError(''); });
  deliverable.addEventListener('change', () => setError(''));
  document.querySelectorAll('[data-example]').forEach(button => {
    button.addEventListener('click', () => {
      request.value = examples[Number(button.dataset.example)];
      deliverable.value = ['Modelo de datos y migraciones','API y autorización','Implementación de una función','Pruebas y auditoría'][Number(button.dataset.example)];
      setError(''); request.focus();
    });
  });
  clear.addEventListener('click', () => { request.value = ''; deliverable.selectedIndex = 0; setError(''); request.focus(); });
  copy.addEventListener('click', async () => {
    if (!answer.textContent) return;
    try { await navigator.clipboard.writeText(answer.textContent); copy.textContent = 'Resultado copiado'; setTimeout(() => { copy.textContent = 'Copiar resultado'; }, 1800); }
    catch (_) { setError('No se pudo copiar automáticamente. Selecciona el texto del resultado para copiarlo.'); }
  });
  form.addEventListener('submit', async event => {
    event.preventDefault();
    const text = request.value.trim();
    const chosen = deliverable.value;
    if (!text) { setError('Describe la función o el requisito de reservas que quieres construir.'); request.focus(); return; }
    if (!ready || !window.pyodide) { setError('El entorno aún se está inicializando. Inténtalo de nuevo en unos segundos.'); return; }
    if (modelMissing()) { setError('Carga primero un modelo desde la barra superior.'); return; }
    if (busy) return;
    setError(''); busy = true; updateState();
    try {
      window.pyodide.globals.set('user_query', JSON.stringify({deliverable: chosen, request: text}));
      const result = await window.pyodide.runPythonAsync('await process_user_query(user_query)');
      const rendered = String(result ?? '').trim();
      if (!rendered) throw new Error('No se recibió contenido.');
      answer.textContent = rendered;
      answer.classList.remove('hidden'); empty.classList.add('hidden');
    } catch (err) {
      setError('No se pudo generar el entregable: ' + (err && err.message ? err.message : 'error desconocido') + '. Comprueba la conexión o el modelo y vuelve a intentarlo.');
    } finally { busy = false; updateState(); }
  });
  updateState();
})();
import json

async def process_user_query(query: str) -> str:
    """Genera un entregable técnico para la aplicación de reservas.

    Args:
        query: JSON con el tipo de entregable y la solicitud del usuario.

    Returns:
        Respuesta de implementación en español.
    """
    try:
        payload = json.loads(query)
        if not isinstance(payload, dict):
            payload = {}
    except (ValueError, TypeError):
        payload = {"request": str(query)}
    allowed = {"Arquitectura y plan", "Modelo de datos y migraciones", "API y autorización", "Frontend y experiencia", "Implementación de una función", "Pruebas y auditoría"}
    deliverable = str(payload.get("deliverable", "Arquitectura y plan"))
    if deliverable not in allowed:
        deliverable = "Arquitectura y plan"
    request = str(payload.get("request", "")).strip()[:6000]
    if not request:
        return "Describe qué parte de la aplicación de reservas necesitas construir."
    baseline = (
        "Sistema real de reservas de productos, inicialmente perfumes, ampliable a otros rubros. "
        "Catálogo buscable con fichas, imágenes, nombre, marca, descripción, notas/categoría, precio, "
        "moneda por producto, visibilidad y stock opcional; ocultos fuera del catálogo público. "
        "Carrito multiproducto con cantidades, eliminación, localStorage solo como respaldo y "
        "persistencia por usuario autenticado. Favoritos por cuenta. Checkout único: subtotal, "
        "envío, total, moneda de visualización/pago, retiro o domicilio, mapa y distancia/tarifa "
        "por km, efectivo o transferencia. Reserva con código único, detalle en Mis reservas y "
        "enlaces de WhatsApp al comercio y al cliente para compartir comprobante, sin afirmar que "
        "WhatsApp recibió un mensaje sin integración real. Administración: productos, publicación, "
        "reservas y estados, marca, cuentas bancarias, WhatsApp, ubicación, tarifas, monedas y "
        "tipos de cambio; solicitudes de arrepentimiento. Backend con base de datos real, "
        "autenticación y permisos comprobados del lado servidor. Productos, reservas y ajustes "
        "persisten en BD; nunca confiar en precios, totales, roles o estados enviados por el cliente."
    )
    prompt = (
        "Responde en español. Entregable: " + deliverable + ".\n"
        "Solicitud concreta: " + request + "\n\n"
        "Requisitos obligatorios:\n" + baseline + "\n\n"
        "Produce una propuesta implementable, centrada en la solicitud. Si no se indicó stack, "
        "propón uno libre y explica brevemente por qué. Da pasos, contratos, SQL o fragmentos de "
        "código cuando correspondan; no inventes funciones ya implementadas ni afirmes que este "
        "HTML es un backend desplegado. Para checkout: recalcular precios, cambio y envío en servidor, "
        "usar transacción para reservar y generar código único; guardar importes y tipos de cambio "
        "aplicados para auditoría. Para seguridad: contraseñas con hash, roles en servidor, "
        "autorización por recurso y pruebas de acceso cruzado. Si el alcance es grande, prioriza "
        "un entregable verificable y enumera dependencias pendientes."
    )
    answer = await agentop_llm.generate(prompt, globals().get("TEMPLATE_SYSTEM_PROMPT", ""))
    return str(answer)