feat: update ESLint and TypeScript configurations across frontend and backend; enhance component structure and improve data handling in various modules
This commit is contained in:
@@ -1,181 +1,192 @@
|
||||
<script lang="ts">
|
||||
import { useQuery, useConvexClient } from "convex-svelte";
|
||||
import { api } from "@sgse-app/backend/convex/_generated/api";
|
||||
import StatsCard from "$lib/components/ti/StatsCard.svelte";
|
||||
import { BarChart3, Users, CheckCircle2, Ban, Clock, Plus, Layers, FileText, Info, Shield, AlertTriangle } from "lucide-svelte";
|
||||
import { useQuery, useConvexClient } from 'convex-svelte';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import StatsCard from '$lib/components/ti/StatsCard.svelte';
|
||||
import {
|
||||
BarChart3,
|
||||
Users,
|
||||
CheckCircle2,
|
||||
Ban,
|
||||
Clock,
|
||||
Plus,
|
||||
Layers,
|
||||
FileText,
|
||||
Info,
|
||||
Shield,
|
||||
AlertTriangle
|
||||
} from 'lucide-svelte';
|
||||
|
||||
import { resolve } from "$app/paths";
|
||||
const client = useConvexClient();
|
||||
const usuariosQuery = useQuery(api.usuarios.listar, {});
|
||||
const estatisticasLGPD = useQuery(api.lgpd.obterEstatisticasLGPD, {});
|
||||
import { resolve } from '$app/paths';
|
||||
const client = useConvexClient();
|
||||
const usuariosQuery = useQuery(api.usuarios.listar, {});
|
||||
const estatisticasLGPD = useQuery(api.lgpd.obterEstatisticasLGPD, {});
|
||||
|
||||
// Verificar se está carregando
|
||||
const carregando = $derived(usuariosQuery === undefined);
|
||||
// Verificar se está carregando
|
||||
const carregando = $derived(usuariosQuery === undefined);
|
||||
|
||||
// Extrair dados dos usuários
|
||||
const usuarios = $derived(usuariosQuery?.data ?? []);
|
||||
// Extrair dados dos usuários
|
||||
const usuarios = $derived(usuariosQuery?.data ?? []);
|
||||
|
||||
// Estatísticas derivadas
|
||||
const stats = $derived.by(() => {
|
||||
// Se ainda está carregando, retorna null para mostrar loading
|
||||
if (carregando) return null;
|
||||
// Estatísticas derivadas
|
||||
const stats = $derived.by(() => {
|
||||
// Se ainda está carregando, retorna null para mostrar loading
|
||||
if (carregando) return null;
|
||||
|
||||
// Se não há usuários, retorna stats zeradas (mas não null para não mostrar loading)
|
||||
if (!Array.isArray(usuarios) || usuarios.length === 0) {
|
||||
return {
|
||||
total: 0,
|
||||
ativos: 0,
|
||||
bloqueados: 0,
|
||||
inativos: 0
|
||||
};
|
||||
}
|
||||
// Se não há usuários, retorna stats zeradas (mas não null para não mostrar loading)
|
||||
if (!Array.isArray(usuarios) || usuarios.length === 0) {
|
||||
return {
|
||||
total: 0,
|
||||
ativos: 0,
|
||||
bloqueados: 0,
|
||||
inativos: 0
|
||||
};
|
||||
}
|
||||
|
||||
const ativos = usuarios.filter(u => u.ativo && !u.bloqueado).length;
|
||||
const bloqueados = usuarios.filter(u => u.bloqueado === true).length;
|
||||
const inativos = usuarios.filter(u => !u.ativo).length;
|
||||
const ativos = usuarios.filter((u) => u.ativo && !u.bloqueado).length;
|
||||
const bloqueados = usuarios.filter((u) => u.bloqueado === true).length;
|
||||
const inativos = usuarios.filter((u) => !u.ativo).length;
|
||||
|
||||
return {
|
||||
total: usuarios.length,
|
||||
ativos,
|
||||
bloqueados,
|
||||
inativos
|
||||
};
|
||||
});
|
||||
return {
|
||||
total: usuarios.length,
|
||||
ativos,
|
||||
bloqueados,
|
||||
inativos
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="container mx-auto px-4 py-6 max-w-7xl">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="p-3 bg-primary/10 rounded-xl">
|
||||
<BarChart3 class="h-8 w-8 text-primary" strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-base-content">Dashboard Administrativo TI</h1>
|
||||
<p class="text-base-content/60 mt-1">Painel de controle e monitoramento do sistema</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container mx-auto max-w-7xl px-4 py-6">
|
||||
<!-- Header -->
|
||||
<div class="mb-8 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="bg-primary/10 rounded-xl p-3">
|
||||
<BarChart3 class="text-primary h-8 w-8" strokeWidth={2} />
|
||||
</div>
|
||||
<div>
|
||||
<h1 class="text-base-content text-3xl font-bold">Dashboard Administrativo TI</h1>
|
||||
<p class="text-base-content/60 mt-1">Painel de controle e monitoramento do sistema</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
{#if stats}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||
<StatsCard
|
||||
title="Total de Usuários"
|
||||
value={stats.total}
|
||||
Icon={Users}
|
||||
color="primary"
|
||||
/>
|
||||
<!-- Stats Cards -->
|
||||
{#if stats}
|
||||
<div class="mb-8 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4">
|
||||
<StatsCard title="Total de Usuários" value={stats.total} Icon={Users} color="primary" />
|
||||
|
||||
<StatsCard
|
||||
title="Usuários Ativos"
|
||||
value={stats.ativos}
|
||||
description="{stats.total > 0 ? ((stats.ativos / stats.total) * 100).toFixed(1) + '% do total' : '0% do total'}"
|
||||
Icon={CheckCircle2}
|
||||
color="success"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Usuários Ativos"
|
||||
value={stats.ativos}
|
||||
description={stats.total > 0
|
||||
? ((stats.ativos / stats.total) * 100).toFixed(1) + '% do total'
|
||||
: '0% do total'}
|
||||
Icon={CheckCircle2}
|
||||
color="success"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Usuários Bloqueados"
|
||||
value={stats.bloqueados}
|
||||
description="Requerem atenção"
|
||||
Icon={Ban}
|
||||
color="error"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Usuários Bloqueados"
|
||||
value={stats.bloqueados}
|
||||
description="Requerem atenção"
|
||||
Icon={Ban}
|
||||
color="error"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Usuários Inativos"
|
||||
value={stats.inativos}
|
||||
description="Desativados"
|
||||
Icon={Clock}
|
||||
color="warning"
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex justify-center items-center py-20">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
</div>
|
||||
{/if}
|
||||
<StatsCard
|
||||
title="Usuários Inativos"
|
||||
value={stats.inativos}
|
||||
description="Desativados"
|
||||
Icon={Clock}
|
||||
color="warning"
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center py-20">
|
||||
<span class="loading loading-spinner loading-lg text-primary"></span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- LGPD Stats Cards -->
|
||||
{#if estatisticasLGPD}
|
||||
<div class="card bg-base-100 shadow-xl mb-8">
|
||||
<div class="card-body">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="card-title text-2xl">LGPD - Proteção de Dados</h2>
|
||||
<a href={resolve("/ti/lgpd")} class="btn btn-sm btn-primary">
|
||||
<Shield class="h-4 w-4" />
|
||||
Acessar LGPD
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<StatsCard
|
||||
title="Solicitações Pendentes"
|
||||
value={estatisticasLGPD.solicitacoesPendentes}
|
||||
description="Aguardando resposta"
|
||||
Icon={AlertTriangle}
|
||||
color="warning"
|
||||
/>
|
||||
<!-- LGPD Stats Cards -->
|
||||
{#if estatisticasLGPD}
|
||||
<div class="card bg-base-100 mb-8 shadow-xl">
|
||||
<div class="card-body">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h2 class="card-title text-2xl">LGPD - Proteção de Dados</h2>
|
||||
<a href={resolve('/ti/lgpd')} class="btn btn-sm btn-primary">
|
||||
<Shield class="h-4 w-4" />
|
||||
Acessar LGPD
|
||||
</a>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-4">
|
||||
<StatsCard
|
||||
title="Solicitações Pendentes"
|
||||
value={estatisticasLGPD.solicitacoesPendentes}
|
||||
description="Aguardando resposta"
|
||||
Icon={AlertTriangle}
|
||||
color="warning"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Solicitações Vencendo"
|
||||
value={estatisticasLGPD.solicitacoesVencendo}
|
||||
description="Prazo próximo"
|
||||
Icon={AlertTriangle}
|
||||
color="error"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Solicitações Vencendo"
|
||||
value={estatisticasLGPD.solicitacoesVencendo}
|
||||
description="Prazo próximo"
|
||||
Icon={AlertTriangle}
|
||||
color="error"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Total de Solicitações"
|
||||
value={estatisticasLGPD.totalSolicitacoes}
|
||||
description="Todas as solicitações"
|
||||
Icon={FileText}
|
||||
color="info"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Total de Solicitações"
|
||||
value={estatisticasLGPD.totalSolicitacoes}
|
||||
description="Todas as solicitações"
|
||||
Icon={FileText}
|
||||
color="info"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Consentimentos Ativos"
|
||||
value={estatisticasLGPD.consentimentosAtivos}
|
||||
description="Consentimentos válidos"
|
||||
Icon={CheckCircle2}
|
||||
color="success"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<StatsCard
|
||||
title="Consentimentos Ativos"
|
||||
value={estatisticasLGPD.consentimentosAtivos}
|
||||
description="Consentimentos válidos"
|
||||
Icon={CheckCircle2}
|
||||
color="success"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Ações Rápidas -->
|
||||
<div class="card bg-base-100 shadow-xl mb-8">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title text-2xl mb-4">Ações Rápidas</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<a href={resolve("/ti/usuarios")} class="btn btn-primary">
|
||||
<Plus class="h-5 w-5" strokeWidth={2} />
|
||||
Criar Usuário
|
||||
</a>
|
||||
<!-- Ações Rápidas -->
|
||||
<div class="card bg-base-100 mb-8 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title mb-4 text-2xl">Ações Rápidas</h2>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||
<a href={resolve('/ti/usuarios')} class="btn btn-primary">
|
||||
<Plus class="h-5 w-5" strokeWidth={2} />
|
||||
Criar Usuário
|
||||
</a>
|
||||
|
||||
<a href={resolve("/ti/perfis")} class="btn btn-secondary">
|
||||
<Layers class="h-5 w-5" strokeWidth={2} />
|
||||
Gerenciar Perfis
|
||||
</a>
|
||||
<a href={resolve('/ti/perfis')} class="btn btn-secondary">
|
||||
<Layers class="h-5 w-5" strokeWidth={2} />
|
||||
Gerenciar Perfis
|
||||
</a>
|
||||
|
||||
<a href={resolve("/ti/auditoria")} class="btn btn-accent">
|
||||
<FileText class="h-5 w-5" strokeWidth={2} />
|
||||
Ver Logs
|
||||
</a>
|
||||
<a href={resolve('/ti/auditoria')} class="btn btn-accent">
|
||||
<FileText class="h-5 w-5" strokeWidth={2} />
|
||||
Ver Logs
|
||||
</a>
|
||||
|
||||
<a href={resolve("/ti/lgpd")} class="btn btn-info">
|
||||
<Shield class="h-5 w-5" strokeWidth={2} />
|
||||
LGPD
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href={resolve('/ti/lgpd')} class="btn btn-info">
|
||||
<Shield class="h-5 w-5" strokeWidth={2} />
|
||||
LGPD
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Informação Sistema -->
|
||||
<div class="alert alert-info">
|
||||
<Info class="stroke-current shrink-0 w-6 h-6" strokeWidth={2} />
|
||||
<span>SGSE - Sistema de Gerenciamento de Secretaria - Versão 2.0 com controle avançado de acesso</span>
|
||||
</div>
|
||||
<!-- Informação Sistema -->
|
||||
<div class="alert alert-info">
|
||||
<Info class="h-6 w-6 shrink-0 stroke-current" strokeWidth={2} />
|
||||
<span
|
||||
>SGSE - Sistema de Gerenciamento de Secretaria - Versão 2.0 com controle avançado de acesso</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user