feat(web): pantalla de permisos de menú (/admin/permisos)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d900a96740
commit
4d4df17d07
11 files changed
+496
-5
No files matched your search
@@ -0,0 +1,56 @@
|
||||
import { type MenuModule, isPathAllowed } from '@basic-erp/sap-types';
|
||||
|
||||
/** Lo que el administrador marca en pantalla para un usuario. */
|
||||
export interface Selection {
|
||||
/** Acceso total (`*`). */
|
||||
all: boolean;
|
||||
/** Rutas de menú marcadas (incluye las cubiertas por un patrón `/modulo/*`). */
|
||||
paths: ReadonlySet<string>;
|
||||
/** Patrones que no corresponden a ninguna pantalla actual (ej. de una versión futura): se conservan tal cual. */
|
||||
extras: readonly string[];
|
||||
}
|
||||
|
||||
/** Módulos que se otorgan con casillas; "Administración" solo llega con acceso total. */
|
||||
export const grantableModules = (menu: readonly MenuModule[]): readonly MenuModule[] => menu.filter((m) => m.id !== 'admin');
|
||||
|
||||
export function toSelection(patterns: readonly string[], menu: readonly MenuModule[]): Selection {
|
||||
const all = patterns.includes('*');
|
||||
const known = menu.flatMap((m) => m.items.map((i) => i.path));
|
||||
const paths = new Set(known.filter((p) => !all && isPathAllowed(patterns, p)));
|
||||
const extras = patterns.filter((p) => p !== '*' && !known.some((k) => isPathAllowed([p], k)));
|
||||
return { all, paths, extras };
|
||||
}
|
||||
|
||||
/**
|
||||
* Patrones a guardar: `*` si es acceso total; si no, `/modulo/*` cuando están todas las pantallas
|
||||
* del módulo y rutas sueltas en otro caso, más los patrones desconocidos conservados.
|
||||
*/
|
||||
export function toPatterns(sel: Selection, menu: readonly MenuModule[]): string[] {
|
||||
if (sel.all) return ['*'];
|
||||
const out: string[] = [];
|
||||
for (const m of grantableModules(menu)) {
|
||||
const checked = m.items.filter((i) => sel.paths.has(i.path));
|
||||
if (checked.length === m.items.length && m.items.length > 0) out.push(`/${m.id}/*`);
|
||||
else out.push(...checked.map((i) => i.path));
|
||||
}
|
||||
return [...out, ...sel.extras];
|
||||
}
|
||||
|
||||
export function togglePath(sel: Selection, path: string, on: boolean): Selection {
|
||||
const paths = new Set(sel.paths);
|
||||
if (on) paths.add(path);
|
||||
else paths.delete(path);
|
||||
return { ...sel, paths };
|
||||
}
|
||||
|
||||
export function toggleModule(sel: Selection, module: MenuModule, on: boolean): Selection {
|
||||
const paths = new Set(sel.paths);
|
||||
for (const i of module.items) {
|
||||
if (on) paths.add(i.path);
|
||||
else paths.delete(i.path);
|
||||
}
|
||||
return { ...sel, paths };
|
||||
}
|
||||
|
||||
export const samePatterns = (a: readonly string[], b: readonly string[]): boolean =>
|
||||
a.length === b.length && [...a].sort().every((p, i) => p === [...b].sort()[i]);
|
||||
Reference in new issue
Block a user