feat(front) now can block
This commit is contained in:
+154
-13
@@ -130,7 +130,11 @@
|
|||||||
</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">Lista de Sesiones</h3>
|
<div class="flex justify-between items-center mb-3">
|
||||||
|
<h3 class="font-bold text-lg">Lista de Sesiones</h3>
|
||||||
|
</div>
|
||||||
|
<input id="sessionSearch" placeholder="Buscar por teléfono, nombre o resumen..."
|
||||||
|
class="w-full bg-zinc-950 border border-zinc-800 rounded-xl p-3 outline-none focus:ring-2 focus:ring-indigo-500 mb-3"/>
|
||||||
<div id="sessionList" class="space-y-3"></div>
|
<div id="sessionList" class="space-y-3"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -250,6 +254,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ========================= -->
|
||||||
|
<!-- BLOCK SESSION MODAL -->
|
||||||
|
<!-- ========================= -->
|
||||||
|
<div id="blockSessionModal" 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">¿Bloquear Sesión?</h2>
|
||||||
|
<p class="text-sm text-zinc-400 mb-6">¿Estás seguro de que quieres bloquear esta sesión? El número de teléfono será bloqueado.</p>
|
||||||
|
|
||||||
|
<input type="hidden" id="blockSessionId" />
|
||||||
|
<input type="hidden" id="blockSessionPhone" />
|
||||||
|
|
||||||
|
<div class="flex gap-3">
|
||||||
|
<button id="confirmBlockSessionBtn" class="flex-1 bg-red-600 hover:bg-red-500 transition rounded-xl p-3 font-semibold text-white">Bloquear</button>
|
||||||
|
<button id="cancelBlockSessionBtn" class="flex-1 bg-zinc-800 hover:bg-zinc-700 transition rounded-xl p-3 font-semibold text-white">Cancelar</button>
|
||||||
|
</div>
|
||||||
|
<div id="blockSessionMsg" class="text-sm mt-3"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// =============================
|
// =============================
|
||||||
// CONFIG
|
// CONFIG
|
||||||
@@ -564,6 +587,8 @@
|
|||||||
// sessions table must exist
|
// sessions table must exist
|
||||||
// columns: id, campaign_id, created_at, ip, user_agent, etc
|
// columns: id, campaign_id, created_at, ip, user_agent, etc
|
||||||
// =============================
|
// =============================
|
||||||
|
let allSessions = [];
|
||||||
|
|
||||||
async function loadSessions() {
|
async function loadSessions() {
|
||||||
const campaignFilter = document.getElementById("sessionsCampaignFilter").value;
|
const campaignFilter = document.getElementById("sessionsCampaignFilter").value;
|
||||||
|
|
||||||
@@ -579,26 +604,43 @@
|
|||||||
|
|
||||||
const { data, error } = await query;
|
const { data, error } = await query;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
document.getElementById("sessionList").innerHTML = `<div class="text-red-400 text-sm">Error loading sessions: ${error.message}</div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
allSessions = data || [];
|
||||||
|
renderSessions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSessions() {
|
||||||
|
const searchTerm = document.getElementById("sessionSearch").value.toLowerCase();
|
||||||
const list = document.getElementById("sessionList");
|
const list = document.getElementById("sessionList");
|
||||||
list.innerHTML = "";
|
list.innerHTML = "";
|
||||||
|
|
||||||
if (error) {
|
let filtered = allSessions;
|
||||||
list.innerHTML = `<div class="text-red-400 text-sm">Error loading sessions: ${error.message}</div>`;
|
if (searchTerm) {
|
||||||
|
filtered = allSessions.filter(s =>
|
||||||
|
(s.phone && s.phone.toLowerCase().includes(searchTerm)) ||
|
||||||
|
(s.name && s.name.toLowerCase().includes(searchTerm)) ||
|
||||||
|
(s.summary && s.summary.toLowerCase().includes(searchTerm))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtered.length === 0) {
|
||||||
|
list.innerHTML = `<div class="text-zinc-400 text-sm">${allSessions.length === 0 ? "No sessions found." : "No sessions match the search."}</div>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data || data.length === 0) {
|
filtered.forEach(s => {
|
||||||
list.innerHTML = `<div class="text-zinc-400 text-sm">No sessions found.</div>`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.forEach(s => {
|
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "bg-zinc-950 border border-zinc-800 rounded-2xl p-4";
|
card.className = "bg-zinc-950 border border-zinc-800 rounded-2xl p-4";
|
||||||
|
|
||||||
const statusClass = s.finished ? "bg-green-500/20 text-green-500" : "bg-amber-500/20 text-amber-500";
|
const statusClass = s.finished ? "bg-green-500/20 text-green-500" : "bg-amber-500/20 text-amber-500";
|
||||||
const statusText = s.finished ? "Finished" : "Active";
|
const statusText = s.finished ? "Finished" : "Active";
|
||||||
|
|
||||||
|
const blockedBadge = s.block ? `<span class="text-[10px] px-2 py-0.5 rounded-full font-bold uppercase bg-red-500/20 text-red-500">Blocked</span>` : '';
|
||||||
|
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<div class="flex justify-between items-start">
|
<div class="flex justify-between items-start">
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
@@ -610,9 +652,22 @@
|
|||||||
<div class="text-[10px] px-2 py-0.5 rounded-full font-bold uppercase ${statusClass}">
|
<div class="text-[10px] px-2 py-0.5 rounded-full font-bold uppercase ${statusClass}">
|
||||||
${statusText}
|
${statusText}
|
||||||
</div>
|
</div>
|
||||||
<button class="deleteSessionBtn bg-red-500/20 hover:bg-red-500/40 text-red-400 p-1.5 rounded-lg transition" data-id="${s.id}" data-phone="${s.phone}">
|
${blockedBadge}
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-3.5 h-3.5"><path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.134-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.067-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /></svg>
|
<div class="relative">
|
||||||
|
<button class="sessionMenuBtn bg-zinc-800 hover:bg-zinc-700 p-1.5 rounded-lg transition" data-id="${s.id}" data-phone="${s.phone}">
|
||||||
|
<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="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z" />
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<div class="sessionMenu hidden absolute right-0 top-full mt-1 bg-zinc-900 border border-zinc-800 rounded-xl shadow-xl z-10 min-w-[140px] overflow-hidden">
|
||||||
|
<button class="blockSessionItem w-full text-left px-4 py-2.5 text-sm hover:bg-zinc-800 transition text-red-400" data-id="${s.id}" data-phone="${s.phone}">
|
||||||
|
${s.block ? 'Desbloquear' : 'Bloquear'}
|
||||||
|
</button>
|
||||||
|
<button class="deleteSessionItem w-full text-left px-4 py-2.5 text-sm hover:bg-zinc-800 transition text-red-400 border-t border-zinc-800" data-id="${s.id}" data-phone="${s.phone}">
|
||||||
|
Eliminar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-zinc-400 mt-2"><span class="font-semibold text-zinc-300">Summary:</span> ${s.summary || "No summary yet."}</div>
|
<div class="text-xs text-zinc-400 mt-2"><span class="font-semibold text-zinc-300">Summary:</span> ${s.summary || "No summary yet."}</div>
|
||||||
@@ -629,17 +684,51 @@
|
|||||||
list.appendChild(card);
|
list.appendChild(card);
|
||||||
});
|
});
|
||||||
|
|
||||||
list.querySelectorAll(".deleteSessionBtn").forEach(btn => {
|
list.querySelectorAll(".sessionMenuBtn").forEach(btn => {
|
||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const menu = btn.nextElementSibling;
|
||||||
|
const wasHidden = menu.classList.contains("hidden");
|
||||||
|
document.querySelectorAll(".sessionMenu").forEach(m => m.classList.add("hidden"));
|
||||||
|
if (wasHidden) menu.classList.remove("hidden");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
list.querySelectorAll(".deleteSessionItem").forEach(btn => {
|
||||||
|
btn.addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
const phone = btn.dataset.phone;
|
const phone = btn.dataset.phone;
|
||||||
const id = btn.dataset.id;
|
const id = btn.dataset.id;
|
||||||
if (id && phone && phone !== 'unknown') {
|
if (id && phone && phone !== 'unknown') {
|
||||||
deleteSession(id, phone);
|
deleteSession(id, phone);
|
||||||
}
|
}
|
||||||
|
document.querySelectorAll(".sessionMenu").forEach(m => m.classList.add("hidden"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
list.querySelectorAll(".blockSessionItem").forEach(btn => {
|
||||||
|
btn.addEventListener("click", (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
const phone = btn.dataset.phone;
|
||||||
|
const id = btn.dataset.id;
|
||||||
|
if (id && phone && phone !== 'unknown') {
|
||||||
|
if (btn.textContent.trim() === 'Desbloquear') {
|
||||||
|
unblockSession(id, phone);
|
||||||
|
} else {
|
||||||
|
openBlockModal(id, phone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.querySelectorAll(".sessionMenu").forEach(m => m.classList.add("hidden"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("click", () => {
|
||||||
|
document.querySelectorAll(".sessionMenu").forEach(m => m.classList.add("hidden"));
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById("sessionSearch").addEventListener("input", renderSessions);
|
||||||
|
|
||||||
async function deleteSession(id, phone) {
|
async function deleteSession(id, phone) {
|
||||||
if (!confirm(`Delete session for ${phone} and all its chat history?`)) return;
|
if (!confirm(`Delete session for ${phone} and all its chat history?`)) return;
|
||||||
|
|
||||||
@@ -666,6 +755,55 @@
|
|||||||
await loadSessions();
|
await loadSessions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openBlockModal(id, phone) {
|
||||||
|
document.getElementById("blockSessionId").value = id;
|
||||||
|
document.getElementById("blockSessionPhone").value = phone;
|
||||||
|
setMessage(document.getElementById("blockSessionMsg"), "");
|
||||||
|
show(document.getElementById("blockSessionModal"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeBlockModal() {
|
||||||
|
hide(document.getElementById("blockSessionModal"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function confirmBlockSession() {
|
||||||
|
const id = document.getElementById("blockSessionId").value;
|
||||||
|
const phone = document.getElementById("blockSessionPhone").value;
|
||||||
|
const msg = document.getElementById("blockSessionMsg");
|
||||||
|
|
||||||
|
setMessage(msg, "Bloqueando...");
|
||||||
|
|
||||||
|
const { error } = await supabaseClient
|
||||||
|
.from("sessions")
|
||||||
|
.update({ block: true })
|
||||||
|
.eq("phone", phone);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
setMessage(msg, "Error: " + error.message, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMessage(msg, "Sesión bloqueada!", false);
|
||||||
|
closeBlockModal();
|
||||||
|
await loadSessions();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function unblockSession(id, phone) {
|
||||||
|
if (!confirm(`Desbloquear sesión para ${phone}?`)) return;
|
||||||
|
|
||||||
|
const { error } = await supabaseClient
|
||||||
|
.from("sessions")
|
||||||
|
.update({ block: false })
|
||||||
|
.eq("phone", phone);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
alert("Error: " + error.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await loadSessions();
|
||||||
|
}
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// MEDIA UPLOAD
|
// MEDIA UPLOAD
|
||||||
// media table must exist
|
// media table must exist
|
||||||
@@ -894,6 +1032,9 @@
|
|||||||
document.getElementById("cancelDeleteMediaBtn").addEventListener("click", closeDeleteMediaModal);
|
document.getElementById("cancelDeleteMediaBtn").addEventListener("click", closeDeleteMediaModal);
|
||||||
document.getElementById("confirmDeleteMediaBtn").addEventListener("click", confirmDeleteMedia);
|
document.getElementById("confirmDeleteMediaBtn").addEventListener("click", confirmDeleteMedia);
|
||||||
|
|
||||||
|
document.getElementById("cancelBlockSessionBtn").addEventListener("click", closeBlockModal);
|
||||||
|
document.getElementById("confirmBlockSessionBtn").addEventListener("click", confirmBlockSession);
|
||||||
|
|
||||||
setupTabs();
|
setupTabs();
|
||||||
|
|
||||||
// Keep session state updated
|
// Keep session state updated
|
||||||
|
|||||||
Reference in New Issue
Block a user