feat: pestañas Mensajes/Correos, ocultar leídas, solicitud inicial, login al expirar, fusión de tickets (UI)

This commit is contained in:
Nurfog committed 2026-11-02 15:00:22 -03:00
1 parent f88d79e2e8
commit ce3fa29b69
4 files changed
+282 -89

No files matched your search

+22
View File
@@ -1,5 +1,16 @@
const API_BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:5000";
// Sesión expirada o inválida: limpia el token y lleva al login.
// Se omite en el propio login (ahí el 401 es "credenciales inválidas")
// y si ya estamos en /login para no recargar en bucle.
function handleUnauthorized(path: string) {
if (typeof window === "undefined") return;
if (path.includes("/login")) return;
if (window.location.pathname === "/login") return;
localStorage.removeItem("helpdesk_token");
window.location.href = "/login";
}
async function fetchApi<T>(
path: string,
options: RequestInit = {}
@@ -18,6 +29,8 @@ async function fetchApi<T>(
const res = await fetch(`${API_BASE}${path}`, { ...options, headers });
if (res.status === 401) handleUnauthorized(path);
if (!res.ok) {
const errorData = await res.json().catch(() => ({}));
throw new Error(errorData.message || `API Error: ${res.status}`);
@@ -110,6 +123,13 @@ export async function updateTicketStatus(id: number, status: string) {
});
}
export async function mergeTickets(targetId: number, sourceId: number) {
return fetchApi<{ ticket: Ticket; merged: { comments: number; attachments: number; inbound: number; sourceId: number } }>(
`/api/helpdesk/ticket/${targetId}/merge`,
{ method: "POST", body: JSON.stringify({ sourceId }) }
);
}
export async function setTicketArchived(id: number, archived: boolean) {
return fetchApi<void>(`/api/helpdesk/ticket/${id}/archive`, {
method: "PUT",
@@ -167,6 +187,7 @@ export async function uploadTicketAttachments(ticketId: number, files: File[]) {
headers: token ? { Authorization: `Bearer ${token}` } : {},
body: form,
});
if (res.status === 401) handleUnauthorized("attachments");
if (!res.ok) {
const errorData = await res.json().catch(() => ({}));
throw new Error(errorData.message || `API Error: ${res.status}`);
@@ -184,6 +205,7 @@ export async function downloadAttachment(attachment: TicketAttachment) {
const res = await fetch(`${API_BASE}/api/helpdesk/attachments/${attachment.id}`, {
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
if (res.status === 401) handleUnauthorized("attachments");
if (!res.ok) throw new Error(`No se pudo descargar (${res.status})`);
const blob = await res.blob();
const url = URL.createObjectURL(blob);