feat: actualizar navegación y gestión de items en el Item Lab, mejorando la carga y edición de datos
This commit is contained in:
@@ -48,10 +48,10 @@
|
||||
|
||||
async function onDelete(e){ const id = e.currentTarget && e.currentTarget.dataset ? e.currentTarget.dataset.id : null; if(!id) return; if(!confirm('Eliminar item?')) return; try{ const res = await fetch('/api/dashboard/' + encodeURIComponent(guildId) + '/items/' + encodeURIComponent(id), { method:'DELETE' }); if(!res.ok) throw new Error('delete-failed'); await fetchItems(); alert('Item eliminado'); }catch(err){ alert('Error al eliminar'); } }
|
||||
|
||||
function onEdit(e){ const id = e.currentTarget && e.currentTarget.dataset ? e.currentTarget.dataset.id : null; if(!id) return; window.location.href = '/dashboard/' + encodeURIComponent(guildId) + '/items/lab?edit=' + encodeURIComponent(id); }
|
||||
function onEdit(e){ const id = e.currentTarget && e.currentTarget.dataset ? e.currentTarget.dataset.id : null; if(!id) return; window.location.href = '/items/lab?guild=' + encodeURIComponent(guildId) + '&edit=' + encodeURIComponent(id); }
|
||||
|
||||
// wire create button (if present) to navigate to lab
|
||||
const createBtn = $('createItemBtn'); if(createBtn) createBtn.addEventListener('click', ()=>{ window.location.href = '/dashboard/' + encodeURIComponent(guildId) + '/items/lab'; });
|
||||
const createBtn = $('createItemBtn'); if(createBtn) createBtn.addEventListener('click', (ev)=>{ ev.preventDefault(); window.location.href = '/items/lab?guild=' + encodeURIComponent(guildId); });
|
||||
|
||||
// initial load
|
||||
fetchItems();
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
(function(){
|
||||
const $ = id => document.getElementById(id);
|
||||
const guildId = (() => { try{ return (document.getElementById('itemsRoot') && document.getElementById('itemsRoot').dataset && document.getElementById('itemsRoot').dataset.guildId) || ''; }catch(e){ return ''; } })();
|
||||
// Detect guildId from itemsRoot dataset when embedded, otherwise read ?guild= from query string when opened as standalone page
|
||||
const guildId = (() => {
|
||||
try{
|
||||
const root = document.getElementById('itemsRoot');
|
||||
if(root && root.dataset && root.dataset.guildId) return root.dataset.guildId;
|
||||
const p = new URLSearchParams(location.search || '');
|
||||
return p.get('guild') || '';
|
||||
}catch(e){ return ''; }
|
||||
})();
|
||||
|
||||
// form elements
|
||||
const form = $('itemLabForm');
|
||||
@@ -54,6 +62,34 @@
|
||||
labReset.addEventListener('click', resetForm);
|
||||
}
|
||||
|
||||
// Prefill when editing: detect ?edit=<id>
|
||||
(async function tryPrefill(){
|
||||
try{
|
||||
const params = new URLSearchParams(location.search || '');
|
||||
const editId = params.get('edit');
|
||||
if(!editId) return;
|
||||
// show temporary loading state
|
||||
const prev = labPreview.textContent;
|
||||
labPreview.textContent = 'Cargando item...';
|
||||
const res = await fetch('/api/dashboard/' + encodeURIComponent(guildId) + '/items/' + encodeURIComponent(editId));
|
||||
if(!res.ok){ labPreview.textContent = 'Error cargando item: HTTP ' + res.status; return; }
|
||||
const j = await res.json();
|
||||
if(!j || !j.ok || !j.item){ labPreview.textContent = 'Item no encontrado'; return; }
|
||||
const item = j.item;
|
||||
// populate fields safely
|
||||
if(labKey) labKey.value = item.key || '';
|
||||
if(labName) labName.value = item.name || '';
|
||||
if(labCategory) labCategory.value = item.category || '';
|
||||
if(labIcon) labIcon.value = item.icon || '';
|
||||
if(labDescription) labDescription.value = item.description || '';
|
||||
if(labTags) labTags.value = Array.isArray(item.tags) ? item.tags.join(',') : (item.tags||'');
|
||||
try{ labProps.value = item.props && typeof item.props === 'object' ? JSON.stringify(item.props, null, 2) : (item.props||'{}'); }catch(e){ labProps.value = '{}'; }
|
||||
renderPreview();
|
||||
// update browser history to remove query param (optional)
|
||||
try{ const u = new URL(location.href); u.searchParams.delete('edit'); window.history.replaceState({}, '', u.toString()); }catch(e){}
|
||||
}catch(e){ console.warn('prefill failed', e); }
|
||||
})();
|
||||
|
||||
// minimal three.js preview
|
||||
function init3D(){
|
||||
try{
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
<%- include('partials/head') %>
|
||||
<div id="item-lab-root" class="p-6">
|
||||
<!DOCTYPE html>
|
||||
<html lang="es" class="scroll-smooth">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Item Lab</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="/assets/css/styles.css?v=1" />
|
||||
</head>
|
||||
<body class="min-h-screen pixel-grid-bg pt-14">
|
||||
<div id="item-lab-root" class="p-6 max-w-6xl mx-auto">
|
||||
<h1 class="text-xl font-bold mb-4">Item Lab</h1>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
@@ -58,4 +67,5 @@
|
||||
</div>
|
||||
<script src="/assets/js/three.min.js"></script>
|
||||
<script src="/assets/js/item_lab.js"></script>
|
||||
<%- include('partials/foot') %>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1867,6 +1867,41 @@ export const server = createServer(
|
||||
}
|
||||
}
|
||||
|
||||
// Standalone Item Lab (opens as a separate page, not inside dashboard)
|
||||
if (url.pathname === "/items/lab" || url.pathname === "/item-lab") {
|
||||
try {
|
||||
const publicViews = path.join(__dirname, "public", "views");
|
||||
const locals = {
|
||||
appName: pkg.name ?? "Amayo Bot",
|
||||
user,
|
||||
selectedGuildId: url.searchParams.get("guild") || null,
|
||||
};
|
||||
const pageFile = path.join(publicViews, "item_lab.ejs");
|
||||
const pageBody = await ejs.renderFile(pageFile, locals, {
|
||||
async: true,
|
||||
views: [publicViews, viewsDir],
|
||||
});
|
||||
res.writeHead(
|
||||
200,
|
||||
applySecurityHeaders({
|
||||
"Content-Type": "text/html; charset=utf-8",
|
||||
})
|
||||
);
|
||||
res.end(pageBody);
|
||||
return;
|
||||
} catch (err) {
|
||||
console.error("render item lab failed", err);
|
||||
res.writeHead(
|
||||
500,
|
||||
applySecurityHeaders({
|
||||
"Content-Type": "text/plain; charset=utf-8",
|
||||
})
|
||||
);
|
||||
res.end("Item Lab render error");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// /dashboard -> main dashboard
|
||||
if (url.pathname === "/dashboard" || url.pathname === "/dashboard/") {
|
||||
// determine whether bot is in each guild (if we have a bot token)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-white text-lg font-semibold">Items</h2>
|
||||
<div>
|
||||
<a id="createItemBtn" href="/dashboard/<%= selectedGuildId %>/items/lab" class="inline-flex items-center gap-2 px-3 py-1 bg-indigo-600 rounded text-white">Crear item</a>
|
||||
<a id="createItemBtn" href="/items/lab?guild=<%= selectedGuildId %>" class="inline-flex items-center gap-2 px-3 py-1 bg-indigo-600 rounded text-white">Crear item</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
<li class="text-sm"><a href="/dashboard/<%= selectedGuild %>/settings" class="flex items-center gap-3"><span class="opacity-80">⚙️</span> General Settings</a></li>
|
||||
<li class="text-sm"><a href="/dashboard/<%= selectedGuild %>/items" class="flex items-center gap-3"><span class="opacity-80">🧭</span> Items</a></li>
|
||||
<li class="text-sm">
|
||||
<a href="/dashboard/<%= selectedGuild %>/items/lab" class="flex items-center gap-3">
|
||||
<a href="/items/lab?guild=<%= selectedGuild %>" class="flex items-center gap-3">
|
||||
<span class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-white/6 animate-pulse">🔬</span>
|
||||
<span>Lab</span>
|
||||
<span id="labSpinner" class="ml-auto opacity-0 text-xs">⏳</span>
|
||||
|
||||
Reference in New Issue
Block a user