fix: enhance data handling in vacation dashboard by adding array checks and improving chart data structure for better stability and performance
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
import { resolve } from '$app/paths';
|
import { resolve } from '$app/paths';
|
||||||
import { onMount, tick } from 'svelte';
|
import { onMount, tick } from 'svelte';
|
||||||
import { SvelteDate, SvelteMap } from 'svelte/reactivity';
|
import { SvelteDate, SvelteMap, SvelteSet } from 'svelte/reactivity';
|
||||||
import { useQuery, useConvexClient } from 'convex-svelte';
|
import { useQuery, useConvexClient } from 'convex-svelte';
|
||||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||||
import type { FunctionReturnType } from 'convex/server';
|
import type { FunctionReturnType } from 'convex/server';
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
|
|
||||||
// Usar último valor válido ou array vazio
|
// Usar último valor válido ou array vazio
|
||||||
const solicitacoes = $derived<TodasSolicitacoes>(
|
const solicitacoes = $derived<TodasSolicitacoes>(
|
||||||
todasSolicitacoesQuery?.data ?? ultimasSolicitacoesValidas
|
(todasSolicitacoesQuery?.data ?? ultimasSolicitacoesValidas) || []
|
||||||
);
|
);
|
||||||
|
|
||||||
// Filtros Dashboard
|
// Filtros Dashboard
|
||||||
@@ -153,6 +153,9 @@
|
|||||||
periodoFim: string;
|
periodoFim: string;
|
||||||
}
|
}
|
||||||
): TodasSolicitacoes {
|
): TodasSolicitacoes {
|
||||||
|
if (!Array.isArray(lista)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
return lista.filter((periodo) => {
|
return lista.filter((periodo) => {
|
||||||
if (filtros.status !== 'todos' && periodo.status !== filtros.status) {
|
if (filtros.status !== 'todos' && periodo.status !== filtros.status) {
|
||||||
return false;
|
return false;
|
||||||
@@ -271,14 +274,14 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const solicitacoesAprovadas = $derived(
|
const solicitacoesAprovadas = $derived(
|
||||||
solicitacoesFiltradas.filter(
|
(Array.isArray(solicitacoesFiltradas) ? solicitacoesFiltradas : []).filter(
|
||||||
(p) =>
|
(p) =>
|
||||||
p.status === 'aprovado' || p.status === 'data_ajustada_aprovada' || p.status === 'EmFérias'
|
p.status === 'aprovado' || p.status === 'data_ajustada_aprovada' || p.status === 'EmFérias'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const periodosDetalhados = $derived<Array<PeriodoDetalhado>>(
|
const periodosDetalhados = $derived<Array<PeriodoDetalhado>>(
|
||||||
solicitacoesAprovadas
|
(Array.isArray(solicitacoesAprovadas) ? solicitacoesAprovadas : [])
|
||||||
.map((periodo) => ({
|
.map((periodo) => ({
|
||||||
feriasId: periodo._id,
|
feriasId: periodo._id,
|
||||||
funcionarioId: periodo.funcionarioId,
|
funcionarioId: periodo.funcionarioId,
|
||||||
@@ -303,6 +306,10 @@
|
|||||||
(() => {
|
(() => {
|
||||||
const agregados = new SvelteMap<string, PeriodoPorMes>();
|
const agregados = new SvelteMap<string, PeriodoPorMes>();
|
||||||
|
|
||||||
|
if (!Array.isArray(periodosDetalhados)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
for (const periodo of periodosDetalhados) {
|
for (const periodo of periodosDetalhados) {
|
||||||
const inicio = new SvelteDate(`${periodo.dataInicio}T00:00:00`);
|
const inicio = new SvelteDate(`${periodo.dataInicio}T00:00:00`);
|
||||||
const chave = `${inicio.getFullYear()}-${String(inicio.getMonth() + 1).padStart(2, '0')}`;
|
const chave = `${inicio.getFullYear()}-${String(inicio.getMonth() + 1).padStart(2, '0')}`;
|
||||||
@@ -531,19 +538,41 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dados para gráfico de área - Funcionários de férias nos próximos 12 meses
|
// Dados para gráfico de área - Funcionários de férias nos próximos 12 meses
|
||||||
const chartDataFuncionariosFerias = $derived(() => {
|
type ChartData = {
|
||||||
const hoje = new Date();
|
labels: string[];
|
||||||
|
datasets: Array<{
|
||||||
|
label: string;
|
||||||
|
data: number[];
|
||||||
|
backgroundColor: string[];
|
||||||
|
borderColor: string;
|
||||||
|
borderWidth: number;
|
||||||
|
pointBackgroundColor: string[];
|
||||||
|
pointBorderColor: string;
|
||||||
|
pointBorderWidth: number;
|
||||||
|
pointRadius: number;
|
||||||
|
pointHoverRadius: number;
|
||||||
|
pointHoverBackgroundColor: string;
|
||||||
|
pointHoverBorderColor: string;
|
||||||
|
pointHoverBorderWidth: number;
|
||||||
|
fill: boolean;
|
||||||
|
tension: number;
|
||||||
|
spanGaps: boolean;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
const chartDataFuncionariosFerias: ChartData = $derived.by(() => {
|
||||||
|
// Sempre criar os 12 meses, mesmo sem dados
|
||||||
|
const hoje = new SvelteDate();
|
||||||
hoje.setHours(0, 0, 0, 0);
|
hoje.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
// Criar array com os próximos 12 meses
|
// Criar array com os próximos 12 meses
|
||||||
const meses: Array<{ mes: string; dataInicio: Date; dataFim: Date; quantidade: number }> = [];
|
const meses: Array<{ mes: string; dataInicio: SvelteDate; dataFim: SvelteDate; quantidade: number }> = [];
|
||||||
|
|
||||||
for (let i = 0; i < 12; i++) {
|
for (let i = 0; i < 12; i++) {
|
||||||
const dataInicioMes = new Date(hoje.getFullYear(), hoje.getMonth() + i, 1);
|
const dataInicioMes = new SvelteDate(hoje.getFullYear(), hoje.getMonth() + i, 1);
|
||||||
const dataFimMes = new Date(hoje.getFullYear(), hoje.getMonth() + i + 1, 0);
|
const dataFimMes = new SvelteDate(hoje.getFullYear(), hoje.getMonth() + i + 1, 0);
|
||||||
dataFimMes.setHours(23, 59, 59, 999);
|
dataFimMes.setHours(23, 59, 59, 999);
|
||||||
|
|
||||||
const mesLabel = format(dataInicioMes, 'MMM/yyyy', { locale: ptBR });
|
const mesLabel = format(new Date(dataInicioMes.getTime()), 'MMM/yyyy', { locale: ptBR });
|
||||||
meses.push({
|
meses.push({
|
||||||
mes: mesLabel,
|
mes: mesLabel,
|
||||||
dataInicio: dataInicioMes,
|
dataInicio: dataInicioMes,
|
||||||
@@ -552,7 +581,8 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filtrar apenas solicitações aprovadas que estão ou estarão em férias
|
// Verificação de segurança e filtrar apenas solicitações aprovadas
|
||||||
|
if (ultimasSolicitacoesValidas && Array.isArray(ultimasSolicitacoesValidas)) {
|
||||||
const solicitacoesAprovadas = ultimasSolicitacoesValidas.filter(
|
const solicitacoesAprovadas = ultimasSolicitacoesValidas.filter(
|
||||||
(s) =>
|
(s) =>
|
||||||
s.status === 'aprovado' ||
|
s.status === 'aprovado' ||
|
||||||
@@ -562,19 +592,19 @@
|
|||||||
|
|
||||||
// Calcular quantos funcionários estarão de férias em cada mês
|
// Calcular quantos funcionários estarão de férias em cada mês
|
||||||
meses.forEach((mesInfo) => {
|
meses.forEach((mesInfo) => {
|
||||||
const funcionariosEmFerias = new Set<string>();
|
const funcionariosEmFerias = new SvelteSet<string>();
|
||||||
|
|
||||||
solicitacoesAprovadas.forEach((solicitacao) => {
|
solicitacoesAprovadas.forEach((solicitacao) => {
|
||||||
if (!solicitacao.funcionarioId) return;
|
if (!solicitacao.funcionarioId) return;
|
||||||
|
|
||||||
const dataInicio = new Date(solicitacao.dataInicio);
|
const dataInicio = new SvelteDate(solicitacao.dataInicio);
|
||||||
const dataFim = new Date(solicitacao.dataFim);
|
const dataFim = new SvelteDate(solicitacao.dataFim);
|
||||||
dataInicio.setHours(0, 0, 0, 0);
|
dataInicio.setHours(0, 0, 0, 0);
|
||||||
dataFim.setHours(23, 59, 59, 999);
|
dataFim.setHours(23, 59, 59, 999);
|
||||||
|
|
||||||
// Verificar se o período de férias se sobrepõe com o mês
|
// Verificar se o período de férias se sobrepõe com o mês
|
||||||
if (
|
if (
|
||||||
(dataInicio <= mesInfo.dataFim && dataFim >= mesInfo.dataInicio)
|
(dataInicio.getTime() <= mesInfo.dataFim.getTime() && dataFim.getTime() >= mesInfo.dataInicio.getTime())
|
||||||
) {
|
) {
|
||||||
funcionariosEmFerias.add(String(solicitacao.funcionarioId));
|
funcionariosEmFerias.add(String(solicitacao.funcionarioId));
|
||||||
}
|
}
|
||||||
@@ -582,6 +612,7 @@
|
|||||||
|
|
||||||
mesInfo.quantidade = funcionariosEmFerias.size;
|
mesInfo.quantidade = funcionariosEmFerias.size;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Cores harmoniosas com o tema (gradiente de azul primary para accent)
|
// Cores harmoniosas com o tema (gradiente de azul primary para accent)
|
||||||
const corBase = '#3b82f6'; // Azul primary
|
const corBase = '#3b82f6'; // Azul primary
|
||||||
@@ -2475,12 +2506,12 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-base-200/40 w-full overflow-x-auto rounded-xl p-4">
|
<div class="bg-base-200/40 w-full overflow-x-auto rounded-xl p-4">
|
||||||
{#if chartDataFuncionariosFerias.labels.length === 0}
|
|
||||||
<div class="flex h-96 items-center justify-center">
|
|
||||||
<p class="text-base-content/60">Sem dados registrados até o momento.</p>
|
|
||||||
</div>
|
|
||||||
{#if chartDataFuncionariosFerias.labels && chartDataFuncionariosFerias.labels.length > 0}
|
{#if chartDataFuncionariosFerias.labels && chartDataFuncionariosFerias.labels.length > 0}
|
||||||
|
<AreaChart data={chartDataFuncionariosFerias} height={400} />
|
||||||
|
{:else}
|
||||||
|
<div class="flex h-96 items-center justify-center">
|
||||||
|
<p class="text-base-content/60">Carregando dados do gráfico...</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user