feat(16-02): display name with fallback in user-detail projections (D-04, UI-SPEC Surface Contract 6)
- ModuleGrantsService.getUserAccess() now selects internalName on the
membership query's group projection (already present via `include:
{ group: true }` on the grant query)
- Both display points (viaGroups names, membership chips' name field)
use internalName ?? name; groups[] sorting now runs over the
displayed name as a result, distinct from GroupsService.listForTenant()
which still sorts by the raw name column
- 3 new test cases: fallback set/unset, sort-by-displayed-name
- No apps/web/ changes (verified via git diff --name-only)
This commit is contained in:
@@ -212,6 +212,16 @@ export class ModuleGrantsService {
|
||||
* Frage (welche Gruppe gewährt dieses Modul, und besteht zusätzlich ein
|
||||
* Direkt-Grant) und behält dafür je Eintrag exakt die Form
|
||||
* { module, viaGroups, direct }.
|
||||
*
|
||||
* Anzeigename mit Fallback (D-04, UI-SPEC Surface Contract 6): beide
|
||||
* Projektionsstellen (viaGroups-Namen, groups[].name) liefern
|
||||
* `group.internalName ?? group.name` — Nullish, nicht Oder, damit ein
|
||||
* bereits auf null normalisierter Leerstring (Task 2) nicht versehentlich
|
||||
* über eine Truthiness-Prüfung nachträglich anders interpretiert wird.
|
||||
* Die Sortierung von `groups` läuft danach automatisch über den
|
||||
* angezeigten Namen, weil sie auf demselben Feld (`name`) aufsetzt —
|
||||
* anders als GroupsService.listForTenant(), das weiterhin über die
|
||||
* Datenbankspalte `name` sortiert.
|
||||
*/
|
||||
async getUserAccess(tenantId: string, userId: string) {
|
||||
await this.assertTargetBelongsToTenant(tenantId, undefined, userId);
|
||||
@@ -237,7 +247,7 @@ export class ModuleGrantsService {
|
||||
// läuft der Mandantenfilter über die Relation `group: { tenantId }`.
|
||||
this.prisma.groupMembership.findMany({
|
||||
where: { userId, group: { tenantId } },
|
||||
include: { group: { select: { id: true, name: true } } },
|
||||
include: { group: { select: { id: true, name: true, internalName: true } } },
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -246,7 +256,7 @@ export class ModuleGrantsService {
|
||||
for (const g of groupGrants as any[]) {
|
||||
if (!g.group) continue;
|
||||
const names = groupNamesByModule.get(g.moduleId) ?? [];
|
||||
names.push(g.group.name);
|
||||
names.push(g.group.internalName ?? g.group.name);
|
||||
groupNamesByModule.set(g.moduleId, names);
|
||||
}
|
||||
|
||||
@@ -261,7 +271,7 @@ export class ModuleGrantsService {
|
||||
.filter((m) => m.group)
|
||||
.map((m) => ({
|
||||
id: m.group.id as string,
|
||||
name: m.group.name as string,
|
||||
name: (m.group.internalName ?? m.group.name) as string,
|
||||
source: m.source as string,
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
Reference in New Issue
Block a user