@sybilion/uilib 1.2.9 → 1.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +10 -7
  2. package/dist/esm/components/ui/AppHeader/AppHeader.styl.js +1 -1
  3. package/dist/esm/components/ui/Logo/Logo.js +2 -1
  4. package/dist/esm/components/ui/NavUserHeader/NavUserHeader.js +19 -6
  5. package/dist/esm/components/ui/NavUserHeader/NavUserHeader.styl.js +2 -2
  6. package/dist/esm/components/ui/Sidebar/Sidebar.styl.js +1 -1
  7. package/dist/esm/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.js +8 -2
  8. package/dist/esm/components/widgets/SignInPage/SignInPage.js +2 -2
  9. package/dist/esm/components/widgets/SybilionAppHeader/SybilionAppHeader.js +2 -2
  10. package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl.js +1 -1
  11. package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthLayout.js +3 -8
  12. package/dist/esm/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl.js +2 -2
  13. package/dist/esm/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.js +1 -1
  14. package/dist/esm/contexts/theme-context.js +44 -0
  15. package/dist/esm/docs/lib/theme.js +35 -3
  16. package/dist/esm/index.js +3 -1
  17. package/dist/esm/sybilion-auth/SybilionAuthProvider.js +23 -10
  18. package/dist/esm/types/src/components/ui/Logo/Logo.d.ts +2 -1
  19. package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.d.ts +1 -1
  20. package/dist/esm/types/src/components/ui/NavUserHeader/NavUserHeader.types.d.ts +3 -0
  21. package/dist/esm/types/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.d.ts +4 -1
  22. package/dist/esm/types/src/components/widgets/SignInPage/SignInPage.d.ts +2 -2
  23. package/dist/esm/types/src/components/widgets/SybilionAppHeader/SybilionAppHeader.d.ts +5 -1
  24. package/dist/esm/types/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.d.ts +2 -5
  25. package/dist/esm/types/src/components/widgets/SybilionAuthLayout/index.d.ts +1 -1
  26. package/dist/esm/types/src/contexts/theme-context.d.ts +20 -0
  27. package/dist/esm/types/src/docs/contexts/theme-context.d.ts +1 -10
  28. package/dist/esm/types/src/docs/lib/theme.d.ts +5 -1
  29. package/dist/esm/types/src/docs/pages/SybilionAuthLayoutPage.d.ts +1 -0
  30. package/dist/esm/types/src/index.d.ts +2 -0
  31. package/dist/esm/types/src/sybilion-auth/SybilionAuthProvider.d.ts +10 -1
  32. package/package.json +2 -4
  33. package/src/components/ui/AppHeader/AppHeader.styl +2 -0
  34. package/src/components/ui/Logo/Logo.tsx +2 -1
  35. package/src/components/ui/NavUserHeader/NavUserHeader.styl +2 -20
  36. package/src/components/ui/NavUserHeader/NavUserHeader.styl.d.ts +0 -3
  37. package/src/components/ui/NavUserHeader/NavUserHeader.tsx +40 -30
  38. package/src/components/ui/NavUserHeader/NavUserHeader.types.ts +3 -0
  39. package/src/components/ui/Sidebar/Sidebar.styl +2 -2
  40. package/src/components/widgets/SidebarDatasetsItemsGrouped/SidebarDatasetsItemsGrouped.tsx +14 -3
  41. package/src/components/widgets/SignInPage/SignInPage.tsx +1 -3
  42. package/src/components/widgets/SybilionAppHeader/SybilionAppHeader.tsx +8 -0
  43. package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl +2 -2
  44. package/src/components/widgets/SybilionAuthLayout/SybilionAuthHeadline.styl.d.ts +10 -2
  45. package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl +20 -9
  46. package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.styl.d.ts +16 -2
  47. package/src/components/widgets/SybilionAuthLayout/SybilionAuthLayout.tsx +4 -17
  48. package/src/components/widgets/SybilionAuthLayout/index.ts +0 -1
  49. package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl +3 -0
  50. package/src/components/widgets/SybilionSignInPanel/SybilionSignInPanel.styl.d.ts +12 -2
  51. package/src/contexts/theme-context.tsx +106 -0
  52. package/src/docs/App/ThemeToggle.tsx +1 -1
  53. package/src/docs/DocsShell.tsx +16 -7
  54. package/src/docs/components/DocsSidebar/DocsSidebar.tsx +1 -0
  55. package/src/docs/contexts/theme-context.tsx +8 -68
  56. package/src/docs/index.tsx +1 -1
  57. package/src/docs/lib/theme.ts +13 -2
  58. package/src/docs/pages/ChartAreaInteractivePage.tsx +2 -2
  59. package/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.styl +1 -1
  60. package/src/docs/pages/StandaloneAppLayoutPage/StandaloneAppLayoutPage.tsx +5 -4
  61. package/src/docs/pages/SybilionAuthLayoutPage.tsx +47 -0
  62. package/src/docs/registry.ts +3 -3
  63. package/src/index.ts +2 -0
  64. package/src/sybilion-auth/SybilionAuthProvider.tsx +34 -8
  65. package/assets/standalone-global.css +0 -257
  66. package/dist/esm/docs/contexts/theme-context.js +0 -14
  67. package/dist/esm/types/src/docs/pages/SybilionAuthProviderPage.d.ts +0 -1
  68. package/docs/standalone-apps.md +0 -552
  69. package/src/docs/pages/SybilionAuthProviderPage.tsx +0 -40
