Use resolve() for all internal hrefs and goto paths to ensure correct
routing
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
import { useQuery } from "convex-svelte";
|
||||
import { api } from "@sgse-app/backend/convex/_generated/api";
|
||||
|
||||
|
||||
import { resolve } from "$app/paths";
|
||||
let abaAtiva = $state<"atividades" | "logins">("atividades");
|
||||
let limite = $state(50);
|
||||
|
||||
|
||||
// Queries com $derived para garantir reatividade
|
||||
const atividades = $derived(useQuery(api.logsAtividades.listarAtividades, { limite }));
|
||||
const logins = $derived(useQuery(api.logsLogin.listarTodosLogins, { limite }));
|
||||
@@ -55,7 +57,7 @@
|
||||
<!-- Breadcrumb -->
|
||||
<div class="text-sm breadcrumbs mb-4">
|
||||
<ul>
|
||||
<li><a href="/ti" class="text-primary hover:underline">TI</a></li>
|
||||
<li><a href={resolve('/ti')} class="text-primary hover:underline">TI</a></li>
|
||||
<li>Auditoria e Logs</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -124,7 +126,7 @@
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="tabs tabs-boxed mb-6 bg-base-100 shadow-lg p-1">
|
||||
<button
|
||||
<button
|
||||
class="tab flex items-center gap-2 {abaAtiva === 'atividades' ? 'tab-active' : ''}"
|
||||
onclick={() => abaAtiva = "atividades"}
|
||||
>
|
||||
@@ -133,7 +135,7 @@
|
||||
</svg>
|
||||
<span class="font-medium">Atividades no Sistema</span>
|
||||
</button>
|
||||
<button
|
||||
<button
|
||||
class="tab flex items-center gap-2 {abaAtiva === 'logins' ? 'tab-active' : ''}"
|
||||
onclick={() => abaAtiva = "logins"}
|
||||
>
|
||||
@@ -159,7 +161,7 @@
|
||||
<option value={200}>200 registros</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex gap-2">
|
||||
<button class="btn btn-outline btn-primary btn-sm gap-2">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
@@ -193,7 +195,7 @@
|
||||
<div class="badge badge-outline badge-lg">{atividades.data.length} registro{atividades.data.length !== 1 ? 's' : ''}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
{#if !atividades?.data}
|
||||
<div class="flex flex-col items-center justify-center py-16">
|
||||
<span class="loading loading-spinner loading-lg text-primary mb-4"></span>
|
||||
@@ -279,7 +281,7 @@
|
||||
<div class="badge badge-outline badge-lg">{logins.data.length} registro{logins.data.length !== 1 ? 's' : ''}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
{#if !logins?.data}
|
||||
<div class="flex flex-col items-center justify-center py-16">
|
||||
<span class="loading loading-spinner loading-lg text-primary mb-4"></span>
|
||||
|
||||
@@ -4,20 +4,21 @@
|
||||
import StatsCard from "$lib/components/ti/StatsCard.svelte";
|
||||
import { BarChart3, Users, CheckCircle2, Ban, Clock, Plus, Layers, FileText, Info } from "lucide-svelte";
|
||||
|
||||
import { resolve } from "$app/paths";
|
||||
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 {
|
||||
@@ -27,11 +28,11 @@
|
||||
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,
|
||||
@@ -58,32 +59,32 @@
|
||||
<!-- 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}
|
||||
<StatsCard
|
||||
title="Total de Usuários"
|
||||
value={stats.total}
|
||||
Icon={Users}
|
||||
color="primary"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Usuários Ativos"
|
||||
value={stats.ativos}
|
||||
|
||||
<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}
|
||||
|
||||
<StatsCard
|
||||
title="Usuários Bloqueados"
|
||||
value={stats.bloqueados}
|
||||
description="Requerem atenção"
|
||||
Icon={Ban}
|
||||
color="error"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
title="Usuários Inativos"
|
||||
value={stats.inativos}
|
||||
|
||||
<StatsCard
|
||||
title="Usuários Inativos"
|
||||
value={stats.inativos}
|
||||
description="Desativados"
|
||||
Icon={Clock}
|
||||
color="warning"
|
||||
@@ -100,17 +101,17 @@
|
||||
<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">
|
||||
<a href={resolve("/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">
|
||||
|
||||
<a href={resolve("/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">
|
||||
|
||||
<a href={resolve("/ti/auditoria")} class="btn btn-accent">
|
||||
<FileText class="h-5 w-5" strokeWidth={2} />
|
||||
Ver Logs
|
||||
</a>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { useQuery, useConvexClient } from 'convex-svelte';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import ProtectedRoute from '$lib/components/ProtectedRoute.svelte';
|
||||
@@ -745,7 +746,7 @@
|
||||
<h4 class="mb-1 font-semibold">Configuração de Permissões</h4>
|
||||
<p class="text-sm">
|
||||
Para configurar permissões específicas deste perfil, acesse o <a
|
||||
href="/ti/painel-permissoes"
|
||||
href={resolve('/ti/painel-permissoes')}
|
||||
class="link link-primary font-semibold">Painel de Permissões</a
|
||||
>.
|
||||
</p>
|
||||
@@ -755,7 +756,7 @@
|
||||
|
||||
<div class="modal-action mt-6">
|
||||
<button type="button" class="btn" onclick={fecharDetalhes}> Fechar </button>
|
||||
<a href="/ti/painel-permissoes" class="btn btn-primary">
|
||||
<a href={resolve('/ti/painel-permissoes')} class="btn btn-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="mr-2 h-5 w-5"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import ProtectedRoute from '$lib/components/ProtectedRoute.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import { resolve } from '$app/paths';
|
||||
</script>
|
||||
|
||||
<ProtectedRoute allowedRoles={['admin', 'ti']} maxLevel={1}>
|
||||
@@ -8,7 +9,7 @@
|
||||
<div class="breadcrumbs mb-4 text-sm">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/" class="text-primary hover:text-primary-focus">
|
||||
<a href={resolve('/')} class="text-primary hover:text-primary-focus">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-4 w-4"
|
||||
@@ -27,7 +28,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/ti" class="text-primary hover:text-primary-focus">TI</a>
|
||||
<a href={resolve('/ti')} class="text-primary hover:text-primary-focus">TI</a>
|
||||
</li>
|
||||
<li class="font-semibold">Personalizar Permissões</li>
|
||||
</ul>
|
||||
@@ -58,7 +59,7 @@
|
||||
Agora as permissões são configuradas por ação em cada perfil no painel de permissões.
|
||||
</p>
|
||||
</div>
|
||||
<button class="btn gap-2" onclick={() => goto('/ti')}>
|
||||
<button class="btn gap-2" onclick={() => goto(resolve('/ti'))}>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
@@ -94,7 +95,7 @@
|
||||
<span>
|
||||
A personalização por usuário foi substituída por <strong>permissões por ação</strong>
|
||||
por perfil. Utilize o
|
||||
<a href="/ti/painel-permissoes" class="link link-primary">Painel de Permissões</a> para configurar.
|
||||
<a href={resolve('/ti/painel-permissoes')} class="link link-primary">Painel de Permissões</a> para configurar.
|
||||
</span>
|
||||
</div>
|
||||
</ProtectedRoute>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import { useQuery, useConvexClient } from 'convex-svelte';
|
||||
import { api } from '@sgse-app/backend/convex/_generated/api';
|
||||
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
|
||||
@@ -561,7 +562,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
<a href="/ti/usuarios/criar" class="btn btn-primary">
|
||||
<a href={resolve('/ti/usuarios/criar')} class="btn btn-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="h-5 w-5"
|
||||
|
||||
Reference in New Issue
Block a user