Replace admin UI with Vue-based login/register interface
This commit is contained in:
@@ -28,459 +28,280 @@
|
|||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: #111827;
|
color: #111827;
|
||||||
}
|
}
|
||||||
|
.field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.field input {
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: #f7f7f8;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #b91c1c;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
.success {
|
||||||
|
color: #047857;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Admin Panel</h1>
|
<h1>Admin Panel</h1>
|
||||||
<p>Modular admin UI: cada módulo se registra automáticamente.</p>
|
<p>Interfaz de administración (Vue 3 + JWT).</p>
|
||||||
|
|
||||||
|
<div id="app">
|
||||||
|
<div v-if="!token" class="module">
|
||||||
|
<h2>Iniciar sesión</h2>
|
||||||
|
<div class="field">
|
||||||
|
<label>Email</label>
|
||||||
|
<input v-model="login.email" type="email" placeholder="admin@local" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Contraseña</label>
|
||||||
|
<input v-model="login.password" type="password" placeholder="••••••" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Tenant</label>
|
||||||
|
<input v-model="login.tenantId" placeholder="default" />
|
||||||
|
</div>
|
||||||
|
<button :disabled="loading" @click="loginUser">Iniciar sesión</button>
|
||||||
|
<div :class="{ error: loginError, success: loginSuccess }">{{ loginMessage }}</div>
|
||||||
|
|
||||||
<div id="auth" class="module">
|
|
||||||
<h2>Acceso</h2>
|
|
||||||
<div id="login-form">
|
|
||||||
<strong>Iniciar sesión</strong><br/>
|
|
||||||
<input id="loginEmail" placeholder="Email" style="margin-bottom:0.5rem;" />
|
|
||||||
<input id="loginPassword" type="password" placeholder="Contraseña" style="margin-bottom:0.5rem;" />
|
|
||||||
<button id="loginBtn">Iniciar sesión</button>
|
|
||||||
<div id="loginStatus" style="margin-top:0.5rem;color:#555;"></div>
|
|
||||||
<hr />
|
<hr />
|
||||||
<strong>Registrar usuario</strong><br/>
|
|
||||||
<input id="registerName" placeholder="Nombre (opcional)" style="margin-bottom:0.5rem;" />
|
<h2>Registrar usuario</h2>
|
||||||
<input id="registerEmail" placeholder="Email" style="margin-bottom:0.5rem;" />
|
<div class="field">
|
||||||
<input id="registerPassword" type="password" placeholder="Contraseña" style="margin-bottom:0.5rem;" />
|
<label>Nombre</label>
|
||||||
<input id="registerTenant" placeholder="Tenant ID" value="default" style="margin-bottom:0.5rem;" />
|
<input v-model="register.name" placeholder="Nombre (opcional)" />
|
||||||
<button id="registerBtn">Registrar</button>
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Email</label>
|
||||||
|
<input v-model="register.email" type="email" placeholder="admin@local" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Contraseña</label>
|
||||||
|
<input v-model="register.password" type="password" placeholder="••••••" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>Tenant</label>
|
||||||
|
<input v-model="register.tenantId" placeholder="default" />
|
||||||
|
</div>
|
||||||
|
<button :disabled="loading" @click="registerUser">Registrar</button>
|
||||||
|
<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>
|
||||||
|
<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 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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="selectedModule" class="module">
|
||||||
|
<h2>Administrar: {{ selectedModule }}</h2>
|
||||||
|
<div v-html="moduleHtml"></div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="main" style="display: none;">
|
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
|
||||||
<label>
|
|
||||||
Tenant ID:
|
|
||||||
<input id="tenantId" value="default" />
|
|
||||||
</label>
|
|
||||||
<button id="refresh" style="margin-left: 0.5rem;">Actualizar módulos</button>
|
|
||||||
<button id="logout" style="margin-left: 0.5rem;">Cerrar sesión</button>
|
|
||||||
<span id="status" style="margin-left: 1rem; color: #555;"></span>
|
|
||||||
|
|
||||||
<div id="modules"></div>
|
|
||||||
|
|
||||||
<div id="module-detail"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const apiRoot = '/api/admin';
|
const { createApp, reactive, ref } = Vue;
|
||||||
const authRoot = '/api/auth';
|
|
||||||
const tokenKey = 'planner_admin_token';
|
|
||||||
|
|
||||||
const authPanel = document.getElementById('auth');
|
createApp({
|
||||||
const mainPanel = document.getElementById('main');
|
setup() {
|
||||||
const tenantInput = document.getElementById('tenantId');
|
const apiRoot = '/api/admin';
|
||||||
const modulesContainer = document.getElementById('modules');
|
const authRoot = '/api/auth';
|
||||||
const detailContainer = document.getElementById('module-detail');
|
const tokenKey = 'planner_admin_token';
|
||||||
const statusEl = document.getElementById('status');
|
|
||||||
|
|
||||||
const loginStatus = document.getElementById('loginStatus');
|
const token = ref(localStorage.getItem(tokenKey) || '');
|
||||||
const loginEmail = document.getElementById('loginEmail');
|
const user = ref(null);
|
||||||
const loginPassword = document.getElementById('loginPassword');
|
const tenantId = ref('default');
|
||||||
const loginBtn = document.getElementById('loginBtn');
|
const status = ref('');
|
||||||
|
const modules = ref([]);
|
||||||
|
const selectedModule = ref('');
|
||||||
|
const moduleHtml = ref('');
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
const registerName = document.getElementById('registerName');
|
const login = reactive({ email: '', password: '', tenantId: 'default' });
|
||||||
const registerEmail = document.getElementById('registerEmail');
|
const register = reactive({ name: '', email: '', password: '', tenantId: 'default' });
|
||||||
const registerPassword = document.getElementById('registerPassword');
|
|
||||||
const registerTenant = document.getElementById('registerTenant');
|
|
||||||
const registerBtn = document.getElementById('registerBtn');
|
|
||||||
|
|
||||||
const logoutBtn = document.getElementById('logout');
|
const loginMessage = ref('');
|
||||||
|
const loginError = ref(false);
|
||||||
|
const loginSuccess = ref(false);
|
||||||
|
|
||||||
let authToken = localStorage.getItem(tokenKey) || '';
|
const registerMessage = ref('');
|
||||||
|
const registerError = ref(false);
|
||||||
|
const registerSuccess = ref(false);
|
||||||
|
|
||||||
function setStatus(msg) {
|
const setStatus = (msg) => (status.value = msg);
|
||||||
if (statusEl) statusEl.textContent = msg;
|
const setToken = (t) => {
|
||||||
}
|
token.value = t;
|
||||||
|
if (t) localStorage.setItem(tokenKey, t);
|
||||||
function setLoginStatus(msg) {
|
else localStorage.removeItem(tokenKey);
|
||||||
if (loginStatus) loginStatus.textContent = msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setToken(token) {
|
|
||||||
authToken = token;
|
|
||||||
if (token) {
|
|
||||||
localStorage.setItem(tokenKey, token);
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(tokenKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function withTenant(url) {
|
|
||||||
const tenantId = tenantInput.value || 'default';
|
|
||||||
const sep = url.includes('?') ? '&' : '?';
|
|
||||||
return `${url}${sep}tenantId=${encodeURIComponent(tenantId)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function api(path, options = {}) {
|
|
||||||
setStatus('Consultando ' + path + '...');
|
|
||||||
if (authToken) {
|
|
||||||
options.headers = {
|
|
||||||
...(options.headers || {}),
|
|
||||||
Authorization: `Bearer ${authToken}`,
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(withTenant(`${apiRoot}${path}`), options);
|
const api = async (path, options = {}) => {
|
||||||
const data = await res.json().catch(() => null);
|
setStatus('Consultando ' + path + '...');
|
||||||
if (!res.ok) {
|
const headers = options.headers || {};
|
||||||
const err = data?.message ? data.message : res.statusText;
|
if (token.value) headers.Authorization = `Bearer ${token.value}`;
|
||||||
setStatus(`Error ${res.status}`);
|
const res = await fetch(`${apiRoot}${path}?tenantId=${encodeURIComponent(tenantId.value)}`, {
|
||||||
throw new Error(err || `HTTP ${res.status}`);
|
...options,
|
||||||
}
|
headers,
|
||||||
setStatus('OK');
|
|
||||||
setTimeout(() => setStatus(''), 1500);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function login() {
|
|
||||||
setLoginStatus('Iniciando sesión...');
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${authRoot}/login`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
tenantId: registerTenant.value || 'default',
|
|
||||||
email: loginEmail.value,
|
|
||||||
password: loginPassword.value,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
if (!res.ok || !data.token) {
|
|
||||||
setLoginStatus(data.message || 'Credenciales inválidas');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setToken(data.token);
|
|
||||||
setLoginStatus('Sesión iniciada');
|
|
||||||
showMain();
|
|
||||||
} catch (err) {
|
|
||||||
setLoginStatus(err.message || err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function register() {
|
|
||||||
setLoginStatus('Registrando...');
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${authRoot}/register`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
tenantId: registerTenant.value || 'default',
|
|
||||||
name: registerName.value,
|
|
||||||
email: registerEmail.value,
|
|
||||||
password: registerPassword.value,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
const data = await res.json();
|
|
||||||
if (!res.ok) {
|
|
||||||
setLoginStatus(data.message || 'Error al registrar');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setLoginStatus('Registrado. Inicia sesión.');
|
|
||||||
} catch (err) {
|
|
||||||
setLoginStatus(err.message || err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function logout() {
|
|
||||||
setToken('');
|
|
||||||
showAuth();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showAuth() {
|
|
||||||
if (authPanel) authPanel.style.display = 'block';
|
|
||||||
if (mainPanel) mainPanel.style.display = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
function showMain() {
|
|
||||||
if (authPanel) authPanel.style.display = 'none';
|
|
||||||
if (mainPanel) mainPanel.style.display = 'block';
|
|
||||||
loadModules();
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatJson(value) {
|
|
||||||
return `<pre style="white-space: pre-wrap; word-break: break-word;">${JSON.stringify(value, null, 2)}</pre>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showError(err) {
|
|
||||||
detailContainer.innerHTML = `<div class="module"><strong>Error:</strong> ${err.message || err}</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderModules(modules) {
|
|
||||||
modulesContainer.innerHTML = '';
|
|
||||||
detailContainer.innerHTML = '';
|
|
||||||
|
|
||||||
if (!modules.length) {
|
|
||||||
modulesContainer.innerHTML = '<p>No hay módulos registrados aún.</p>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
modules.forEach((mod) => {
|
|
||||||
const card = document.createElement('div');
|
|
||||||
card.className = 'module';
|
|
||||||
const title = document.createElement('h2');
|
|
||||||
title.textContent = mod.name;
|
|
||||||
const meta = document.createElement('p');
|
|
||||||
meta.innerHTML = `<span class="badge">${mod.key}</span> ${mod.description || ''}`;
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.textContent = 'Administrar';
|
|
||||||
button.onclick = () => showModule(mod.key);
|
|
||||||
card.append(title, meta, button);
|
|
||||||
modulesContainer.appendChild(card);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadModules() {
|
|
||||||
modulesContainer.innerHTML = '<p>Cargando módulos...</p>';
|
|
||||||
try {
|
|
||||||
const modules = await api('/modules');
|
|
||||||
renderModules(modules);
|
|
||||||
} catch (err) {
|
|
||||||
showError(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createButton(label, onClick) {
|
|
||||||
const btn = document.createElement('button');
|
|
||||||
btn.textContent = label;
|
|
||||||
btn.style.marginRight = '0.5rem';
|
|
||||||
btn.onclick = onClick;
|
|
||||||
return btn;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createInput(name, placeholder = '', type = 'text') {
|
|
||||||
const input = document.createElement('input');
|
|
||||||
input.name = name;
|
|
||||||
input.placeholder = placeholder;
|
|
||||||
input.type = type;
|
|
||||||
input.style.marginRight = '0.5rem';
|
|
||||||
input.style.marginBottom = '0.5rem';
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showModule(key) {
|
|
||||||
detailContainer.innerHTML = `<div class="module"><h2>Administrar: ${key}</h2><p>Cargando...</p></div>`;
|
|
||||||
|
|
||||||
if (key === 'guest') {
|
|
||||||
return showGuestAdmin();
|
|
||||||
}
|
|
||||||
if (key === 'todo') {
|
|
||||||
return showTodoAdmin();
|
|
||||||
}
|
|
||||||
if (key === 'gift') {
|
|
||||||
return showGiftAdmin();
|
|
||||||
}
|
|
||||||
|
|
||||||
detailContainer.innerHTML = `<div class="module"><p>No hay operaciones definidas para este módulo.</p></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showGuestAdmin() {
|
|
||||||
detailContainer.innerHTML = '';
|
|
||||||
const panel = document.createElement('div');
|
|
||||||
panel.className = 'module';
|
|
||||||
panel.innerHTML = '<h3>Invitados</h3>';
|
|
||||||
|
|
||||||
const listBtn = createButton('Listar invitados', async () => {
|
|
||||||
try {
|
|
||||||
const guests = await api('/guest');
|
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(guests);
|
|
||||||
} catch (err) {
|
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const createForm = document.createElement('div');
|
|
||||||
createForm.innerHTML = '<strong>Crear invitado</strong><br/>';
|
|
||||||
const nameInput = createInput('name', 'Nombre');
|
|
||||||
const emailInput = createInput('email', 'Email');
|
|
||||||
const phoneInput = createInput('phone', 'Teléfono');
|
|
||||||
const createBtn = createButton('Crear', async () => {
|
|
||||||
try {
|
|
||||||
const guest = await api('/guest', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: nameInput.value,
|
|
||||||
email: emailInput.value,
|
|
||||||
phone: phoneInput.value,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(guest);
|
const data = await res.json().catch(() => null);
|
||||||
} catch (err) {
|
if (!res.ok) {
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
const err = data?.message || res.statusText;
|
||||||
}
|
setStatus(`Error ${res.status}`);
|
||||||
});
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
setStatus('OK');
|
||||||
|
setTimeout(() => setStatus(''), 1500);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
const rsvpForm = document.createElement('div');
|
const logout = () => {
|
||||||
rsvpForm.innerHTML = '<strong>Actualizar RSVP</strong><br/>';
|
setToken('');
|
||||||
const idInput = createInput('id', 'ID del invitado');
|
user.value = null;
|
||||||
const rsvpInput = createInput('rsvp', 'true/false');
|
selectedModule.value = '';
|
||||||
const updateBtn = createButton('Actualizar', async () => {
|
moduleHtml.value = '';
|
||||||
try {
|
};
|
||||||
const updated = await api(`/guest/${idInput.value}/rsvp`, {
|
|
||||||
method: 'PATCH',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({ rsvp: rsvpInput.value === 'true' }),
|
|
||||||
});
|
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(updated);
|
|
||||||
} catch (err) {
|
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
createForm.append(nameInput, emailInput, phoneInput, createBtn);
|
const loginUser = async () => {
|
||||||
rsvpForm.append(idInput, rsvpInput, updateBtn);
|
loginMessage.value = '';
|
||||||
|
loginError.value = false;
|
||||||
|
loginSuccess.value = false;
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${authRoot}/login`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
tenantId: login.tenantId || 'default',
|
||||||
|
email: login.email,
|
||||||
|
password: login.password,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok || !data.token) {
|
||||||
|
loginMessage.value = data.message || 'Credenciales inválidas';
|
||||||
|
loginError.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setToken(data.token);
|
||||||
|
user.value = { email: login.email, name: data.name };
|
||||||
|
loginMessage.value = 'Sesión iniciada correctamente';
|
||||||
|
loginSuccess.value = true;
|
||||||
|
await loadModules();
|
||||||
|
} catch (err) {
|
||||||
|
loginMessage.value = err.message || err;
|
||||||
|
loginError.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const result = document.createElement('div');
|
const registerUser = async () => {
|
||||||
result.className = 'result';
|
registerMessage.value = '';
|
||||||
result.style.marginTop = '1rem';
|
registerError.value = false;
|
||||||
|
registerSuccess.value = false;
|
||||||
|
loading.value = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${authRoot}/register`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
tenantId: register.tenantId || 'default',
|
||||||
|
name: register.name,
|
||||||
|
email: register.email,
|
||||||
|
password: register.password,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (!res.ok) {
|
||||||
|
registerMessage.value = data.message || 'Error al registrar';
|
||||||
|
registerError.value = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
registerMessage.value = 'Registrado. Inicia sesión.';
|
||||||
|
registerSuccess.value = true;
|
||||||
|
} catch (err) {
|
||||||
|
registerMessage.value = err.message || err;
|
||||||
|
registerError.value = true;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
panel.append(listBtn, document.createElement('br'), createForm, rsvpForm, result);
|
const loadModules = async () => {
|
||||||
detailContainer.appendChild(panel);
|
if (!token.value) return;
|
||||||
}
|
try {
|
||||||
|
modules.value = await api('/modules');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async function showTodoAdmin() {
|
const selectModule = (key) => {
|
||||||
detailContainer.innerHTML = '';
|
selectedModule.value = key;
|
||||||
const panel = document.createElement('div');
|
moduleHtml.value = `<p>Seleccionado <strong>${key}</strong>. Usa los botones del módulo para operar.</p>`;
|
||||||
panel.className = 'module';
|
};
|
||||||
panel.innerHTML = '<h3>To-dos</h3>';
|
|
||||||
|
|
||||||
const listBtn = createButton('Listar To-dos', async () => {
|
return {
|
||||||
try {
|
token,
|
||||||
const todos = await api('/todo');
|
user,
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(todos);
|
tenantId,
|
||||||
} catch (err) {
|
status,
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
modules,
|
||||||
}
|
selectedModule,
|
||||||
});
|
moduleHtml,
|
||||||
|
login,
|
||||||
const createForm = document.createElement('div');
|
register,
|
||||||
createForm.innerHTML = '<strong>Crear To-do</strong><br/>';
|
loginMessage,
|
||||||
const titleInput = createInput('title', 'Título');
|
loginError,
|
||||||
const descInput = createInput('description', 'Descripción');
|
loginSuccess,
|
||||||
const createBtn = createButton('Crear', async () => {
|
registerMessage,
|
||||||
try {
|
registerError,
|
||||||
const todo = await api('/todo', {
|
registerSuccess,
|
||||||
method: 'POST',
|
loading,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
loginUser,
|
||||||
body: JSON.stringify({
|
registerUser,
|
||||||
title: titleInput.value,
|
logout,
|
||||||
description: descInput.value,
|
loadModules,
|
||||||
}),
|
selectModule,
|
||||||
});
|
};
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(todo);
|
},
|
||||||
} catch (err) {
|
}).mount('#app');
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const completeForm = document.createElement('div');
|
|
||||||
completeForm.innerHTML = '<strong>Marcar completado</strong><br/>';
|
|
||||||
const todoIdInput = createInput('todoId', 'ID del To-do');
|
|
||||||
const completeBtn = createButton('Completar', async () => {
|
|
||||||
try {
|
|
||||||
const completed = await api(`/todo/${todoIdInput.value}/complete`, {
|
|
||||||
method: 'PATCH',
|
|
||||||
});
|
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(completed);
|
|
||||||
} catch (err) {
|
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
completeForm.append(todoIdInput, completeBtn);
|
|
||||||
|
|
||||||
const result = document.createElement('div');
|
|
||||||
result.className = 'result';
|
|
||||||
result.style.marginTop = '1rem';
|
|
||||||
|
|
||||||
panel.append(listBtn, document.createElement('br'), createForm, completeForm, result);
|
|
||||||
detailContainer.appendChild(panel);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showGiftAdmin() {
|
|
||||||
detailContainer.innerHTML = '';
|
|
||||||
const panel = document.createElement('div');
|
|
||||||
panel.className = 'module';
|
|
||||||
panel.innerHTML = '<h3>Regalos</h3>';
|
|
||||||
|
|
||||||
const listBtn = createButton('Listar regalos', async () => {
|
|
||||||
try {
|
|
||||||
const gifts = await api('/gift');
|
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(gifts);
|
|
||||||
} catch (err) {
|
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const createForm = document.createElement('div');
|
|
||||||
createForm.innerHTML = '<strong>Crear Regalo</strong><br/>';
|
|
||||||
const nameInput = createInput('name', 'Nombre');
|
|
||||||
const descInput = createInput('description', 'Descripción');
|
|
||||||
const priceInput = createInput('price', 'Precio', 'number');
|
|
||||||
const createBtn = createButton('Crear', async () => {
|
|
||||||
try {
|
|
||||||
const gift = await api('/gift', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
name: nameInput.value,
|
|
||||||
description: descInput.value,
|
|
||||||
price: Number(priceInput.value) || undefined,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(gift);
|
|
||||||
} catch (err) {
|
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const contributionForm = document.createElement('div');
|
|
||||||
contributionForm.innerHTML = '<strong>Agregar contribución</strong><br/>';
|
|
||||||
const giftIdInput = createInput('giftId', 'ID del regalo');
|
|
||||||
const contribNameInput = createInput('contributorName', 'Nombre');
|
|
||||||
const amountInput = createInput('amount', 'Monto', 'number');
|
|
||||||
const contribBtn = createButton('Agregar', async () => {
|
|
||||||
try {
|
|
||||||
const contribution = await api(`/gift/${giftIdInput.value}/contribution`, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
contributorName: contribNameInput.value,
|
|
||||||
amount: Number(amountInput.value) || 0,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
panel.querySelector('.result')!.innerHTML = formatJson(contribution);
|
|
||||||
} catch (err) {
|
|
||||||
panel.querySelector('.result')!.innerHTML = `<pre>${err.message || err}</pre>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
contributionForm.append(giftIdInput, contribNameInput, amountInput, contribBtn);
|
|
||||||
|
|
||||||
const result = document.createElement('div');
|
|
||||||
result.className = 'result';
|
|
||||||
result.style.marginTop = '1rem';
|
|
||||||
|
|
||||||
panel.append(listBtn, document.createElement('br'), createForm, contributionForm, result);
|
|
||||||
detailContainer.appendChild(panel);
|
|
||||||
}
|
|
||||||
|
|
||||||
loginBtn.addEventListener('click', login);
|
|
||||||
registerBtn.addEventListener('click', register);
|
|
||||||
logoutBtn.addEventListener('click', logout);
|
|
||||||
document.getElementById('refresh').addEventListener('click', loadModules);
|
|
||||||
tenantInput.addEventListener('change', loadModules);
|
|
||||||
|
|
||||||
if (authToken) {
|
|
||||||
showMain();
|
|
||||||
} else {
|
|
||||||
showAuth();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user