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.
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.
Template Metadata
- Slug
- arquitecto-de-reservas-de-perfumes
- Created By
- shido
- Created
- Nov 06, 2026
- Usage Count
- 1
Tags
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)
Name your agent
Based on . You'll get your own private copy to try and customize.