feat(chrome)!: v0.4.0 — AdminShell + headers as /chrome sub-export

New `./chrome` entrypoint exporting `<AdminShell>` and the header
components (Search, SearchHistory, SearchOptions, Messages, BrowseApps,
HeaderCustomers, HeaderContacts, LogoutButton). Refactored from the
Chronos-style AdminShell that gscCRM was vendoring byte-for-byte —
header/footer/sidebar are now a single shared surface across apps.

Explicit props contract (no site-informations.json, no internal data
sources): `menus`, `apps`, `user`, `brand` are required; `features.*`
flags gate every section (search/browseApps/messages/notifications/
subbar*/pageHeader*/activityPanel/chat/footer); `slots.*` lets apps
inject content; `labels` overrides the next-intl "chrome" namespace.

Locale-aware navigation: chrome calls useLocale() and prepends
/{locale} to internal menu URLs, leaving externals (http(s)://…) and
the "#" sentinel alone. Breadcrumbs and the path-derived page title
strip the leading locale segment so they read "Contacts" not
"En › Contacts". Necessary for `localePrefix: 'always'` consumers like
gscCRM.

Phosphor 2.x icons: `normalizeIconClass` prepends the base `ph` class
(compound selectors `.ph.ph-house:before` require both). All hardcoded
`<i className="ph-…">` sites switched to `ph ph-…`.

`next-intl` and `next-auth` moved to peerDependencies (with devDep
copies for the kit's own typecheck/build). Consumers must symlink their
installed copies into the kit's node_modules at build time — otherwise
useTranslations()/useSession() bind to a separate React context and
next-intl throws Error(void 0) on render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude
2026-05-12 11:24:16 +02:00
parent 387e10b2fb
commit 440f815df7
18 changed files with 2146 additions and 14 deletions

View File

@@ -0,0 +1,46 @@
"use client";
import { signOut } from "next-auth/react";
/**
* Default flow (shared org Keycloak):
* 1. GET /api/auth/logout — host app returns { logoutUrl } pointing at
* Keycloak's end_session endpoint (id_token_hint included).
* 2. next-auth signOut() locally — fires events.signOut for backchannel
* revocation; redirect:false so we control the navigation.
* 3. Navigate to logoutUrl — kills the SSO cookie at Keycloak.
*
* Apps without /api/auth/logout (or that need a different flow) pass
* `onSignOut` to fully replace this behavior.
*/
type LogoutButtonProps = {
label: string;
onSignOut?: () => void | Promise<void>;
};
export function LogoutButton({ label, onSignOut }: LogoutButtonProps) {
const handleLogout = async () => {
if (onSignOut) {
await onSignOut();
return;
}
let logoutUrl = "/logged-out";
try {
const res = await fetch("/api/auth/logout");
const body = await res.json();
if (body?.logoutUrl) logoutUrl = body.logoutUrl;
} catch {
// fall through with local-only logout
}
await signOut({ redirect: false });
window.location.href = logoutUrl;
};
return (
<button type="button" onClick={handleLogout} className="dropdown-item">
<i className="ph-sign-out me-2"></i>
{label}
</button>
);
}