metis-desk/packages/rdx-ui/src/lib/entity-column-configuration.ts
David ba034794c6 feat: add column browser fixture and entity column configuration
- Introduced `ColumnBrowserFixture` component for testing column visibility and interactions.
- Created `entity-column-configuration.ts` to define types and utility functions for managing entity columns.
- Implemented Playwright tests in `columns-ui.test.py` to validate column behavior and preferences.
- Added `visual-audit-volume.py` script to enhance the visual audit setup with synthetic data.
2026-10-22 10:10:34 +02:00

67 lines
1.8 KiB
TypeScript

export type EntityColumnMetadata = {
groupId?: string;
keywords?: readonly string[];
};
export type EntityColumnGroup = {
id: string;
label: string;
description?: string;
};
export type EntityColumnOption = EntityColumnMetadata & {
id: string;
label: string;
visible: boolean;
hideable: boolean;
};
/** Normaliza tildes sin equiparar ñ y n, incluidas sus representaciones Unicode descompuestas. */
export function normalizeColumnSearch(value: string): string {
return value
.trim()
.toLowerCase()
.normalize("NFD")
.replace(/n\u0303/g, "ñ")
.replace(/\p{M}/gu, "");
}
/** Los IDs conservan identidad; la primera declaración de un grupo u opción prevalece. */
export function groupColumnOptions(
options: readonly EntityColumnOption[],
groups: readonly EntityColumnGroup[],
search: string
) {
const term = normalizeColumnSearch(search);
const uniqueOptions = new Map<string, EntityColumnOption>();
const uniqueGroups = new Map<string, EntityColumnGroup>();
for (const group of groups) {
if (!uniqueGroups.has(group.id)) {
uniqueGroups.set(group.id, group);
}
}
for (const option of options) {
if (!uniqueOptions.has(option.id)) {
uniqueOptions.set(option.id, option);
}
}
const matches = [...uniqueOptions.values()].filter((option) =>
[option.label, ...(option.keywords ?? [])].some((value) =>
normalizeColumnSearch(value).includes(term)
)
);
const grouped = [...uniqueGroups.values()].map((group) => ({
group,
options: matches.filter((option) => option.groupId === group.id),
}));
const ungrouped = matches.filter(
(option) => !(option.groupId && uniqueGroups.has(option.groupId))
);
return [
...grouped.filter((group) => group.options.length > 0),
...(ungrouped.length ? [{ group: undefined, options: ungrouped }] : []),
];
}