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:
72
src/chrome/labels.ts
Normal file
72
src/chrome/labels.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
"use client";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import type { ChromeLabels } from "./types";
|
||||
|
||||
/**
|
||||
* Default English chrome labels. Used as fallback when next-intl namespace
|
||||
* "chrome" doesn't resolve a key and no per-key override is passed in props.
|
||||
*/
|
||||
export const DEFAULT_CHROME_LABELS: ChromeLabels = {
|
||||
navigation: "Navigation",
|
||||
main: "MAIN",
|
||||
dashboard: "Dashboard",
|
||||
support: "Support",
|
||||
settings: "Settings",
|
||||
allSettings: "All Settings",
|
||||
logout: "Logout",
|
||||
docs: "Docs",
|
||||
browseApps: "Browse apps",
|
||||
viewAll: "View all",
|
||||
activityTitle: "Activity",
|
||||
newNotifications: "New notifications",
|
||||
olderNotifications: "Older notifications",
|
||||
noOlderNotifications: "No older notifications",
|
||||
noNewNotifications: "No new notifications",
|
||||
expandSidebar: "Expand sidebar",
|
||||
collapseSidebar: "Collapse sidebar",
|
||||
closeSidebar: "Close sidebar",
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve chrome labels with three-level precedence:
|
||||
* 1. `overrides` prop (per-key escape hatch)
|
||||
* 2. next-intl namespace "chrome" message
|
||||
* 3. hardcoded English default
|
||||
*
|
||||
* Apps add `"chrome": { ... }` to their next-intl messages to translate.
|
||||
*/
|
||||
export function useChromeLabels(overrides?: Partial<ChromeLabels>): ChromeLabels {
|
||||
let t: ((key: string) => string) | null = null;
|
||||
try {
|
||||
t = useTranslations("chrome");
|
||||
} catch {
|
||||
t = null;
|
||||
}
|
||||
|
||||
const resolved = { ...DEFAULT_CHROME_LABELS };
|
||||
|
||||
if (t) {
|
||||
for (const key of Object.keys(resolved) as (keyof ChromeLabels)[]) {
|
||||
try {
|
||||
const v = t(key);
|
||||
// next-intl returns the key path when the message is missing — treat
|
||||
// that as "no translation" so we fall back to the English default.
|
||||
if (v && v !== key && !v.startsWith("chrome.")) {
|
||||
resolved[key] = v;
|
||||
}
|
||||
} catch {
|
||||
// Missing key — keep the default.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (overrides) {
|
||||
for (const k of Object.keys(overrides) as (keyof ChromeLabels)[]) {
|
||||
const v = overrides[k];
|
||||
if (v != null) resolved[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
Reference in New Issue
Block a user