@@ -1,552 +0,0 @@
1
- # Standalone Sybilion apps (@sybilion/uilib)
2
-
3
- Greenfield SPA on **your own origin**: `@sybilion/uilib` for layout/UI, `SybilionAuthProvider` + Sybilion API for data—no iframe in the main client.
4
-
5
- **Agents / humans:** `AppShell` + `AppShellMainContent`; spacing via uilib primitives (e.g. `Gap`), not ad hoc gutters. **Each `<Route>` page:** follow **§4 Route page body** (one canonical pattern below); **Discovering** explains how to find other `@sybilion/uilib` exports.
6
-
7
- **Local-first:** After scaffolding, the user should run **`yarn dev`** (or `npm run dev`) on **localhost** with **no deploy** (e.g. Vercel) required. Commit **`.env.example`**; the user copies it to **`.env`**. In development, the Sybilion API is reached via a **same-origin `/api` proxy** so the browser avoids CORS; production builds still use `VITE_SYBILION_API_BASE_URL` on the client, and the real API must allow **CORS** for your deployed `Origin` unless you terminate API calls on the same host.
8
-
9
- ## 1. Dependencies and global CSS
10
-
11
- Vite-based apps (recommended for new standalone SPAs):
12
-
13
- ```bash
14
- yarn add react react-dom react-router-dom @auth0/auth0-react @sybilion/uilib @sybilion/sdk
15
- yarn add -D vite @vitejs/plugin-react
16
- ```
17
-
18
- **React ecosystem versions (avoid collisions):** declare **`react`**, **`react-dom`**, **`react-router-dom`**, and **`@auth0/auth0-react`** as **direct** dependencies and align them with **`@sybilion/uilib`**, not arbitrary latest majors.
19
-
20
- 1. After installing uilib, open **`node_modules/@sybilion/uilib/package.json`** (or this repo’s root **`package.json`** at the release you pin).
21
- 2. **`peerDependencies`** — satisfy these ranges in your app (`react` / `react-dom`, `react-router-dom`, `@auth0/auth0-react`, and **`vite`** when you use the Vite helpers).
22
- 3. **`devDependencies`** — for **`react`**, **`react-dom`**, and **`react-router-dom`**, prefer **the same versions uilib lists there** (what its docs build and tests run against). Re-read this file whenever you bump **`@sybilion/uilib`** so your app does not drift to another React major while uilib does not.
23
-
24
- Mismatched or duplicated React (two copies in the bundle) causes **invalid hook call** / subtle runtime bugs — and specifically causes **Radix UI interactive widgets** (dropdowns, dialogs, tooltips) to **silently fail** (context broken across the module boundary). If Yarn/npm hoists a second React, fix upstream ranges or use **`resolutions`** (Yarn) / **`overrides`** (npm) so the tree resolves to **one** `react` / `react-dom` pair matching (3). Also add `dedupe` + `@radix-ui/` alias in `vite.config.ts` (see **Local dev: Vite API proxy**).
25
-
26
- **Current required versions (as of uilib 1.2.x):** `react` / `react-dom` **`^19`**, `@types/react` / `@types/react-dom` **`^19`**. Add resolutions so nested copies stay aligned:
27
-
28
- ```json
29
- {
30
- "resolutions": {
31
- "@types/react": "^19",
32
- "@types/react-dom": "^19"
33
- }
34
- }
35
- ```
36
-
37
- Import tokens/fonts once (typically `src/main.tsx`):
38
-
39
- ```ts
40
- import '@sybilion/uilib/standalone-global.css';
41
- ```
42
-
43
- ### Branding (static SVGs + optional tab icon)
44
-
45
- **Header + auth hero:** Copy packaged SVGs into **`public/`** so **`Logo`** (**`SYBILION_STANDALONE_LOGO_PUBLIC_URL`** → **`/logo.svg`**) and **`SybilionAuthLayout`** (**`SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL`** → **`/sybilion_bg.svg`**) resolve at runtime. Package paths: **`@sybilion/uilib/logo.svg`**, **`@sybilion/uilib/sybilion-bg.svg`**.
46
-
47
- ```bash
48
- mkdir -p public \
49
- && cp node_modules/@sybilion/uilib/logo.svg public/logo.svg \
50
- && cp node_modules/@sybilion/uilib/sybilion-bg.svg public/sybilion_bg.svg
51
- ```
52
-
53
- Logo source in the package: **`src/assets/logo.svg`** (**cyan** glyph).
54
-
55
- **Browser tab (optional):** The favicon (tiny icon next to the tab title) and the document title come from **`index.html`**, not from React. **`Logo`** does not set them. To replace Vite’s default tab icon with the package logo, add **`href="/logo.svg"`** — same as **`SYBILION_STANDALONE_LOGO_PUBLIC_URL`** from **`@sybilion/uilib`** — plus **`<title>`** in **`index.html`** `<head>`:
56
-
57
- ```html
58
- <link rel="icon" href="/logo.svg" type="image/svg+xml" />
59
- <title>Your app name</title>
60
- ```
61
-
62
- Mount the tree with `ReactDOM.createRoot` → `App` (wrap with `StrictMode` if you want).
63
-
64
- ### `package.json` scripts (required)
65
-
66
- Add a **`dev`** script so the app is runnable immediately after clone. Typical Vite setup:
67
-
68
- ```json
69
- {
70
- "scripts": {
71
- "dev": "vite",
72
- "build": "vite build",
73
- "preview": "vite preview"
74
- }
75
- }
76
- ```
77
-
78
- ### `.env.example`
79
-
80
- Commit **`.env.example`** at the app root (no secrets). Minimum variables:
81
-
82
- | Variable | Purpose |
83
- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
84
- | `PORT` | Vite **dev** and **preview** server port. **`PORT=3000`** matches the current **Auth0 test tenant** (`http://localhost:3000` callback / web origins). Override if your tenant uses another port. |
85
- | `VITE_SYBILION_API_BASE_URL` | Real Sybilion API origin (no trailing slash). Used as **proxy target** in dev and as SDK `baseUrl` in production builds. |
86
- | `VITE_AUTH0_DOMAIN` | Auth0 domain (§3). |
87
- | `VITE_AUTH0_CLIENT_ID` | Auth0 SPA client id (§3). |
88
-
89
- Example **`.env.example`** content for the app root:
90
-
91
- ```env
92
- # Copy to `.env` and fill in values. Do not commit `.env`.
93
- # PORT: Vite dev/preview bind (sybilionStandaloneViteDev reads this). 3000 matches Auth0 test tenant localhost URLs.
94
- PORT=3000
95
-
96
- VITE_SYBILION_API_BASE_URL=https://api-dev.sybilion.com
97
- VITE_AUTH0_DOMAIN=your-tenant.eu.auth0.com
98
- VITE_AUTH0_CLIENT_ID=your-spa-client-id
99
- ```
100
-
101
- ## 2. SDK (`@sybilion/sdk`)
102
-
103
- Typed HTTP client for the Sybilion API. Env vars depend on bundler; for Vite, only `import.meta.env.VITE_*` reaches the client.
104
-
105
- Create **one** instance at module scope (e.g. `src/libs/sybilion-sdk.ts`) so React never reconstructs the client (`useMemo` not needed), and reuse it for auth + data:
106
-
107
- ```ts
108
- import { createSybilionSDK } from '@sybilion/sdk';
109
-
110
- export const sybilionJwtStorageKey = 'sybilion.standalone.jwt';
111
-
112
- export const sybilionSdk = createSybilionSDK({
113
- baseUrl: import.meta.env.DEV
114
- ? ''
115
- : (import.meta.env.VITE_SYBILION_API_BASE_URL as string),
116
- apiPrefix: '/api',
117
- getToken: () =>
118
- typeof localStorage !== 'undefined'
119
- ? (localStorage.getItem(sybilionJwtStorageKey) ?? undefined)
120
- : undefined,
121
- });
122
- ```
123
-
124
- **Options:** `baseUrl` — API origin only (no trailing slash) **in production**; in **development** use `''` so requests stay **same-origin** (`/api/v1/...`) and the Vite dev server **proxies `/api`** to `VITE_SYBILION_API_BASE_URL` (see **Local dev: Vite API proxy** below). `apiPrefix` — default `'/api'` so calls go to `{baseUrl}/api/v1/...`. `getToken` — must read the same key you pass as `sybilionTokenStorageKey` on `SybilionAuthProvider` (§3).
125
-
126
- ```ts
127
- import { sybilionSdk } from './libs/sybilion-sdk';
128
-
129
- await sybilionSdk.raw.datasets.getById(datasetId);
130
- ```
131
-
132
- - `sybilionSdk.auth` — `loginWithAuth0Identity`, `getMe`, `updateMe` (Auth0 bootstrap + user profile).
133
- - `sybilionSdk.raw` — thin `GET`/`POST`/… wrappers for `/v1/...` paths (e.g. `raw.analyses.driversMapOnce`; parsed JSON, no app-specific shaping).
134
- - `sybilionSdk.resources` — higher-level helpers (datasets, drivers, subscriptions) on top of `raw`.
135
-
136
- Package README: [`@sybilion/sdk`](https://www.npmjs.com/package/@sybilion/sdk) — monorepo: [`../../sdk/README.md`](../../sdk/README.md).
137
-
138
- ## Local dev: Vite API proxy
139
-
140
- Avoid browser CORS in development by serving the SPA from Vite and proxying **`/api`** to the real API.
141
-
142
- **Reference `vite.config.ts` (minimal standalone Sybilion SPA):** merge your own `plugins` / options into this shape; keep `resolve.dedupe` and the `@radix-ui/` alias unless you know the dependency tree dedupes React and Radix correctly on its own.
143
-
144
- - **`sybilionStandaloneViteDev`** (`@sybilion/uilib/vite-standalone-dev`) — reads **`PORT`** (defaults **3000** if unset or invalid) for **`server`** and **`preview`** bind; sets **`proxy['/api']`** → **`VITE_SYBILION_API_BASE_URL`** with `changeOrigin` and `secure: true`. Pass Vite’s **`mode`** so env (including `VITE_*`) and proxy target resolve the same way as `vite` / `vite build`.
145
- - **`defineConfig(({ mode }) => …)`** — callback form gives `mode` per command (`development` / `production` / …); forward it into `sybilionStandaloneViteDev`.
146
- - **`plugins: [react()]`** — add other plugins (e.g. SVGR) in the same array.
147
- - **`resolve.dedupe`** — force one physical copy of `react`, `react-dom`, `react-router`, and `react-router-dom`. If the app and `@sybilion/uilib` resolve different copies, React context and router state break (`Invalid hook call`, blank subtree, Radix menus that never open). `react-router` is listed because it is a shared transitive dependency of the router packages and can duplicate independently.
148
- - **`resolve.alias` for `@radix-ui/*`** — uilib’s published bundle uses bare `@radix-ui/react-*` imports. Yarn/npm can still place those packages under `node_modules/@sybilion/uilib/node_modules/@radix-ui/…`, so Vite may bundle two Radix trees and split React context across them. The regex alias rewrites every `@radix-ui/…` import to **`./node_modules/@radix-ui/`** (project root). **`path.resolve(… ) + '/'`** is required so subpaths like `@radix-ui/react-dialog` resolve under that folder.
149
- - **`path`** — Node built-in `node:path`; only used at config evaluation time.
150
-
151
- ```ts
152
- // vite.config.ts — reference config for a standalone app using @sybilion/uilib + proxy.
153
- import { sybilionStandaloneViteDev } from '@sybilion/uilib/vite-standalone-dev';
154
- import react from '@vitejs/plugin-react';
155
- import path from 'node:path';
156
- import { defineConfig } from 'vite';
157
-
158
- // Pass `mode` into sybilionStandaloneViteDev so proxy + VITE_* match the active Vite command.
159
- export default defineConfig(({ mode }) => ({
160
- ...sybilionStandaloneViteDev({ mode }),
161
- plugins: [react()],
162
- resolve: {
163
- // One copy of React + Router across app and uilib (avoids invalid hook call / dead Radix UI).
164
- dedupe: ['react', 'react-dom', 'react-router', 'react-router-dom'],
165
- alias: [
166
- {
167
- find: /^@radix-ui\//,
168
- // Pin all Radix imports to the app’s root node_modules; trailing slash keeps subpaths working.
169
- replacement: path.resolve('./node_modules/@radix-ui/') + '/',
170
- },
171
- ],
172
- },
173
- }));
174
- ```
175
-
176
- Combine with an **empty `baseUrl` in dev** in the SDK module (§2). In **`preview`** builds, keep **`.env`** with **`VITE_SYBILION_API_BASE_URL`** so `vite preview` can still proxy API calls locally.
177
-
178
- ## Discovering `@sybilion/uilib` components (agents)
179
-
180
- Prefer **`@sybilion/uilib`** over bespoke layout/controls.
181
-
182
- 1. **Barrel:** uilib repo `src/index.ts`, or **`node_modules/@sybilion/uilib/dist/esm/types/index.d.ts`** after install — scan `export * from './components/ui/...'`.
183
- 2. **Page cluster:** `PageScroll`, `AppShell`, `PageHeader`, `PageFooter`, tabs/columns, **`SybilionAppHeader`** (workspace switcher + **`NavUserHeader`**), etc. come from the same package; **what to compose and when** lives only in **§4 Route page body** (not repeated here).
184
- 3. **Examples:** `src/docs/pages/*Page.tsx`; **`PagePage`** (`slug page`) — minimal **`PageContent` + `PageContentSection`**.
185
-
186
- ## Local dev: apps with a Go server
187
-
188
- Some templates include a **Go** server. This repo does **not** provide Go middleware. The contract: whatever serves the **same origin the browser uses for the SPA** must **reverse-proxy** path prefix **`/api`** (or your SDK `apiPrefix`) to the Sybilion API. Use **`baseUrl: ''`** in dev when those requests are same-origin. Name and document server-side env vars (e.g. API upstream URL) in the Go project.
189
-
190
- ## 3. Auth (`SybilionAuthProvider`)
191
-
192
- Use inside `BrowserRouter` if redirects hit a callback route.
193
-
194
- Wire the SDK module from §2 — no second `createSybilionSDK` here:
195
-
196
- ```tsx
197
- import type { ReactNode } from 'react';
198
-
199
- import { SybilionAuthProvider } from '@sybilion/uilib';
200
-
201
- import { sybilionJwtStorageKey, sybilionSdk } from './libs/sybilion-sdk';
202
-
203
- export function AppProviders({ children }: { children: ReactNode }) {
204
- const auth0Domain = import.meta.env.VITE_AUTH0_DOMAIN as string;
205
- const auth0ClientId = import.meta.env.VITE_AUTH0_CLIENT_ID as string;
206
-
207
- return (
208
- <SybilionAuthProvider
209
- sdk={sybilionSdk}
210
- sybilionTokenStorageKey={sybilionJwtStorageKey}
211
- auth0Domain={auth0Domain}
212
- auth0ClientId={auth0ClientId}
213
- redirectUri={window.location.origin}
214
- >
215
- {children}
216
- </SybilionAuthProvider>
217
- );
218
- }
219
- ```
220
-
221
- **Flow:** Auth0 SPA → `sybilionSdk.auth.loginWithAuth0Identity(<Auth0 AT>)` → Sybilion JWT (`data.token` / `token`) persisted → same `sybilionSdk` + `getToken` attach Bearer on requests; `useSybilionApiFetch()` uses `getSybilionApiOriginFromSdk(sdk)` for URLs (paths like `/api/v1/...`).
222
-
223
- **Defaults** for `authorizationParams` match sybilion-client (Management audience + `openid profile email offline_access …`); override if your Auth0 SPA needs a Resource Server audience (backend confirms).
224
-
225
- | Layer | Configure |
226
- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
227
- | **Auth0** | Callback, logout, and web origins → your URLs (+ previews). |
228
- | **Sybilion API** | CORS → your deploy `Origin`. |
229
- | **App** | §2 SDK module (`baseUrl`, `apiPrefix`, `getToken`); pass `sdk` + matching `sybilionTokenStorageKey`; Auth0 `domain` / `clientId`; redirect usually `window.location.origin`. |
230
-
231
- **Hooks:** `useSybilionAuth()`, `useSybilionApiFetch()` (or `createSybilionApiFetch` / `sybilionApiFetch` helpers).
232
-
233
- ### Loading the user profile for `SybilionAppHeader`
234
-
235
- `useSybilionAuth()` gives you `isAuthenticated` / `logout` but **not** a user object. Fetch it once with `sybilionSdk.auth.getMe()` after authentication. The response shape is:
236
-
237
- ```ts
238
- // GET /api/v1/users/me → { data: { user: BackendUser }, message, status }
239
- // BackendUser includes `picture` (profile image URL from the API). Do not read deprecated `avatar` on the wire.
240
- ```
241
-
242
- **Agents:** import **`MeResponse`** (and rely on **`BackendUser`** inside it) from `@sybilion/sdk`. Profile image on the API is **`picture` only**; `NavUserHeader` / `SybilionAppHeader` still expect the prop key **`avatar`**—map with `avatar: u.picture ?? ''`. No `UserProfile` intersections, no `u.avatar` fallback, no manual `res as { data?: … }` once `getMe()` is typed.
243
-
244
- `NavUserHeader` (used inside `SybilionAppHeader`) accepts `user: { name, email, avatar? }`.
245
-
246
- ```ts
247
- import type { MeResponse } from '@sybilion/sdk';
248
-
249
- useEffect(() => {
250
- if (!isAuthenticated) {
251
- setUser(null);
252
- return;
253
- }
254
- sybilionSdk.auth
255
- .getMe()
256
- .then((res: MeResponse) => {
257
- const u = res.data?.user;
258
- if (u)
259
- setUser({
260
- name: u.name,
261
- email: u.email,
262
- avatar: u.picture ?? '',
263
- });
264
- })
265
- .catch(() => undefined);
266
- }, [isAuthenticated]);
267
- ```
268
-
269
- Pass `user` (or `null` while loading with `isLoading` on `NavUserHeader`) and `isAuthenticated` to `SybilionAppHeader`.
270
-
271
- ### Sign-in page (unauthenticated)
272
-
273
- Agents building the Auth0 entry route:
274
-
275
- 1. **Public assets:** Same **`public/`** SVG setup as §1 _Branding (static SVGs + optional tab icon)_ (**`/sybilion_bg.svg`** for **`SYBILION_STANDALONE_AUTH_HERO_BG_PUBLIC_URL`** / **`SybilionAuthLayout`**).
276
-
277
- 2. **Routing:** Register **`/sign-in`** (and your Auth0 **`/callback`** route if applicable) **outside** **`AppLayout`** — full viewport auth chrome must not sit under **`AppShell`** / sidebar / **`SybilionAppHeader`**. Pattern: top-level `<Routes>` with `<Route path="/sign-in" … />` as a sibling of the branch that renders **`AppLayout`**, both wrapped by **`SybilionAuthProvider`** (§3).
278
-
279
- 3. **Composition:**
280
- - **`SignInPage`** (`@sybilion/uilib`) — drop-in when **`SybilionAuthProvider`** wraps the tree. Calls **`useSybilionAuth`**. **`loginWithRedirect`** merges defaults (`prompt`, `screen_hint`, `origin`) with optional **`loginRedirectOptions`** / **`authorizationParams`** for tenant-specific **`connection`** or audiences.
281
- - **Custom:** compose **`SybilionAuthLayout`** + **`SybilionSignInPanel`** and wire **`loginWithRedirect`** yourself from **`useSybilionAuth`**.
282
-
283
- 4. **Footer chip:** Pass **`versionLabel`** into **`SignInPage`** (or **`SybilionSignInPanel`**) when you want **`v…`** linked to **`releasesTo`** (default **`/releases`**).
284
-
285
- ## 4. Layout (AppShell)
286
-
287
- With §2 `sybilionSdk` and §3 `AppProviders` / `SybilionAuthProvider` defined, compose routing + shell so Auth0 callbacks and JWT-backed hooks wrap the whole UI.
288
-
289
- ### Root wiring (`App.tsx`)
290
-
291
- Order outside → in: `BrowserRouter` (callbacks) → `AppProviders` (§3) → `SidebarProvider` (sidebar width / open state for `Sidebar` primitives) → `AppLayout` → `Routes`.
292
-
293
- `AppLayout` renders the persistent chrome (sidebar, header, footer) and the active route renders inside its main column via `children`. Add `HomePage` / `DatasetsPage` as your own page components; register an Auth0 callback route here if `SybilionAuthProvider` uses a dedicated path.
294
-
295
- ```tsx
296
- import { BrowserRouter, Route, Routes } from 'react-router-dom';
297
-
298
- import { SidebarProvider } from '@sybilion/uilib';
299
-
300
- import { AppLayout } from './AppLayout';
301
- import { AppProviders } from './AppProviders';
302
- import { DatasetsPage } from './pages/DatasetsPage';
303
- import { HomePage } from './pages/HomePage';
304
-
305
- export function App() {
306
- return (
307
- <BrowserRouter>
308
- <AppProviders>
309
- <SidebarProvider
310
- sidebarWidthStorageKey="myapp.sidebarWidthPx"
311
- persistSidebarWidthWithoutConsent
312
- >
313
- <AppLayout>
314
- <Routes>
315
- <Route path="/" element={<HomePage />} />
316
- <Route path="/datasets" element={<DatasetsPage />} />
317
- </Routes>
318
- </AppLayout>
319
- </SidebarProvider>
320
- </AppProviders>
321
- </BrowserRouter>
322
- );
323
- }
324
- ```
325
-
326
- `SidebarProvider`: pass only the props you need. `sidebarWidthStorageKey` namespaces width in localStorage. For production apps with cookie-based consent, drop `persistSidebarWidthWithoutConsent` and pass `userId` so width persistence follows your consent rules.
327
-
328
- ### `AppLayout` (sidebar + main + `children`)
329
-
330
- `AppSidebar` is a sibling of `AppShellMainContent` inside `AppShell`. The matched route (`<Routes>` from `App.tsx`) renders as `{children}` in the main column.
331
-
332
- **Header row:** pass **`header={<AppHeaderHost />}`** to **`AppShellMainContent`**, then **`SybilionAppHeader`** as the **first** child before `{children}`. **`SybilionAppHeader`** portals workspace switcher + embedded **`NavUserHeader`** into that shell header (and **`page-header-actions`** so **`PageHeader`** toolbars line up).
333
-
334
- Props: all **`WorkspaceAppSwitcher`** props plus all **`NavUserHeader`** props (`user`, **`menuItems`** as **`DropdownMenuItem`** rows, **`theme`**, **`onLogout`**, etc.), and optional **`pageHeaderId`**, **`actionsAnchorId`**, **`actionsAnchorClassName`**.
335
-
336
- ```tsx
337
- import type { ReactNode } from 'react';
338
- import { useLocation, useNavigate } from 'react-router-dom';
339
-
340
- import {
341
- AppHeaderHost,
342
- AppShell,
343
- AppShellMainContent,
344
- DropdownMenuItem,
345
- PageFooter,
346
- PageScroll,
347
- SybilionAppHeader,
348
- } from '@sybilion/uilib';
349
-
350
- import { AppSidebar } from './AppSidebar';
351
-
352
- export function AppLayout({ children }: { children: ReactNode }) {
353
- const location = useLocation();
354
- const navigate = useNavigate();
355
-
356
- return (
357
- <PageScroll>
358
- <AppShell>
359
- <AppSidebar />
360
-
361
- <AppShellMainContent
362
- header={<AppHeaderHost />}
363
- footer={<PageFooter versionLink="/releases" versionLabel="0.0.1" />}
364
- >
365
- <SybilionAppHeader
366
- pathname={location.pathname}
367
- onNavigate={href => navigate(href)}
368
- authenticated={false}
369
- appsStorageKey="myapp.workspaceApps"
370
- defaultApps={[]}
371
- user={{ name: 'Analyst', email: 'you@example.com', avatar: '' }}
372
- theme="light"
373
- onThemeToggle={() => undefined}
374
- onLogout={() => undefined}
375
- isAuthenticated={false}
376
- menuItems={
377
- <>
378
- <DropdownMenuItem>Account</DropdownMenuItem>
379
- <DropdownMenuItem>Settings</DropdownMenuItem>
380
- </>
381
- }
382
- />
383
- {children}
384
- </AppShellMainContent>
385
- </AppShell>
386
- </PageScroll>
387
- );
388
- }
389
- ```
390
-
391
- Wire **`authenticated`**, **`user`** / **`isAuthenticated`**, **`theme`** / **`onLogout`**, **`menuItems`**, and **`defaultApps`** / **`appsStorageKey`** to real auth and workspace config (`useSybilionAuth`, §3). **`NavUserHeader`** behavior reference: `src/docs/pages/NavUserHeaderPage.tsx` (slug `nav-user-header`). Full shell preview: `src/docs/pages/StandaloneAppLayoutPage` (slug **`standalone-app-layout`**).
392
-
393
- #### Sidebar (`AppSidebar.tsx`)
394
-
395
- App-specific sidebar component — keeps the navigation surface out of `AppLayout` so the shell stays generic. Compose your nav from `@sybilion/uilib` primitives (`Sidebar` + `SidebarContent` + `SidebarGroup` + `SidebarMenu*`) and product widgets like `SidebarDatasetsItemsGrouped` (collapsible groups + nested rows for datasets — see demo `src/docs/pages/SidebarDatasetsItemsGroupedPage.tsx`, slug `sidebar-datasets-items-grouped`).
396
-
397
- ```tsx
398
- import { useEffect, useState } from 'react';
399
- import { NavLink, useNavigate } from 'react-router-dom';
400
-
401
- import {
402
- Sidebar,
403
- SidebarContent,
404
- SidebarDatasetsItemsGrouped,
405
- type SidebarDatasetsItemsGroupedDataset,
406
- SidebarGroup,
407
- SidebarMenu,
408
- SidebarMenuButton,
409
- SidebarMenuItem,
410
- } from '@sybilion/uilib';
411
-
412
- import { sybilionSdk } from './libs/sybilion-sdk';
413
-
414
- export function AppSidebar() {
415
- const navigate = useNavigate();
416
- const [datasets, setDatasets] = useState<
417
- SidebarDatasetsItemsGroupedDataset[]
418
- >([]);
419
- const [selectedDatasetId, setSelectedDatasetId] = useState<number>();
420
-
421
- useEffect(() => {
422
- sybilionSdk.raw.datasetsIndex(1, 50).then(res => {
423
- setDatasets(res?.data?.datasets ?? []);
424
- });
425
- }, []);
426
-
427
- return (
428
- <Sidebar variant="inset" collapsible="offcanvas">
429
- <SidebarContent>
430
- <SidebarGroup>
431
- <SidebarMenu>
432
- <SidebarMenuItem>
433
- <SidebarMenuButton asChild>
434
- <NavLink to="/" end>
435
- Home
436
- </NavLink>
437
- </SidebarMenuButton>
438
- </SidebarMenuItem>
439
- <SidebarMenuItem>
440
- <SidebarMenuButton asChild>
441
- <NavLink to="/datasets">Datasets</NavLink>
442
- </SidebarMenuButton>
443
- </SidebarMenuItem>
444
- </SidebarMenu>
445
- </SidebarGroup>
446
-
447
- <SidebarDatasetsItemsGrouped
448
- groupBy="regions"
449
- datasets={datasets}
450
- selectedDatasetId={selectedDatasetId}
451
- onDatasetClick={id => {
452
- setSelectedDatasetId(id);
453
- navigate(`/datasets/${id}`);
454
- }}
455
- />
456
- </SidebarContent>
457
- </Sidebar>
458
- );
459
- }
460
- ```
461
-
462
- Data loading uses §2 `sybilionSdk` directly — for production swap the inline `useEffect` for your data layer (React Query, SWR, context, etc.). `groupBy` accepts `'regions' | 'target_type' | 'categories'`; the widget owns its expand state and notifies on selection via `onDatasetClick`.
463
-
464
- ### Route page body (`PageHeader`, `PageContent`, `PageContentSection`)
465
-
466
- **Canonical rule for this doc:** the shell (`AppLayout`) owns **global** chrome only. Every **route** (component under `<Routes>`) builds the main column with **`PageHeader` → `PageContent` → `PageContentSection`** (and other uilib primitives inside sections)—not a padded root `<div>`/`<main>` unless nothing fits.
467
-
468
- Pieces:
469
-
470
- | Piece | Role |
471
- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
472
- | **`PageHeader`** | Route title (`h1`), optional **breadcrumbs**, **subheader**, **actions** (toolbar). Renders the shared header chrome (collapsing title on scroll uses `PageContext` from `PageScroll`). |
473
- | **`PageContent`** | Outer wrapper for the scrollable main column body. Use **`variant="clean"`** when you need the alternate spacing preset. |
474
- | **`PageContentSection`** | Vertical **section** blocks inside the page (group related UI; stack multiple sections). Accepts normal `div` props (e.g. `className`, `style`; HTML **`title`** is the native tooltip attribute, not a section heading—pass a real heading element as a child if needed). |
475
-
476
- **Example — `HomePage.tsx` (fragment inside `AppShellMainContent` / `<Routes>`):**
477
-
478
- ```tsx
479
- import { PageContent, PageContentSection, PageHeader } from '@sybilion/uilib';
480
-
481
- export function HomePage() {
482
- return (
483
- <>
484
- <PageHeader
485
- breadcrumbs={[{ label: 'Home', href: '/' }]}
486
- title="Home"
487
- subheader="Short route description."
488
- />
489
- <PageContent>
490
- <PageContentSection>
491
- {/* tables, cards, charts — use uilib components where they exist */}
492
- </PageContentSection>
493
- </PageContent>
494
- </>
495
- );
496
- }
497
- ```
498
-
499
- Set **`breadcrumbSidebarTrigger={false}`** on `PageHeader` only when **no** `SidebarProvider` wraps the tree (rare for standalone apps; default is fine when the sidebar exists).
500
-
501
- ### Full pattern
502
-
503
- Composition: `PageScroll` → `AppShell` → `AppSidebar` → `AppShellMainContent` with **`AppHeaderHost`** + **`SybilionAppHeader`**, `PageFooter`, and the active route as `children`. **`SidebarProvider`** wraps `AppLayout`. **Route main column:** subsection **Route page body** above. Add `Theme` from `@homecode/ui` only when your product uses those primitives alongside uilib.
504
-
505
- ### Greenfield checklist (agents)
506
-
507
- | Step | Deliverable |
508
- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
509
- | React / router | **`react`**, **`react-dom`**, **`react-router-dom`**, **`@auth0/auth0-react`** (and **`vite`** if applicable) aligned with **`@sybilion/uilib`** **`package.json`** — §1 _React ecosystem versions_. |
510
- | Env files | **`.env.example`** (committed) + **`.env`** locally; **`PORT=3000`** recommended for Auth0 test tenant; **`VITE_*`** as in §1 table. |
511
- | Scripts | **`package.json`** includes mandatory **`dev`** (e.g. `"vite"`); optional **`build`**, **`preview`**. |
512
- | Vite proxy | **`vite.config.ts`** spreads **`sybilionStandaloneViteDev({ mode })`**; SDK **`baseUrl`** empty in dev (§2). |
513
- | Files | `src/libs/sybilion-sdk.ts`, `AppProviders.tsx`, `AppLayout.tsx`, `AppSidebar.tsx`, `App.tsx`, `main.tsx`, route pages under e.g. `src/pages/`. |
514
- | Branding | §1 _Branding_: **`public/logo.svg`** + **`public/sybilion_bg.svg`** (one **`mkdir` + two `cp`** block). **`index.html`**: `<title>`; optional **`<link rel="icon">`** for tab icon. |
515
- | Sign-in | §3 _Sign-in page (unauthenticated)_: **`SignInPage`** or **`SybilionAuthLayout`** + **`SybilionSignInPanel`**; **`/sign-in`** not inside **`AppLayout`**; **`SybilionAuthProvider`** wraps router. |
516
- | Pages + UI | **§4 Route page body** (mandatory stack) + **Discovering** (barrel-first; bespoke markup only when no export fits). |
517
- | Auth0 | SPA callback / logout URLs + allowed web origins → **`http://localhost:<PORT>`** for local dev and deploy URLs (and previews). |
518
- | API | **Dev:** proxy handles API traffic (no browser CORS to API). **Prod:** Sybilion backend **CORS** → your deploy `Origin`, unless API is same-origin. |
519
- | Go (if applicable) | Server proxies **`/api`** to Sybilion; SPA dev **`baseUrl`** stays `''` when same-origin. |
520
-
521
- ### Glossary (high-use pieces)
522
-
523
- | Component / API | What it is for |
524
- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
525
- | `PageScroll` | Page-level vertical scroll wrapper; usual outer shell for the app body. |
526
- | `AppShell` | Layout grid container; `Sidebar` + `AppShellMainContent` as siblings inside it. |
527
- | `AppShellMainContent` | Main column: `header`, scrollable body (`children`), `footer`. |
528
- | `SybilionAppHeader` | **Default** standalone top header: **`AppShellMainContent`** `header` = **`AppHeaderHost`**, first body child = **`SybilionAppHeader`** — workspace switcher + embedded **`NavUserHeader`** (props incl. **`menuItems`**); aligns **`PageHeader`** via **`page-header-actions`**. |
529
- | `WorkspaceAppSwitcher` | Dropdown of workspace apps (`WorkspaceAppEntry[]`); composed inside **`SybilionAppHeader`**. |
530
- | `NavUserHeader` | Header user menu (avatar, account, theme, logout). |
531
- | `Sidebar`, `SidebarProvider` | Collapsible rail + context (`@sybilion/uilib`). Wrap `SidebarProvider` above `AppLayout`; render `Sidebar` inside `AppShell` (usually via `AppSidebar`). |
532
- | `AppSidebar` | App-specific component (`src/AppSidebar.tsx`) composing `Sidebar` + nav links + product widgets. Keeps `AppLayout` generic. |
533
- | `SidebarDatasetsItemsGrouped` | Dataset list widget for the sidebar: collapsible groups (`regions` / `target_type` / `categories`) with nested rows + selection callback. |
534
- | `SidebarTrigger` | Toggle sidebar visibility (especially mobile / `offcanvas`). |
535
- | `PageFooter` | Standard footer; requires `versionLink` + `versionLabel`. |
536
- | `Gap` | Spacing primitive between flex children. |
537
- | `PageHeader`, `PageContent`, `PageContentSection`, … | **§4 Route page body** (roles, `breadcrumbSidebarTrigger`, example). Same barrel also has `PageTabs`, `PageColumns`, `SectionHeader`, `PageEmptyCanvas`, … |
538
- | `SybilionAuthProvider` | Auth0 + Sybilion JWT (§3). |
539
- | `SignInPage` | Full sign-in route using **`SybilionAuthLayout`** + **`SybilionSignInPanel`** + **`useSybilionAuth`**; §3 sign-in subsection. |
540
- | `SybilionAuthLayout` | Split-view auth chrome (hero + form column); optional **`heroBackgroundUrl`** (default **`/sybilion_bg.svg`**). |
541
- | `SybilionSignInPanel` | Presentational primary button, error line, forgot-password link, optional version link; §3 sign-in subsection. |
542
- | `useSybilionAuth` | User session + login/logout under provider. |
543
- | `useSybilionApiFetch` | Authenticated `fetch` using stored JWT. |
544
-
545
- ## 5. Data
546
-
547
- Inside `SybilionAuthProvider`, use `useSybilionApiFetch()` for authenticated `fetch`, or import `sybilionSdk` from §2 for `raw` / `resources` — same JWT storage and API origin either way.
548
-
549
- ## Related
550
-
551
- - [auth0-configuration-guide.md](https://github.com/Mir-Insight/sybilion-client/blob/main/docs/auth0-configuration-guide.md)
552
- - [server-auth-verification.md](https://github.com/Mir-Insight/sybilion-client/blob/main/docs/server-auth-verification.md)
@@ -1,40 +0,0 @@
1
- import { PageContentSection } from '#uilib/components/ui/Page';
2
-
3
- import { AppPageHeader } from '../components/AppPageHeader/AppPageHeader';
4
- import { DocsHeaderActions } from '../docsHeaderActions';
5
-
6
- export default function SybilionAuthProviderPage() {
7
- return (
8
- <>
9
- <AppPageHeader
10
- breadcrumbs={[{ label: 'SybilionAuthProvider' }]}
11
- title="SybilionAuthProvider"
12
- subheader="Auth0 SPA → sdk.auth.loginWithAuth0Identity → Sybilion JWT. Pass createSybilionSDK instance via sdk prop; greenfield: docs/standalone-apps.md (yarn add includes @sybilion/sdk, @auth0/auth0-react)."
13
- actions={<DocsHeaderActions />}
14
- />
15
- <PageContentSection title="Exports">
16
- <p style={{ marginTop: 0 }}>
17
- From <code>@sybilion/uilib</code>:
18
- </p>
19
- <ul>
20
- <li>
21
- <code>SybilionAuthProvider</code> (<code>sdk</code> prop)
22
- </li>
23
- <li>
24
- <code>getSybilionApiOriginFromSdk</code>
25
- </li>
26
- <li>
27
- <code>useSybilionAuth</code>
28
- </li>
29
- <li>
30
- <code>useSybilionApiFetch</code>,{' '}
31
- <code>createSybilionApiFetch</code>, <code>sybilionApiFetch</code>
32
- </li>
33
- <li>
34
- <code>exchangeAuth0AccessTokenForSybilionJwt</code> (advanced)
35
- </li>
36
- </ul>
37
- </PageContentSection>
38
- </>
39
- );
40
- }