Add sidebar menu with module actions in admin UI
This commit is contained in:
@@ -63,8 +63,8 @@
|
||||
<h1>Admin Panel</h1>
|
||||
<p>Interfaz de administración (Vue 3 + JWT).</p>
|
||||
|
||||
<div id="app">
|
||||
<div v-if="!token" class="module">
|
||||
<div id="app" style="display: flex; gap: 1rem;">
|
||||
<div v-if="!token" class="module" style="flex: 1;">
|
||||
<h2>Iniciar sesión</h2>
|
||||
<div class="field">
|
||||
<label>Email</label>
|
||||
@@ -104,32 +104,60 @@
|
||||
<div :class="{ error: registerError, success: registerSuccess }">{{ registerMessage }}</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="module">
|
||||
<div style="display:flex; align-items:center; gap:1rem; margin-bottom:1rem;">
|
||||
<div>
|
||||
<strong>Autenticado como:</strong> {{ user?.name || user?.email }}
|
||||
<div v-else style="flex: 1; display: flex; gap: 1rem;">
|
||||
<div style="width: 240px;">
|
||||
<div class="module">
|
||||
<div style="display:flex; align-items:center; justify-content:space-between;">
|
||||
<div>
|
||||
<div><strong>{{ user?.name || user?.email }}</strong></div>
|
||||
<div style="font-size:0.85rem; color:#555;">Tenant: {{ tenantId }}</div>
|
||||
</div>
|
||||
<button @click="logout">Salir</button>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="logout">Cerrar sesión</button>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
Tenant ID:
|
||||
<input v-model="tenantId" />
|
||||
</label>
|
||||
<button @click="loadModules" style="margin-left:0.5rem;">Actualizar módulos</button>
|
||||
<span style="margin-left:1rem; color:#555;">{{ status }}</span>
|
||||
<div class="module">
|
||||
<h3>Módulos</h3>
|
||||
<div v-for="mod in modules" :key="mod.key" style="margin-bottom:0.5rem;">
|
||||
<button
|
||||
:style="{
|
||||
width: '100%',
|
||||
textAlign: 'left',
|
||||
padding: '0.5rem',
|
||||
border: selectedModule === mod.key ? '1px solid #333' : '1px solid #ccc',
|
||||
background: selectedModule === mod.key ? '#f0f0f0' : '#fff',
|
||||
}"
|
||||
@click="selectModule(mod.key)">
|
||||
{{ mod.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="modules.length" style="margin-top:1rem;">
|
||||
<div v-for="mod in modules" :key="mod.key" class="module">
|
||||
<h2>{{ mod.name }}</h2>
|
||||
<p><span class="badge">{{ mod.key }}</span> {{ mod.description }}</p>
|
||||
<button @click="selectModule(mod.key)">Administrar</button>
|
||||
<div class="module" v-if="selectedModule">
|
||||
<h3>Acciones</h3>
|
||||
<div v-for="action in actions" :key="action.key" style="margin-bottom:0.4rem;">
|
||||
<button style="width: 100%; text-align: left;" @click="action.handler()">{{ action.label }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedModule" class="module">
|
||||
<h2>Administrar: {{ selectedModule }}</h2>
|
||||
<div v-html="moduleHtml"></div>
|
||||
<div style="flex: 1;">
|
||||
<div class="module">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<div>
|
||||
<label>
|
||||
Tenant ID:
|
||||
<input v-model="tenantId" style="margin-left:0.5rem;" />
|
||||
</label>
|
||||
</div>
|
||||
<div style="color:#555;">{{ status }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="module">
|
||||
<h2 v-if="selectedModule">Administrar: {{ selectedModule }}</h2>
|
||||
<div v-html="moduleHtml"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -263,6 +291,125 @@
|
||||
}
|
||||
};
|
||||
|
||||
const formatJson = (value) => `<pre style="white-space: pre-wrap; word-break: break-word;">${JSON.stringify(value, null, 2)}</pre>`;
|
||||
|
||||
const showResult = (value) => {
|
||||
moduleHtml.value = formatJson(value);
|
||||
};
|
||||
|
||||
const confirmPrompt = (message, defaultValue = '') => {
|
||||
const result = prompt(message, defaultValue);
|
||||
return result === null ? null : result.trim();
|
||||
};
|
||||
|
||||
const listGuests = async () => {
|
||||
const guests = await api('/guest');
|
||||
showResult(guests);
|
||||
};
|
||||
|
||||
const createGuest = async () => {
|
||||
const name = confirmPrompt('Nombre');
|
||||
if (!name) return;
|
||||
const email = confirmPrompt('Email');
|
||||
const phone = confirmPrompt('Teléfono');
|
||||
const guest = await api('/guest', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, email, phone }),
|
||||
});
|
||||
showResult(guest);
|
||||
};
|
||||
|
||||
const updateGuestRsvp = async () => {
|
||||
const id = confirmPrompt('ID del invitado');
|
||||
if (!id) return;
|
||||
const rsvp = confirmPrompt('RSVP (true/false)', 'true');
|
||||
const updated = await api(`/guest/${id}/rsvp`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ rsvp: rsvp === 'true' }),
|
||||
});
|
||||
showResult(updated);
|
||||
};
|
||||
|
||||
const listTodos = async () => {
|
||||
const todos = await api('/todo');
|
||||
showResult(todos);
|
||||
};
|
||||
|
||||
const createTodo = async () => {
|
||||
const title = confirmPrompt('Título');
|
||||
if (!title) return;
|
||||
const description = confirmPrompt('Descripción');
|
||||
const todo = await api('/todo', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title, description }),
|
||||
});
|
||||
showResult(todo);
|
||||
};
|
||||
|
||||
const completeTodo = async () => {
|
||||
const id = confirmPrompt('ID del To-do');
|
||||
if (!id) return;
|
||||
const completed = await api(`/todo/${id}/complete`, {
|
||||
method: 'PATCH',
|
||||
});
|
||||
showResult(completed);
|
||||
};
|
||||
|
||||
const listGifts = async () => {
|
||||
const gifts = await api('/gift');
|
||||
showResult(gifts);
|
||||
};
|
||||
|
||||
const createGift = async () => {
|
||||
const name = confirmPrompt('Nombre del regalo');
|
||||
if (!name) return;
|
||||
const description = confirmPrompt('Descripción');
|
||||
const price = confirmPrompt('Precio');
|
||||
const gift = await api('/gift', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ name, description, price: Number(price) || 0 }),
|
||||
});
|
||||
showResult(gift);
|
||||
};
|
||||
|
||||
const contributeGift = async () => {
|
||||
const id = confirmPrompt('ID del regalo');
|
||||
if (!id) return;
|
||||
const name = confirmPrompt('Nombre del contribuyente');
|
||||
const amount = confirmPrompt('Monto');
|
||||
const contribution = await api(`/gift/${id}/contribution`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ contributorName: name, amount: Number(amount) || 0 }),
|
||||
});
|
||||
showResult(contribution);
|
||||
};
|
||||
|
||||
const actions = computed(() => {
|
||||
const map = {
|
||||
guest: [
|
||||
{ key: 'list', label: 'Listar invitados', handler: listGuests },
|
||||
{ key: 'create', label: 'Crear invitado', handler: createGuest },
|
||||
{ key: 'rsvp', label: 'Actualizar RSVP', handler: updateGuestRsvp },
|
||||
],
|
||||
todo: [
|
||||
{ key: 'list', label: 'Listar To-dos', handler: listTodos },
|
||||
{ key: 'create', label: 'Crear To-do', handler: createTodo },
|
||||
{ key: 'complete', label: 'Completar To-do', handler: completeTodo },
|
||||
],
|
||||
gift: [
|
||||
{ key: 'list', label: 'Listar regalos', handler: listGifts },
|
||||
{ key: 'create', label: 'Crear regalo', handler: createGift },
|
||||
{ key: 'contribute', label: 'Agregar contribución', handler: contributeGift },
|
||||
],
|
||||
};
|
||||
return map[selectedModule.value] || [];
|
||||
});
|
||||
|
||||
const loadModules = async () => {
|
||||
if (!token.value) return;
|
||||
try {
|
||||
@@ -274,7 +421,7 @@
|
||||
|
||||
const selectModule = (key) => {
|
||||
selectedModule.value = key;
|
||||
moduleHtml.value = `<p>Seleccionado <strong>${key}</strong>. Usa los botones del módulo para operar.</p>`;
|
||||
moduleHtml.value = `<p>Seleccionado <strong>${key}</strong>. Usa las acciones en el menú.</p>`;
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -285,6 +432,7 @@
|
||||
modules,
|
||||
selectedModule,
|
||||
moduleHtml,
|
||||
actions,
|
||||
login,
|
||||
register,
|
||||
loginMessage,
|
||||
@@ -304,4 +452,7 @@
|
||||
}).mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user