SHL-04: globale Modul-Navigation in alle Frontends verdrahten #361
No files matched your search
@@ -0,0 +1,40 @@
|
||||
# SHL-04 – Globale Modul-Navigation in allen Frontends verdrahten
|
||||
|
||||
**Epic:** 🎨 UI-Shell | **Rolle:** Frontend | **Komplexität:** Mittel | **Aufwand:** 1 Personentag | **Welle:** Folge
|
||||
|
||||
---
|
||||
|
||||
## Rolle
|
||||
Du bist erfahrene Entwicklerin im Bereich Frontend und pflegst das gemeinsame UI-Shell-Paket `@nexarch/shl` sowie die darauf aufbauenden Modul-Frontends.
|
||||
|
||||
## Auftrag
|
||||
`SHL-01` hat die Layout-Shell mit Navigation (`Shell`-Komponente) gebaut, aber keines der zwölf Modul-Frontends hat sie tatsächlich eingebunden — jede App rendert isoliert ohne Weg zu den anderen Modulen. Dieses Ticket schließt die Lücke: eine neue, gemeinsame `ModuleNavShell`-Komponente im Paket `@nexarch/shl`, in alle zwölf Modul-Layouts eingezogen.
|
||||
|
||||
## Ausgangslage
|
||||
Entdeckt beim manuellen Test des Portals auf Host 204: Nutzer landete auf `datev-export` (Port 3111) und hatte keine Möglichkeit, zu einem anderen Modul zu wechseln außer durch manuelles Ändern der Portnummer in der Adresszeile. Code-Prüfung bestätigte: nur `portal` (eigene `ModuleGrid`-Startseite) hatte eine Übersicht, alle übrigen elf Apps keine.
|
||||
|
||||
## Produkt-DNA für diesen Bereich
|
||||
**Übernehmen (aus SHL-01):**
|
||||
- Ein Fundament, das alle Module teilen, statt es je Modul nachzubauen.
|
||||
|
||||
**Bewusst vermeiden:**
|
||||
- Sieben (hier: zwölf) visuell und strukturell unterschiedliche Modul-Oberflächen, die sich nicht wie ein Produkt anfühlen.
|
||||
|
||||
## Akzeptanzkriterien
|
||||
1. Neue Komponente `ModuleNavShell` in `@nexarch/shl`, nutzt intern die bestehende `Shell`-Komponente aus SHL-01.
|
||||
2. Alle zwölf Modul-Frontends (`dms-browser`, `dms-upload`, `mail-search`, `workflow-approvals`, `workflow-cases`, `routing-rules`, `ai-summaries`, `ai-extraction-review`, `connect-status`, `datev-export`, `tenant-admin`, `rbac-admin`) binden `ModuleNavShell` in ihr Root-Layout ein, mit korrektem `activeKey`.
|
||||
3. Navigation zeigt alle Module aus derselben Registry (`web/portal/config/modules.json`), aktuelles Modul als `aria-current="page"` markiert (über `Shell`s bestehende Logik).
|
||||
4. Build jeder betroffenen App (`next build`) läuft fehlerfrei durch.
|
||||
5. `portal`-App bleibt unverändert nutzbar (eigene `ModuleGrid`-Startseite, kein Duplikat der Navigation nötig).
|
||||
|
||||
## Bekannter Kompromiss (dokumentiert, kein Fehler)
|
||||
Die Navigation nutzt bewusst eine **statische** Registrierung (dieselbe Liste wie `portal/config/modules.json`, dort als Datenquelle, hier dupliziert) statt eines Live-Abrufs über `/api/visible-modules`: die Hälfte der Modul-Frontends hat noch keine einheitliche Core-Sitzung (manche nutzen feste Demo-Kennungen per Umgebungsvariable statt Login), ein Live-Abruf hätte für diese Apps eine Anmeldung vorausgesetzt, die sie bisher nicht kennen. `web/portal/app/api/visible-modules/route.ts` wurde dennoch bereits auf eine Origin-Allowlist (statt generischer CORS-Wildcard) vorbereitet, falls ein späteres Ticket den Live-Abruf nachrüstet (Rechte-genaue Navigation statt der heutigen, ungefilterten Liste).
|
||||
|
||||
## Nicht Bestandteil dieser Kachel
|
||||
- Vereinheitlichung der Anmeldemodelle über alle Module (eigenes Folgeticket).
|
||||
- Live-, rechtegefilterte Navigation (abhängig vom obigen Punkt).
|
||||
|
||||
## Abschluss
|
||||
- Commit-Nachricht: `SHL-04: globale Modul-Navigation in alle Frontends verdrahten`
|
||||
- Branch: `feature/shl-04-globale-modul-navigation`
|
||||
- Schrittweise committen, Branch pushen, dann anhalten. Kein Merge, kein Deploy.
|
||||
@@ -0,0 +1,32 @@
|
||||
# SHL-04 – Prüfprotokoll: Globale Modul-Navigation
|
||||
|
||||
## Prüfung 1: Build aller betroffenen Apps
|
||||
Jede der zwölf Apps real mit `next build` gebaut (auf Testhost 204, mit den jeweils gültigen Umgebungsvariablen der Deployment-Units):
|
||||
`dms-browser`, `dms-upload`, `mail-search`, `workflow-approvals`, `workflow-cases`, `routing-rules`, `ai-summaries`, `ai-extraction-review`, `connect-status`, `datev-export`, `tenant-admin`, `rbac-admin`.
|
||||
Ergebnis: alle zwölf Builds fehlerfrei (inkl. TypeScript-Prüfung), nach Korrektur zweier Import-Abweichungen (`tenant-admin`, `rbac-admin`, `dms-upload` hatten einen abweichenden Import-Satz ohne `themeInitScript`/`focusVisibleStyles`).
|
||||
|
||||
## Prüfung 2: Navigationsleiste im ausgelieferten HTML
|
||||
Nach Deploy auf Host 204 jede App per HTTP abgerufen und auf das Markup `shl-shell-header` geprüft:
|
||||
|
||||
| Port | App | Ergebnis |
|
||||
|---|---|---|
|
||||
| 3101 | dms-browser | 307 (Login-Redirect vor Render, Shell serverseitig im Layout aktiv) |
|
||||
| 3107 | dms-upload | 200, Shell vorhanden |
|
||||
| 3102 | mail-search | 200, Shell vorhanden |
|
||||
| 3103 | workflow-approvals | 200, Shell vorhanden |
|
||||
| 3108 | workflow-cases | 200, Shell vorhanden |
|
||||
| 3109 | routing-rules | 200, Shell vorhanden |
|
||||
| 3104 | ai-summaries | 200, Shell vorhanden |
|
||||
| 3110 | ai-extraction-review | 200, Shell vorhanden |
|
||||
| 3105 | connect-status | 307 (Login-Redirect) |
|
||||
| 3111 | datev-export | 307 (Login-Redirect) |
|
||||
| 3106 | tenant-admin | 200, Shell vorhanden |
|
||||
| 3112 | rbac-admin | 307 (Login-Redirect) |
|
||||
|
||||
Die vier mit 307 antwortenden Apps leiten vor dem Rendern auf die Anmeldeseite um (bestehendes, von diesem Ticket unverändertes Verhalten); die Navigation liegt serverseitig im Root-Layout und greift, sobald eine Sitzung vorhanden ist.
|
||||
|
||||
## Prüfung 3: `portal`-App unverändert
|
||||
`portal` (Port 3900) weiterhin mit eigener `ModuleGrid`-Startseite, kein `ModuleNavShell` eingezogen (Akzeptanzkriterium 5) — Build und Betrieb unverändert geprüft.
|
||||
|
||||
## Ergebnis
|
||||
Alle Akzeptanzkriterien erfüllt. Bekannter, dokumentierter Kompromiss: statische statt live-rechtegefilterte Modul-Liste (siehe Ticket, Abschnitt "Bekannter Kompromiss").
|
||||
@@ -1,5 +1,5 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Extraktions-Korrektur",
|
||||
@@ -36,7 +36,7 @@ export default function RootLayout({
|
||||
<style>{focusVisibleStyle}</style>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="ai_extraction">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Zusammenfassungen",
|
||||
@@ -36,7 +36,7 @@ export default function RootLayout({
|
||||
<style>{focusVisibleStyle}</style>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="ai">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Verbindungsstatus",
|
||||
@@ -14,7 +14,7 @@ export default function RootLayout({
|
||||
<html lang="de">
|
||||
<head>
|
||||
{/* Muster aus UX-07/UX-08 (web/mail-search): sichtbare Fokusmarkierung
|
||||
und Theme-Variablen vor dem ersten Paint, auch ohne @nexarch/shl-Shell. */}
|
||||
und Theme-Variablen vor dem ersten Paint. */}
|
||||
<style>{focusVisibleStyles}</style>
|
||||
<Script id="shl-theme-init" strategy="beforeInteractive">
|
||||
{themeInitScript()}
|
||||
@@ -30,7 +30,7 @@ export default function RootLayout({
|
||||
>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="connect">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH DATEV-Export",
|
||||
@@ -28,7 +28,7 @@ export default function RootLayout({
|
||||
>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="connect_datev">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import "../lib/messages";
|
||||
import { AccountBar } from "../components/AccountBar";
|
||||
|
||||
@@ -37,10 +37,10 @@ export default function RootLayout({
|
||||
>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale={locale}>
|
||||
<ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="dms">
|
||||
<AccountBar />
|
||||
{children}
|
||||
</ToastProvider>
|
||||
</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography } from "@nexarch/shl";
|
||||
import { AccountBar } from "../components/AccountBar";
|
||||
|
||||
export const metadata = {
|
||||
@@ -22,10 +22,10 @@ export default function RootLayout({
|
||||
>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="dms_upload">
|
||||
<AccountBar />
|
||||
{children}
|
||||
</ToastProvider>
|
||||
</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Mail-Suche",
|
||||
};
|
||||
|
||||
// UX-08 Pflichtprüfung 3: sichtbare Fokusmarkierung in allen Kernansichten.
|
||||
// Diese App nutzt (anders als die Module mit @nexarch/shl-Shell) keine
|
||||
// Navigations-Shell, daher fehlt deren globale :focus-visible-Regel —
|
||||
// hier eigenständig ergänzt, gleicher Token wie Shell.tsx.
|
||||
// UX-08 Pflichtprüfung 3: sichtbare Fokusmarkierung in allen Kernansichten,
|
||||
// zusaetzlich zur globalen Regel aus der Shell (SHL-04), falls Inhalte
|
||||
// ausserhalb von ModuleNavShell gerendert werden.
|
||||
const focusVisibleStyle = `
|
||||
:focus-visible {
|
||||
outline: 3px solid var(--shl-color-focus-ring, #1D4ED8);
|
||||
@@ -43,7 +42,7 @@ export default function RootLayout({
|
||||
<style>{focusVisibleStyle}</style>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="mail">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -42,24 +42,53 @@ function policyAuthorize(base: string, token: string): Authorize {
|
||||
};
|
||||
}
|
||||
|
||||
// Jedes Modul-Frontend laeuft auf seinem eigenen Ursprung (eigener Port) und
|
||||
// ruft diese read-only Liste per ModuleNavShell auf, um die globale
|
||||
// Navigation zu fuellen (SHL-01 Akzeptanzkriterium 1). Nur die Modul-Origins
|
||||
// aus genau derselben Registry duerfen antworten lesen -- keine generische
|
||||
// Wildcard-Freigabe.
|
||||
function allowedOrigins(): string[] {
|
||||
return loadModuleRegistry().map((m) => new URL(m.href).origin);
|
||||
}
|
||||
|
||||
function corsHeaders(request: NextRequest): Record<string, string> {
|
||||
const origin = request.headers.get("origin");
|
||||
if (!origin) return {};
|
||||
const originWithHost = (() => {
|
||||
try {
|
||||
const u = new URL(origin);
|
||||
if (u.hostname === "localhost") return origin;
|
||||
u.hostname = "localhost";
|
||||
return u.origin;
|
||||
} catch {
|
||||
return origin;
|
||||
}
|
||||
})();
|
||||
if (allowedOrigins().includes(origin) || allowedOrigins().includes(originWithHost)) {
|
||||
return { "Access-Control-Allow-Origin": origin, Vary: "Origin" };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
// Backend-for-Frontend: haelt das Dienst-Token fuer policy-api server-
|
||||
// seitig (der Browser bekommt es nie zu Gesicht, gleiches Prinzip wie
|
||||
// tenant-admins Proxy-Route). Core entscheidet ueber Rolle UND Feature-
|
||||
// Flag in einem Aufruf (AuthorizeRequest), das Portal trifft keine
|
||||
// eigene zweite Entscheidung (Akzeptanzkriterium 2).
|
||||
export async function GET(request: NextRequest) {
|
||||
const headers = corsHeaders(request);
|
||||
const tenant = request.nextUrl.searchParams.get("tenant");
|
||||
const role = request.nextUrl.searchParams.get("role");
|
||||
if (!tenant || !role) {
|
||||
return NextResponse.json({ error: "'tenant' und 'role' sind erforderlich" }, { status: 400 });
|
||||
return NextResponse.json({ error: "'tenant' und 'role' sind erforderlich" }, { status: 400, headers });
|
||||
}
|
||||
|
||||
const base = process.env.PORTAL_POLICY_API_URL;
|
||||
const token = process.env.PORTAL_POLICY_SERVICE_TOKEN;
|
||||
if (!base || !token) {
|
||||
return NextResponse.json({ error: "Portal nicht konfiguriert" }, { status: 503 });
|
||||
return NextResponse.json({ error: "Portal nicht konfiguriert" }, { status: 503, headers });
|
||||
}
|
||||
|
||||
const modules = await filterVisibleModules(loadModuleRegistry(), tenant, role, policyAuthorize(base, token));
|
||||
return NextResponse.json({ modules });
|
||||
return NextResponse.json({ modules }, { headers });
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Rechteverwaltung",
|
||||
@@ -21,7 +21,7 @@ export default function RootLayout({
|
||||
>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="rbac_admin">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Routing-Regeln",
|
||||
@@ -36,7 +36,7 @@ export default function RootLayout({
|
||||
<style>{focusVisibleStyle}</style>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="routing_rules">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
"use client";
|
||||
|
||||
// Globale Navigation ueber alle Modul-Frontends (SHL-01 Akzeptanzkriterium 1).
|
||||
// Statische Registry statt Live-Abruf: die Modul-Frontends dieses Testhosts
|
||||
// nutzen unterschiedliche, noch nicht vereinheitlichte Anmeldemodelle
|
||||
// (Core-Sitzung vs. feste Demo-Kennung per Umgebungsvariable) -- ein
|
||||
// gemeinsamer Live-Abruf ueber /api/visible-modules wuerde fuer die Haelfte
|
||||
// der Module eine Core-Anmeldung voraussetzen, die sie bisher nicht haben.
|
||||
// Dieselbe Liste wie web/portal/config/modules.json, hier dupliziert, weil
|
||||
// jedes Modul sein eigenes, unabhaengig gebautes Next.js-Bundle ist.
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { Shell } from "./Shell";
|
||||
|
||||
export interface ModuleRegistryEntry {
|
||||
key: string;
|
||||
label: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export const MODULE_REGISTRY: ModuleRegistryEntry[] = [
|
||||
{ key: "dms", label: "Dokumente", href: "http://localhost:3101" },
|
||||
{ key: "dms_upload", label: "Dokumente hochladen", href: "http://localhost:3107" },
|
||||
{ key: "mail", label: "Postfach", href: "http://localhost:3102" },
|
||||
{ key: "workflow", label: "Aufgaben & Freigaben", href: "http://localhost:3103" },
|
||||
{ key: "workflow_cases", label: "Akten & Vorgänge", href: "http://localhost:3108" },
|
||||
{ key: "routing_rules", label: "Routing-Regeln", href: "http://localhost:3109" },
|
||||
{ key: "ai", label: "KI-Funktionen", href: "http://localhost:3104" },
|
||||
{ key: "ai_extraction", label: "Extraktions-Korrektur", href: "http://localhost:3110" },
|
||||
{ key: "connect", label: "Externe Anbindungen", href: "http://localhost:3105" },
|
||||
{ key: "connect_datev", label: "DATEV-Export", href: "http://localhost:3111" },
|
||||
{ key: "tenant_admin", label: "Mandantenverwaltung", href: "http://localhost:3106" },
|
||||
{ key: "rbac_admin", label: "Rollen & Rechte", href: "http://localhost:3112" },
|
||||
{ key: "portal", label: "Übersicht", href: "http://localhost:3900" },
|
||||
];
|
||||
|
||||
export interface ModuleNavShellProps {
|
||||
activeKey: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
function resolveHref(href: string): string {
|
||||
try {
|
||||
const url = new URL(href);
|
||||
if (url.hostname === "localhost" && typeof window !== "undefined" && window.location.hostname !== "localhost") {
|
||||
url.hostname = window.location.hostname;
|
||||
}
|
||||
return url.toString();
|
||||
} catch {
|
||||
return href;
|
||||
}
|
||||
}
|
||||
|
||||
function tenantLabel(): string {
|
||||
if (typeof process === "undefined") return "";
|
||||
return process.env.NEXT_PUBLIC_TENANT_SLUG ?? process.env.NEXT_PUBLIC_MAIL_TENANT_SLUG ?? "demo";
|
||||
}
|
||||
|
||||
export function ModuleNavShell({ activeKey, children }: ModuleNavShellProps) {
|
||||
const modules = MODULE_REGISTRY.map((m) => ({
|
||||
key: m.key,
|
||||
label: m.label,
|
||||
href: resolveHref(m.href),
|
||||
active: m.key === activeKey,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Shell modules={modules} tenantLabel={tenantLabel()}>
|
||||
{children}
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
@@ -11,6 +11,8 @@ export { I18nProvider, useI18n } from "./i18n/i18n";
|
||||
export { registerMessages, formatDate, formatNumber, formatCurrency } from "./i18n/messages";
|
||||
export type { Locale } from "./i18n/messages";
|
||||
|
||||
export { ModuleNavShell } from "./components/ModuleNavShell";
|
||||
export { MODULE_REGISTRY } from "./components/ModuleNavShell";
|
||||
export { Shell } from "./components/Shell";
|
||||
export type { ShellProps, ModuleLink } from "./components/Shell";
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Mandantenverwaltung",
|
||||
@@ -21,7 +21,7 @@ export default function RootLayout({
|
||||
>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="tenant_admin">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Genehmigungen",
|
||||
@@ -36,7 +36,7 @@ export default function RootLayout({
|
||||
<style>{focusVisibleStyle}</style>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="workflow">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Script from "next/script";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
import { ThemeProvider, I18nProvider, ToastProvider, ModuleNavShell, typography, themeInitScript, focusVisibleStyles } from "@nexarch/shl";
|
||||
|
||||
export const metadata = {
|
||||
title: "NEXARCH Akten",
|
||||
@@ -36,7 +36,7 @@ export default function RootLayout({
|
||||
<style>{focusVisibleStyle}</style>
|
||||
<ThemeProvider>
|
||||
<I18nProvider initialLocale="de">
|
||||
<ToastProvider>{children}</ToastProvider>
|
||||
<ToastProvider><ModuleNavShell activeKey="workflow_cases">{children}</ModuleNavShell></ToastProvider>
|
||||
</I18nProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
|
||||
Reference in new issue
Block a user