@psnext/design-system 2.1.0 → 2.3.0
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.
- package/DESIGN.md +3 -3
- package/dist/Header.cjs +90 -115
- package/dist/Header.cjs.map +1 -1
- package/dist/Header.js +86 -117
- package/dist/Header.js.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index10.d.cts +6 -6
- package/dist/index10.d.cts.map +1 -1
- package/dist/index10.d.ts +2 -2
- package/dist/index11.d.cts +3 -3
- package/dist/index11.d.cts.map +1 -1
- package/dist/index11.d.ts +3 -3
- package/dist/index12.d.cts +12 -12
- package/dist/index12.d.ts +11 -11
- package/dist/index13.d.cts +6 -6
- package/dist/index13.d.ts +6 -6
- package/dist/index14.d.cts +5 -5
- package/dist/index14.d.ts +5 -5
- package/dist/index15.d.cts +3 -3
- package/dist/index15.d.ts +3 -3
- package/dist/index16.d.cts +2 -2
- package/dist/index16.d.ts +2 -2
- package/dist/index17.d.cts +4 -4
- package/dist/index17.d.ts +4 -4
- package/dist/index18.d.cts +11 -11
- package/dist/index18.d.cts.map +1 -1
- package/dist/index18.d.ts +11 -11
- package/dist/index19.d.cts +10 -10
- package/dist/index19.d.ts +10 -10
- package/dist/index2.d.cts +8 -8
- package/dist/index2.d.ts +8 -8
- package/dist/index20.d.cts +16 -16
- package/dist/index20.d.ts +16 -16
- package/dist/index21.d.cts +4 -4
- package/dist/index21.d.ts +4 -4
- package/dist/index22.d.cts +11 -11
- package/dist/index22.d.ts +11 -11
- package/dist/index23.d.cts +16 -16
- package/dist/index23.d.ts +16 -16
- package/dist/index24.d.cts +10 -10
- package/dist/index24.d.cts.map +1 -1
- package/dist/index24.d.ts +10 -10
- package/dist/index25.d.cts +2 -2
- package/dist/index25.d.ts +2 -2
- package/dist/index26.d.cts +13 -13
- package/dist/index26.d.cts.map +1 -1
- package/dist/index26.d.ts +13 -13
- package/dist/index27.d.cts +7 -7
- package/dist/index27.d.cts.map +1 -1
- package/dist/index27.d.ts +7 -7
- package/dist/index28.d.cts +5 -5
- package/dist/index28.d.cts.map +1 -1
- package/dist/index28.d.ts +4 -4
- package/dist/index29.d.cts +4 -4
- package/dist/index29.d.cts.map +1 -1
- package/dist/index29.d.ts +4 -4
- package/dist/index3.d.cts +13 -13
- package/dist/index3.d.ts +13 -13
- package/dist/index30.d.cts +6 -6
- package/dist/index30.d.cts.map +1 -1
- package/dist/index30.d.ts +5 -5
- package/dist/index31.d.cts +4 -4
- package/dist/index31.d.cts.map +1 -1
- package/dist/index31.d.ts +5 -5
- package/dist/index32.d.cts +5 -5
- package/dist/index32.d.cts.map +1 -1
- package/dist/index32.d.ts +5 -5
- package/dist/index33.d.cts +10 -10
- package/dist/index33.d.cts.map +1 -1
- package/dist/index33.d.ts +10 -10
- package/dist/index34.d.cts +5 -5
- package/dist/index34.d.cts.map +1 -1
- package/dist/index34.d.ts +5 -5
- package/dist/index35.d.cts +16 -16
- package/dist/index35.d.cts.map +1 -1
- package/dist/index35.d.ts +16 -16
- package/dist/index35.d.ts.map +1 -1
- package/dist/index36.d.cts +3 -3
- package/dist/index36.d.cts.map +1 -1
- package/dist/index36.d.ts +3 -3
- package/dist/index37.d.cts +4 -4
- package/dist/index37.d.cts.map +1 -1
- package/dist/index37.d.ts +4 -4
- package/dist/index38.d.cts +2 -2
- package/dist/index38.d.cts.map +1 -1
- package/dist/index38.d.ts +2 -2
- package/dist/index39.d.cts +17 -17
- package/dist/index39.d.ts +17 -17
- package/dist/index4.d.cts +2 -2
- package/dist/index4.d.cts.map +1 -1
- package/dist/index4.d.ts +2 -2
- package/dist/index40.d.cts +5 -5
- package/dist/index40.d.cts.map +1 -1
- package/dist/index40.d.ts +5 -5
- package/dist/index41.d.cts +11 -11
- package/dist/index41.d.cts.map +1 -1
- package/dist/index41.d.ts +11 -11
- package/dist/index41.d.ts.map +1 -1
- package/dist/index42.d.cts +8 -8
- package/dist/index42.d.cts.map +1 -1
- package/dist/index42.d.ts +8 -8
- package/dist/index42.d.ts.map +1 -1
- package/dist/index43.d.cts +9 -9
- package/dist/index43.d.cts.map +1 -1
- package/dist/index43.d.ts +9 -9
- package/dist/index43.d.ts.map +1 -1
- package/dist/index45.d.cts +8 -8
- package/dist/index45.d.cts.map +1 -1
- package/dist/index45.d.ts +8 -8
- package/dist/index45.d.ts.map +1 -1
- package/dist/index46.d.cts +2 -2
- package/dist/index46.d.cts.map +1 -1
- package/dist/index46.d.ts +2 -2
- package/dist/index47.d.cts +4 -4
- package/dist/index47.d.ts +4 -4
- package/dist/index48.d.cts +4 -4
- package/dist/index48.d.cts.map +1 -1
- package/dist/index48.d.ts +4 -4
- package/dist/index49.d.cts +3 -3
- package/dist/index49.d.cts.map +1 -1
- package/dist/index49.d.ts +3 -3
- package/dist/index5.d.cts +7 -7
- package/dist/index5.d.ts +7 -7
- package/dist/index50.d.cts +11 -11
- package/dist/index50.d.cts.map +1 -1
- package/dist/index50.d.ts +11 -11
- package/dist/index51.d.cts +9 -9
- package/dist/index51.d.cts.map +1 -1
- package/dist/index51.d.ts +9 -9
- package/dist/index51.d.ts.map +1 -1
- package/dist/index52.d.cts +5 -5
- package/dist/index52.d.ts +5 -5
- package/dist/index53.d.cts +27 -27
- package/dist/index53.d.cts.map +1 -1
- package/dist/index53.d.ts +27 -27
- package/dist/index53.d.ts.map +1 -1
- package/dist/index54.d.cts +5 -5
- package/dist/index54.d.cts.map +1 -1
- package/dist/index54.d.ts +5 -5
- package/dist/index55.d.cts +2 -2
- package/dist/index55.d.ts +2 -2
- package/dist/index56.d.cts +2 -2
- package/dist/index56.d.ts +2 -2
- package/dist/index57.d.cts +2 -2
- package/dist/index57.d.cts.map +1 -1
- package/dist/index57.d.ts +2 -2
- package/dist/index57.d.ts.map +1 -1
- package/dist/index58.d.cts +4 -4
- package/dist/index58.d.cts.map +1 -1
- package/dist/index58.d.ts +4 -4
- package/dist/index59.d.cts +9 -9
- package/dist/index59.d.ts +9 -9
- package/dist/index6.d.cts +5 -5
- package/dist/index6.d.ts +5 -5
- package/dist/index6.d.ts.map +1 -1
- package/dist/index60.d.cts +9 -9
- package/dist/index60.d.cts.map +1 -1
- package/dist/index60.d.ts +9 -9
- package/dist/index61.d.cts +7 -7
- package/dist/index61.d.cts.map +1 -1
- package/dist/index61.d.ts +4 -4
- package/dist/index62.d.cts +4 -4
- package/dist/index62.d.cts.map +1 -1
- package/dist/index62.d.ts +4 -4
- package/dist/index63.d.cts +2 -2
- package/dist/index63.d.cts.map +1 -1
- package/dist/index63.d.ts +2 -2
- package/dist/index64.d.cts +5 -5
- package/dist/index64.d.cts.map +1 -1
- package/dist/index64.d.ts +5 -5
- package/dist/index65.d.cts +3 -3
- package/dist/index65.d.ts +3 -3
- package/dist/index66.d.cts +4 -4
- package/dist/index66.d.cts.map +1 -1
- package/dist/index66.d.ts +2 -2
- package/dist/index66.d.ts.map +1 -1
- package/dist/index67.d.cts +2 -2
- package/dist/index67.d.cts.map +1 -1
- package/dist/index67.d.ts +2 -2
- package/dist/index67.d.ts.map +1 -1
- package/dist/index68.d.cts +7 -7
- package/dist/index68.d.cts.map +1 -1
- package/dist/index68.d.ts +4 -4
- package/dist/index68.d.ts.map +1 -1
- package/dist/index69.d.cts +7 -7
- package/dist/index69.d.cts.map +1 -1
- package/dist/index69.d.ts +4 -4
- package/dist/index7.d.cts +8 -8
- package/dist/index7.d.ts +8 -8
- package/dist/index71.d.cts +37 -21
- package/dist/index71.d.cts.map +1 -1
- package/dist/index71.d.ts +37 -21
- package/dist/index71.d.ts.map +1 -1
- package/dist/index8.d.cts +4 -4
- package/dist/index8.d.cts.map +1 -1
- package/dist/index8.d.ts +4 -4
- package/dist/index9.d.cts +2 -2
- package/dist/index9.d.cts.map +1 -1
- package/dist/index9.d.ts +2 -2
- package/dist/index9.d.ts.map +1 -1
- package/dist/patterns/DataTable/index.d.cts +3 -3
- package/dist/patterns/DataTable/index.d.cts.map +1 -1
- package/dist/patterns/DataTable/index.d.ts +3 -3
- package/dist/patterns/Footer/index.d.cts +7 -7
- package/dist/patterns/Footer/index.d.cts.map +1 -1
- package/dist/patterns/Footer/index.d.ts +7 -7
- package/dist/patterns/Footer/index.d.ts.map +1 -1
- package/dist/patterns/Header/index.cjs +1 -0
- package/dist/patterns/Header/index.d.cts +2 -2
- package/dist/patterns/Header/index.d.ts +2 -2
- package/dist/patterns/Header/index.js +2 -2
- package/dist/patterns/MediaObject/index.d.cts +12 -12
- package/dist/patterns/MediaObject/index.d.cts.map +1 -1
- package/dist/patterns/MediaObject/index.d.ts +11 -11
- package/dist/patterns/SectionCard/index.d.cts +2 -2
- package/dist/patterns/SectionCard/index.d.cts.map +1 -1
- package/dist/patterns/SectionCard/index.d.ts +2 -2
- package/dist/patterns/SectionCard/index.d.ts.map +1 -1
- package/dist/patterns/SectionHeading/index.d.cts +2 -2
- package/dist/patterns/SectionHeading/index.d.cts.map +1 -1
- package/dist/patterns/SectionHeading/index.d.ts +2 -2
- package/dist/patterns/SectionHeading/index.d.ts.map +1 -1
- package/dist/patterns/index.cjs +1 -0
- package/dist/patterns/index.d.cts +2 -2
- package/dist/patterns/index.d.ts +2 -2
- package/dist/patterns/index.js +2 -2
- package/package.json +1 -1
package/DESIGN.md
CHANGED
|
@@ -536,9 +536,9 @@ Stories in `src/<layer>/<Component>/<Component>.stories.tsx` are the canonical e
|
|
|
536
536
|
- **Notes**: Responsive 3-region grid (start/center/end), stacks centered on mobile, spreads on md+. Compositional — assemble from the region parts + Text/Link/Logo.
|
|
537
537
|
|
|
538
538
|
### Header
|
|
539
|
-
- **Exports**: `Header` (wired), `HeaderBar`, `HeaderLogo`, `OrgPill`, `ProfileMenu`, `ThemeToggle`, `ProjectPicker`; hooks/data layer `useGlobalHeader`, `useHeaderCredits`, `useHeaderProject`, `getSessionToken`, `isSessionValid`, `buildEndpoints`, `DEFAULT_LABELS`; consts/types `TOGGLE_PROJECT_DROPDOWN_EVENT`, `GlobalHeaderState`, `ToggleProjectDropdownDetail`, `HeaderSelection`, `UseHeaderProjectResult`, `Labels`, `Fetcher`, `HeaderEndpoints`, `HeaderProps`, `ProjectNode`
|
|
540
|
-
- **Custom props** (HeaderProps): `apiBaseUrl` — studio-auth API host (required); `environmentId` — reads `{environmentId}-sessionId` cookie (required); `logoType` — ThemeProduct, defaults to active product; `logoHref` — logo link destination (defaults to the product's public site); `showOrg`/`showProjectDropdown`/`showProfile`/`showThemeSwitcher`/`showCredits` — visibility toggles (all default `false`); `pageTitle`; `feedbackUrl`; `clientId` — active org id; `_dangerouslyInjectAccessToken` — escape hatch, avoid; `onProjectChange
|
|
541
|
-
- **Notes**: Must render inside a `<ThemeProvider>`; owns an internal singleton QueryClient (no app setup). Logged-out users see no user content; `ProjectNode` shape = `{ id, name, children: ProjectNode[], isContextOnly? }`.
|
|
539
|
+
- **Exports**: `Header` (wired), `HeaderBar`, `HeaderLogo`, `OrgPill`, `ProfileMenu`, `ThemeToggle`, `ProjectPicker`; hooks/data layer `useGlobalHeader`, `useHeaderCredits`, `useHeaderProject`, `getSessionToken`, `getSessionClaims`, `isSessionValid`, `buildEndpoints`, `DEFAULT_LABELS`; consts/types `TOGGLE_PROJECT_DROPDOWN_EVENT`, `GlobalHeaderState`, `ToggleProjectDropdownDetail`, `HeaderSelection`, `UseHeaderProjectResult`, `Labels`, `Fetcher`, `HeaderEndpoints`, `HeaderProps`, `ProjectNode`, `SessionClaims`
|
|
540
|
+
- **Custom props** (HeaderProps): `apiBaseUrl` — studio-auth API host (required); `environmentId` — reads `{environmentId}-sessionId` cookie (required); `logoType` — ThemeProduct, defaults to active product; `logoHref` — logo link destination (defaults to the product's public site); `showOrg`/`showProjectDropdown`/`showProfile`/`showThemeSwitcher`/`showCredits` — visibility toggles (all default `false`); `pageTitle`; `feedbackUrl`; `clientId` — active org id; `_dangerouslyInjectAccessToken` — escape hatch, avoid; `onProjectChange` — fires on user pick or automatic default; `logoAddon` — node between logo and picker; `actionsStart`/`actionsEnd` — nodes at the start/end of the right-hand actions cluster; `endpoints` — per-path overrides; `fetcher` — custom fetch (default global); `staleTime` (default 5 min); `gcTime` (default 10 min).
|
|
541
|
+
- **Notes**: Must render inside a `<ThemeProvider>`; owns an internal singleton QueryClient (no app setup). Auth state and user identity (email, client id) are decoded from the `{environmentId}-sessionId` JWT cookie — no `/me` call. Logged-out users see no user content; `ProjectNode` shape = `{ id, name, children: ProjectNode[], isContextOnly? }`. Project selection is per-tab (sessionStorage); explicit picks are remembered cross-tab in localStorage `lastSelectedProject`, and a tab with no valid selection auto-defaults to that (else the first selectable project) — there is no selection overlay.
|
|
542
542
|
|
|
543
543
|
### MediaObject
|
|
544
544
|
- **Exports**: `MediaObject`, `MediaObjectImage`, `MediaObjectIcon`, `MediaObjectContent`, `MediaObjectTitle`, `MediaObjectText`, `MediaObjectActions`, `MediaObjectGroup`, `mediaObjectGroupVariants`, types `MediaObjectProps`, `MediaObjectImageProps`, `MediaObjectTitleProps`, `MediaObjectGroupProps`
|
package/dist/Header.cjs
CHANGED
|
@@ -28,6 +28,14 @@ function findProjectName(tree, id) {
|
|
|
28
28
|
if (found) return found;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
/** First selectable node in the tree (depth-first, skipping context-only ancestors). */
|
|
32
|
+
function firstSelectableProject(tree) {
|
|
33
|
+
for (const node of tree) {
|
|
34
|
+
if (!node.isContextOnly) return node;
|
|
35
|
+
const found = firstSelectableProject(node.children);
|
|
36
|
+
if (found) return found;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
31
39
|
/** Filter the tree to nodes matching `term`, preserving ancestors of matches. */
|
|
32
40
|
function filterTree(nodes, term) {
|
|
33
41
|
if (!term) return nodes;
|
|
@@ -179,17 +187,14 @@ const DEFAULT_LABELS = {
|
|
|
179
187
|
searchPlaceholder: "Search project",
|
|
180
188
|
emptyStateText: "No projects found",
|
|
181
189
|
profileMenuLabel: "Profile menu",
|
|
182
|
-
logoutLabel: "Logout"
|
|
183
|
-
overlayTitle: "Select a Project",
|
|
184
|
-
overlayDescription: "Choose a project to get started. You can change it anytime from the header."
|
|
190
|
+
logoutLabel: "Logout"
|
|
185
191
|
};
|
|
186
192
|
//#endregion
|
|
187
193
|
//#region src/patterns/Header/api.ts
|
|
188
194
|
const PATHS = {
|
|
189
195
|
token: "/api/v1/studio-auth/sessions/token",
|
|
190
|
-
me: "/api/v1/onboarding/users/me",
|
|
191
196
|
logout: "/api/v1/studio-auth/sessions/logout",
|
|
192
|
-
projects: (clientId) => `/api/v1/
|
|
197
|
+
projects: (clientId) => `/api/v1/client-projects/getAllProjects?limit=50&page=1&sort=name&sortOrder=ASC&clientId=${encodeURIComponent(clientId)}&metaData=true`,
|
|
193
198
|
credits: "/save/api/v1/users"
|
|
194
199
|
};
|
|
195
200
|
/** Join a base URL and path without doubling slashes. */
|
|
@@ -199,7 +204,6 @@ function join(baseUrl, path) {
|
|
|
199
204
|
function buildEndpoints(apiBaseUrl, overrides) {
|
|
200
205
|
return {
|
|
201
206
|
token: overrides?.token ?? join(apiBaseUrl, PATHS.token),
|
|
202
|
-
me: overrides?.me ?? join(apiBaseUrl, PATHS.me),
|
|
203
207
|
logout: overrides?.logout ?? join(apiBaseUrl, PATHS.logout),
|
|
204
208
|
projects: overrides?.projects ?? ((clientId) => join(apiBaseUrl, PATHS.projects(clientId))),
|
|
205
209
|
credits: overrides?.credits ?? join(apiBaseUrl, PATHS.credits)
|
|
@@ -211,16 +215,6 @@ async function fetchToken(endpoints, fetcher) {
|
|
|
211
215
|
const { accessToken } = await res.json();
|
|
212
216
|
return accessToken;
|
|
213
217
|
}
|
|
214
|
-
async function fetchMe(endpoints, accessToken, fetcher) {
|
|
215
|
-
const res = await fetcher(endpoints.me, {
|
|
216
|
-
credentials: "include",
|
|
217
|
-
headers: { Authorization: `Bearer ${accessToken}` }
|
|
218
|
-
});
|
|
219
|
-
if (!res.ok) throw new Error(`me request failed: ${res.status}`);
|
|
220
|
-
const data = (await res.json()).response?.data;
|
|
221
|
-
if (!data) throw new Error("me response missing data");
|
|
222
|
-
return data;
|
|
223
|
-
}
|
|
224
218
|
async function fetchProjects(endpoints, clientId, accessToken, fetcher) {
|
|
225
219
|
const res = await fetcher(endpoints.projects(clientId), {
|
|
226
220
|
credentials: "include",
|
|
@@ -254,6 +248,7 @@ async function callLogout(endpoints, fetcher) {
|
|
|
254
248
|
//#endregion
|
|
255
249
|
//#region src/patterns/Header/storage.ts
|
|
256
250
|
const PROJECT_KEY = "ds-header-project";
|
|
251
|
+
const LAST_PROJECT_KEY = "lastSelectedProject";
|
|
257
252
|
function getStoredProject() {
|
|
258
253
|
try {
|
|
259
254
|
const raw = sessionStorage.getItem(PROJECT_KEY);
|
|
@@ -272,6 +267,20 @@ function clearStoredProject() {
|
|
|
272
267
|
sessionStorage.removeItem(PROJECT_KEY);
|
|
273
268
|
} catch {}
|
|
274
269
|
}
|
|
270
|
+
/** The project the user last explicitly selected, in any tab (localStorage). */
|
|
271
|
+
function getLastSelectedProject() {
|
|
272
|
+
try {
|
|
273
|
+
const raw = localStorage.getItem(LAST_PROJECT_KEY);
|
|
274
|
+
return raw ? JSON.parse(raw) : null;
|
|
275
|
+
} catch {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function setLastSelectedProject(project) {
|
|
280
|
+
try {
|
|
281
|
+
localStorage.setItem(LAST_PROJECT_KEY, JSON.stringify(project));
|
|
282
|
+
} catch {}
|
|
283
|
+
}
|
|
275
284
|
//#endregion
|
|
276
285
|
//#region src/patterns/Header/header-store.ts
|
|
277
286
|
function initialState() {
|
|
@@ -307,14 +316,22 @@ const headerStore = {
|
|
|
307
316
|
getServerSnapshot() {
|
|
308
317
|
return SERVER_SNAPSHOT;
|
|
309
318
|
},
|
|
310
|
-
/**
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
319
|
+
/**
|
|
320
|
+
* Set the selected project. `name` is best-effort; the Header backfills it from
|
|
321
|
+
* the tree. Explicit selections are also remembered in localStorage as the
|
|
322
|
+
* cross-tab default; pass `remember: false` for internal backfill/auto-select
|
|
323
|
+
* writes so they don't overwrite the user's last explicit choice.
|
|
324
|
+
*/
|
|
325
|
+
setProject(id, name = "", clientId, { remember = true } = {}) {
|
|
326
|
+
if (id) {
|
|
327
|
+
const project = {
|
|
328
|
+
id,
|
|
329
|
+
name,
|
|
330
|
+
clientId: clientId ?? state.clientId
|
|
331
|
+
};
|
|
332
|
+
setStoredProject(project);
|
|
333
|
+
if (remember) setLastSelectedProject(project);
|
|
334
|
+
} else clearStoredProject();
|
|
318
335
|
state = {
|
|
319
336
|
...state,
|
|
320
337
|
selectedProjectId: id,
|
|
@@ -414,6 +431,24 @@ function decodeJwt(token) {
|
|
|
414
431
|
return null;
|
|
415
432
|
}
|
|
416
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Decode the user claims from the session cookie's JWT — no network call.
|
|
436
|
+
* Returns null when the cookie is absent or undecodable.
|
|
437
|
+
*/
|
|
438
|
+
function getSessionClaims(environmentId) {
|
|
439
|
+
const token = getSessionToken(environmentId);
|
|
440
|
+
if (!token) return null;
|
|
441
|
+
const payload = decodeJwt(token);
|
|
442
|
+
if (!payload) return null;
|
|
443
|
+
const str = (v) => typeof v === "string" ? v : void 0;
|
|
444
|
+
return {
|
|
445
|
+
email: str(payload.email),
|
|
446
|
+
name: str(payload.name),
|
|
447
|
+
userId: str(payload.user_id),
|
|
448
|
+
clientId: str(payload.client_id),
|
|
449
|
+
exp: typeof payload.exp === "number" ? payload.exp : void 0
|
|
450
|
+
};
|
|
451
|
+
}
|
|
417
452
|
/** True when a valid, unexpired session cookie exists for the environment. */
|
|
418
453
|
function isSessionValid(environmentId) {
|
|
419
454
|
const token = getSessionToken(environmentId);
|
|
@@ -433,6 +468,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
|
|
|
433
468
|
const fetcher = react$1.useMemo(() => fetcherProp ?? ((input, init) => fetch(input, init)), [fetcherProp]);
|
|
434
469
|
const endpoints = react$1.useMemo(() => buildEndpoints(apiBaseUrl, endpointOverrides), [apiBaseUrl, endpointOverrides]);
|
|
435
470
|
const isAuthenticated = isSessionValid(environmentId);
|
|
471
|
+
const claims = getSessionClaims(environmentId);
|
|
436
472
|
const tokenQuery = (0, _tanstack_react_query.useQuery)({
|
|
437
473
|
queryKey: [
|
|
438
474
|
"ds-header",
|
|
@@ -445,20 +481,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
|
|
|
445
481
|
gcTime
|
|
446
482
|
});
|
|
447
483
|
const token = injectedToken ?? tokenQuery.data;
|
|
448
|
-
const
|
|
449
|
-
const meQuery = (0, _tanstack_react_query.useQuery)({
|
|
450
|
-
queryKey: [
|
|
451
|
-
"ds-header",
|
|
452
|
-
"me",
|
|
453
|
-
endpoints.me,
|
|
454
|
-
token
|
|
455
|
-
],
|
|
456
|
-
queryFn: () => fetchMe(endpoints, token, fetcher),
|
|
457
|
-
enabled: isAuthenticated && !!token && wantsUser,
|
|
458
|
-
staleTime,
|
|
459
|
-
gcTime
|
|
460
|
-
});
|
|
461
|
-
const effectiveClientId = clientId ?? meQuery.data?.clientId;
|
|
484
|
+
const effectiveClientId = clientId ?? claims?.clientId;
|
|
462
485
|
const projectsQuery = (0, _tanstack_react_query.useQuery)({
|
|
463
486
|
queryKey: [
|
|
464
487
|
"ds-header",
|
|
@@ -484,7 +507,7 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
|
|
|
484
507
|
gcTime
|
|
485
508
|
});
|
|
486
509
|
const projectsTree = projectsQuery.data?.projectsTree ?? [];
|
|
487
|
-
const userEmail = showProfile ?
|
|
510
|
+
const userEmail = showProfile ? claims?.email : void 0;
|
|
488
511
|
const creditsHref = react$1.useMemo(() => {
|
|
489
512
|
try {
|
|
490
513
|
return new URL("/user/activity", apiBaseUrl).toString();
|
|
@@ -493,6 +516,10 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
|
|
|
493
516
|
}
|
|
494
517
|
}, [apiBaseUrl]);
|
|
495
518
|
const selection = useHeaderStore();
|
|
519
|
+
const onProjectChangeRef = react$1.useRef(onProjectChange);
|
|
520
|
+
react$1.useEffect(() => {
|
|
521
|
+
onProjectChangeRef.current = onProjectChange;
|
|
522
|
+
});
|
|
496
523
|
react$1.useEffect(() => {
|
|
497
524
|
if (effectiveClientId) headerStore.reconcileClient(effectiveClientId);
|
|
498
525
|
}, [effectiveClientId]);
|
|
@@ -500,10 +527,19 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
|
|
|
500
527
|
if (!projectsQuery.data) return;
|
|
501
528
|
const tree = projectsQuery.data.projectsTree;
|
|
502
529
|
const id = headerStore.getSnapshot().selectedProjectId;
|
|
503
|
-
if (id &&
|
|
504
|
-
else if (id) {
|
|
530
|
+
if (id && nodeExistsInTree(tree, id)) {
|
|
505
531
|
const name = findProjectName(tree, id);
|
|
506
|
-
if (name && name !== headerStore.getSnapshot().selectedProjectName) headerStore.setProject(id, name, effectiveClientId);
|
|
532
|
+
if (name && name !== headerStore.getSnapshot().selectedProjectName) headerStore.setProject(id, name, effectiveClientId, { remember: false });
|
|
533
|
+
} else {
|
|
534
|
+
const last = getLastSelectedProject();
|
|
535
|
+
const fallback = last && nodeExistsInTree(tree, last.id) ? {
|
|
536
|
+
id: last.id,
|
|
537
|
+
name: findProjectName(tree, last.id) ?? last.name
|
|
538
|
+
} : firstSelectableProject(tree);
|
|
539
|
+
if (fallback) {
|
|
540
|
+
headerStore.setProject(fallback.id, fallback.name, effectiveClientId, { remember: false });
|
|
541
|
+
onProjectChangeRef.current?.(fallback.id);
|
|
542
|
+
} else if (id) headerStore.clearProject();
|
|
507
543
|
}
|
|
508
544
|
headerStore.setOrganization(projectsQuery.data.organizationName, effectiveClientId);
|
|
509
545
|
}, [projectsQuery.data, effectiveClientId]);
|
|
@@ -541,7 +577,6 @@ function useGlobalHeader({ apiBaseUrl, environmentId, clientId, _dangerouslyInje
|
|
|
541
577
|
credits: selection.credits,
|
|
542
578
|
creditsHref,
|
|
543
579
|
projectDropdownVisible,
|
|
544
|
-
showOverlay: isAuthenticated && projectDropdownVisible && !selection.selectedProjectId,
|
|
545
580
|
selectProject,
|
|
546
581
|
logout
|
|
547
582
|
};
|
|
@@ -653,7 +688,7 @@ function Header(props) {
|
|
|
653
688
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeaderInner, { ...props })
|
|
654
689
|
});
|
|
655
690
|
}
|
|
656
|
-
function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown = false, showProfile = false, showThemeSwitcher = false, showCredits = false, pageTitle, feedbackUrl, logoAddon,
|
|
691
|
+
function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown = false, showProfile = false, showThemeSwitcher = false, showCredits = false, pageTitle, feedbackUrl, logoAddon, actionsStart, actionsEnd, className, ...rest }) {
|
|
657
692
|
const { product } = require_primitives_ThemeProvider_index.useTheme();
|
|
658
693
|
const brand = logoType ?? product;
|
|
659
694
|
const state = useGlobalHeader({
|
|
@@ -667,8 +702,8 @@ function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown
|
|
|
667
702
|
const showOrgUi = showOrg && state.isAuthenticated;
|
|
668
703
|
const showProfileUi = showProfile && state.isAuthenticated;
|
|
669
704
|
const showCreditsUi = showCredits && state.isAuthenticated && state.credits !== void 0;
|
|
670
|
-
const showActions = showThemeSwitcher || showProfileUi || showCreditsUi || !!feedbackUrl;
|
|
671
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(
|
|
705
|
+
const showActions = showThemeSwitcher || showProfileUi || showCreditsUi || !!feedbackUrl || !!actionsStart || !!actionsEnd;
|
|
706
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(HeaderBar, {
|
|
672
707
|
className,
|
|
673
708
|
children: [
|
|
674
709
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(HeaderLogo, {
|
|
@@ -702,6 +737,7 @@ function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown
|
|
|
702
737
|
showActions && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
703
738
|
className: "ml-auto flex items-center gap-2",
|
|
704
739
|
children: [
|
|
740
|
+
actionsStart,
|
|
705
741
|
feedbackUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_primitives_Button_index.Button, {
|
|
706
742
|
variant: "ghost",
|
|
707
743
|
size: "icon",
|
|
@@ -724,78 +760,11 @@ function HeaderInner({ logoType, logoHref, showOrg = false, showProjectDropdown
|
|
|
724
760
|
showProfileUi && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProfileMenu, {
|
|
725
761
|
email: state.userEmail,
|
|
726
762
|
onLogout: state.logout
|
|
727
|
-
})
|
|
763
|
+
}),
|
|
764
|
+
actionsEnd
|
|
728
765
|
]
|
|
729
766
|
})
|
|
730
767
|
]
|
|
731
|
-
}), showProjectUi && state.showOverlay && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProjectOverlay, {
|
|
732
|
-
organizationName: state.organizationName,
|
|
733
|
-
projectsTree: state.projectsTree,
|
|
734
|
-
selectedProjectId: state.selectedProjectId,
|
|
735
|
-
onSelect: state.selectProject,
|
|
736
|
-
isLoading: state.isProjectsLoading,
|
|
737
|
-
onClose: onCloseProjectOverlay
|
|
738
|
-
})] });
|
|
739
|
-
}
|
|
740
|
-
function ProjectOverlay({ organizationName, projectsTree, selectedProjectId, onSelect, isLoading, onClose }) {
|
|
741
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
742
|
-
role: "dialog",
|
|
743
|
-
"aria-modal": "true",
|
|
744
|
-
"aria-label": DEFAULT_LABELS.overlayTitle,
|
|
745
|
-
className: "fixed inset-0 z-50 flex items-center justify-center bg-primary-bg/80 backdrop-blur-sm",
|
|
746
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
747
|
-
className: "relative mx-1 flex w-full max-w-md flex-col items-center gap-6 rounded-16 border bg-glass-none-bg p-2 text-primary-txt shadow-xl",
|
|
748
|
-
children: [
|
|
749
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_primitives_Button_index.Button, {
|
|
750
|
-
variant: "ghost",
|
|
751
|
-
size: "icon",
|
|
752
|
-
onClick: onClose ?? (() => window.history.back()),
|
|
753
|
-
className: "absolute top-4 right-4",
|
|
754
|
-
"aria-label": "Close",
|
|
755
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.X, { className: "size-5" })
|
|
756
|
-
}),
|
|
757
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
758
|
-
className: "flex flex-col items-center gap-3 text-center",
|
|
759
|
-
children: [
|
|
760
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
761
|
-
className: "rounded-full bg-primary/10 p-4",
|
|
762
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.FolderOpen, { className: "size-8 text-primary" })
|
|
763
|
-
}),
|
|
764
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
765
|
-
className: "font-semibold text-primary-txt text-xl",
|
|
766
|
-
children: DEFAULT_LABELS.overlayTitle
|
|
767
|
-
}),
|
|
768
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
769
|
-
className: "text-muted-txt text-sm",
|
|
770
|
-
children: DEFAULT_LABELS.overlayDescription
|
|
771
|
-
})
|
|
772
|
-
]
|
|
773
|
-
}),
|
|
774
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
775
|
-
className: "flex w-full justify-center gap-2",
|
|
776
|
-
children: [organizationName && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
777
|
-
className: require_cn.cn(PILL, "select-none"),
|
|
778
|
-
title: organizationName,
|
|
779
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
780
|
-
className: "shrink-0 text-muted-txt",
|
|
781
|
-
children: [DEFAULT_LABELS.orgLabel, ":"]
|
|
782
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
783
|
-
className: "max-w-52 truncate font-medium",
|
|
784
|
-
children: organizationName
|
|
785
|
-
})]
|
|
786
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProjectPicker, {
|
|
787
|
-
projectsTree,
|
|
788
|
-
selectedProjectId,
|
|
789
|
-
onSelect,
|
|
790
|
-
projectLabel: DEFAULT_LABELS.projectLabel,
|
|
791
|
-
projectPlaceholder: DEFAULT_LABELS.projectPlaceholder,
|
|
792
|
-
searchPlaceholder: DEFAULT_LABELS.searchPlaceholder,
|
|
793
|
-
emptyStateText: DEFAULT_LABELS.emptyStateText,
|
|
794
|
-
isLoading
|
|
795
|
-
})]
|
|
796
|
-
})
|
|
797
|
-
]
|
|
798
|
-
})
|
|
799
768
|
});
|
|
800
769
|
}
|
|
801
770
|
//#endregion
|
|
@@ -859,6 +828,12 @@ Object.defineProperty(exports, "buildEndpoints", {
|
|
|
859
828
|
return buildEndpoints;
|
|
860
829
|
}
|
|
861
830
|
});
|
|
831
|
+
Object.defineProperty(exports, "getSessionClaims", {
|
|
832
|
+
enumerable: true,
|
|
833
|
+
get: function() {
|
|
834
|
+
return getSessionClaims;
|
|
835
|
+
}
|
|
836
|
+
});
|
|
862
837
|
Object.defineProperty(exports, "getSessionToken", {
|
|
863
838
|
enumerable: true,
|
|
864
839
|
get: function() {
|