From d186a989b6808ff39fcd43a9f0b478aebc4aa346 Mon Sep 17 00:00:00 2001 From: zwlucas Date: Fri, 29 May 2026 17:21:48 -0300 Subject: feat: enhance maintenance management and heartbeat history features - Updated Store interface to include methods for managing maintenances. - Modified API functions to support pagination for heartbeat history and added CRUD operations for maintenances. - Enhanced types to include paginated responses and initial log entries. - Implemented maintenance management UI in the admin panel with create, edit, and delete functionalities. - Updated service detail page to display paginated heartbeat history and improved chart rendering. - Refactored status page to accommodate new data structures and ensure consistent data handling. Signed-off-by: Lucas Faria Mendes --- frontend/src/routes/(admin)/admin/+page.svelte | 309 +++++++++++++++++++++++-- 1 file changed, 286 insertions(+), 23 deletions(-) (limited to 'frontend/src/routes/(admin)/admin') diff --git a/frontend/src/routes/(admin)/admin/+page.svelte b/frontend/src/routes/(admin)/admin/+page.svelte index 24b56db..5be9154 100644 --- a/frontend/src/routes/(admin)/admin/+page.svelte +++ b/frontend/src/routes/(admin)/admin/+page.svelte @@ -13,6 +13,7 @@ token = data.token; services = data.services ?? []; initialized = true; + fetchMaintenances(); } }); let editingSvc: any = $state(null); @@ -21,10 +22,25 @@ let testing = $state>({}); let toggling = $state>({}); + // maintenance state + let maintenances = $state([]); + let showMtnForm = $state(false); + let editingMtn: any = $state(null); + let mtnSaving = $state(false); + let mtnFormError = $state(''); + function apiHeaders() { return { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` }; } + async function fetchMaintenances() { + try { + const res = await fetch(`${API}/maintenances`, { headers: { Authorization: `Bearer ${token}` } }); + if (await handleUnauthorized(res)) return; + if (res.ok) maintenances = await res.json(); + } catch {} + } + async function handleUnauthorized(res: Response) { if (res.status === 401) { window.location.href = '/login'; @@ -144,6 +160,75 @@ toggling = { ...toggling, [id]: false }; } + function openMtnCreate() { + editingMtn = null; + mtnFormError = ''; + showMtnForm = true; + } + + function openMtnEdit(m: any) { + editingMtn = { ...m }; + mtnFormError = ''; + showMtnForm = true; + } + + function closeMtnForm() { + showMtnForm = false; + editingMtn = null; + mtnFormError = ''; + } + + async function handleMtnSubmit(e: Event) { + e.preventDefault(); + mtnSaving = true; + mtnFormError = ''; + const form = e.target as HTMLFormElement; + const fd = new FormData(form); + const body: Record = { + title: fd.get('title'), + start_time: fd.get('start_time'), + end_time: fd.get('end_time'), + is_active: fd.get('is_active') === 'on', + }; + try { + const id = fd.get('id'); + if (id) { + const res = await fetch(`${API}/maintenances/${id}`, { + method: 'PUT', + headers: apiHeaders(), + body: JSON.stringify(body), + }); + if (await handleUnauthorized(res)) return; + if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao salvar'); + } else { + const res = await fetch(`${API}/maintenances`, { + method: 'POST', + headers: apiHeaders(), + body: JSON.stringify({ title: body.title, start_time: body.start_time, end_time: body.end_time }), + }); + if (await handleUnauthorized(res)) return; + if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao salvar'); + } + await fetchMaintenances(); + closeMtnForm(); + } catch (e: any) { + mtnFormError = e.message; + } finally { + mtnSaving = false; + } + } + + async function handleMtnDelete(id: number) { + try { + const res = await fetch(`${API}/maintenances/${id}`, { + method: 'DELETE', + headers: apiHeaders() + }); + if (await handleUnauthorized(res)) return; + if (res.ok) await fetchMaintenances(); + } catch {} + } + async function handleTest(id: number) { testing = { ...testing, [id]: true }; try { @@ -274,7 +359,78 @@ {/each} {/if} - +
+
+
+

Manutenções Programadas

+

Janelas de manutenção exibem um aviso no topo do site

+
+ +
+ + {#if maintenances.length === 0} +
+

Nenhuma manutenção agendada

+
+ {:else} +
+ + + + + + + + + + + + {#each maintenances as mtn (mtn.id)} + + + + + + + + {/each} + +
TítuloInícioFimAtivaAções
{mtn.title}{new Date(mtn.start_time).toLocaleString('pt-BR')}{new Date(mtn.end_time).toLocaleString('pt-BR')} + + {mtn.is_active ? 'Sim' : 'Não'} + + +
+ + +
+
+
+ {/if} +
+ + {#if showForm}
- -
+ + -
- - -
+
+ + +
-
+
{/if} + +{#if showMtnForm} + +
+
e.stopPropagation()} + > +
+

+ {editingMtn ? 'Editar Manutenção' : 'Nova Manutenção'} +

+ +
+ +
+ {#if editingMtn} + + {/if} + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + {#if mtnFormError} +

{mtnFormError}

+ {/if} + +
+ + +
+
+
+
+{/if}