- Refactored multiple Svelte components to enhance code clarity and maintainability. - Standardized formatting and indentation across various files for consistency. - Improved error handling messages in the AprovarAusencias component for better user feedback. - Updated class names in the UI components to align with the new design system. - Removed unnecessary whitespace and comments to streamline the codebase.
471 lines
16 KiB
Svelte
471 lines
16 KiB
Svelte
<script lang="ts">
|
|
import { onMount, onDestroy } from "svelte";
|
|
import { useQuery, useConvexClient } from "convex-svelte";
|
|
import { api } from "@sgse-app/backend/convex/_generated/api";
|
|
import { startMetricsCollection } from "$lib/utils/metricsCollector";
|
|
import AlertConfigModal from "./AlertConfigModal.svelte";
|
|
import ReportGeneratorModal from "./ReportGeneratorModal.svelte";
|
|
|
|
const client = useConvexClient();
|
|
const ultimaMetrica = useQuery(api.monitoramento.obterUltimaMetrica, {});
|
|
|
|
let showAlertModal = $state(false);
|
|
let showReportModal = $state(false);
|
|
let stopCollection: (() => void) | null = null;
|
|
|
|
// Métricas derivadas
|
|
const metrics = $derived(ultimaMetrica || null);
|
|
|
|
// Função para obter cor baseada no valor
|
|
function getStatusColor(
|
|
value: number | undefined,
|
|
type: "normal" | "inverted" = "normal",
|
|
): string {
|
|
if (value === undefined) return "badge-ghost";
|
|
|
|
if (type === "normal") {
|
|
// Para CPU, RAM, Storage: maior é pior
|
|
if (value < 60) return "badge-success";
|
|
if (value < 80) return "badge-warning";
|
|
return "badge-error";
|
|
} else {
|
|
// Para métricas onde menor é melhor (latência, erros)
|
|
if (value < 100) return "badge-success";
|
|
if (value < 500) return "badge-warning";
|
|
return "badge-error";
|
|
}
|
|
}
|
|
|
|
function getProgressColor(value: number | undefined): string {
|
|
if (value === undefined) return "progress-ghost";
|
|
|
|
if (value < 60) return "progress-success";
|
|
if (value < 80) return "progress-warning";
|
|
return "progress-error";
|
|
}
|
|
|
|
// Iniciar coleta de métricas ao montar
|
|
onMount(() => {
|
|
stopCollection = startMetricsCollection(client, 2000); // Atualização a cada 2 segundos
|
|
});
|
|
|
|
// Parar coleta ao desmontar
|
|
onDestroy(() => {
|
|
if (stopCollection) {
|
|
stopCollection();
|
|
}
|
|
});
|
|
|
|
function formatValue(
|
|
value: number | undefined,
|
|
suffix: string = "%",
|
|
): string {
|
|
if (value === undefined) return "N/A";
|
|
return `${value.toFixed(1)}${suffix}`;
|
|
}
|
|
</script>
|
|
|
|
<div
|
|
class="card bg-linear-to-br from-base-100 to-base-200 shadow-2xl border-2 border-primary/20"
|
|
>
|
|
<div class="card-body">
|
|
<!-- Header -->
|
|
<div
|
|
class="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6"
|
|
>
|
|
<div class="flex items-center gap-2">
|
|
<div class="badge badge-success badge-lg gap-2 animate-pulse">
|
|
<div class="w-2 h-2 bg-white rounded-full"></div>
|
|
Tempo Real - Atualização a cada 2s
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
<button
|
|
type="button"
|
|
class="btn btn-primary btn-sm"
|
|
onclick={() => (showAlertModal = true)}
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-5 w-5"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"
|
|
/>
|
|
</svg>
|
|
Configurar Alertas
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="btn btn-secondary btn-sm"
|
|
onclick={() => (showReportModal = true)}
|
|
>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-5 w-5"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
/>
|
|
</svg>
|
|
Gerar Relatório
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Métricas Grid -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<!-- CPU Usage -->
|
|
<div
|
|
class="stat bg-base-100 rounded-2xl shadow-lg border border-primary/10 hover:shadow-xl transition-all duration-300 hover:scale-105"
|
|
>
|
|
<div class="stat-figure text-primary">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-10 w-10"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="stat-title font-semibold">CPU</div>
|
|
<div class="stat-value text-primary text-3xl">
|
|
{formatValue(metrics?.cpuUsage)}
|
|
</div>
|
|
<div class="stat-desc mt-2">
|
|
<div class="badge {getStatusColor(metrics?.cpuUsage)} badge-sm">
|
|
{metrics?.cpuUsage !== undefined && metrics.cpuUsage < 60
|
|
? "Normal"
|
|
: metrics?.cpuUsage !== undefined && metrics.cpuUsage < 80
|
|
? "Atenção"
|
|
: "Crítico"}
|
|
</div>
|
|
</div>
|
|
<progress
|
|
class="progress {getProgressColor(metrics?.cpuUsage)} w-full mt-2"
|
|
value={metrics?.cpuUsage || 0}
|
|
max="100"
|
|
></progress>
|
|
</div>
|
|
|
|
<!-- Memory Usage -->
|
|
<div
|
|
class="stat bg-base-100 rounded-2xl shadow-lg border border-success/10 hover:shadow-xl transition-all duration-300 hover:scale-105"
|
|
>
|
|
<div class="stat-figure text-success">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-10 w-10"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="stat-title font-semibold">Memória RAM</div>
|
|
<div class="stat-value text-success text-3xl">
|
|
{formatValue(metrics?.memoryUsage)}
|
|
</div>
|
|
<div class="stat-desc mt-2">
|
|
<div class="badge {getStatusColor(metrics?.memoryUsage)} badge-sm">
|
|
{metrics?.memoryUsage !== undefined && metrics.memoryUsage < 60
|
|
? "Normal"
|
|
: metrics?.memoryUsage !== undefined && metrics.memoryUsage < 80
|
|
? "Atenção"
|
|
: "Crítico"}
|
|
</div>
|
|
</div>
|
|
<progress
|
|
class="progress {getProgressColor(metrics?.memoryUsage)} w-full mt-2"
|
|
value={metrics?.memoryUsage || 0}
|
|
max="100"
|
|
></progress>
|
|
</div>
|
|
|
|
<!-- Network Latency -->
|
|
<div
|
|
class="stat bg-base-100 rounded-2xl shadow-lg border border-warning/10 hover:shadow-xl transition-all duration-300 hover:scale-105"
|
|
>
|
|
<div class="stat-figure text-warning">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-10 w-10"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="stat-title font-semibold">Latência de Rede</div>
|
|
<div class="stat-value text-warning text-3xl">
|
|
{formatValue(metrics?.networkLatency, "ms")}
|
|
</div>
|
|
<div class="stat-desc mt-2">
|
|
<div
|
|
class="badge {getStatusColor(
|
|
metrics?.networkLatency,
|
|
'inverted',
|
|
)} badge-sm"
|
|
>
|
|
{metrics?.networkLatency !== undefined &&
|
|
metrics.networkLatency < 100
|
|
? "Excelente"
|
|
: metrics?.networkLatency !== undefined &&
|
|
metrics.networkLatency < 500
|
|
? "Boa"
|
|
: "Lenta"}
|
|
</div>
|
|
</div>
|
|
<progress
|
|
class="progress progress-warning w-full mt-2"
|
|
value={Math.min((metrics?.networkLatency || 0) / 10, 100)}
|
|
max="100"
|
|
></progress>
|
|
</div>
|
|
|
|
<!-- Storage Usage -->
|
|
<div
|
|
class="stat bg-base-100 rounded-2xl shadow-lg border border-info/10 hover:shadow-xl transition-all duration-300 hover:scale-105"
|
|
>
|
|
<div class="stat-figure text-info">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-10 w-10"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="stat-title font-semibold">Armazenamento</div>
|
|
<div class="stat-value text-info text-3xl">
|
|
{formatValue(metrics?.storageUsed)}
|
|
</div>
|
|
<div class="stat-desc mt-2">
|
|
<div class="badge {getStatusColor(metrics?.storageUsed)} badge-sm">
|
|
{metrics?.storageUsed !== undefined && metrics.storageUsed < 60
|
|
? "Normal"
|
|
: metrics?.storageUsed !== undefined && metrics.storageUsed < 80
|
|
? "Atenção"
|
|
: "Crítico"}
|
|
</div>
|
|
</div>
|
|
<progress
|
|
class="progress progress-info w-full mt-2"
|
|
value={metrics?.storageUsed || 0}
|
|
max="100"
|
|
></progress>
|
|
</div>
|
|
|
|
<!-- Usuários Online -->
|
|
<div
|
|
class="stat bg-base-100 rounded-2xl shadow-lg border border-accent/10 hover:shadow-xl transition-all duration-300 hover:scale-105"
|
|
>
|
|
<div class="stat-figure text-accent">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-10 w-10"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="stat-title font-semibold">Usuários Online</div>
|
|
<div class="stat-value text-accent text-3xl">
|
|
{metrics?.usuariosOnline || 0}
|
|
</div>
|
|
<div class="stat-desc mt-2">
|
|
<div class="badge badge-accent badge-sm">Tempo Real</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Mensagens por Minuto -->
|
|
<div
|
|
class="stat bg-base-100 rounded-2xl shadow-lg border border-secondary/10 hover:shadow-xl transition-all duration-300 hover:scale-105"
|
|
>
|
|
<div class="stat-figure text-secondary">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-10 w-10"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="stat-title font-semibold">Mensagens/min</div>
|
|
<div class="stat-value text-secondary text-3xl">
|
|
{metrics?.mensagensPorMinuto || 0}
|
|
</div>
|
|
<div class="stat-desc mt-2">
|
|
<div class="badge badge-secondary badge-sm">Atividade</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tempo de Resposta -->
|
|
<div
|
|
class="stat bg-base-100 rounded-2xl shadow-lg border border-primary/10 hover:shadow-xl transition-all duration-300 hover:scale-105"
|
|
>
|
|
<div class="stat-figure text-primary">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-10 w-10"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="stat-title font-semibold">Tempo Resposta</div>
|
|
<div class="stat-value text-primary text-3xl">
|
|
{formatValue(metrics?.tempoRespostaMedio, "ms")}
|
|
</div>
|
|
<div class="stat-desc mt-2">
|
|
<div
|
|
class="badge {getStatusColor(
|
|
metrics?.tempoRespostaMedio,
|
|
'inverted',
|
|
)} badge-sm"
|
|
>
|
|
{metrics?.tempoRespostaMedio !== undefined &&
|
|
metrics.tempoRespostaMedio < 100
|
|
? "Rápido"
|
|
: metrics?.tempoRespostaMedio !== undefined &&
|
|
metrics.tempoRespostaMedio < 500
|
|
? "Normal"
|
|
: "Lento"}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Erros -->
|
|
<div
|
|
class="stat bg-base-100 rounded-2xl shadow-lg border border-error/10 hover:shadow-xl transition-all duration-300 hover:scale-105"
|
|
>
|
|
<div class="stat-figure text-error">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
class="h-10 w-10"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
stroke="currentColor"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
<div class="stat-title font-semibold">Erros (30s)</div>
|
|
<div class="stat-value text-error text-3xl">
|
|
{metrics?.errosCount || 0}
|
|
</div>
|
|
<div class="stat-desc mt-2">
|
|
<div
|
|
class="badge {(metrics?.errosCount || 0) === 0
|
|
? 'badge-success'
|
|
: 'badge-error'} badge-sm"
|
|
>
|
|
{(metrics?.errosCount || 0) === 0 ? "Sem erros" : "Verificar logs"}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Info Footer -->
|
|
<div class="alert alert-info mt-6 shadow-lg">
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
fill="none"
|
|
viewBox="0 0 24 24"
|
|
class="stroke-current shrink-0 w-6 h-6"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
></path>
|
|
</svg>
|
|
<div>
|
|
<h3 class="font-bold">Monitoramento Ativo</h3>
|
|
<div class="text-xs">
|
|
Métricas coletadas automaticamente a cada 2 segundos.
|
|
{#if metrics?.timestamp}
|
|
Última atualização: {new Date(metrics.timestamp).toLocaleString(
|
|
"pt-BR",
|
|
)}
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modals -->
|
|
{#if showAlertModal}
|
|
<AlertConfigModal onClose={() => (showAlertModal = false)} />
|
|
{/if}
|
|
|
|
{#if showReportModal}
|
|
<ReportGeneratorModal onClose={() => (showReportModal = false)} />
|
|
{/if}
|