to spanish, also let delete

This commit is contained in:
2026-05-16 12:13:32 -06:00
parent 521bf5007f
commit 6eabf016c5
+121 -41
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Campaign Dashboard</title> <title>Panel de Campañas</title>
<!-- Tailwind CDN --> <!-- Tailwind CDN -->
<script src="https://cdn.tailwindcss.com"></script> <script src="https://cdn.tailwindcss.com"></script>
@@ -19,21 +19,21 @@
<!-- ========================= --> <!-- ========================= -->
<div id="loginScreen" class="flex items-center justify-center min-h-screen p-6"> <div id="loginScreen" class="flex items-center justify-center min-h-screen p-6">
<div class="w-full max-w-md bg-zinc-900 border border-zinc-800 rounded-2xl shadow-lg p-6"> <div class="w-full max-w-md bg-zinc-900 border border-zinc-800 rounded-2xl shadow-lg p-6">
<h1 class="text-2xl font-bold mb-2">🏠 Campaign Dashboard</h1> <h1 class="text-2xl font-bold mb-2">🏠 Panel de Campañas</h1>
<p class="text-zinc-400 text-sm mb-6"> <p class="text-zinc-400 text-sm mb-6">
Login to manage campaigns, sessions, and media uploads. Inicia sesión para gestionar campañas, sesiones y archivos.
</p> </p>
<div class="space-y-4"> <div class="space-y-4">
<input id="loginEmail" type="email" placeholder="Email" <input id="loginEmail" type="email" placeholder="Correo"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500"/> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500"/>
<input id="loginPassword" type="password" placeholder="Password" <input id="loginPassword" type="password" placeholder="Contraseña"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500"/> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500"/>
<button id="loginBtn" <button id="loginBtn"
class="w-full bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold"> class="w-full bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold">
Login Entrar
</button> </button>
<div id="loginError" class="text-red-400 text-sm"></div> <div id="loginError" class="text-red-400 text-sm"></div>
@@ -49,13 +49,13 @@
<!-- Top Bar --> <!-- Top Bar -->
<div class="flex items-center justify-between p-4 border-b border-zinc-800 bg-zinc-950 sticky top-0 z-50"> <div class="flex items-center justify-between p-4 border-b border-zinc-800 bg-zinc-950 sticky top-0 z-50">
<div> <div>
<h2 class="font-bold text-lg">Dashboard</h2> <h2 class="font-bold text-lg">Panel</h2>
<p id="userEmail" class="text-xs text-zinc-400"></p> <p id="userEmail" class="text-xs text-zinc-400"></p>
</div> </div>
<button id="logoutBtn" <button id="logoutBtn"
class="bg-zinc-800 hover:bg-zinc-700 px-4 py-2 rounded-xl text-sm font-semibold"> class="bg-zinc-800 hover:bg-zinc-700 px-4 py-2 rounded-xl text-sm font-semibold">
Logout Cerrar Sesión
</button> </button>
</div> </div>
@@ -63,13 +63,13 @@
<div class="p-4"> <div class="p-4">
<div class="grid grid-cols-3 gap-2 bg-zinc-900 border border-zinc-800 rounded-2xl p-2"> <div class="grid grid-cols-3 gap-2 bg-zinc-900 border border-zinc-800 rounded-2xl p-2">
<button class="tabBtn bg-indigo-600 rounded-xl py-2 text-sm font-semibold" data-tab="campaignsTab"> <button class="tabBtn bg-indigo-600 rounded-xl py-2 text-sm font-semibold" data-tab="campaignsTab">
Campaigns Campañas
</button> </button>
<button class="tabBtn bg-zinc-800 rounded-xl py-2 text-sm font-semibold" data-tab="sessionsTab"> <button class="tabBtn bg-zinc-800 rounded-xl py-2 text-sm font-semibold" data-tab="sessionsTab">
Sessions Sesiones
</button> </button>
<button class="tabBtn bg-zinc-800 rounded-xl py-2 text-sm font-semibold" data-tab="mediaTab"> <button class="tabBtn bg-zinc-800 rounded-xl py-2 text-sm font-semibold" data-tab="mediaTab">
Media Archivos
</button> </button>
</div> </div>
</div> </div>
@@ -80,18 +80,18 @@
<div id="campaignsTab" class="tabContent p-4 space-y-4"> <div id="campaignsTab" class="tabContent p-4 space-y-4">
<div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4"> <div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4">
<h3 class="font-bold text-lg mb-3">Create Campaign</h3> <h3 class="font-bold text-lg mb-3">Crear Campaña</h3>
<div class="space-y-3"> <div class="space-y-3">
<input id="newCampaignName" placeholder="Campaign name" <input id="newCampaignName" placeholder="Nombre de campaña"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none"/> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none"/>
<textarea id="newCampaignNotes" placeholder="AI Prompt / Instructions (optional)" <textarea id="newCampaignNotes" placeholder="Instrucciones para IA (opcional)"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none"></textarea> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none"></textarea>
<button id="createCampaignBtn" <button id="createCampaignBtn"
class="w-full bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold"> class="w-full bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold">
Create Campaign Crear Campaña
</button> </button>
<div id="campaignCreateMsg" class="text-sm text-zinc-400"></div> <div id="campaignCreateMsg" class="text-sm text-zinc-400"></div>
@@ -100,9 +100,9 @@
<div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4"> <div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4">
<div class="flex justify-between items-center mb-3"> <div class="flex justify-between items-center mb-3">
<h3 class="font-bold text-lg">Campaign List</h3> <h3 class="font-bold text-lg">Lista de Campañas</h3>
<button id="toggleInactiveBtn" class="text-xs bg-zinc-800 hover:bg-zinc-700 px-3 py-1.5 rounded-lg border border-zinc-700 transition"> <button id="toggleInactiveBtn" class="text-xs bg-zinc-800 hover:bg-zinc-700 px-3 py-1.5 rounded-lg border border-zinc-700 transition">
Show Inactive Mostrar Inactivas
</button> </button>
</div> </div>
@@ -116,21 +116,21 @@
<div id="sessionsTab" class="tabContent hidden p-4 space-y-4"> <div id="sessionsTab" class="tabContent hidden p-4 space-y-4">
<div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4"> <div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4">
<h3 class="font-bold text-lg mb-3">Sessions</h3> <h3 class="font-bold text-lg mb-3">Sesiones</h3>
<select id="sessionsCampaignFilter" <select id="sessionsCampaignFilter"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-3"> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-3">
<option value="">All campaigns</option> <option value="">Todas las campañas</option>
</select> </select>
<button id="refreshSessionsBtn" <button id="refreshSessionsBtn"
class="w-full bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold"> class="w-full bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold">
Refresh Sessions Actualizar Sesiones
</button> </button>
</div> </div>
<div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4"> <div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4">
<h3 class="font-bold text-lg mb-3">Session List</h3> <h3 class="font-bold text-lg mb-3">Lista de Sesiones</h3>
<div id="sessionList" class="space-y-3"></div> <div id="sessionList" class="space-y-3"></div>
</div> </div>
</div> </div>
@@ -141,43 +141,43 @@
<div id="mediaTab" class="tabContent hidden p-4 space-y-4"> <div id="mediaTab" class="tabContent hidden p-4 space-y-4">
<div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4"> <div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4">
<h3 class="font-bold text-lg mb-3">Upload Media</h3> <h3 class="font-bold text-lg mb-3">Subir Archivo</h3>
<select id="mediaCampaignSelect" <select id="mediaCampaignSelect"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-3"> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-3">
<option value="">Select campaign</option> <option value="">Seleccionar campaña</option>
</select> </select>
<input id="mediaCategory" placeholder="Category (e.g. kitchen, front, bathroom)" <input id="mediaCategory" placeholder="Categoría (ej. cocina, frente, baño)"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-3 focus:ring-2 focus:ring-indigo-500"/> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-3 focus:ring-2 focus:ring-indigo-500"/>
<input id="mediaType" placeholder="Type (e.g. photo, video)" <input id="mediaType" placeholder="Tipo (ej. foto, video)"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-4 focus:ring-2 focus:ring-indigo-500"/> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-4 focus:ring-2 focus:ring-indigo-500"/>
<div class="mb-2 text-xs font-semibold text-zinc-400 uppercase tracking-wider">File Source</div> <div class="mb-2 text-xs font-semibold text-zinc-400 uppercase tracking-wider">Fuente de archivo</div>
<input id="mediaFile" type="file" accept="image/*,video/*" <input id="mediaFile" type="file" accept="image/*,video/*"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500"/> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500"/>
<div class="py-2 flex items-center justify-center"> <div class="py-2 flex items-center justify-center">
<div class="h-px bg-zinc-800 w-full"></div> <div class="h-px bg-zinc-800 w-full"></div>
<span class="px-3 text-xs text-zinc-500 font-bold mb-1">OR</span> <span class="px-3 text-xs text-zinc-500 font-bold mb-1">O</span>
<div class="h-px bg-zinc-800 w-full"></div> <div class="h-px bg-zinc-800 w-full"></div>
</div> </div>
<input id="mediaUrl" type="url" placeholder="Paste remote URL (e.g. Google Drive, Imgur)" <input id="mediaUrl" type="url" placeholder="Pegar URL remota (ej. Google Drive, Imgur)"
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-5 focus:ring-2 focus:ring-indigo-500"/> class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none mb-5 focus:ring-2 focus:ring-indigo-500"/>
<button id="uploadMediaBtn" <button id="uploadMediaBtn"
class="w-full bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold text-white"> class="w-full bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold text-white">
Save Media Guardar Archivo
</button> </button>
<div id="uploadMsg" class="text-sm text-zinc-400 mt-2"></div> <div id="uploadMsg" class="text-sm text-zinc-400 mt-2"></div>
</div> </div>
<div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4"> <div class="bg-zinc-900 border border-zinc-800 rounded-2xl p-4">
<h3 class="font-bold text-lg mb-3">Media List</h3> <h3 class="font-bold text-lg mb-3">Lista de Archivos</h3>
<div id="mediaList" class="columns-2 md:columns-3 lg:columns-4 gap-3 space-y-3"></div> <div id="mediaList" class="columns-2 md:columns-3 lg:columns-4 gap-3 space-y-3"></div>
</div> </div>
</div> </div>
@@ -190,7 +190,7 @@
<div id="editCampaignModal" class="hidden fixed inset-0 z-50 bg-black/60 backdrop-blur-sm flex items-center justify-center p-4"> <div id="editCampaignModal" class="hidden fixed inset-0 z-50 bg-black/60 backdrop-blur-sm flex items-center justify-center p-4">
<div class="bg-zinc-900 border border-zinc-800 rounded-2xl max-w-lg w-full p-6 shadow-2xl"> <div class="bg-zinc-900 border border-zinc-800 rounded-2xl max-w-lg w-full p-6 shadow-2xl">
<div class="flex justify-between items-center mb-4"> <div class="flex justify-between items-center mb-4">
<h2 class="text-xl font-bold">Edit Campaign</h2> <h2 class="text-xl font-bold">Editar Campaña</h2>
<button id="closeModalBtn" class="text-zinc-500 hover:text-white transition text-2xl">&times;</button> <button id="closeModalBtn" class="text-zinc-500 hover:text-white transition text-2xl">&times;</button>
</div> </div>
@@ -198,39 +198,58 @@
<input type="hidden" id="editCampaignId" /> <input type="hidden" id="editCampaignId" />
<div> <div>
<label class="block text-xs font-semibold text-zinc-400 mb-1">Name</label> <label class="block text-xs font-semibold text-zinc-400 mb-1">Nombre</label>
<input id="editCampaignName" class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500"/> <input id="editCampaignName" class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500"/>
</div> </div>
<div> <div>
<label class="block text-xs font-semibold text-zinc-400 mb-1">Keywords (comma separated)</label> <label class="block text-xs font-semibold text-zinc-400 mb-1">Palabras clave (separadas por coma)</label>
<input id="editCampaignKeywords" class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500" placeholder="e.g. house, pool, garden"/> <input id="editCampaignKeywords" class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500" placeholder="ej. casa, piscina, jardín"/>
</div> </div>
<div> <div>
<label class="block text-xs font-semibold text-zinc-400 mb-1">AI Prompt</label> <label class="block text-xs font-semibold text-zinc-400 mb-1">Instrucciones para IA</label>
<textarea id="editCampaignPrompt" class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none h-24 focus:ring-2 focus:ring-indigo-500" placeholder="Instructions for AI..."></textarea> <textarea id="editCampaignPrompt" class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none h-24 focus:ring-2 focus:ring-indigo-500" placeholder="Instrucciones para IA..."></textarea>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input type="checkbox" id="editCampaignWelcomePhotos" class="w-4 h-4 rounded border-zinc-800 bg-zinc-950 text-indigo-600 focus:ring-indigo-500"/> <input type="checkbox" id="editCampaignWelcomePhotos" class="w-4 h-4 rounded border-zinc-800 bg-zinc-950 text-indigo-600 focus:ring-indigo-500"/>
<label for="editCampaignWelcomePhotos" class="text-sm font-semibold text-zinc-300">Require Welcome Photos</label> <label for="editCampaignWelcomePhotos" class="text-sm font-semibold text-zinc-300">Requerir Fotos de Bienvenida</label>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<input type="checkbox" id="editCampaignActive" class="w-4 h-4 rounded border-zinc-800 bg-zinc-950 text-indigo-600 focus:ring-indigo-500"/> <input type="checkbox" id="editCampaignActive" class="w-4 h-4 rounded border-zinc-800 bg-zinc-950 text-indigo-600 focus:ring-indigo-500"/>
<label for="editCampaignActive" class="text-sm font-semibold text-zinc-300">Campaign Active</label> <label for="editCampaignActive" class="text-sm font-semibold text-zinc-300">Campaña Activa</label>
</div> </div>
<div class="mt-6 flex gap-3"> <div class="mt-6 flex gap-3">
<button id="saveCampaignBtn" class="flex-1 bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold text-white">Save Changes</button> <button id="saveCampaignBtn" class="flex-1 bg-indigo-600 hover:bg-indigo-500 transition rounded-xl p-3 font-semibold text-white">Guardar Cambios</button>
<button id="cancelModalBtn" class="flex-1 bg-zinc-800 hover:bg-zinc-700 transition rounded-xl p-3 font-semibold text-white">Cancel</button> <button id="cancelModalBtn" class="flex-1 bg-zinc-800 hover:bg-zinc-700 transition rounded-xl p-3 font-semibold text-white">Cancelar</button>
</div> </div>
<div id="editModalMsg" class="text-sm text-center"></div> <div id="editModalMsg" class="text-sm text-center"></div>
</div> </div>
</div> </div>
</div> </div>
<!-- ========================= -->
<!-- DELETE MEDIA MODAL -->
<!-- ========================= -->
<div id="deleteMediaModal" class="hidden fixed inset-0 z-50 bg-black/60 backdrop-blur-sm flex items-center justify-center p-4">
<div class="bg-zinc-900 border border-zinc-800 rounded-2xl max-w-sm w-full p-6 shadow-2xl text-center">
<h2 class="text-xl font-bold mb-3">¿Eliminar Archivo?</h2>
<p class="text-sm text-zinc-400 mb-6">¿Estás seguro de que quieres eliminar esta foto? Esta acción no se puede deshacer.</p>
<input type="hidden" id="deleteMediaId" />
<input type="hidden" id="deleteMediaUrl" />
<div class="flex gap-3">
<button id="confirmDeleteMediaBtn" class="flex-1 bg-red-600 hover:bg-red-500 transition rounded-xl p-3 font-semibold text-white">Eliminar</button>
<button id="cancelDeleteMediaBtn" class="flex-1 bg-zinc-800 hover:bg-zinc-700 transition rounded-xl p-3 font-semibold text-white">Cancelar</button>
</div>
<div id="deleteMediaMsg" class="text-sm mt-3"></div>
</div>
</div>
<script> <script>
// ============================= // =============================
// CONFIG // CONFIG
@@ -737,7 +756,14 @@
card.className = "bg-zinc-950 border border-zinc-800 rounded-xl overflow-hidden break-inside-avoid mb-3"; card.className = "bg-zinc-950 border border-zinc-800 rounded-xl overflow-hidden break-inside-avoid mb-3";
card.innerHTML = ` card.innerHTML = `
<img src="${m.url}" class="w-full h-auto block" loading="lazy" /> <div class="relative group">
<img src="${m.url}" class="w-full h-auto block" loading="lazy" />
<button class="deleteMediaBtn absolute top-2 right-2 bg-black/60 hover:bg-black/90 text-white rounded-lg p-1.5 opacity-100 md:opacity-0 md:group-hover:opacity-100 transition-opacity" data-id="${m.id}" data-url="${m.url}" title="Delete media">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="p-2.5"> <div class="p-2.5">
<div class="text-[10px] font-bold text-indigo-400 uppercase tracking-tight mb-0.5">${m.category || "photo"}${m.type || "unknown"}</div> <div class="text-[10px] font-bold text-indigo-400 uppercase tracking-tight mb-0.5">${m.category || "photo"}${m.type || "unknown"}</div>
<div class="text-xs font-semibold text-zinc-200 truncate">${campaignName}</div> <div class="text-xs font-semibold text-zinc-200 truncate">${campaignName}</div>
@@ -747,6 +773,57 @@
list.appendChild(card); list.appendChild(card);
}); });
document.querySelectorAll(".deleteMediaBtn").forEach(btn => {
btn.addEventListener("click", () => {
openDeleteMediaModal(btn.dataset.id, btn.dataset.url);
});
});
}
function openDeleteMediaModal(id, url) {
document.getElementById("deleteMediaId").value = id;
document.getElementById("deleteMediaUrl").value = url;
setMessage(document.getElementById("deleteMediaMsg"), "");
show(document.getElementById("deleteMediaModal"));
}
function closeDeleteMediaModal() {
hide(document.getElementById("deleteMediaModal"));
}
async function confirmDeleteMedia() {
const id = document.getElementById("deleteMediaId").value;
const url = document.getElementById("deleteMediaUrl").value;
const msg = document.getElementById("deleteMediaMsg");
setMessage(msg, "Deleting...");
const { error: dbError } = await supabaseClient
.from("media")
.delete()
.eq("id", id);
if (dbError) {
setMessage(msg, "Error: " + dbError.message, true);
return;
}
if (url.includes(STORAGE_BUCKET)) {
try {
const parts = url.split(STORAGE_BUCKET + "/");
if (parts.length >= 2) {
const filePath = parts[1];
await supabaseClient.storage.from(STORAGE_BUCKET).remove([filePath]);
}
} catch (e) {
console.error("Failed to delete from storage", e);
}
}
setMessage(msg, "Deleted successfully!");
closeDeleteMediaModal();
await loadMedia();
} }
// ============================= // =============================
@@ -771,6 +848,9 @@
document.getElementById("uploadMediaBtn").addEventListener("click", uploadMedia); document.getElementById("uploadMediaBtn").addEventListener("click", uploadMedia);
document.getElementById("cancelDeleteMediaBtn").addEventListener("click", closeDeleteMediaModal);
document.getElementById("confirmDeleteMediaBtn").addEventListener("click", confirmDeleteMedia);
setupTabs(); setupTabs();
// Keep session state updated // Keep session state updated