fix(web): Oberflaechentexte ohne Mandanten-Begriff, Organisationsauswahl im Marktplatz nur bei mehreren
Tessera CI/CD / Lint & Type Check (push) Successful in 57s
Tessera CI/CD / Tests (push) Successful in 1m51s
Tessera CI/CD / Desktop-Pakete bauen (push) Successful in 19s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m23s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-03 02:58:50 +02:00
parent 2053dbac28
commit 3562b154f0
7 changed files with 47 additions and 31 deletions
@@ -13,7 +13,7 @@ const messages: Record<string, Record<string, unknown>> = {
title: 'Freigaben-Matrix',
searchPlaceholder: 'Module oder Gruppen durchsuchen...',
emptyModules:
'Es sind noch keine Module für diesen Mandanten aktiviert. Aktiviere zuerst ein Modul unter Module.',
'Es sind noch keine Module aktiviert. Aktiviere zuerst ein Modul unter Module.',
emptyModulesLink: 'Zu Module',
adminNote:
'ADMIN und SUPER_ADMIN haben immer Zugriff auf alle aktiven Module — diese Matrix betrifft nur die Rolle USER.',
@@ -153,7 +153,7 @@ describe('AdminModuleGrantsPage (Permission-Matrix)', () => {
render(<AdminModuleGrantsPage />);
await waitFor(() => {
expect(screen.getByText(/keine Module für diesen Mandanten aktiviert/)).toBeInTheDocument();
expect(screen.getByText(/noch keine Module aktiviert/)).toBeInTheDocument();
});
const link = screen.getByText('Zu Module');
expect(link.closest('a')).toHaveAttribute('href', '/admin/modules');
@@ -34,7 +34,8 @@ export function TenantContextSelector() {
.finally(() => setLoading(false));
}, [isSuperAdmin]);
if (!isSuperAdmin) return null;
// Nur bei mehreren Organisationen sinnvoll; im Ein-Firmen-Betrieb ausgeblendet.
if (!isSuperAdmin || (!loading && tenants.length <= 1)) return null;
return (
<div className="flex flex-wrap items-center gap-3 rounded-lg bg-card px-4 py-3 shadow-sm dark:border dark:border-border">
@@ -25,7 +25,7 @@ vi.mock('next-intl', () => ({
errorLoad: 'Module konnten nicht geladen werden.',
toastActivated: 'Modul erfolgreich aktiviert',
accessDenied: 'Zugriff verweigert',
tenantContextLabel: 'Mandanten-Kontext',
tenantContextLabel: 'Organisation',
};
let result = translations[key] ?? key;
if (params) {
@@ -27,7 +27,7 @@ vi.mock('next-intl', () => ({
toastActivated: 'Modul erfolgreich aktiviert',
toastDeactivated: 'Modul erfolgreich deaktiviert',
accessDenied: 'Zugriff verweigert',
tenantContextLabel: 'Mandanten-Kontext',
tenantContextLabel: 'Organisation',
deactivateDialogTitle: 'Modul deaktivieren',
deactivateDialogBody: 'Moechten Sie {moduleName} fuer diesen Mandanten deaktivieren?',
deactivateDialogConfirm: 'Deaktivieren',
@@ -118,6 +118,21 @@ describe('TenantContextSelector', () => {
expect(screen.getByText('Tenant Beta')).toBeInTheDocument();
});
it('blendet die Auswahl bei nur einer Organisation aus', async () => {
mockAuthStore.mockImplementation(
(selector: (state: { user: { id: string; username: string; displayName: string; role: string; tenantId: string } }) => unknown) =>
selector({
user: { id: 'u1', username: 'superadmin', displayName: 'Super', role: 'SUPER_ADMIN', tenantId: 't1' },
}),
);
vi.stubGlobal(
'fetch',
vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve([mockTenants[0]]) })),
);
const { container } = render(<TenantContextSelector />);
await waitFor(() => expect(container.innerHTML).toBe(''));
});
it('renders nothing for ADMIN and does not load the tenant list', () => {
mockAuthStore.mockImplementation(
(selector: (state: { user: { id: string; username: string; displayName: string; role: string; tenantId: string } }) => unknown) =>
@@ -120,7 +120,7 @@ describe('WelcomeMailTemplateEditor', () => {
]) {
expect(within(box).getByRole('button', { name: `${token} einfügen` })).toBeTruthy();
}
expect(within(box).getByText('Name Ihres Mandanten')).toBeTruthy();
expect(within(box).getByText('Name Ihrer Organisation')).toBeTruthy();
expect(within(box).getByText('max.mustermann@example.com')).toBeTruthy();
});
+13 -13
View File
@@ -555,7 +555,7 @@
"viaGroupsColumn": "Über Gruppe(n)",
"directColumn": "Direkt",
"noInheritance": "–",
"noActiveModules": "Für diesen Mandanten sind keine Module aktiviert.",
"noActiveModules": "Es sind keine Module aktiviert.",
"directCheckboxLabel": "{module} direkt für {user} {granted, select, true {freigeben} other {entziehen}}",
"directLevelLabel": "Stufe der direkten Freigabe von {module} für {user}",
"manageMarker": "Verwalten",
@@ -751,7 +751,7 @@
},
"welcomeMail": {
"title": "Willkommensmail",
"description": "Hier legen Sie fest, mit welchen Worten Tessera neue Benutzer begrüßt. Die Vorlage gilt für Ihren Mandanten und wird bei jeder Willkommensmail aus der Benutzerverwaltung verwendet.",
"description": "Hier legen Sie fest, mit welchen Worten Tessera neue Benutzer begrüßt. Die Vorlage gilt für alle Benutzer und wird bei jeder Willkommensmail aus der Benutzerverwaltung verwendet.",
"loading": "Vorlage wird geladen …",
"fixed": {
"title": "Was Sie anpassen können – und was immer drin bleibt",
@@ -832,7 +832,7 @@
"example": "https://tessera.example.com"
},
"firma": {
"meaning": "Name Ihres Mandanten",
"meaning": "Name Ihrer Organisation",
"example": "Beispiel GmbH"
}
}
@@ -846,13 +846,13 @@
"frameTitle": "Vorschau der Willkommensmail",
"error": "Die Vorschau konnte nicht erstellt werden.",
"blocked": "Die Vorschau wird aktualisiert, sobald alle Felder in Ordnung sind.",
"sampleHint": "Die Vorschau verwendet Beispielwerte (Max Mustermann) und den Namen Ihres Mandanten. Der Link „Passwort festlegen“ ist in der Vorschau nur ein Beispiel."
"sampleHint": "Die Vorschau verwendet Beispielwerte (Max Mustermann) und den Namen Ihrer Organisation. Der Link „Passwort festlegen“ ist in der Vorschau nur ein Beispiel."
}
}
},
"adminModules": {
"title": "Modulverwaltung",
"description": "Module für Ihren Mandanten aktivieren oder deaktivieren",
"description": "Module aktivieren oder deaktivieren",
"activate": "Aktivieren",
"deactivate": "Deaktivieren",
"activated": "Aktiviert",
@@ -868,7 +868,7 @@
"grants": {
"title": "Freigaben-Matrix",
"searchPlaceholder": "Module oder Gruppen durchsuchen...",
"emptyModules": "Es sind noch keine Module für diesen Mandanten aktiviert. Aktiviere zuerst ein Modul unter Module.",
"emptyModules": "Es sind noch keine Module aktiviert. Aktiviere zuerst ein Modul unter Module.",
"emptyModulesLink": "Zu Module",
"levelExplanation": "„Benutzen“: Das Modul öffnen und damit arbeiten. „Verwalten“: zusätzlich die Einstellungen dieses Moduls ändern. Freigaben vergeben und Module aktivieren dürfen weiterhin nur Administratoren. Hat jemand über mehrere Wege Zugriff, gilt die höhere Stufe.",
"adminNote": "Administratoren haben immer Zugriff auf alle aktiven Module und dürfen deren Einstellungen ändern – diese Matrix betrifft nur Benutzer ohne Administratorrechte.",
@@ -964,7 +964,7 @@
"notFoundDescription": "Das angeforderte Modul existiert nicht oder ist nicht aktiviert.",
"backToCategory": "Zurück zur Kategorie",
"allModules": "Module",
"allModulesDescription": "Verfügbare Module für Ihren Mandanten",
"allModulesDescription": "Verfügbare Module",
"noActiveModules": "Keine Module aktiviert. Bitten Sie Ihren Administrator, Module zu aktivieren.",
"accessDenied": {
"title": "Kein Zugriff auf dieses Modul",
@@ -1194,7 +1194,7 @@
},
"marketplace": {
"title": "Marktplatz",
"subtitle": "Entdecken und aktivieren Sie Module für Ihren Mandanten",
"subtitle": "Entdecken und aktivieren Sie Module",
"activate": "Modul aktivieren",
"deactivate": "Deaktivieren",
"searchPlaceholder": "Module suchen...",
@@ -1212,13 +1212,13 @@
"toastActivateError": "Aktivierung fehlgeschlagen. Bitte versuchen Sie es erneut.",
"toastDeactivateError": "Deaktivierung fehlgeschlagen. Bitte versuchen Sie es erneut.",
"deactivateDialogTitle": "Modul deaktivieren",
"deactivateDialogBody": "Möchten Sie {moduleName} für diesen Mandanten deaktivieren? Das Modul wird aus der Seitenleiste entfernt.",
"deactivateDialogBody": "Möchten Sie {moduleName} deaktivieren? Das Modul wird aus der Seitenleiste entfernt.",
"deactivateDialogConfirm": "Deaktivieren",
"deactivateDialogCancel": "Abbrechen",
"tenantContextLabel": "Mandanten-Kontext",
"tenantContextLabel": "Organisation",
"backToMarketplace": "Zurück zum Marktplatz",
"detailVersion": "Version {version}",
"detailStatusActive": "Aktiviert für diesen Mandanten",
"detailStatusActive": "Aktiviert",
"detailStatusInactive": "Nicht aktiviert",
"accessDenied": "Zugriff verweigert",
"statusNoAccess": "Kein Zugriff",
@@ -1528,7 +1528,7 @@
"settings": {
"title": "Ausschreibungs-Radar — Einstellungen",
"rssSectionTitle": "RSS-Feeds",
"rssSectionBody": "Öffentliche, plattformweite RSS-Quellen (z. B. service.bund.de oder eine subreport-elvis-Kommunalfeed) — gilt für alle Mandanten gleich, nicht pro Mandant konfigurierbar.",
"rssSectionBody": "Öffentliche, plattformweite RSS-Quellen (z. B. service.bund.de oder eine subreport-elvis-Kommunalfeed) — gilt für alle gleich.",
"rssSectionUserNote": "Persönliche Feeds legen Sie auf „Meine Quellen\" an.",
"accessDeniedText": "Diese Seite verwaltet plattformweite Einstellungen und ist Administratoren vorbehalten.",
"digestIntervalLabel": "Digest-Intervall",
@@ -1611,7 +1611,7 @@
},
"mySources": {
"title": "Meine Quellen",
"intro": "Ausschreibungen, die aus Ihrem Postfach hereinkommen, erscheinen danach in der Trefferliste aller Kolleginnen und Kollegen Ihres Mandanten — es gibt keine getrennte Sichtbarkeit je Postfach.",
"intro": "Ausschreibungen, die aus Ihrem Postfach hereinkommen, erscheinen danach in der Trefferliste aller Kolleginnen und Kollegen — es gibt keine getrennte Sichtbarkeit je Postfach.",
"mailboxSectionTitle": "Mein Postfach",
"feedsSectionTitle": "Meine Feeds",
"notificationSectionTitle": "Benachrichtigung",
+12 -12
View File
@@ -555,7 +555,7 @@
"viaGroupsColumn": "Via Group(s)",
"directColumn": "Direct",
"noInheritance": "–",
"noActiveModules": "No modules are activated for this tenant.",
"noActiveModules": "No modules are activated.",
"directCheckboxLabel": "{granted, select, true {Grant} other {Revoke}} {module} directly for {user}",
"directLevelLabel": "Level of the direct grant of {module} for {user}",
"manageMarker": "Manage",
@@ -751,7 +751,7 @@
},
"welcomeMail": {
"title": "Welcome email",
"description": "Choose the words Tessera uses to greet new users. The template applies to your tenant and is used for every welcome email sent from user management.",
"description": "Choose the words Tessera uses to greet new users. The template applies to all users and is used for every welcome email sent from user management.",
"loading": "Loading template …",
"fixed": {
"title": "What you can change – and what always stays",
@@ -832,7 +832,7 @@
"example": "https://tessera.example.com"
},
"firma": {
"meaning": "Name of your tenant",
"meaning": "Name of your organization",
"example": "Example Ltd"
}
}
@@ -846,13 +846,13 @@
"frameTitle": "Welcome email preview",
"error": "The preview could not be created.",
"blocked": "The preview updates as soon as all fields are valid.",
"sampleHint": "The preview uses sample values (Max Mustermann) and the name of your tenant. The “Set password” link is only an example in the preview."
"sampleHint": "The preview uses sample values (Max Mustermann) and the name of your organization. The “Set password” link is only an example in the preview."
}
}
},
"adminModules": {
"title": "Module Management",
"description": "Activate or deactivate modules for your tenant",
"description": "Activate or deactivate modules",
"activate": "Activate",
"deactivate": "Deactivate",
"activated": "Activated",
@@ -868,7 +868,7 @@
"grants": {
"title": "Grants Matrix",
"searchPlaceholder": "Search modules or groups...",
"emptyModules": "No modules are activated for this tenant yet. Activate a module under Modules first.",
"emptyModules": "No modules are activated yet. Activate a module under Modules first.",
"emptyModulesLink": "Go to Modules",
"levelExplanation": "“Use”: open the module and work with it. “Manage”: additionally change the settings of this module. Only administrators may grant access and activate modules. If someone has access through several routes, the higher level applies.",
"adminNote": "Administrators always have access to all active modules and may change their settings — this matrix only affects users without administrator rights.",
@@ -964,7 +964,7 @@
"notFoundDescription": "The requested module does not exist or is not activated.",
"backToCategory": "Back to category",
"allModules": "Modules",
"allModulesDescription": "Available modules for your tenant",
"allModulesDescription": "Available modules",
"noActiveModules": "No modules activated. Ask your administrator to enable modules.",
"accessDenied": {
"title": "No Access to This Module",
@@ -1194,7 +1194,7 @@
},
"marketplace": {
"title": "Marketplace",
"subtitle": "Discover and activate modules for your tenant",
"subtitle": "Discover and activate modules",
"activate": "Activate Module",
"deactivate": "Deactivate",
"searchPlaceholder": "Search modules...",
@@ -1212,13 +1212,13 @@
"toastActivateError": "Activation failed. Please try again.",
"toastDeactivateError": "Deactivation failed. Please try again.",
"deactivateDialogTitle": "Deactivate Module",
"deactivateDialogBody": "Do you want to deactivate {moduleName} for this tenant? The module will be removed from the sidebar.",
"deactivateDialogBody": "Do you want to deactivate {moduleName}? The module will be removed from the sidebar.",
"deactivateDialogConfirm": "Deactivate",
"deactivateDialogCancel": "Cancel",
"tenantContextLabel": "Tenant Context",
"tenantContextLabel": "Organization",
"backToMarketplace": "Back to Marketplace",
"detailVersion": "Version {version}",
"detailStatusActive": "Activated for this tenant",
"detailStatusActive": "Activated",
"detailStatusInactive": "Not activated",
"accessDenied": "Access denied",
"statusNoAccess": "No Access",
@@ -1528,7 +1528,7 @@
"settings": {
"title": "Tender Radar — Settings",
"rssSectionTitle": "RSS Feeds",
"rssSectionBody": "Public, platform-wide RSS sources (e.g. service.bund.de or a subreport-elvis municipal feed) — applies equally to all tenants, not configurable per tenant.",
"rssSectionBody": "Public, platform-wide RSS sources (e.g. service.bund.de or a subreport-elvis municipal feed) — applies to everyone equally.",
"rssSectionUserNote": "Add personal feeds under \"My sources\".",
"accessDeniedText": "This page manages platform-wide settings and is reserved for administrators.",
"digestIntervalLabel": "Digest Interval",