Files
sgse-app/apps/web/src/routes/(dashboard)/ti/painel-administrativo/+page.svelte
deyvisonwanderley c459297968 refactor: update components to use lucide icons and improve structure
- Replaced SVG icons with lucide-svelte components across various files for consistency and improved performance.
- Refactored ActionGuard, ErrorModal, FileUpload, and other components to enhance readability and maintainability.
- Updated the dashboard pages to include new icons and improved layout for better user experience.
- Enhanced StatsCard component to support dynamic icon rendering, allowing for more flexible usage.
- Improved overall styling and structure in multiple components to align with design standards.
2025-11-05 12:09:41 -03:00

127 lines
3.8 KiB
Svelte

<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 } from "lucide-svelte";
const client = useConvexClient();
const usuariosQuery = useQuery(api.usuarios.listar, {});
// Verificar se está carregando
const carregando = $derived(usuariosQuery === undefined);
// 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;
// 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;
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>
<!-- 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"
/>
<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 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}
<!-- 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="/ti/usuarios" class="btn btn-primary">
<Plus class="h-5 w-5" strokeWidth={2} />
Criar Usuário
</a>
<a href="/ti/perfis" class="btn btn-secondary">
<Layers class="h-5 w-5" strokeWidth={2} />
Gerenciar Perfis
</a>
<a href="/ti/auditoria" class="btn btn-accent">
<FileText class="h-5 w-5" strokeWidth={2} />
Ver Logs
</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>Sistema de Gestão da Secretaria de Esportes - Versão 2.0 com controle avançado de acesso</span>
</div>
</div>