274 lines
8.6 KiB
TypeScript
274 lines
8.6 KiB
TypeScript
import {
|
|
BadgeCheck,
|
|
ClipboardList,
|
|
Scale,
|
|
ShieldCheck,
|
|
Users,
|
|
Wallet,
|
|
Warehouse,
|
|
} from 'lucide-react';
|
|
import type { LucideIcon } from 'lucide-react';
|
|
|
|
export type ProcurementEntry = {
|
|
id: string;
|
|
label: string;
|
|
path: string;
|
|
roles?: string[];
|
|
fullPage?: boolean;
|
|
};
|
|
export type ProcurementGroup = {
|
|
id: string;
|
|
label: string;
|
|
icon: LucideIcon;
|
|
entries: ProcurementEntry[];
|
|
};
|
|
const readers = ['admin', 'procurement', 'finance', 'department_head'];
|
|
const finance = ['admin', 'finance'];
|
|
const inventory = [...readers, 'warehouse'];
|
|
|
|
export function procurementNavigation(
|
|
role: string,
|
|
payables: boolean,
|
|
): ProcurementGroup[] {
|
|
const groups: ProcurementGroup[] = [
|
|
{
|
|
id: 'requests',
|
|
label: 'Requisições',
|
|
icon: ClipboardList,
|
|
entries: [
|
|
{
|
|
id: 'new_request',
|
|
label: 'Nova requisição',
|
|
path: '/requisicoes/criar',
|
|
},
|
|
{
|
|
id: 'my_requests',
|
|
label: 'Minhas requisições',
|
|
path: '/requisicoes?view=mine',
|
|
},
|
|
{
|
|
id: 'my_drafts',
|
|
label: 'Os meus rascunhos',
|
|
path: '/requisicoes?view=mine&status=draft',
|
|
},
|
|
{
|
|
id: 'all_requests',
|
|
label: 'Todas as autorizadas',
|
|
path: '/requisicoes?view=all',
|
|
},
|
|
{ id: 'planning', label: 'Planeamento', path: '/planeamento' },
|
|
],
|
|
},
|
|
{
|
|
id: 'approvals',
|
|
label: 'Aprovações',
|
|
icon: BadgeCheck,
|
|
entries: [
|
|
{
|
|
id: 'pending_approvals',
|
|
label: 'Pendentes para mim',
|
|
path: '/requisicoes?view=approvals',
|
|
roles: readers,
|
|
},
|
|
{
|
|
id: 'decisions',
|
|
label: 'Minhas decisões',
|
|
path: '/requisicoes?view=decided',
|
|
roles: readers,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'sourcing',
|
|
label: 'Consultas e adjudicações',
|
|
icon: Scale,
|
|
entries: [
|
|
{
|
|
id: 'quotations',
|
|
label: 'Cotações e comparação',
|
|
path: '/cotacoes',
|
|
roles: readers,
|
|
},
|
|
{
|
|
id: 'orders',
|
|
label: 'Adjudicações e ordens',
|
|
path: '/ordens',
|
|
roles: readers,
|
|
},
|
|
{
|
|
id: 'purchase_review',
|
|
label: 'Revisão e cancelamento',
|
|
path: '/revisao-compras',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'suppliers',
|
|
label: 'Fornecedores',
|
|
icon: Users,
|
|
entries: [
|
|
{
|
|
id: 'suppliers',
|
|
label: 'Cadastro e documentos',
|
|
path: '/fornecedores',
|
|
roles: readers,
|
|
},
|
|
{
|
|
id: 'supplier_evaluations',
|
|
label: 'Avaliação de fornecedores',
|
|
path: '/fornecedores/avaliacoes',
|
|
roles: readers,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'receiving',
|
|
label: 'Recepções e armazém',
|
|
icon: Warehouse,
|
|
entries: [
|
|
{
|
|
id: 'receipts',
|
|
label: 'Recepções e divergências',
|
|
path: '/recepcoes',
|
|
roles: inventory,
|
|
},
|
|
{
|
|
id: 'stock',
|
|
label: 'Artigos e stock',
|
|
path: '/stock',
|
|
roles: ['admin', 'procurement', 'finance', 'warehouse'],
|
|
},
|
|
{
|
|
id: 'stock_operations',
|
|
label: 'Saídas e inventários',
|
|
path: '/stock/operacoes',
|
|
roles: ['admin', 'procurement', 'finance', 'warehouse'],
|
|
},
|
|
{
|
|
id: 'equipment',
|
|
label: 'Equipamentos e peças',
|
|
path: '/equipamentos',
|
|
roles: inventory,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'finance',
|
|
label: 'Finanças',
|
|
icon: Wallet,
|
|
entries: [
|
|
{
|
|
id: 'invoices',
|
|
label: 'Facturas e conferência',
|
|
path: '/facturas',
|
|
roles: finance,
|
|
},
|
|
...(payables
|
|
? [
|
|
{
|
|
id: 'payables',
|
|
label: 'Obrigações a fornecedores',
|
|
path: '/obrigacoes-fornecedores',
|
|
roles: finance,
|
|
fullPage: true,
|
|
},
|
|
]
|
|
: []),
|
|
{
|
|
id: 'payments',
|
|
label: 'Pagamentos externos',
|
|
path: '/pagamentos',
|
|
roles: finance,
|
|
},
|
|
{
|
|
id: 'budgets',
|
|
label: 'Orçamentos e reforços',
|
|
path: '/orcamentos',
|
|
roles: finance,
|
|
},
|
|
{
|
|
id: 'investment',
|
|
label: 'Regras de investimento',
|
|
path: '/regras-investimento',
|
|
roles: ['admin', 'procurement', 'finance'],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'administration',
|
|
label: 'Administração e controlo',
|
|
icon: ShieldCheck,
|
|
entries: [
|
|
{
|
|
id: 'company',
|
|
label: 'Empresa, equipa e centros',
|
|
path: '/empresa',
|
|
roles: ['admin'],
|
|
},
|
|
{
|
|
id: 'admission_policy',
|
|
label: 'Política de admissão',
|
|
path: '/politica-admissao',
|
|
roles: ['admin'],
|
|
},
|
|
{
|
|
id: 'recommendation_policy',
|
|
label: 'Política de recomendação',
|
|
path: '/politica-recomendacao',
|
|
roles: ['admin'],
|
|
},
|
|
{
|
|
id: 'conflicts',
|
|
label: 'Conflitos de interesses',
|
|
path: '/conflitos',
|
|
},
|
|
],
|
|
},
|
|
];
|
|
return groups
|
|
.map((group) => ({
|
|
...group,
|
|
entries: group.entries.filter(
|
|
(entry) => !entry.roles || entry.roles.includes(role),
|
|
),
|
|
}))
|
|
.filter((group) => group.entries.length);
|
|
}
|
|
|
|
export function activeProcurementEntry(
|
|
groups: ProcurementGroup[],
|
|
url: string,
|
|
): string | undefined {
|
|
const current = new URL(url, 'http://local');
|
|
if (/^\/ordens\/\d+\/recepcoes/.test(current.pathname)) return 'receipts';
|
|
if (/^\/requisicoes\/\d+\/adjudicacao(?:\/|$)/.test(current.pathname))
|
|
return 'orders';
|
|
if (
|
|
/^\/requisicoes\/\d+\/(cotacoes|abertura|qualificacao|admissao|recomendacao|relatorio)(?:\/|$)/.test(
|
|
current.pathname,
|
|
)
|
|
)
|
|
return 'quotations';
|
|
const matches = groups
|
|
.flatMap((group) => group.entries)
|
|
.filter((entry) => {
|
|
const target = new URL(entry.path, 'http://local');
|
|
if (
|
|
current.pathname !== target.pathname &&
|
|
!current.pathname.startsWith(target.pathname + '/')
|
|
)
|
|
return false;
|
|
return [...target.searchParams].every(
|
|
([key, value]) =>
|
|
(current.searchParams.get(key) ??
|
|
(key === 'view' ? 'all' : null)) === value,
|
|
);
|
|
});
|
|
const specificity = (path: string) => {
|
|
const target = new URL(path, 'http://local');
|
|
return target.pathname.length * 100 + target.searchParams.size;
|
|
};
|
|
matches.sort((a, b) => specificity(b.path) - specificity(a.path));
|
|
return matches[0]?.id;
|
|
}
|