Merge pull request #63 from killer-cf/feat-pedidos

Feat pedidos
This commit is contained in:
Kilder Costa
2025-12-12 10:22:53 -03:00
committed by GitHub
11 changed files with 766 additions and 103 deletions

View File

@@ -2,6 +2,7 @@
import { api } from '@sgse-app/backend/convex/_generated/api';
import { useConvexClient, useQuery } from 'convex-svelte';
import type { FunctionReference } from 'convex/server';
import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
import {
Home,
User,
@@ -73,7 +74,7 @@
}
// Estrutura do menu definida no frontend
const MENU_STRUCTURE: MenuItem[] = [
const MENU_STRUCTURE = [
{
label: 'Dashboard',
icon: 'Home',
@@ -83,12 +84,44 @@
label: 'Gestão de Pessoas',
icon: 'Users',
link: '/recursos-humanos',
permission: { recurso: 'funcionarios', acao: 'ver' },
permission: { recurso: 'gestao_pessoas', acao: 'ver' },
submenus: [
{
label: 'Funcionários',
link: '/recursos-humanos/funcionarios',
permission: { recurso: 'funcionarios', acao: 'listar' }
permission: { recurso: 'funcionarios', acao: 'listar' },
exact: true
},
{
label: 'Cadastro de Funcionários',
link: '/recursos-humanos/funcionarios/cadastro',
permission: { recurso: 'funcionarios', acao: 'criar' }
},
{
label: 'Exclusão de Funcionários',
link: '/recursos-humanos/funcionarios/excluir',
permission: { recurso: 'funcionarios', acao: 'excluir' }
},
{
label: 'Férias',
link: '/recursos-humanos/ferias',
permission: { recurso: 'ferias', acao: 'dashboard' }
},
{
label: 'Atestados de Licenças',
link: '/recursos-humanos/atestados-licencas',
permission: { recurso: 'atestados_licencas', acao: 'listar' }
},
{
label: 'Controle de Ponto',
link: '/recursos-humanos/controle-ponto',
permission: { recurso: 'ponto', acao: 'ver' },
exact: true
},
{
label: 'Banco de Horas',
link: '/recursos-humanos/controle-ponto/banco-horas',
permission: { recurso: 'banco_horas', acao: 'ver' }
},
{
label: 'Registro de Ponto',
@@ -129,25 +162,25 @@
{
label: 'Objetos',
icon: 'Tag',
link: resolve('/compras/objetos'),
link: '/compras/objetos',
permission: { recurso: 'objetos', acao: 'listar' }
},
{
label: 'Atas de Registro',
icon: 'FileText',
link: resolve('/compras/atas'),
link: '/compras/atas',
permission: { recurso: 'atas', acao: 'listar' }
},
{
label: 'Contratos',
icon: 'FileText',
link: resolve('/licitacoes/contratos'),
link: '/licitacoes/contratos',
permission: { recurso: 'contratos', acao: 'listar' }
},
{
label: 'Empresas',
icon: 'Briefcase',
link: resolve('/licitacoes/empresas'),
link: '/licitacoes/empresas',
permission: { recurso: 'empresas', acao: 'listar' }
},
{
@@ -174,7 +207,7 @@
link: '/ti',
permission: { recurso: 'ti_painel_administrativo', acao: 'ver' }
}
];
] as const satisfies readonly MenuItem[];
type IconType = typeof Home;
@@ -192,6 +225,20 @@
const permissionsQuery = useQuery(api.menu.getUserPermissions as FunctionReference<'query'>, {});
// Filtrar menu baseado nas permissões do usuário
function filterSubmenusByPermissions(
items: SubMenuItem[],
isMaster: boolean,
permissionsSet: Set<string>
): SubMenuItem[] {
if (isMaster) return items;
return items.filter((item) => {
if (!item.permission) return true;
const key = `${item.permission.recurso}.${item.permission.acao}`;
return permissionsSet.has(key);
});
}
function filterMenuByPermissions(
items: MenuItem[],
isMaster: boolean,
@@ -199,30 +246,32 @@
): MenuItem[] {
if (isMaster) return items;
return items
.map((item) => {
// Verifica permissão do item atual
let hasPermission = true;
if (item.permission) {
const key = `${item.permission.recurso}.${item.permission.acao}`;
hasPermission = permissionsSet.has(key);
}
const filtered: MenuItem[] = [];
// Se não tem permissão, não mostra
if (!hasPermission) return null;
for (const item of items) {
// Verifica permissão do item atual
let hasPermission = true;
if (item.permission) {
const key = `${item.permission.recurso}.${item.permission.acao}`;
hasPermission = permissionsSet.has(key);
}
// Se tiver submenus, filtra eles recursivamente
let filteredSubmenus: MenuItem[] | undefined = undefined;
if (item.submenus) {
filteredSubmenus = filterMenuByPermissions(item.submenus, isMaster, permissionsSet);
}
if (!hasPermission) continue;
return {
...item,
submenus: filteredSubmenus && filteredSubmenus.length > 0 ? filteredSubmenus : undefined
};
})
.filter((item): item is MenuItem => item !== null);
// Se tiver submenus, filtra e só mantém se sobrar algo
let filteredSubmenus: SubMenuItem[] | undefined = undefined;
if (item.submenus) {
const subs = filterSubmenusByPermissions(item.submenus, isMaster, permissionsSet);
filteredSubmenus = subs.length > 0 ? subs : undefined;
}
filtered.push({
...item,
submenus: filteredSubmenus
});
}
return filtered;
}
// Menu filtrado reativo
@@ -230,7 +279,7 @@
const data = permissionsQuery.data;
if (!data) return [];
const permissionsSet = new Set(data.permissions);
const permissionsSet = new Set((data.permissions ?? []) as string[]);
return filterMenuByPermissions(MENU_STRUCTURE, data.isMaster, permissionsSet);
});
@@ -438,7 +487,11 @@
}
// Buscar o usuário no Convex usando getCurrentUser
const usuario = await convexClient.query(api.auth.getCurrentUser, {});
// (o typesafe FunctionReference pode falhar aqui; tipamos o retorno e mantemos a chamada)
const usuario = (await convexClient.query(
api.auth.getCurrentUser as unknown as FunctionReference<'query'>,
{}
)) as { _id?: Id<'usuarios'> } | null;
if (usuario && usuario._id) {
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {