feat: implement error handling and logging in server hooks to capture and notify on 404 and 500 errors, enhancing server reliability and monitoring
This commit is contained in:
@@ -0,0 +1,218 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import { Chart, registerables } from 'chart.js';
|
||||
import { BarChart3, XCircle, FileText } from 'lucide-svelte';
|
||||
|
||||
interface Estatisticas {
|
||||
totalRegistros: number;
|
||||
dentroDoPrazo: number;
|
||||
foraDoPrazo: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
estatisticas?: Estatisticas;
|
||||
chartData?: {
|
||||
labels: string[];
|
||||
datasets: Array<{
|
||||
label: string;
|
||||
data: number[];
|
||||
backgroundColor: string;
|
||||
borderColor: string;
|
||||
borderWidth: number;
|
||||
}>;
|
||||
} | null;
|
||||
isLoading?: boolean;
|
||||
error?: Error | null;
|
||||
}
|
||||
|
||||
let { estatisticas = undefined, chartData = null, isLoading = false, error = null }: Props = $props();
|
||||
|
||||
let chartCanvas: HTMLCanvasElement;
|
||||
let chartInstance: Chart | null = null;
|
||||
|
||||
onMount(() => {
|
||||
Chart.register(...registerables);
|
||||
const timeoutId = setTimeout(() => {
|
||||
if (chartCanvas && estatisticas && chartData && !chartInstance) {
|
||||
try {
|
||||
criarGrafico();
|
||||
} catch (err) {
|
||||
console.error('Erro ao criar gráfico no onMount:', err);
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
function criarGrafico() {
|
||||
if (!chartCanvas || !estatisticas || !chartData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = chartCanvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chartInstance) {
|
||||
chartInstance.destroy();
|
||||
chartInstance = null;
|
||||
}
|
||||
|
||||
try {
|
||||
chartInstance = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: chartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top',
|
||||
labels: {
|
||||
color: 'hsl(var(--bc))',
|
||||
font: {
|
||||
size: 12,
|
||||
family: "'Inter', sans-serif"
|
||||
},
|
||||
usePointStyle: true,
|
||||
padding: 15
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.85)',
|
||||
titleColor: '#fff',
|
||||
bodyColor: '#fff',
|
||||
borderColor: 'hsl(var(--p))',
|
||||
borderWidth: 1,
|
||||
padding: 12,
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
const label = context.dataset.label || '';
|
||||
const value = context.parsed.y;
|
||||
const total = estatisticas!.totalRegistros;
|
||||
const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : '0.0';
|
||||
return `${label}: ${value} (${percentage}%)`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
color: 'hsl(var(--bc))',
|
||||
font: {
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: 'rgba(0, 0, 0, 0.05)'
|
||||
},
|
||||
ticks: {
|
||||
color: 'hsl(var(--bc))',
|
||||
font: {
|
||||
size: 11
|
||||
},
|
||||
stepSize: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
duration: 1000,
|
||||
easing: 'easeInOutQuart'
|
||||
},
|
||||
interaction: {
|
||||
mode: 'index',
|
||||
intersect: false
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao criar gráfico:', error);
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (chartCanvas && estatisticas && chartData) {
|
||||
if (chartInstance) {
|
||||
chartInstance.destroy();
|
||||
chartInstance = null;
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(() => {
|
||||
try {
|
||||
criarGrafico();
|
||||
} catch (err) {
|
||||
console.error('Erro ao criar gráfico no effect:', err);
|
||||
}
|
||||
}, 200);
|
||||
|
||||
return () => {
|
||||
clearTimeout(timeoutId);
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="card bg-base-100/90 border-base-300 mb-8 border shadow-xl backdrop-blur-sm">
|
||||
<div class="card-body">
|
||||
<div class="mb-6 flex items-center justify-between">
|
||||
<h2 class="card-title text-2xl">
|
||||
<div class="bg-primary/10 rounded-lg p-2">
|
||||
<BarChart3 class="text-primary h-6 w-6" strokeWidth={2.5} />
|
||||
</div>
|
||||
<span>Visão Geral das Estatísticas</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="bg-base-200/50 border-base-300 relative h-80 w-full rounded-xl border p-4">
|
||||
{#if isLoading}
|
||||
<div class="bg-base-200/30 absolute inset-0 flex items-center justify-center rounded-xl">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
<span class="text-base-content/70 font-medium">Carregando estatísticas...</span>
|
||||
</div>
|
||||
</div>
|
||||
{:else if error}
|
||||
<div class="bg-base-200/30 absolute inset-0 flex items-center justify-center rounded-xl">
|
||||
<div class="alert alert-error shadow-lg">
|
||||
<XCircle class="h-6 w-6" />
|
||||
<div>
|
||||
<h3 class="font-bold">Erro ao carregar estatísticas</h3>
|
||||
<div class="mt-1 text-sm">
|
||||
{error?.message || String(error) || 'Erro desconhecido'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if !estatisticas || !chartData}
|
||||
<div class="bg-base-200/30 absolute inset-0 flex items-center justify-center rounded-xl">
|
||||
<div class="text-center">
|
||||
<FileText class="text-base-content/30 mx-auto mb-2 h-12 w-12" />
|
||||
<p class="text-base-content/70">Nenhuma estatística disponível</p>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<canvas bind:this={chartCanvas} class="h-full w-full"></canvas>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user