Files
gsc-web-kit/README.md
Claude 440f815df7 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>
2026-05-12 11:24:16 +02:00

236 lines
8.3 KiB
Markdown

# `@gsc/web-kit`
App skeleton for GSC Next.js frontends. Curates `@limitless/ui` primitives behind a pre-configured layout + auth + data/forms/feedback/navigation stack so apps just write their domain pages.
See the implementation plan in the parent repo for the full module map. This is a `file:` dep consumed by every GSC frontend.
## Install (in a consumer app)
```jsonc
// package.json
{
"dependencies": {
"@gsc/web-kit": "file:../../../templates/gsc-web-kit"
}
}
```
## Layered architecture
```
your app
└── @gsc/web-kit ← this package (layout, auth, data, forms…)
└── @limitless/ui ← Bootstrap-flavoured primitives
└── bootstrap
```
## Sub-exports
```ts
import "@gsc/web-kit/css"; // CSS bundle (layout-3 + JetBrains Mono)
// Chrome — unified header/footer/sidebar shell
import { AdminShell } from "@gsc/web-kit/chrome";
// Lower-level layout primitives
import { AppLayout } from "@gsc/web-kit/layout";
import { useShell } from "@gsc/web-kit/shell";
import { fetchShellConfig } from "@gsc/web-kit/shell/server";
// Auth (NextAuth v5 + Keycloak)
import { createAuth } from "@gsc/web-kit/auth/server";
import { createAuthMiddleware } from "@gsc/web-kit/auth/middleware";
import { signInRedirect } from "@gsc/web-kit/auth";
// Building blocks — curated re-exports from @limitless/ui
import {
Table, DataTable, Pagination, TreeView, Timeline,
Calendar, Gallery, Sortable, ListGroup,
StatWidget, ProgressWidget, ChartWidget, ContentWidget,
} from "@gsc/web-kit/data";
import {
FormGroup, FormControl, FormCheck, Select, InputGroup,
SelectSingle, MultiSelect, TagsSelect, AsyncSelect,
DatePicker, ColorPicker, TagInput, FileUpload,
Slider, Rating, DualListBox, ImageCropper, Wizard, Stepper,
// validation
useValidation, useFieldValidation, required, email, password,
noInjection, europeanAddress, /* …etc */
} from "@gsc/web-kit/forms";
import {
Alert, Toast, Notification, Modal, Offcanvas,
Popover, Tooltip, SweetAlert, Spinner,
Progress, ProgressStacked, IdleTimeout, FAB,
} from "@gsc/web-kit/feedback";
import {
Breadcrumbs, Nav, Tabs, Pills, Dropdown, ContextMenu,
Scrollspy, PageHeader, Accordion, Collapse, Carousel,
Embed, SyntaxHighlighter, Card, Badge, Button, Media,
} from "@gsc/web-kit/navigation";
import { useDisclosure } from "@gsc/web-kit/utils";
```
The `/api` sub-export is reserved for a future HTTP client helper; it currently re-exports nothing.
## Phases
| Phase | Scope | Status |
|---|---|---|
| 1 | Package scaffold + CSS bundle + sub-export stubs | **done** |
| 2 | layout · auth · shell — usable end-to-end with shell-api | **done** |
| 3 | data · forms — curated re-exports from limitless + validation | **done (v0.3.0)** |
| 4 | feedback · navigation · utils — curated re-exports from limitless | **done (v0.3.0)** |
| 5 | chrome · AdminShell + headers + LogoutButton + nav migrations | **done (v0.4.0)** |
| 5a | Roll out chrome to gscCRM / gscChronos / gscAdmin / gscPortal | in progress |
| 6 | api · HTTP client helper (Bearer injection, 401 → signInRedirect) | planned |
---
## Chrome (`/chrome`) — v0.4.0
Unified `AdminShell` providing navbar, subbar, page-header, sidebar, footer,
optional chat overlay and activity panel. Every app receives the same UI
chrome and toggles features it doesn't use via `features` props.
### Adopting chrome in a new app
1. **Add the dep** (already a `file:` resolve to this kit) and import:
```tsx
// app/[locale]/layout.tsx
import { AdminShell } from "@gsc/web-kit/chrome";
import "@gsc/web-kit/css";
```
2. **Apply nav migrations** to your app's database:
```bash
# In your app's migrations directory, copy the two kit-canonical files verbatim
cp node_modules/@gsc/web-kit/migrations/nav-schema.up.sql apps/<your-app>/migrations/00X_nav_schema.up.sql
cp node_modules/@gsc/web-kit/migrations/nav-apps-seed.up.sql apps/<your-app>/migrations/00Y_nav_apps_seed.up.sql
# Then copy the menu-items template once and adapt to your app's menu
cp node_modules/@gsc/web-kit/migrations/nav-menu-items-template.sql apps/<your-app>/migrations/00Z_nav_menu_items.up.sql
# edit 00Z_… to replace example rows with your app's sidebar/topbar entries
psql "$DATABASE_URL" -f apps/<your-app>/migrations/00X_nav_schema.up.sql
psql "$DATABASE_URL" -f apps/<your-app>/migrations/00Y_nav_apps_seed.up.sql
psql "$DATABASE_URL" -f apps/<your-app>/migrations/00Z_nav_menu_items.up.sql
```
Re-copy `nav-schema` + `nav-apps-seed` on every kit upgrade. The
menu-items file is yours after the first copy — the kit never touches it
again.
3. **Add Prisma** to read the data:
```prisma
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
schemas = ["nav"]
}
generator client {
provider = "prisma-client-js"
previewFeatures = ["multiSchema"]
}
```
Add `"postinstall": "prisma generate"` to package.json. Copy `prisma/`
into the Docker image *before* `npm install` so the generate step sees it.
4. **Wire the layout server component**:
```tsx
const [sidebar, topbar, subbar, apps] = await Promise.all([
getMenuItemsByType("sidebar"),
getMenuItemsByType("topbar"),
getMenuItemsByType("subbar"),
getApps(),
]);
return (
<AdminShell
menus={{ sidebar, topbar, subbar }}
apps={apps}
user={{ displayName, email, avatarUrl }}
brand={{
name: "MyApp",
product: "GoSec MyApp",
logoUrl: "https://assets.gosec.cloud/logos/logo.svg",
websiteUrl: "https://gosec.cloud",
supportUrl: "https://support.gosec.cloud/",
docsUrl: "https://support.gosec.cloud/docs",
copyrightStartYear: 2024,
}}
features={{ chat: false, activityPanel: false }}
>
{children}
</AdminShell>
);
```
### Props reference
`<AdminShell>` is the kit's contract — see `src/chrome/types.ts` for the
authoritative TypeScript definition. Summary:
- **Data**: `menus`, `apps`, `user`, `notificationCount?`, `activity?`
- **Brand** (required): `name`, `product`, `logoUrl`, `websiteUrl`,
`supportUrl`, `docsUrl`, `copyrightStartYear` (+ optional `logoSmallUrl`)
- **`features`** (all optional booleans, sensible defaults):
`search`, `searchHistory`, `searchOptions`, `browseApps`, `messages`,
`notifications`, `subbar`, `subbarSupport`, `subbarSettings`,
`pageHeader`, `pageHeaderCustomers`, `pageHeaderContacts`,
`activityPanel`, `chat`, `footer`
- **`slots`** (ReactNode overrides):
`pageTitle`, `pageHeaderExtras`, `subbarExtras`, `activityPanel`,
`navbarExtras`, `footerExtras`
- **Behavior**: `onSignOut?` (default `next-auth signOut`),
`labels?: Partial<ChromeLabels>` (override individual chrome strings)
### i18n
Chrome's own strings come from next-intl namespace `chrome` with English
fallbacks. Add to your app's `common.json`:
```json
{
"chrome": {
"navigation": "Navigation",
"logout": "Logout",
"support": "Support",
...
}
}
```
Menu item labels (`menu.dashboard`, `menu.accounts`, …) live in your app's
existing translation namespace.
### Semver
Public surface = `<AdminShell>` props + exported types + migration files +
chrome CSS class names.
- **Major** — removed/renamed prop, changed prop shape, removed feature
flag, renamed CSS class, changed migration DDL.
- **Minor** — new optional prop, new feature flag (default off), new
slot, new export, new migration file.
- **Patch** — bugfix, internal refactor.
Deprecations land for one minor release with `console.warn` and a
`CHANGELOG.md` note before removal in the next major. Pin
`"@gsc/web-kit": "^X.Y.Z"` to receive minors automatically.
---
## Notes
- `AppLayout` is a thin wrapper around `<AppShell>` from `@limitless/ui` — they share the `ShellConfig` DTO, so the kit owns the consumer-facing surface without duplicating chrome code.
- All form, data, feedback, and navigation modules are **curated re-exports**: apps should never need to reach into `@limitless/ui` directly.
- The `Widget` family in `/data` is intentionally narrow (`StatWidget`, `ProgressWidget`, `ChartWidget`, `ContentWidget`) — the installed limitless dist has a duplicate `Widget` file/folder collision, so only names exported by both are passed through.