@cboxdk/cbox-ui 0.6.0 → 0.8.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/README.md +100 -2
- package/dist/charts.d.ts +82 -0
- package/dist/charts.js +189 -0
- package/dist/charts.js.map +1 -0
- package/dist/chunk-OFTNLF7H.js +55 -0
- package/dist/chunk-OFTNLF7H.js.map +1 -0
- package/dist/engine-4DRZ3POZ.js +10 -0
- package/dist/engine-4DRZ3POZ.js.map +1 -0
- package/dist/index.d.ts +207 -7
- package/dist/index.js +573 -152
- package/dist/index.js.map +1 -1
- package/dist/scale-BaaY5qJs.d.ts +24 -0
- package/package.json +10 -1
- package/tokens/cbox.css +99 -88
package/dist/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { X, Menu, PanelLeftOpen, PanelLeftClose } from 'lucide-react';
|
|
1
|
+
import { cn } from './chunk-OFTNLF7H.js';
|
|
2
|
+
export { cn, niceTicks, timeTicks } from './chunk-OFTNLF7H.js';
|
|
3
|
+
import { createContext, useContext, useMemo, useSyncExternalStore, useState, useEffect, useRef, useCallback, useId } from 'react';
|
|
4
|
+
import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
import { X, Menu, PanelLeftOpen, PanelLeftClose, PinOff, Pin } from 'lucide-react';
|
|
6
6
|
|
|
7
|
-
// src/context/cbox-id.tsx
|
|
8
7
|
var CboxIdContext = createContext(null);
|
|
9
8
|
function CboxIdProvider({
|
|
10
9
|
value,
|
|
@@ -24,9 +23,6 @@ function useCboxId() {
|
|
|
24
23
|
function useCboxIdOptional() {
|
|
25
24
|
return useContext(CboxIdContext);
|
|
26
25
|
}
|
|
27
|
-
function cn(...inputs) {
|
|
28
|
-
return twMerge(clsx(inputs));
|
|
29
|
-
}
|
|
30
26
|
function AppContent({ children, className, ...props }) {
|
|
31
27
|
return /* @__PURE__ */ jsx(
|
|
32
28
|
"main",
|
|
@@ -43,6 +39,118 @@ function AppContent({ children, className, ...props }) {
|
|
|
43
39
|
function AppShell({ children }) {
|
|
44
40
|
return /* @__PURE__ */ jsx("div", { className: "flex h-svh w-full bg-canvas text-foreground", children });
|
|
45
41
|
}
|
|
42
|
+
var Localization = createContext({
|
|
43
|
+
locale: "en",
|
|
44
|
+
t: interpolate,
|
|
45
|
+
formatDate: (value) => new Date(value).toLocaleString()
|
|
46
|
+
});
|
|
47
|
+
function interpolate(message, values = {}) {
|
|
48
|
+
return message.replace(
|
|
49
|
+
/\{([a-zA-Z][a-zA-Z0-9_]*)\}/g,
|
|
50
|
+
(match, key) => Object.prototype.hasOwnProperty.call(values, key) ? String(values[key]) : match
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function CboxLocalizationProvider({ locale, translate, formatDate = defaultDate, children }) {
|
|
54
|
+
const value = useMemo(() => ({ locale, t: translate, formatDate }), [locale, translate, formatDate]);
|
|
55
|
+
return /* @__PURE__ */ jsx(Localization.Provider, { value, children });
|
|
56
|
+
}
|
|
57
|
+
function useCboxLocalization() {
|
|
58
|
+
return useContext(Localization);
|
|
59
|
+
}
|
|
60
|
+
function defaultDate(value) {
|
|
61
|
+
return new Date(value).toLocaleString();
|
|
62
|
+
}
|
|
63
|
+
var KEPT = "cbox-app-catalog";
|
|
64
|
+
var EVENT = "cbox-app-catalog";
|
|
65
|
+
var EMPTY = [];
|
|
66
|
+
var cache = { raw: null, apps: EMPTY };
|
|
67
|
+
function storage() {
|
|
68
|
+
try {
|
|
69
|
+
return typeof window === "undefined" ? null : window.localStorage;
|
|
70
|
+
} catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function readKnownAppCatalog() {
|
|
75
|
+
let raw = null;
|
|
76
|
+
try {
|
|
77
|
+
raw = storage()?.getItem(KEPT) ?? null;
|
|
78
|
+
} catch {
|
|
79
|
+
raw = null;
|
|
80
|
+
}
|
|
81
|
+
if (raw === cache.raw) return cache.apps;
|
|
82
|
+
let apps = EMPTY;
|
|
83
|
+
try {
|
|
84
|
+
const value = raw === null ? [] : JSON.parse(raw);
|
|
85
|
+
if (Array.isArray(value)) {
|
|
86
|
+
apps = value.flatMap((entry) => {
|
|
87
|
+
if (typeof entry !== "object" || entry === null) return [];
|
|
88
|
+
const app = entry;
|
|
89
|
+
if (typeof app.key !== "string" || typeof app.name !== "string") return [];
|
|
90
|
+
return [{ key: app.key, name: app.name, icon_url: secureIconUrl(app.icon_url), icon_dark_url: secureIconUrl(app.icon_dark_url) }];
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
apps = EMPTY;
|
|
95
|
+
}
|
|
96
|
+
cache = { raw, apps };
|
|
97
|
+
return apps;
|
|
98
|
+
}
|
|
99
|
+
function subscribe(change) {
|
|
100
|
+
if (typeof window === "undefined") return () => void 0;
|
|
101
|
+
const storageChange = (event) => {
|
|
102
|
+
if (event.key === KEPT) change();
|
|
103
|
+
};
|
|
104
|
+
document.addEventListener(EVENT, change);
|
|
105
|
+
window.addEventListener("storage", storageChange);
|
|
106
|
+
return () => {
|
|
107
|
+
document.removeEventListener(EVENT, change);
|
|
108
|
+
window.removeEventListener("storage", storageChange);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function rememberAppCatalog(apps) {
|
|
112
|
+
const kept = apps.map(({ key, name, icon_url, icon_dark_url }) => ({ key, name, icon_url: icon_url ?? null, icon_dark_url: icon_dark_url ?? null }));
|
|
113
|
+
try {
|
|
114
|
+
storage()?.setItem(KEPT, JSON.stringify(kept));
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
if (typeof document !== "undefined") document.dispatchEvent(new CustomEvent(EVENT));
|
|
118
|
+
}
|
|
119
|
+
function useKnownAppCatalog() {
|
|
120
|
+
return useSyncExternalStore(subscribe, readKnownAppCatalog, () => EMPTY);
|
|
121
|
+
}
|
|
122
|
+
function secureIconUrl(url) {
|
|
123
|
+
if (typeof url !== "string" || url === "") return null;
|
|
124
|
+
try {
|
|
125
|
+
const parsed = new URL(url);
|
|
126
|
+
return parsed.protocol === "https:" ? parsed.href : null;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function initials(name) {
|
|
132
|
+
return name.replace(/^Cbox\s*·\s*/i, "").slice(0, 2);
|
|
133
|
+
}
|
|
134
|
+
function CboxAppIcon({ name, iconUrl, iconDarkUrl, className = "h-6 w-6", fallback }) {
|
|
135
|
+
const light = secureIconUrl(iconUrl);
|
|
136
|
+
const dark = secureIconUrl(iconDarkUrl);
|
|
137
|
+
const [failed, setFailed] = useState(null);
|
|
138
|
+
const key = `${light ?? ""}|${dark ?? ""}`;
|
|
139
|
+
if (light === null || failed === key) {
|
|
140
|
+
if (fallback !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
141
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("grid shrink-0 place-items-center rounded bg-secondary text-[11px] font-semibold text-foreground", className), children: initials(name) });
|
|
142
|
+
}
|
|
143
|
+
const lost = () => setFailed(key);
|
|
144
|
+
return /* @__PURE__ */ jsxs("span", { "aria-hidden": true, className: cn("cbx-app-icon-box inline-flex shrink-0 items-center justify-center", className), children: [
|
|
145
|
+
/* @__PURE__ */ jsx("img", { src: light, alt: "", decoding: "async", onError: lost, className: cn("cbx-app-icon", dark === null ? "cbx-app-icon-single" : "cbx-app-icon-light", "h-full w-full") }),
|
|
146
|
+
dark !== null && /* @__PURE__ */ jsx("img", { src: dark, alt: "", decoding: "async", onError: lost, className: "cbx-app-icon cbx-app-icon-dark h-full w-full" })
|
|
147
|
+
] });
|
|
148
|
+
}
|
|
149
|
+
function CboxServiceMark({ appKey, className = "h-6 w-6", fallback = null }) {
|
|
150
|
+
const app = useKnownAppCatalog().find((entry) => entry.key === appKey);
|
|
151
|
+
if (app === void 0 || app.icon_url === null) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
152
|
+
return /* @__PURE__ */ jsx(CboxAppIcon, { name: app.name, iconUrl: app.icon_url, iconDarkUrl: app.icon_dark_url, className, fallback: fallback ?? void 0 });
|
|
153
|
+
}
|
|
46
154
|
function CboxBrand({
|
|
47
155
|
appKey,
|
|
48
156
|
submark,
|
|
@@ -52,7 +160,7 @@ function CboxBrand({
|
|
|
52
160
|
className
|
|
53
161
|
}) {
|
|
54
162
|
if (collapsed) {
|
|
55
|
-
|
|
163
|
+
const cboxIcon = /* @__PURE__ */ jsx(
|
|
56
164
|
"img",
|
|
57
165
|
{
|
|
58
166
|
src: `${assetBase}/cbox-icon.png`,
|
|
@@ -63,6 +171,7 @@ function CboxBrand({
|
|
|
63
171
|
className: cn("h-9 w-9 rounded-md", className)
|
|
64
172
|
}
|
|
65
173
|
);
|
|
174
|
+
return /* @__PURE__ */ jsx(CboxServiceMark, { appKey, className: cn("h-9 w-9", className), fallback: cboxIcon });
|
|
66
175
|
}
|
|
67
176
|
const wordmark = tone === "light" ? `${assetBase}/cbox-logo-white.png` : `${assetBase}/cbox-logo.png`;
|
|
68
177
|
const wordmarkRetina = tone === "light" ? `${assetBase}/cbox-logo-white@2x.png` : `${assetBase}/cbox-logo@2x.png`;
|
|
@@ -84,14 +193,17 @@ function CboxBrand({
|
|
|
84
193
|
draggable: false
|
|
85
194
|
}
|
|
86
195
|
),
|
|
87
|
-
submarkText !== null && submarkText !== "" ? /* @__PURE__ */
|
|
196
|
+
submarkText !== null && submarkText !== "" ? /* @__PURE__ */ jsxs(
|
|
88
197
|
"span",
|
|
89
198
|
{
|
|
90
199
|
className: cn(
|
|
91
|
-
"text-
|
|
200
|
+
"text-[13px] font-semibold whitespace-nowrap",
|
|
92
201
|
tone === "light" ? "text-white/60" : "text-muted-foreground/80"
|
|
93
202
|
),
|
|
94
|
-
children:
|
|
203
|
+
children: [
|
|
204
|
+
"\xB7 ",
|
|
205
|
+
submarkText
|
|
206
|
+
]
|
|
95
207
|
}
|
|
96
208
|
) : null
|
|
97
209
|
]
|
|
@@ -109,6 +221,7 @@ function CboxAppSidebar({
|
|
|
109
221
|
collapsed = false,
|
|
110
222
|
className
|
|
111
223
|
}) {
|
|
224
|
+
const { t } = useCboxLocalization();
|
|
112
225
|
return /* @__PURE__ */ jsxs(
|
|
113
226
|
"aside",
|
|
114
227
|
{
|
|
@@ -124,7 +237,7 @@ function CboxAppSidebar({
|
|
|
124
237
|
LinkComponent,
|
|
125
238
|
{
|
|
126
239
|
href: brandHref,
|
|
127
|
-
"aria-label": "Home",
|
|
240
|
+
"aria-label": t("Home"),
|
|
128
241
|
className: cn(
|
|
129
242
|
"flex h-14 shrink-0 items-center border-b border-sidebar-border/70",
|
|
130
243
|
collapsed ? "justify-center px-0" : "px-4"
|
|
@@ -197,6 +310,7 @@ function CboxMobileNavDrawer({
|
|
|
197
310
|
currentPath,
|
|
198
311
|
LinkComponent
|
|
199
312
|
}) {
|
|
313
|
+
const { t } = useCboxLocalization();
|
|
200
314
|
useEffect(() => {
|
|
201
315
|
if (!open) {
|
|
202
316
|
return;
|
|
@@ -238,7 +352,7 @@ function CboxMobileNavDrawer({
|
|
|
238
352
|
{
|
|
239
353
|
role: "dialog",
|
|
240
354
|
"aria-modal": "true",
|
|
241
|
-
"aria-label": "Navigation",
|
|
355
|
+
"aria-label": t("Navigation"),
|
|
242
356
|
className: cn(
|
|
243
357
|
"absolute inset-y-0 left-0 flex w-[280px] max-w-[80vw] flex-col border-r border-sidebar-border bg-sidebar shadow-xl transition-transform duration-200 ease-out",
|
|
244
358
|
open ? "translate-x-0" : "-translate-x-full"
|
|
@@ -249,7 +363,7 @@ function CboxMobileNavDrawer({
|
|
|
249
363
|
LinkComponent,
|
|
250
364
|
{
|
|
251
365
|
href: brandHref,
|
|
252
|
-
"aria-label": "Home",
|
|
366
|
+
"aria-label": t("Home"),
|
|
253
367
|
className: "flex items-center",
|
|
254
368
|
children: /* @__PURE__ */ jsx(
|
|
255
369
|
CboxBrand,
|
|
@@ -267,7 +381,7 @@ function CboxMobileNavDrawer({
|
|
|
267
381
|
{
|
|
268
382
|
type: "button",
|
|
269
383
|
onClick: onClose,
|
|
270
|
-
"aria-label": "Close navigation",
|
|
384
|
+
"aria-label": t("Close navigation"),
|
|
271
385
|
className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
|
|
272
386
|
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4", strokeWidth: 1.8 })
|
|
273
387
|
}
|
|
@@ -314,6 +428,7 @@ function CboxMobileNavDrawer({
|
|
|
314
428
|
);
|
|
315
429
|
}
|
|
316
430
|
function AppSwitcher({ className, onLockedAppClick }) {
|
|
431
|
+
const { t } = useCboxLocalization();
|
|
317
432
|
const cboxId = useCboxIdOptional();
|
|
318
433
|
const [open, setOpen] = useState(false);
|
|
319
434
|
const [query, setQuery] = useState("");
|
|
@@ -396,7 +511,7 @@ function AppSwitcher({ className, onLockedAppClick }) {
|
|
|
396
511
|
}
|
|
397
512
|
}, [navigableHrefs.length, activeIndex]);
|
|
398
513
|
const triggerMonogram = currentApp?.monogram ?? currentApp?.name.slice(0, 1).toUpperCase() ?? "\u2B1B";
|
|
399
|
-
const triggerName = currentApp?.name ?? "Apps";
|
|
514
|
+
const triggerName = currentApp?.name ?? t("Apps");
|
|
400
515
|
const triggerAccent = currentApp?.accentColor;
|
|
401
516
|
function onSearchKeyDown(event) {
|
|
402
517
|
if (event.key === "ArrowDown") {
|
|
@@ -444,21 +559,13 @@ function AppSwitcher({ className, onLockedAppClick }) {
|
|
|
444
559
|
/* @__PURE__ */ jsx(
|
|
445
560
|
"span",
|
|
446
561
|
{
|
|
447
|
-
style: triggerAccent !== void 0 ? {
|
|
562
|
+
style: triggerAccent !== void 0 && secureIconUrl(currentApp?.iconUrl) === null ? {
|
|
448
563
|
backgroundColor: triggerAccent,
|
|
449
564
|
color: "#fff",
|
|
450
565
|
borderColor: triggerAccent
|
|
451
566
|
} : void 0,
|
|
452
567
|
className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-sm border border-border bg-secondary text-[10px] font-bold",
|
|
453
|
-
children: currentApp
|
|
454
|
-
"img",
|
|
455
|
-
{
|
|
456
|
-
src: currentApp.iconUrl,
|
|
457
|
-
alt: "",
|
|
458
|
-
className: "h-3.5 w-3.5",
|
|
459
|
-
loading: "eager"
|
|
460
|
-
}
|
|
461
|
-
) : triggerMonogram
|
|
568
|
+
children: currentApp ? /* @__PURE__ */ jsx(CboxAppIcon, { name: currentApp.name, iconUrl: currentApp.iconUrl, iconDarkUrl: currentApp.iconDarkUrl, className: "h-3.5 w-3.5", fallback: /* @__PURE__ */ jsx(Fragment, { children: triggerMonogram }) }) : triggerMonogram
|
|
462
569
|
}
|
|
463
570
|
),
|
|
464
571
|
/* @__PURE__ */ jsx("span", { className: "max-w-[140px] truncate", children: triggerName }),
|
|
@@ -470,10 +577,10 @@ function AppSwitcher({ className, onLockedAppClick }) {
|
|
|
470
577
|
"div",
|
|
471
578
|
{
|
|
472
579
|
role: "dialog",
|
|
473
|
-
"aria-label": "Switch app",
|
|
580
|
+
"aria-label": t("Switch app"),
|
|
474
581
|
className: "absolute left-0 top-full z-50 mt-1 w-[min(22rem,calc(100vw-1.5rem))] overflow-hidden rounded-lg border border-border bg-popover shadow-lg",
|
|
475
582
|
children: [
|
|
476
|
-
/* @__PURE__ */ jsx("div", { className: "border-b border-border/70 px-3 pt-2.5 pb-1.5", children: /* @__PURE__ */ jsx("p", { className: "text-mono text-[10px] font-semibold tracking-[0.18em] text-muted-foreground uppercase", children: "Switch app" }) }),
|
|
583
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-border/70 px-3 pt-2.5 pb-1.5", children: /* @__PURE__ */ jsx("p", { className: "text-mono text-[10px] font-semibold tracking-[0.18em] text-muted-foreground uppercase", children: t("Switch app") }) }),
|
|
477
584
|
/* @__PURE__ */ jsx("div", { className: "border-b border-border/70 px-2 py-1.5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md px-2 py-1 focus-within:bg-secondary", children: [
|
|
478
585
|
/* @__PURE__ */ jsx(SearchIcon, {}),
|
|
479
586
|
/* @__PURE__ */ jsx(
|
|
@@ -484,8 +591,8 @@ function AppSwitcher({ className, onLockedAppClick }) {
|
|
|
484
591
|
value: query,
|
|
485
592
|
onChange: (e) => setQuery(e.target.value),
|
|
486
593
|
onKeyDown: onSearchKeyDown,
|
|
487
|
-
placeholder: "Filter apps\u2026",
|
|
488
|
-
"aria-label": "Filter apps",
|
|
594
|
+
placeholder: t("Filter apps\u2026"),
|
|
595
|
+
"aria-label": t("Filter apps"),
|
|
489
596
|
autoComplete: "off",
|
|
490
597
|
spellCheck: false,
|
|
491
598
|
className: "w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground/60 focus:outline-none"
|
|
@@ -493,15 +600,7 @@ function AppSwitcher({ className, onLockedAppClick }) {
|
|
|
493
600
|
)
|
|
494
601
|
] }) }),
|
|
495
602
|
/* @__PURE__ */ jsxs("div", { className: "max-h-[60vh] overflow-y-auto py-1", children: [
|
|
496
|
-
totalMatches === 0 ? /* @__PURE__ */
|
|
497
|
-
"No apps match",
|
|
498
|
-
" ",
|
|
499
|
-
/* @__PURE__ */ jsxs("span", { className: "text-mono text-foreground", children: [
|
|
500
|
-
"\u201C",
|
|
501
|
-
query,
|
|
502
|
-
"\u201D"
|
|
503
|
-
] })
|
|
504
|
-
] }) : null,
|
|
603
|
+
totalMatches === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-[12px] text-muted-foreground", children: t("No apps match \u201C{query}\u201D", { query }) }) : null,
|
|
505
604
|
entitled.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-0.5 p-1", children: entitled.map((app) => {
|
|
506
605
|
const navIndex = findNavIndex(navigableHrefs, app);
|
|
507
606
|
return /* @__PURE__ */ jsx(
|
|
@@ -523,7 +622,7 @@ function AppSwitcher({ className, onLockedAppClick }) {
|
|
|
523
622
|
}) }) : null,
|
|
524
623
|
locked.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
525
624
|
entitled.length > 0 ? /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border/50" }) : null,
|
|
526
|
-
/* @__PURE__ */ jsx("p", { className: "text-mono px-3 py-1.5 text-[10px] font-semibold tracking-[0.18em] text-muted-foreground/70 uppercase", children: "Available" }),
|
|
625
|
+
/* @__PURE__ */ jsx("p", { className: "text-mono px-3 py-1.5 text-[10px] font-semibold tracking-[0.18em] text-muted-foreground/70 uppercase", children: t("Available") }),
|
|
527
626
|
/* @__PURE__ */ jsx("ul", { className: "space-y-0.5 p-1 pt-0", children: locked.map((app) => {
|
|
528
627
|
const navIndex = findNavIndex(navigableHrefs, app);
|
|
529
628
|
return /* @__PURE__ */ jsx(
|
|
@@ -561,11 +660,12 @@ function AppRow({
|
|
|
561
660
|
onLockedAppClick,
|
|
562
661
|
onNavigate
|
|
563
662
|
}) {
|
|
663
|
+
const { t } = useCboxLocalization();
|
|
564
664
|
const isComingSoon = app.comingSoon === true;
|
|
565
665
|
const isLocked = app.entitled === false || isComingSoon;
|
|
566
666
|
const monogram = app.monogram ?? app.name.slice(0, 1).toUpperCase();
|
|
567
667
|
const accent = app.accentColor;
|
|
568
|
-
const tileStyle = accent !== void 0 && app.iconUrl ===
|
|
668
|
+
const tileStyle = accent !== void 0 && secureIconUrl(app.iconUrl) === null ? { backgroundColor: accent, color: "#fff", borderColor: accent } : void 0;
|
|
569
669
|
const href = orgSlug !== void 0 && app.current !== true ? buildOrgUrl(app.url, orgSlug) : app.url;
|
|
570
670
|
const tile = /* @__PURE__ */ jsx(
|
|
571
671
|
"div",
|
|
@@ -576,7 +676,7 @@ function AppRow({
|
|
|
576
676
|
app.current && "ring-2 ring-accent-edge ring-offset-1 ring-offset-popover",
|
|
577
677
|
isLocked && "opacity-60 grayscale"
|
|
578
678
|
),
|
|
579
|
-
children:
|
|
679
|
+
children: /* @__PURE__ */ jsx(CboxAppIcon, { name: app.name, iconUrl: app.iconUrl, iconDarkUrl: app.iconDarkUrl, className: "h-5 w-5", fallback: /* @__PURE__ */ jsx("span", { children: monogram }) })
|
|
580
680
|
}
|
|
581
681
|
);
|
|
582
682
|
const body = /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
|
|
@@ -591,7 +691,7 @@ function AppRow({
|
|
|
591
691
|
children: app.name
|
|
592
692
|
}
|
|
593
693
|
),
|
|
594
|
-
app.current ? /* @__PURE__ */ jsx("span", { className: "text-mono shrink-0 rounded-sm border border-accent-edge/40 bg-accent-soft px-1 text-[9px] font-semibold tracking-[0.14em] text-accent uppercase", children: "current" }) : null
|
|
694
|
+
app.current ? /* @__PURE__ */ jsx("span", { className: "text-mono shrink-0 rounded-sm border border-accent-edge/40 bg-accent-soft px-1 text-[9px] font-semibold tracking-[0.14em] text-accent uppercase", children: t("current") }) : null
|
|
595
695
|
] }),
|
|
596
696
|
app.description ? /* @__PURE__ */ jsx(
|
|
597
697
|
"span",
|
|
@@ -604,9 +704,9 @@ function AppRow({
|
|
|
604
704
|
}
|
|
605
705
|
) : null
|
|
606
706
|
] });
|
|
607
|
-
const trailing = isComingSoon ? /* @__PURE__ */ jsx("span", { className: "text-mono shrink-0 rounded-sm border border-border bg-secondary px-1.5 py-0.5 text-[9px] font-semibold tracking-[0.14em] text-muted-foreground uppercase", children: "soon" }) : isLocked ? /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1 text-[10px] font-medium text-muted-foreground", children: [
|
|
707
|
+
const trailing = isComingSoon ? /* @__PURE__ */ jsx("span", { className: "text-mono shrink-0 rounded-sm border border-border bg-secondary px-1.5 py-0.5 text-[9px] font-semibold tracking-[0.14em] text-muted-foreground uppercase", children: t("soon") }) : isLocked ? /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1 text-[10px] font-medium text-muted-foreground", children: [
|
|
608
708
|
/* @__PURE__ */ jsx(Lock, {}),
|
|
609
|
-
"Add"
|
|
709
|
+
t("Add")
|
|
610
710
|
] }) : null;
|
|
611
711
|
if (isComingSoon) {
|
|
612
712
|
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
@@ -614,7 +714,7 @@ function AppRow({
|
|
|
614
714
|
{
|
|
615
715
|
"aria-disabled": true,
|
|
616
716
|
className: "flex w-full cursor-not-allowed items-center gap-3 rounded-md px-2.5 py-2",
|
|
617
|
-
title:
|
|
717
|
+
title: t("{name} \u2014 coming soon", { name: app.name }),
|
|
618
718
|
children: [
|
|
619
719
|
tile,
|
|
620
720
|
body,
|
|
@@ -638,7 +738,7 @@ function AppRow({
|
|
|
638
738
|
highlighted && "bg-secondary",
|
|
639
739
|
onLockedAppClick !== void 0 ? "hover:bg-secondary" : "cursor-default"
|
|
640
740
|
),
|
|
641
|
-
title:
|
|
741
|
+
title: t("Add {name} to your plan", { name: app.name }),
|
|
642
742
|
children: [
|
|
643
743
|
tile,
|
|
644
744
|
body,
|
|
@@ -832,6 +932,7 @@ function SurfaceModal({
|
|
|
832
932
|
size = "wide",
|
|
833
933
|
children
|
|
834
934
|
}) {
|
|
935
|
+
const { t } = useCboxLocalization();
|
|
835
936
|
const dialogRef = useRef(null);
|
|
836
937
|
const previousFocusRef = useRef(null);
|
|
837
938
|
useEffect(() => {
|
|
@@ -928,7 +1029,7 @@ function SurfaceModal({
|
|
|
928
1029
|
type: "button",
|
|
929
1030
|
onClick: onClose,
|
|
930
1031
|
className: "ml-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
|
|
931
|
-
"aria-label": "Close",
|
|
1032
|
+
"aria-label": t("Close"),
|
|
932
1033
|
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4", strokeWidth: 1.8 })
|
|
933
1034
|
}
|
|
934
1035
|
)
|
|
@@ -947,6 +1048,7 @@ var TONES = {
|
|
|
947
1048
|
critical: "destructive"
|
|
948
1049
|
};
|
|
949
1050
|
function InboxSurface({ open, onClose }) {
|
|
1051
|
+
const { t, formatDate } = useCboxLocalization();
|
|
950
1052
|
const transport = useTransport();
|
|
951
1053
|
const cboxId = useCboxIdOptional();
|
|
952
1054
|
const [inbox, setInbox] = useState(null);
|
|
@@ -974,7 +1076,7 @@ function InboxSurface({ open, onClose }) {
|
|
|
974
1076
|
if (!cancelled) setInbox(body);
|
|
975
1077
|
} catch (e) {
|
|
976
1078
|
if (!cancelled) {
|
|
977
|
-
setError(e instanceof Error ? e.message : "Load failed");
|
|
1079
|
+
setError(e instanceof Error ? e.message : t("Load failed"));
|
|
978
1080
|
}
|
|
979
1081
|
} finally {
|
|
980
1082
|
if (!cancelled) setLoading(false);
|
|
@@ -1025,15 +1127,15 @@ function InboxSurface({ open, onClose }) {
|
|
|
1025
1127
|
{
|
|
1026
1128
|
open,
|
|
1027
1129
|
onClose,
|
|
1028
|
-
eyebrow: "Notifications",
|
|
1029
|
-
title: unread > 0 ?
|
|
1130
|
+
eyebrow: t("Notifications"),
|
|
1131
|
+
title: unread > 0 ? t("Inbox \xB7 {count} unread", { count: unread }) : t("Inbox \xB7 all caught up"),
|
|
1030
1132
|
headerActions: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1031
1133
|
/* @__PURE__ */ jsx(
|
|
1032
1134
|
FilterTab,
|
|
1033
1135
|
{
|
|
1034
1136
|
active: filter === "all",
|
|
1035
1137
|
onClick: () => setFilter("all"),
|
|
1036
|
-
children: "All"
|
|
1138
|
+
children: t("All")
|
|
1037
1139
|
}
|
|
1038
1140
|
),
|
|
1039
1141
|
/* @__PURE__ */ jsx(
|
|
@@ -1041,18 +1143,12 @@ function InboxSurface({ open, onClose }) {
|
|
|
1041
1143
|
{
|
|
1042
1144
|
active: filter === "unread",
|
|
1043
1145
|
onClick: () => setFilter("unread"),
|
|
1044
|
-
children: "Unread"
|
|
1146
|
+
children: t("Unread")
|
|
1045
1147
|
}
|
|
1046
1148
|
)
|
|
1047
1149
|
] }),
|
|
1048
1150
|
footer: inbox !== null && baseUrl !== null ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1049
|
-
/* @__PURE__ */
|
|
1050
|
-
"Showing ",
|
|
1051
|
-
filtered.length,
|
|
1052
|
-
" of ",
|
|
1053
|
-
items.length,
|
|
1054
|
-
"."
|
|
1055
|
-
] }),
|
|
1151
|
+
/* @__PURE__ */ jsx("span", { children: t("Showing {shown} of {total}.", { shown: filtered.length, total: items.length }) }),
|
|
1056
1152
|
/* @__PURE__ */ jsx(
|
|
1057
1153
|
"a",
|
|
1058
1154
|
{
|
|
@@ -1060,18 +1156,18 @@ function InboxSurface({ open, onClose }) {
|
|
|
1060
1156
|
target: "_blank",
|
|
1061
1157
|
rel: "noreferrer",
|
|
1062
1158
|
className: "font-medium text-foreground hover:underline",
|
|
1063
|
-
children: "Open in new tab \u2197"
|
|
1159
|
+
children: t("Open in new tab \u2197")
|
|
1064
1160
|
}
|
|
1065
1161
|
)
|
|
1066
1162
|
] }) : /* @__PURE__ */ jsx("span", { children: "\u2014" }),
|
|
1067
1163
|
children: error !== null ? /* @__PURE__ */ jsxs("div", { className: "px-5 py-8 text-center", children: [
|
|
1068
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: "Couldn't load inbox" }),
|
|
1164
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-destructive", children: t("Couldn't load inbox") }),
|
|
1069
1165
|
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: error })
|
|
1070
|
-
] }) : loading && inbox === null ? /* @__PURE__ */ jsx("div", { className: "px-5 py-12 text-center text-sm text-muted-foreground", children: "Loading\u2026" }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-5 py-12", children: /* @__PURE__ */ jsx(
|
|
1166
|
+
] }) : loading && inbox === null ? /* @__PURE__ */ jsx("div", { className: "px-5 py-12 text-center text-sm text-muted-foreground", children: t("Loading\u2026") }) : filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-5 py-12", children: /* @__PURE__ */ jsx(
|
|
1071
1167
|
EmptyState,
|
|
1072
1168
|
{
|
|
1073
|
-
title: filter === "unread" ? "No unread notifications" : "No notifications yet",
|
|
1074
|
-
description: "When a Cbox app sends you something \u2014 invitation accepted, billing event, security alert \u2014 it lands here."
|
|
1169
|
+
title: filter === "unread" ? t("No unread notifications") : t("No notifications yet"),
|
|
1170
|
+
description: t("When a Cbox app sends you something \u2014 invitation accepted, billing event, security alert \u2014 it lands here.")
|
|
1075
1171
|
}
|
|
1076
1172
|
) }) : /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/70", children: filtered.map((item) => {
|
|
1077
1173
|
const data = item.data ?? {};
|
|
@@ -1105,9 +1201,7 @@ function InboxSurface({ open, onClose }) {
|
|
|
1105
1201
|
/* @__PURE__ */ jsxs("p", { className: "text-mono mt-1 text-[10px] text-muted-foreground", children: [
|
|
1106
1202
|
item.event_type,
|
|
1107
1203
|
" \xB7 ",
|
|
1108
|
-
|
|
1109
|
-
item.created_at
|
|
1110
|
-
).toLocaleString()
|
|
1204
|
+
formatDate(item.created_at)
|
|
1111
1205
|
] })
|
|
1112
1206
|
] }),
|
|
1113
1207
|
item.read_at === null ? /* @__PURE__ */ jsx(
|
|
@@ -1120,8 +1214,8 @@ function InboxSurface({ open, onClose }) {
|
|
|
1120
1214
|
void markRead(item.id);
|
|
1121
1215
|
},
|
|
1122
1216
|
className: "shrink-0 rounded-md border border-border bg-card px-2 py-1 text-xs font-medium text-foreground transition-colors hover:border-border-strong",
|
|
1123
|
-
"aria-label": "Mark as read",
|
|
1124
|
-
children: "Mark read"
|
|
1217
|
+
"aria-label": t("Mark as read"),
|
|
1218
|
+
children: t("Mark read")
|
|
1125
1219
|
}
|
|
1126
1220
|
) : null
|
|
1127
1221
|
] }) })
|
|
@@ -1225,6 +1319,7 @@ function useTransport() {
|
|
|
1225
1319
|
return t;
|
|
1226
1320
|
}
|
|
1227
1321
|
function NotificationsBell({ className }) {
|
|
1322
|
+
const { t, formatDate } = useCboxLocalization();
|
|
1228
1323
|
const ctx = useCboxIdOptional();
|
|
1229
1324
|
const surfaces = useCboxSurfacesOptional();
|
|
1230
1325
|
const [open, setOpen] = useState(false);
|
|
@@ -1256,7 +1351,7 @@ function NotificationsBell({ className }) {
|
|
|
1256
1351
|
"button",
|
|
1257
1352
|
{
|
|
1258
1353
|
type: "button",
|
|
1259
|
-
"aria-label": "Notifications",
|
|
1354
|
+
"aria-label": t("Notifications"),
|
|
1260
1355
|
disabled: true,
|
|
1261
1356
|
className: "relative flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground opacity-60 cursor-default",
|
|
1262
1357
|
children: /* @__PURE__ */ jsx(Bell, {})
|
|
@@ -1271,7 +1366,7 @@ function NotificationsBell({ className }) {
|
|
|
1271
1366
|
{
|
|
1272
1367
|
type: "button",
|
|
1273
1368
|
onClick: () => setOpen((v) => !v),
|
|
1274
|
-
"aria-label": unreadCount > 0 ?
|
|
1369
|
+
"aria-label": unreadCount > 0 ? t("{count} unread notifications", { count: unreadCount }) : t("Notifications"),
|
|
1275
1370
|
"aria-expanded": open,
|
|
1276
1371
|
"aria-haspopup": "menu",
|
|
1277
1372
|
className: "relative flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground data-[open=true]:bg-secondary data-[open=true]:text-foreground",
|
|
@@ -1289,15 +1384,12 @@ function NotificationsBell({ className }) {
|
|
|
1289
1384
|
className: "absolute right-0 z-30 mt-2 w-[360px] origin-top-right rounded-md border border-border bg-popover text-popover-foreground shadow-lg",
|
|
1290
1385
|
children: [
|
|
1291
1386
|
/* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between border-b border-border/70 px-3 py-2", children: [
|
|
1292
|
-
/* @__PURE__ */ jsx("span", { className: "text-mono text-[10px] font-semibold tracking-[0.18em] uppercase text-muted-foreground", children: "Notifications" }),
|
|
1293
|
-
unreadCount > 0 ? /* @__PURE__ */
|
|
1294
|
-
unreadCount,
|
|
1295
|
-
" unread"
|
|
1296
|
-
] }) : null
|
|
1387
|
+
/* @__PURE__ */ jsx("span", { className: "text-mono text-[10px] font-semibold tracking-[0.18em] uppercase text-muted-foreground", children: t("Notifications") }),
|
|
1388
|
+
unreadCount > 0 ? /* @__PURE__ */ jsx("span", { className: "text-mono text-[10px] tabular-nums text-muted-foreground", children: t("{count} unread", { count: unreadCount }) }) : null
|
|
1297
1389
|
] }),
|
|
1298
1390
|
/* @__PURE__ */ jsx("div", { className: "max-h-[420px] overflow-y-auto", children: previewItems === void 0 || previewItems.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "px-3 py-8 text-center", children: [
|
|
1299
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: "All caught up" }),
|
|
1300
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: "No recent notifications." })
|
|
1391
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: t("All caught up") }),
|
|
1392
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t("No recent notifications.") })
|
|
1301
1393
|
] }) : /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border/70", children: previewItems.map((item) => /* @__PURE__ */ jsx(
|
|
1302
1394
|
PreviewRow,
|
|
1303
1395
|
{
|
|
@@ -1316,14 +1408,14 @@ function NotificationsBell({ className }) {
|
|
|
1316
1408
|
surfaces.open("inbox");
|
|
1317
1409
|
},
|
|
1318
1410
|
className: "flex w-full items-center justify-center border-t border-border/70 px-3 py-2 text-xs font-medium text-foreground transition-colors hover:bg-secondary",
|
|
1319
|
-
children: "See all notifications \u2192"
|
|
1411
|
+
children: t("See all notifications \u2192")
|
|
1320
1412
|
}
|
|
1321
1413
|
) : /* @__PURE__ */ jsx(
|
|
1322
1414
|
"a",
|
|
1323
1415
|
{
|
|
1324
1416
|
href: inboxUrl,
|
|
1325
1417
|
className: "flex items-center justify-center border-t border-border/70 px-3 py-2 text-xs font-medium text-foreground transition-colors hover:bg-secondary",
|
|
1326
|
-
children: "See all notifications \u2192"
|
|
1418
|
+
children: t("See all notifications \u2192")
|
|
1327
1419
|
}
|
|
1328
1420
|
)
|
|
1329
1421
|
]
|
|
@@ -1336,10 +1428,11 @@ function PreviewRow({
|
|
|
1336
1428
|
markReadUrl,
|
|
1337
1429
|
onMarkedRead
|
|
1338
1430
|
}) {
|
|
1431
|
+
const { t, formatDate } = useCboxLocalization();
|
|
1339
1432
|
const tone = item.severity === "critical" ? "bg-destructive" : item.severity === "warning" ? "bg-warning" : "bg-info";
|
|
1340
1433
|
const ts = (() => {
|
|
1341
1434
|
try {
|
|
1342
|
-
return
|
|
1435
|
+
return formatDate(item.createdAt);
|
|
1343
1436
|
} catch {
|
|
1344
1437
|
return item.createdAt;
|
|
1345
1438
|
}
|
|
@@ -1393,8 +1486,8 @@ function PreviewRow({
|
|
|
1393
1486
|
type: "button",
|
|
1394
1487
|
onClick: markRead,
|
|
1395
1488
|
className: "shrink-0 rounded-md px-2 py-1 text-[10px] font-medium text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground",
|
|
1396
|
-
"aria-label": "Mark as read",
|
|
1397
|
-
title: "Mark as read",
|
|
1489
|
+
"aria-label": t("Mark as read"),
|
|
1490
|
+
title: t("Mark as read"),
|
|
1398
1491
|
children: "\u2713"
|
|
1399
1492
|
}
|
|
1400
1493
|
) : null
|
|
@@ -1432,7 +1525,8 @@ function Bell() {
|
|
|
1432
1525
|
}
|
|
1433
1526
|
);
|
|
1434
1527
|
}
|
|
1435
|
-
function SearchTrigger({ onOpen, className, label
|
|
1528
|
+
function SearchTrigger({ onOpen, className, label }) {
|
|
1529
|
+
const { t } = useCboxLocalization();
|
|
1436
1530
|
const [shortcut, setShortcut] = useState("\u2318K");
|
|
1437
1531
|
useEffect(() => {
|
|
1438
1532
|
if (typeof navigator === "undefined") return;
|
|
@@ -1447,10 +1541,10 @@ function SearchTrigger({ onOpen, className, label = "Search" }) {
|
|
|
1447
1541
|
"hidden h-8 w-[180px] items-center gap-2 rounded-md border border-border bg-card pr-1.5 pl-2.5 text-xs text-muted-foreground transition-colors hover:border-border-strong hover:text-foreground sm:inline-flex",
|
|
1448
1542
|
className
|
|
1449
1543
|
),
|
|
1450
|
-
"aria-label": "Open command palette",
|
|
1544
|
+
"aria-label": t("Open command palette"),
|
|
1451
1545
|
children: [
|
|
1452
1546
|
/* @__PURE__ */ jsx(SearchIcon2, {}),
|
|
1453
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: label }),
|
|
1547
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: label ?? t("Search") }),
|
|
1454
1548
|
/* @__PURE__ */ jsx("kbd", { className: "text-mono ml-1 rounded-sm border border-border bg-secondary px-1 py-0.5 text-[10px] font-medium tracking-tight text-muted-foreground", children: shortcut })
|
|
1455
1549
|
]
|
|
1456
1550
|
}
|
|
@@ -1476,18 +1570,28 @@ function SearchIcon2() {
|
|
|
1476
1570
|
}
|
|
1477
1571
|
);
|
|
1478
1572
|
}
|
|
1479
|
-
|
|
1573
|
+
var Anchor = ({ href, className, children }) => /* @__PURE__ */ jsx("a", { href, className, children });
|
|
1574
|
+
function UserMenu({ extras, className, placement = "bottom-end", LinkComponent: Link = Anchor }) {
|
|
1575
|
+
const { t } = useCboxLocalization();
|
|
1480
1576
|
const { user, currentOrganization, urls } = useCboxId();
|
|
1481
1577
|
const [open, setOpen] = useState(false);
|
|
1482
|
-
const
|
|
1483
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("relative", className),
|
|
1578
|
+
const initials2 = user.name.split(/\s+/).filter(Boolean).slice(0, 2).map((n) => n[0]?.toUpperCase() ?? "").join("");
|
|
1579
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), onKeyDown: (event) => {
|
|
1580
|
+
if (event.key === "Escape") setOpen(false);
|
|
1581
|
+
}, onBlur: (event) => {
|
|
1582
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
1583
|
+
}, children: [
|
|
1484
1584
|
/* @__PURE__ */ jsx(
|
|
1485
1585
|
"button",
|
|
1486
1586
|
{
|
|
1487
1587
|
type: "button",
|
|
1488
1588
|
onClick: () => setOpen((v) => !v),
|
|
1489
1589
|
className: "flex h-9 items-center gap-2 rounded-md px-1.5 hover:bg-secondary",
|
|
1490
|
-
"aria-label": "Open user menu",
|
|
1590
|
+
"aria-label": t("Open user menu"),
|
|
1591
|
+
"aria-expanded": open,
|
|
1592
|
+
onKeyDown: (event) => {
|
|
1593
|
+
if (event.key === "Escape") setOpen(false);
|
|
1594
|
+
},
|
|
1491
1595
|
children: user.avatar_url ? /* @__PURE__ */ jsx(
|
|
1492
1596
|
"img",
|
|
1493
1597
|
{
|
|
@@ -1495,10 +1599,10 @@ function UserMenu({ extras, className }) {
|
|
|
1495
1599
|
alt: "",
|
|
1496
1600
|
className: "h-7 w-7 rounded-full object-cover"
|
|
1497
1601
|
}
|
|
1498
|
-
) : /* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-accent-soft text-[11px] font-semibold text-foreground", children:
|
|
1602
|
+
) : /* @__PURE__ */ jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-accent-soft text-[11px] font-semibold text-foreground", children: initials2 || "?" })
|
|
1499
1603
|
}
|
|
1500
1604
|
),
|
|
1501
|
-
open ? /* @__PURE__ */ jsxs("div", { className: "absolute
|
|
1605
|
+
open ? /* @__PURE__ */ jsxs("div", { className: cn("absolute z-50 w-64 rounded-lg border border-border bg-popover p-1 shadow-md", placement === "top-start" ? "bottom-full left-0 mb-2" : "right-0 top-full mt-1"), children: [
|
|
1502
1606
|
/* @__PURE__ */ jsxs("div", { className: "px-2 py-2", children: [
|
|
1503
1607
|
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: user.name }),
|
|
1504
1608
|
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: user.email })
|
|
@@ -1509,24 +1613,24 @@ function UserMenu({ extras, className }) {
|
|
|
1509
1613
|
] }) : null,
|
|
1510
1614
|
/* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border" }),
|
|
1511
1615
|
/* @__PURE__ */ jsxs(
|
|
1512
|
-
|
|
1616
|
+
Link,
|
|
1513
1617
|
{
|
|
1514
1618
|
href: urls.accountUrl,
|
|
1515
1619
|
className: "flex items-center justify-between rounded-sm px-2 py-1.5 text-sm hover:bg-secondary",
|
|
1516
1620
|
children: [
|
|
1517
|
-
/* @__PURE__ */ jsx("span", { children: "Manage account" }),
|
|
1621
|
+
/* @__PURE__ */ jsx("span", { children: t("Manage account") }),
|
|
1518
1622
|
/* @__PURE__ */ jsx(CrossAppArrow, {})
|
|
1519
1623
|
]
|
|
1520
1624
|
}
|
|
1521
1625
|
),
|
|
1522
1626
|
urls.orgSettingsUrl && currentOrganization ? /* @__PURE__ */ jsxs(
|
|
1523
|
-
|
|
1627
|
+
Link,
|
|
1524
1628
|
{
|
|
1525
1629
|
href: urls.orgSettingsUrl,
|
|
1526
1630
|
className: "flex items-center justify-between rounded-sm px-2 py-1.5 text-sm hover:bg-secondary",
|
|
1527
1631
|
children: [
|
|
1528
1632
|
/* @__PURE__ */ jsxs("span", { className: "truncate", children: [
|
|
1529
|
-
"Team & billing",
|
|
1633
|
+
t("Team & billing"),
|
|
1530
1634
|
/* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
1531
1635
|
" \xB7 ",
|
|
1532
1636
|
currentOrganization.name
|
|
@@ -1544,7 +1648,7 @@ function UserMenu({ extras, className }) {
|
|
|
1544
1648
|
{
|
|
1545
1649
|
type: "submit",
|
|
1546
1650
|
className: "flex w-full items-center rounded-sm px-2 py-1.5 text-left text-sm hover:bg-secondary",
|
|
1547
|
-
children: "Sign out"
|
|
1651
|
+
children: t("Sign out")
|
|
1548
1652
|
}
|
|
1549
1653
|
)
|
|
1550
1654
|
] })
|
|
@@ -1594,6 +1698,7 @@ function CboxAppShell({
|
|
|
1594
1698
|
topbarRightCluster,
|
|
1595
1699
|
children
|
|
1596
1700
|
}) {
|
|
1701
|
+
const { t } = useCboxLocalization();
|
|
1597
1702
|
const ToggleIcon = sidebarCollapsed ? PanelLeftOpen : PanelLeftClose;
|
|
1598
1703
|
const [mobileNavOpen, setMobileNavOpen] = useState(false);
|
|
1599
1704
|
useEffect(() => {
|
|
@@ -1636,8 +1741,8 @@ function CboxAppShell({
|
|
|
1636
1741
|
type: "button",
|
|
1637
1742
|
onClick: () => setMobileNavOpen(true),
|
|
1638
1743
|
className: "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground lg:hidden",
|
|
1639
|
-
"aria-label": "Open navigation",
|
|
1640
|
-
title: "Open navigation",
|
|
1744
|
+
"aria-label": t("Open navigation"),
|
|
1745
|
+
title: t("Open navigation"),
|
|
1641
1746
|
children: /* @__PURE__ */ jsx(Menu, { className: "h-4 w-4", strokeWidth: 1.8 })
|
|
1642
1747
|
}
|
|
1643
1748
|
),
|
|
@@ -1647,8 +1752,8 @@ function CboxAppShell({
|
|
|
1647
1752
|
type: "button",
|
|
1648
1753
|
onClick: onToggleSidebar,
|
|
1649
1754
|
className: "hidden h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground lg:inline-flex",
|
|
1650
|
-
"aria-label": sidebarCollapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1651
|
-
title: sidebarCollapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1755
|
+
"aria-label": sidebarCollapsed ? t("Expand sidebar") : t("Collapse sidebar"),
|
|
1756
|
+
title: sidebarCollapsed ? t("Expand sidebar") : t("Collapse sidebar"),
|
|
1652
1757
|
children: /* @__PURE__ */ jsx(
|
|
1653
1758
|
ToggleIcon,
|
|
1654
1759
|
{
|
|
@@ -1678,6 +1783,7 @@ function ContextBar({
|
|
|
1678
1783
|
LinkComponent = DefaultLink,
|
|
1679
1784
|
className
|
|
1680
1785
|
}) {
|
|
1786
|
+
const { t } = useCboxLocalization();
|
|
1681
1787
|
if (crumbs.length === 0) {
|
|
1682
1788
|
return null;
|
|
1683
1789
|
}
|
|
@@ -1685,7 +1791,7 @@ function ContextBar({
|
|
|
1685
1791
|
"nav",
|
|
1686
1792
|
{
|
|
1687
1793
|
"data-component": "context-bar",
|
|
1688
|
-
"aria-label": "Context",
|
|
1794
|
+
"aria-label": t("Context"),
|
|
1689
1795
|
className: cn("flex min-w-0 items-center gap-1", className),
|
|
1690
1796
|
children: crumbs.map((crumb, index) => /* @__PURE__ */ jsxs(
|
|
1691
1797
|
"div",
|
|
@@ -1721,6 +1827,7 @@ function ContextCrumbChip({
|
|
|
1721
1827
|
crumb,
|
|
1722
1828
|
LinkComponent
|
|
1723
1829
|
}) {
|
|
1830
|
+
const { t } = useCboxLocalization();
|
|
1724
1831
|
const [open, setOpen] = useState(false);
|
|
1725
1832
|
const ref = useRef(null);
|
|
1726
1833
|
const listId = useId();
|
|
@@ -1742,7 +1849,7 @@ function ContextCrumbChip({
|
|
|
1742
1849
|
};
|
|
1743
1850
|
}, [open]);
|
|
1744
1851
|
const hasMenu = crumb.items.length > 0 || crumb.footerLink !== void 0;
|
|
1745
|
-
return /* @__PURE__ */ jsxs("div", { ref, className: "relative", children: [
|
|
1852
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: "relative min-w-0", children: [
|
|
1746
1853
|
/* @__PURE__ */ jsxs(
|
|
1747
1854
|
"button",
|
|
1748
1855
|
{
|
|
@@ -1751,7 +1858,7 @@ function ContextCrumbChip({
|
|
|
1751
1858
|
"aria-expanded": hasMenu ? open : void 0,
|
|
1752
1859
|
"aria-haspopup": hasMenu ? "listbox" : void 0,
|
|
1753
1860
|
"aria-controls": hasMenu ? listId : void 0,
|
|
1754
|
-
"aria-label": crumb.ariaLabel ??
|
|
1861
|
+
"aria-label": crumb.ariaLabel ?? t("Switch {name}", { name: crumb.label }),
|
|
1755
1862
|
disabled: !hasMenu,
|
|
1756
1863
|
className: cn(
|
|
1757
1864
|
"flex h-8 min-w-0 items-center gap-2 rounded-md border border-transparent px-2 text-[13px] text-foreground transition-colors",
|
|
@@ -1767,7 +1874,7 @@ function ContextCrumbChip({
|
|
|
1767
1874
|
size: "sm"
|
|
1768
1875
|
}
|
|
1769
1876
|
) : null,
|
|
1770
|
-
/* @__PURE__ */ jsx("span", { className: "truncate font-
|
|
1877
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-semibold", children: crumb.label }),
|
|
1771
1878
|
hasMenu ? /* @__PURE__ */ jsx(Chevron2, { open }) : null
|
|
1772
1879
|
]
|
|
1773
1880
|
}
|
|
@@ -1822,6 +1929,7 @@ function ContextItemRow({
|
|
|
1822
1929
|
LinkComponent,
|
|
1823
1930
|
onAfterClick
|
|
1824
1931
|
}) {
|
|
1932
|
+
const { t } = useCboxLocalization();
|
|
1825
1933
|
const body = /* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-0 items-center gap-2.5 px-2.5 py-2", children: [
|
|
1826
1934
|
item.avatar !== void 0 ? /* @__PURE__ */ jsx(
|
|
1827
1935
|
ContextAvatarChip,
|
|
@@ -1850,7 +1958,7 @@ function ContextItemRow({
|
|
|
1850
1958
|
children: /* @__PURE__ */ jsx(
|
|
1851
1959
|
"span",
|
|
1852
1960
|
{
|
|
1853
|
-
"aria-label": item.settingsAriaLabel ??
|
|
1961
|
+
"aria-label": item.settingsAriaLabel ?? t("Settings for {name}", { name: item.label }),
|
|
1854
1962
|
onClickCapture: (event) => {
|
|
1855
1963
|
event.stopPropagation();
|
|
1856
1964
|
onAfterClick();
|
|
@@ -1922,8 +2030,8 @@ function ContextAvatarChip({
|
|
|
1922
2030
|
fallbackLabel,
|
|
1923
2031
|
size = "md"
|
|
1924
2032
|
}) {
|
|
1925
|
-
const
|
|
1926
|
-
const sizeClass = size === "sm" ? "h-
|
|
2033
|
+
const initials2 = avatar.initials ?? deriveInitials(fallbackLabel);
|
|
2034
|
+
const sizeClass = size === "sm" ? "h-[26px] w-[26px] text-[10px]" : "h-7 w-7 text-[11px]";
|
|
1927
2035
|
const style = avatar.src === void 0 && avatar.accent !== void 0 ? {
|
|
1928
2036
|
backgroundColor: avatar.accent,
|
|
1929
2037
|
color: "#fff"
|
|
@@ -1951,7 +2059,7 @@ function ContextAvatarChip({
|
|
|
1951
2059
|
avatar.accent === void 0 && "bg-info-soft text-info",
|
|
1952
2060
|
sizeClass
|
|
1953
2061
|
),
|
|
1954
|
-
children:
|
|
2062
|
+
children: initials2
|
|
1955
2063
|
}
|
|
1956
2064
|
);
|
|
1957
2065
|
}
|
|
@@ -2023,6 +2131,7 @@ function CboxOrgContextBar({
|
|
|
2023
2131
|
LinkComponent,
|
|
2024
2132
|
onNavigate
|
|
2025
2133
|
}) {
|
|
2134
|
+
const { t } = useCboxLocalization();
|
|
2026
2135
|
const cboxId = useCboxIdOptional();
|
|
2027
2136
|
if (cboxId === null) {
|
|
2028
2137
|
return null;
|
|
@@ -2041,7 +2150,7 @@ function CboxOrgContextBar({
|
|
|
2041
2150
|
// Cogwheel shortcut → id's org-settings page. Cross-app
|
|
2042
2151
|
// deep link; id handles the auth and rendering.
|
|
2043
2152
|
settingsHref: `${idBase}/org/${org.slug}/settings`,
|
|
2044
|
-
settingsAriaLabel:
|
|
2153
|
+
settingsAriaLabel: t("Settings for {name}", { name: org.name })
|
|
2045
2154
|
}));
|
|
2046
2155
|
const onSelect = (slug) => {
|
|
2047
2156
|
const org = organizations.find((o) => o.slug === slug);
|
|
@@ -2063,10 +2172,10 @@ function CboxOrgContextBar({
|
|
|
2063
2172
|
items,
|
|
2064
2173
|
onSelect,
|
|
2065
2174
|
footerLink: {
|
|
2066
|
-
label: "Manage organizations",
|
|
2175
|
+
label: t("Manage organizations"),
|
|
2067
2176
|
href: `${idBase}/settings/organizations`
|
|
2068
2177
|
},
|
|
2069
|
-
ariaLabel: "Switch organization"
|
|
2178
|
+
ariaLabel: t("Switch organization")
|
|
2070
2179
|
}
|
|
2071
2180
|
]
|
|
2072
2181
|
}
|
|
@@ -2080,6 +2189,7 @@ function deriveInitials2(label) {
|
|
|
2080
2189
|
return (label.slice(0, 2) || "?").toUpperCase();
|
|
2081
2190
|
}
|
|
2082
2191
|
function OrgSwitcher({ Trigger = DefaultTrigger, className }) {
|
|
2192
|
+
const { t } = useCboxLocalization();
|
|
2083
2193
|
const { currentOrganization, organizations, onSwitchOrganization } = useCboxId();
|
|
2084
2194
|
const [open, setOpen] = useState(false);
|
|
2085
2195
|
const [query, setQuery] = useState("");
|
|
@@ -2179,10 +2289,10 @@ function OrgSwitcher({ Trigger = DefaultTrigger, className }) {
|
|
|
2179
2289
|
"div",
|
|
2180
2290
|
{
|
|
2181
2291
|
role: "dialog",
|
|
2182
|
-
"aria-label": "Switch organization",
|
|
2292
|
+
"aria-label": t("Switch organization"),
|
|
2183
2293
|
className: "absolute left-0 top-full z-50 mt-1 w-64 overflow-hidden rounded-md border border-border bg-popover shadow-md",
|
|
2184
2294
|
children: [
|
|
2185
|
-
/* @__PURE__ */ jsx("div", { className: "border-b border-border/70 px-3 pt-2 pb-1", children: /* @__PURE__ */ jsx("p", { className: "text-mono text-[10px] font-semibold tracking-[0.18em] text-muted-foreground uppercase", children: "Switch organization" }) }),
|
|
2295
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-border/70 px-3 pt-2 pb-1", children: /* @__PURE__ */ jsx("p", { className: "text-mono text-[10px] font-semibold tracking-[0.18em] text-muted-foreground uppercase", children: t("Switch organization") }) }),
|
|
2186
2296
|
/* @__PURE__ */ jsx("div", { className: "border-b border-border/70 px-2 py-1.5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md px-2 py-1 focus-within:bg-secondary", children: [
|
|
2187
2297
|
/* @__PURE__ */ jsx(SearchIcon3, {}),
|
|
2188
2298
|
/* @__PURE__ */ jsx(
|
|
@@ -2193,23 +2303,15 @@ function OrgSwitcher({ Trigger = DefaultTrigger, className }) {
|
|
|
2193
2303
|
value: query,
|
|
2194
2304
|
onChange: (e) => setQuery(e.target.value),
|
|
2195
2305
|
onKeyDown: onSearchKeyDown,
|
|
2196
|
-
placeholder: "Filter\u2026",
|
|
2197
|
-
"aria-label": "Filter organizations",
|
|
2306
|
+
placeholder: t("Filter\u2026"),
|
|
2307
|
+
"aria-label": t("Filter organizations"),
|
|
2198
2308
|
autoComplete: "off",
|
|
2199
2309
|
spellCheck: false,
|
|
2200
2310
|
className: "w-full bg-transparent text-sm text-foreground placeholder:text-muted-foreground/60 focus:outline-none"
|
|
2201
2311
|
}
|
|
2202
2312
|
)
|
|
2203
2313
|
] }) }),
|
|
2204
|
-
/* @__PURE__ */ jsx("div", { className: "max-h-[50vh] overflow-y-auto p-1", children: filtered.length === 0 ? /* @__PURE__ */
|
|
2205
|
-
"No organizations match",
|
|
2206
|
-
" ",
|
|
2207
|
-
/* @__PURE__ */ jsxs("span", { className: "text-mono text-foreground", children: [
|
|
2208
|
-
"\u201C",
|
|
2209
|
-
query,
|
|
2210
|
-
"\u201D"
|
|
2211
|
-
] })
|
|
2212
|
-
] }) : /* @__PURE__ */ jsx("ul", { className: "space-y-0.5", children: filtered.map((org, index) => {
|
|
2314
|
+
/* @__PURE__ */ jsx("div", { className: "max-h-[50vh] overflow-y-auto p-1", children: filtered.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-[12px] text-muted-foreground", children: t("No organizations match \u201C{query}\u201D", { query }) }) : /* @__PURE__ */ jsx("ul", { className: "space-y-0.5", children: filtered.map((org, index) => {
|
|
2213
2315
|
const active = currentOrganization?.id === org.id;
|
|
2214
2316
|
const highlighted = index === activeIndex;
|
|
2215
2317
|
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
@@ -2224,7 +2326,7 @@ function OrgSwitcher({ Trigger = DefaultTrigger, className }) {
|
|
|
2224
2326
|
),
|
|
2225
2327
|
children: [
|
|
2226
2328
|
/* @__PURE__ */ jsx("span", { className: "truncate", children: org.name }),
|
|
2227
|
-
active ? /* @__PURE__ */ jsx("span", { className: "text-mono text-[10px] text-muted-foreground", children: "current" }) : null
|
|
2329
|
+
active ? /* @__PURE__ */ jsx("span", { className: "text-mono text-[10px] text-muted-foreground", children: t("current") }) : null
|
|
2228
2330
|
]
|
|
2229
2331
|
}
|
|
2230
2332
|
) }, org.id);
|
|
@@ -2320,6 +2422,7 @@ function TopBar({
|
|
|
2320
2422
|
);
|
|
2321
2423
|
}
|
|
2322
2424
|
function AppLauncher({ className, onLockedAppClick }) {
|
|
2425
|
+
const { t } = useCboxLocalization();
|
|
2323
2426
|
const cboxId = useCboxIdOptional();
|
|
2324
2427
|
const [open, setOpen] = useState(false);
|
|
2325
2428
|
const containerRef = useRef(null);
|
|
@@ -2360,7 +2463,7 @@ function AppLauncher({ className, onLockedAppClick }) {
|
|
|
2360
2463
|
"button",
|
|
2361
2464
|
{
|
|
2362
2465
|
type: "button",
|
|
2363
|
-
"aria-label": "Open app launcher",
|
|
2466
|
+
"aria-label": t("Open app launcher"),
|
|
2364
2467
|
"aria-expanded": open,
|
|
2365
2468
|
onClick: () => setOpen((v) => !v),
|
|
2366
2469
|
className: cn(
|
|
@@ -2374,10 +2477,10 @@ function AppLauncher({ className, onLockedAppClick }) {
|
|
|
2374
2477
|
"div",
|
|
2375
2478
|
{
|
|
2376
2479
|
role: "dialog",
|
|
2377
|
-
"aria-label": "Cbox app launcher",
|
|
2480
|
+
"aria-label": t("Cbox app launcher"),
|
|
2378
2481
|
className: "absolute right-0 top-full z-50 mt-1.5 w-[min(20rem,calc(100vw-1.5rem))] overflow-hidden rounded-lg border border-border bg-popover shadow-lg",
|
|
2379
2482
|
children: [
|
|
2380
|
-
/* @__PURE__ */ jsx("div", { className: "border-b border-border/70 px-3 py-2.5", children: /* @__PURE__ */ jsx("p", { className: "text-mono text-[10px] font-semibold tracking-[0.18em] text-muted-foreground uppercase", children: "Cbox suite" }) }),
|
|
2483
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-border/70 px-3 py-2.5", children: /* @__PURE__ */ jsx("p", { className: "text-mono text-[10px] font-semibold tracking-[0.18em] text-muted-foreground uppercase", children: t("Cbox suite") }) }),
|
|
2381
2484
|
/* @__PURE__ */ jsxs("div", { className: "max-h-[60vh] overflow-y-auto py-1", children: [
|
|
2382
2485
|
entitled.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-0.5 p-1", children: entitled.map((app) => /* @__PURE__ */ jsx(
|
|
2383
2486
|
AppRow2,
|
|
@@ -2390,7 +2493,7 @@ function AppLauncher({ className, onLockedAppClick }) {
|
|
|
2390
2493
|
)) }) : null,
|
|
2391
2494
|
locked.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2392
2495
|
entitled.length > 0 ? /* @__PURE__ */ jsx("div", { className: "my-1 border-t border-border/50" }) : null,
|
|
2393
|
-
/* @__PURE__ */ jsx("p", { className: "text-mono px-3 py-1.5 text-[10px] font-semibold tracking-[0.18em] text-muted-foreground/70 uppercase", children: "Available" }),
|
|
2496
|
+
/* @__PURE__ */ jsx("p", { className: "text-mono px-3 py-1.5 text-[10px] font-semibold tracking-[0.18em] text-muted-foreground/70 uppercase", children: t("Available") }),
|
|
2394
2497
|
/* @__PURE__ */ jsx("ul", { className: "space-y-0.5 p-1 pt-0", children: locked.map((app) => /* @__PURE__ */ jsx(
|
|
2395
2498
|
AppRow2,
|
|
2396
2499
|
{
|
|
@@ -2408,11 +2511,12 @@ function AppLauncher({ className, onLockedAppClick }) {
|
|
|
2408
2511
|
] });
|
|
2409
2512
|
}
|
|
2410
2513
|
function AppRow2({ app, orgSlug, onLockedAppClick }) {
|
|
2514
|
+
const { t } = useCboxLocalization();
|
|
2411
2515
|
const isComingSoon = app.comingSoon === true;
|
|
2412
2516
|
const isLocked = app.entitled === false || isComingSoon;
|
|
2413
2517
|
const monogram = app.monogram ?? app.name.slice(0, 1).toUpperCase();
|
|
2414
2518
|
const accent = app.accentColor;
|
|
2415
|
-
const tileStyle = accent !== void 0 && app.iconUrl ===
|
|
2519
|
+
const tileStyle = accent !== void 0 && secureIconUrl(app.iconUrl) === null ? { backgroundColor: accent, color: "#fff", borderColor: accent } : void 0;
|
|
2416
2520
|
const href = orgSlug !== void 0 && app.current !== true ? buildOrgUrl2(app.url, orgSlug) : app.url;
|
|
2417
2521
|
const tile = /* @__PURE__ */ jsx(
|
|
2418
2522
|
"div",
|
|
@@ -2423,7 +2527,7 @@ function AppRow2({ app, orgSlug, onLockedAppClick }) {
|
|
|
2423
2527
|
app.current && "ring-2 ring-accent-edge ring-offset-1 ring-offset-popover",
|
|
2424
2528
|
isLocked && "opacity-60 grayscale"
|
|
2425
2529
|
),
|
|
2426
|
-
children:
|
|
2530
|
+
children: /* @__PURE__ */ jsx(CboxAppIcon, { name: app.name, iconUrl: app.iconUrl, iconDarkUrl: app.iconDarkUrl, className: "h-5 w-5", fallback: /* @__PURE__ */ jsx("span", { children: monogram }) })
|
|
2427
2531
|
}
|
|
2428
2532
|
);
|
|
2429
2533
|
const body = /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [
|
|
@@ -2438,7 +2542,7 @@ function AppRow2({ app, orgSlug, onLockedAppClick }) {
|
|
|
2438
2542
|
children: app.name
|
|
2439
2543
|
}
|
|
2440
2544
|
),
|
|
2441
|
-
app.current ? /* @__PURE__ */ jsx("span", { className: "text-mono shrink-0 rounded-sm border border-accent-edge/40 bg-accent-soft px-1 text-[9px] font-semibold tracking-[0.14em] text-accent uppercase", children: "current" }) : null
|
|
2545
|
+
app.current ? /* @__PURE__ */ jsx("span", { className: "text-mono shrink-0 rounded-sm border border-accent-edge/40 bg-accent-soft px-1 text-[9px] font-semibold tracking-[0.14em] text-accent uppercase", children: t("current") }) : null
|
|
2442
2546
|
] }),
|
|
2443
2547
|
app.description ? /* @__PURE__ */ jsx(
|
|
2444
2548
|
"span",
|
|
@@ -2451,9 +2555,9 @@ function AppRow2({ app, orgSlug, onLockedAppClick }) {
|
|
|
2451
2555
|
}
|
|
2452
2556
|
) : null
|
|
2453
2557
|
] });
|
|
2454
|
-
const trailing = isComingSoon ? /* @__PURE__ */ jsx("span", { className: "text-mono shrink-0 rounded-sm border border-border bg-secondary px-1.5 py-0.5 text-[9px] font-semibold tracking-[0.14em] text-muted-foreground uppercase", children: "soon" }) : isLocked ? /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1 text-[10px] font-medium text-muted-foreground", children: [
|
|
2558
|
+
const trailing = isComingSoon ? /* @__PURE__ */ jsx("span", { className: "text-mono shrink-0 rounded-sm border border-border bg-secondary px-1.5 py-0.5 text-[9px] font-semibold tracking-[0.14em] text-muted-foreground uppercase", children: t("soon") }) : isLocked ? /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1 text-[10px] font-medium text-muted-foreground", children: [
|
|
2455
2559
|
/* @__PURE__ */ jsx(Lock2, {}),
|
|
2456
|
-
"Add"
|
|
2560
|
+
t("Add")
|
|
2457
2561
|
] }) : null;
|
|
2458
2562
|
if (isComingSoon) {
|
|
2459
2563
|
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
@@ -2461,7 +2565,7 @@ function AppRow2({ app, orgSlug, onLockedAppClick }) {
|
|
|
2461
2565
|
{
|
|
2462
2566
|
"aria-disabled": true,
|
|
2463
2567
|
className: "flex w-full cursor-not-allowed items-center gap-3 rounded-md px-2.5 py-2",
|
|
2464
|
-
title:
|
|
2568
|
+
title: t("{name} \u2014 coming soon", { name: app.name }),
|
|
2465
2569
|
children: [
|
|
2466
2570
|
tile,
|
|
2467
2571
|
body,
|
|
@@ -2480,7 +2584,7 @@ function AppRow2({ app, orgSlug, onLockedAppClick }) {
|
|
|
2480
2584
|
"group flex w-full items-center gap-3 rounded-md px-2.5 py-2 text-left transition-colors",
|
|
2481
2585
|
onLockedAppClick !== void 0 ? "hover:bg-secondary" : "cursor-default"
|
|
2482
2586
|
),
|
|
2483
|
-
title:
|
|
2587
|
+
title: t("Add {name} to your plan", { name: app.name }),
|
|
2484
2588
|
children: [
|
|
2485
2589
|
tile,
|
|
2486
2590
|
body,
|
|
@@ -2553,12 +2657,182 @@ function Lock2() {
|
|
|
2553
2657
|
}
|
|
2554
2658
|
);
|
|
2555
2659
|
}
|
|
2660
|
+
var DEFAULT_REFRESH_SECONDS = 60;
|
|
2661
|
+
function parseAppCatalog(ok, body) {
|
|
2662
|
+
const data = typeof body === "object" && body !== null ? body : {};
|
|
2663
|
+
if (!ok) {
|
|
2664
|
+
const authorize = typeof data.authorize_url === "string" ? data.authorize_url : null;
|
|
2665
|
+
if (authorize !== null || data.state === "authorization_required") {
|
|
2666
|
+
return { status: "authorization_required", authorizeUrl: authorize };
|
|
2667
|
+
}
|
|
2668
|
+
return { status: "unavailable" };
|
|
2669
|
+
}
|
|
2670
|
+
if (!Array.isArray(data.apps)) {
|
|
2671
|
+
return { status: "unavailable" };
|
|
2672
|
+
}
|
|
2673
|
+
const apps = [];
|
|
2674
|
+
for (const entry of data.apps) {
|
|
2675
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
2676
|
+
const app = entry;
|
|
2677
|
+
if (typeof app.key !== "string" || typeof app.name !== "string" || typeof app.launch_url !== "string") continue;
|
|
2678
|
+
if (!/^https?:\/\//.test(app.launch_url)) continue;
|
|
2679
|
+
apps.push({
|
|
2680
|
+
key: app.key,
|
|
2681
|
+
name: app.name,
|
|
2682
|
+
launch_url: app.launch_url,
|
|
2683
|
+
icon_url: typeof app.icon_url === "string" ? app.icon_url : null,
|
|
2684
|
+
icon_dark_url: typeof app.icon_dark_url === "string" ? app.icon_dark_url : null,
|
|
2685
|
+
description: typeof app.description === "string" ? app.description : null
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
const refresh = typeof data.refresh_after === "number" && Number.isFinite(data.refresh_after) ? data.refresh_after : DEFAULT_REFRESH_SECONDS;
|
|
2689
|
+
return { status: "ready", apps, refreshAfter: Math.max(1, Math.min(DEFAULT_REFRESH_SECONDS, refresh)) };
|
|
2690
|
+
}
|
|
2691
|
+
function authorizeHref(authorizeUrl, location) {
|
|
2692
|
+
if (authorizeUrl === null) return null;
|
|
2693
|
+
try {
|
|
2694
|
+
const url = new URL(authorizeUrl, location.origin);
|
|
2695
|
+
if (url.origin !== location.origin) return null;
|
|
2696
|
+
url.searchParams.set("return_to", location.pathname + location.search);
|
|
2697
|
+
return url.pathname + url.search;
|
|
2698
|
+
} catch {
|
|
2699
|
+
return null;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
function CboxAppCatalog({ endpoint, currentAppKey, className }) {
|
|
2703
|
+
const { t } = useCboxLocalization();
|
|
2704
|
+
const [open, setOpen] = useState(false);
|
|
2705
|
+
const [state, setState] = useState({ status: "idle" });
|
|
2706
|
+
const container = useRef(null);
|
|
2707
|
+
const flight = useRef(null);
|
|
2708
|
+
const expires = useRef(0);
|
|
2709
|
+
const load = useCallback(async () => {
|
|
2710
|
+
if (endpoint === null || flight.current !== null) return;
|
|
2711
|
+
const request = new AbortController();
|
|
2712
|
+
flight.current = request;
|
|
2713
|
+
setState((current) => current.status === "ready" ? current : { status: "loading" });
|
|
2714
|
+
try {
|
|
2715
|
+
const response = await fetch(endpoint, {
|
|
2716
|
+
credentials: "same-origin",
|
|
2717
|
+
cache: "no-store",
|
|
2718
|
+
headers: { Accept: "application/json", "X-Requested-With": "XMLHttpRequest" },
|
|
2719
|
+
signal: request.signal
|
|
2720
|
+
});
|
|
2721
|
+
const body = await response.json().catch(() => null);
|
|
2722
|
+
if (request.signal.aborted) return;
|
|
2723
|
+
const next = parseAppCatalog(response.ok, body);
|
|
2724
|
+
expires.current = next.status === "ready" ? Date.now() + next.refreshAfter * 1e3 : 0;
|
|
2725
|
+
setState(next);
|
|
2726
|
+
if (next.status === "ready") rememberAppCatalog(next.apps);
|
|
2727
|
+
} catch {
|
|
2728
|
+
if (!request.signal.aborted) {
|
|
2729
|
+
expires.current = 0;
|
|
2730
|
+
setState({ status: "unavailable" });
|
|
2731
|
+
}
|
|
2732
|
+
} finally {
|
|
2733
|
+
if (flight.current === request) flight.current = null;
|
|
2734
|
+
}
|
|
2735
|
+
}, [endpoint]);
|
|
2736
|
+
const refresh = useCallback(() => {
|
|
2737
|
+
if (document.visibilityState === "visible" && Date.now() >= expires.current) void load();
|
|
2738
|
+
}, [load]);
|
|
2739
|
+
useEffect(() => {
|
|
2740
|
+
if (!open) return;
|
|
2741
|
+
refresh();
|
|
2742
|
+
const outside = (event) => {
|
|
2743
|
+
if (container.current !== null && !container.current.contains(event.target)) setOpen(false);
|
|
2744
|
+
};
|
|
2745
|
+
const key = (event) => {
|
|
2746
|
+
if (event.key === "Escape") setOpen(false);
|
|
2747
|
+
};
|
|
2748
|
+
window.addEventListener("pointerdown", outside);
|
|
2749
|
+
window.addEventListener("keydown", key);
|
|
2750
|
+
window.addEventListener("focus", refresh);
|
|
2751
|
+
document.addEventListener("visibilitychange", refresh);
|
|
2752
|
+
return () => {
|
|
2753
|
+
window.removeEventListener("pointerdown", outside);
|
|
2754
|
+
window.removeEventListener("keydown", key);
|
|
2755
|
+
window.removeEventListener("focus", refresh);
|
|
2756
|
+
document.removeEventListener("visibilitychange", refresh);
|
|
2757
|
+
};
|
|
2758
|
+
}, [open, refresh]);
|
|
2759
|
+
useEffect(() => {
|
|
2760
|
+
if (!open || state.status !== "ready") return;
|
|
2761
|
+
const timer = window.setTimeout(refresh, Math.max(50, expires.current - Date.now()));
|
|
2762
|
+
return () => window.clearTimeout(timer);
|
|
2763
|
+
}, [open, state, refresh]);
|
|
2764
|
+
useEffect(() => () => flight.current?.abort(), []);
|
|
2765
|
+
useEffect(() => {
|
|
2766
|
+
expires.current = 0;
|
|
2767
|
+
setState({ status: "idle" });
|
|
2768
|
+
}, [endpoint]);
|
|
2769
|
+
const reconnect = state.status === "authorization_required" && typeof window !== "undefined" ? authorizeHref(state.authorizeUrl, window.location) : null;
|
|
2770
|
+
return /* @__PURE__ */ jsxs("div", { ref: container, className: cn("relative", className), children: [
|
|
2771
|
+
/* @__PURE__ */ jsx(
|
|
2772
|
+
"button",
|
|
2773
|
+
{
|
|
2774
|
+
type: "button",
|
|
2775
|
+
"aria-label": t("Switch app"),
|
|
2776
|
+
title: t("Switch app"),
|
|
2777
|
+
"aria-haspopup": "dialog",
|
|
2778
|
+
"aria-expanded": open,
|
|
2779
|
+
disabled: endpoint === null,
|
|
2780
|
+
onClick: () => setOpen((value) => !value),
|
|
2781
|
+
className: cn(
|
|
2782
|
+
"flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground disabled:cursor-default disabled:opacity-60 disabled:hover:bg-transparent",
|
|
2783
|
+
open && "bg-secondary text-foreground"
|
|
2784
|
+
),
|
|
2785
|
+
children: /* @__PURE__ */ jsx(Grid2, {})
|
|
2786
|
+
}
|
|
2787
|
+
),
|
|
2788
|
+
open ? /* @__PURE__ */ jsxs("div", { role: "dialog", "aria-label": t("Switch app"), className: "absolute right-0 top-full z-50 mt-1.5 w-[min(18.25rem,calc(100vw-1rem))] overflow-hidden rounded-lg border border-border bg-popover p-3 shadow-lg", children: [
|
|
2789
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-2 text-sm font-semibold text-foreground", children: t("Switch app") }),
|
|
2790
|
+
/* @__PURE__ */ jsxs("div", { "aria-live": "polite", children: [
|
|
2791
|
+
(state.status === "idle" || state.status === "loading") && /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-sm text-muted-foreground", children: t("Loading apps\u2026") }),
|
|
2792
|
+
state.status === "unavailable" && /* @__PURE__ */ jsxs("div", { className: "space-y-2 px-1 py-2 text-sm", children: [
|
|
2793
|
+
/* @__PURE__ */ jsx("p", { role: "alert", className: "text-muted-foreground", children: t("Apps could not be loaded.") }),
|
|
2794
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => void load(), className: "font-medium text-foreground underline", children: t("Try again") })
|
|
2795
|
+
] }),
|
|
2796
|
+
state.status === "authorization_required" && /* @__PURE__ */ jsxs("div", { className: "space-y-2 px-1 py-2 text-sm", children: [
|
|
2797
|
+
/* @__PURE__ */ jsx("p", { role: "alert", className: "text-muted-foreground", children: t("Apps could not be loaded.") }),
|
|
2798
|
+
reconnect !== null ? /* @__PURE__ */ jsx("a", { href: reconnect, className: "font-medium text-foreground underline", children: t("Reconnect to Cbox ID") }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void load(), className: "font-medium text-foreground underline", children: t("Try again") })
|
|
2799
|
+
] }),
|
|
2800
|
+
state.status === "ready" && (state.apps.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-sm text-muted-foreground", children: t("No apps available") }) : /* @__PURE__ */ jsx("nav", { "aria-label": t("Switch app"), className: "grid grid-cols-3 gap-1", children: state.apps.map((app) => {
|
|
2801
|
+
const current = app.key === currentAppKey;
|
|
2802
|
+
return /* @__PURE__ */ jsxs(
|
|
2803
|
+
"a",
|
|
2804
|
+
{
|
|
2805
|
+
href: app.launch_url,
|
|
2806
|
+
"aria-current": current ? "true" : void 0,
|
|
2807
|
+
title: app.description ?? app.name,
|
|
2808
|
+
onClick: () => setOpen(false),
|
|
2809
|
+
className: cn("relative flex min-h-[60px] min-w-0 flex-col items-center justify-center gap-1.5 rounded-md p-2 text-center text-[13px] font-medium leading-tight text-foreground hover:bg-secondary", current && "bg-accent-soft"),
|
|
2810
|
+
children: [
|
|
2811
|
+
/* @__PURE__ */ jsx(CboxAppIcon, { name: app.name, iconUrl: app.icon_url, iconDarkUrl: app.icon_dark_url }),
|
|
2812
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-full break-words", children: app.name.replace(/^Cbox\s*·\s*/i, "") }),
|
|
2813
|
+
current && /* @__PURE__ */ jsx(Check2, {})
|
|
2814
|
+
]
|
|
2815
|
+
},
|
|
2816
|
+
app.key
|
|
2817
|
+
);
|
|
2818
|
+
}) }))
|
|
2819
|
+
] })
|
|
2820
|
+
] }) : null
|
|
2821
|
+
] });
|
|
2822
|
+
}
|
|
2823
|
+
function Grid2() {
|
|
2824
|
+
return /* @__PURE__ */ jsx("svg", { width: "18", height: "18", viewBox: "0 0 18 18", fill: "currentColor", "aria-hidden": true, children: [3, 9, 15].flatMap((y) => [3, 9, 15].map((x) => /* @__PURE__ */ jsx("circle", { cx: x, cy: y, r: "1.65" }, `${x}-${y}`))) });
|
|
2825
|
+
}
|
|
2826
|
+
function Check2() {
|
|
2827
|
+
return /* @__PURE__ */ jsx("svg", { "aria-hidden": true, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", className: "absolute right-2 top-1.5 h-3 w-3 text-accent", children: /* @__PURE__ */ jsx("path", { d: "m5 12 4 4L19 6" }) });
|
|
2828
|
+
}
|
|
2556
2829
|
function Combobox(props) {
|
|
2830
|
+
const { t } = useCboxLocalization();
|
|
2557
2831
|
const {
|
|
2558
2832
|
items,
|
|
2559
|
-
placeholder = "Select\u2026",
|
|
2560
|
-
searchPlaceholder = "Search\u2026",
|
|
2561
|
-
emptyText = "Nothing matches.",
|
|
2833
|
+
placeholder = t("Select\u2026"),
|
|
2834
|
+
searchPlaceholder = t("Search\u2026"),
|
|
2835
|
+
emptyText = t("Nothing matches."),
|
|
2562
2836
|
footerLink,
|
|
2563
2837
|
disabled = false,
|
|
2564
2838
|
required = false,
|
|
@@ -2855,7 +3129,7 @@ function ComboboxGroupRows({
|
|
|
2855
3129
|
/* @__PURE__ */ jsx("span", { className: "truncate text-[13px] font-medium text-foreground", children: opt.label }),
|
|
2856
3130
|
opt.secondary !== void 0 ? /* @__PURE__ */ jsx("span", { className: "text-mono truncate text-[10px] text-muted-foreground", children: opt.secondary }) : null
|
|
2857
3131
|
] }),
|
|
2858
|
-
isActive ? /* @__PURE__ */ jsx(
|
|
3132
|
+
isActive ? /* @__PURE__ */ jsx(Check3, {}) : null
|
|
2859
3133
|
] });
|
|
2860
3134
|
const className = cn(
|
|
2861
3135
|
"group block w-full rounded-sm transition-colors text-left",
|
|
@@ -2947,7 +3221,7 @@ function ComboboxAvatarChip({
|
|
|
2947
3221
|
avatar,
|
|
2948
3222
|
fallbackLabel
|
|
2949
3223
|
}) {
|
|
2950
|
-
const
|
|
3224
|
+
const initials2 = avatar.initials ?? deriveInitials3(fallbackLabel);
|
|
2951
3225
|
const style = avatar.src === void 0 && avatar.accent !== void 0 ? { backgroundColor: avatar.accent, color: "#fff" } : void 0;
|
|
2952
3226
|
if (avatar.src !== void 0) {
|
|
2953
3227
|
return /* @__PURE__ */ jsx(
|
|
@@ -2968,7 +3242,7 @@ function ComboboxAvatarChip({
|
|
|
2968
3242
|
"flex h-5 w-5 shrink-0 items-center justify-center rounded-sm text-[9px] font-semibold tracking-tight",
|
|
2969
3243
|
avatar.accent === void 0 && "bg-info-soft text-info"
|
|
2970
3244
|
),
|
|
2971
|
-
children:
|
|
3245
|
+
children: initials2
|
|
2972
3246
|
}
|
|
2973
3247
|
);
|
|
2974
3248
|
}
|
|
@@ -3037,7 +3311,7 @@ function Chevron4({ open }) {
|
|
|
3037
3311
|
}
|
|
3038
3312
|
);
|
|
3039
3313
|
}
|
|
3040
|
-
function
|
|
3314
|
+
function Check3() {
|
|
3041
3315
|
return /* @__PURE__ */ jsx(
|
|
3042
3316
|
"svg",
|
|
3043
3317
|
{
|
|
@@ -3103,7 +3377,7 @@ function DataPanel({
|
|
|
3103
3377
|
"section",
|
|
3104
3378
|
{
|
|
3105
3379
|
className: cn(
|
|
3106
|
-
"rounded-lg border border-border bg-card text-card-foreground shadow-[
|
|
3380
|
+
"rounded-lg border border-border bg-card text-card-foreground shadow-[0_1px_2px_rgb(0_0_0/0.04),0_4px_12px_rgb(0_0_0/0.03)]",
|
|
3107
3381
|
className
|
|
3108
3382
|
),
|
|
3109
3383
|
...rest,
|
|
@@ -3289,12 +3563,13 @@ function PageTabs({
|
|
|
3289
3563
|
);
|
|
3290
3564
|
}
|
|
3291
3565
|
function DesktopStrip({ items, activeHref, LinkComponent }) {
|
|
3566
|
+
const { t } = useCboxLocalization();
|
|
3292
3567
|
return /* @__PURE__ */ jsx(
|
|
3293
3568
|
"nav",
|
|
3294
3569
|
{
|
|
3295
3570
|
className: "flex min-h-12 flex-1 items-center gap-1 overflow-x-auto overflow-y-hidden py-1.5",
|
|
3296
3571
|
role: "tablist",
|
|
3297
|
-
"aria-label": "Section navigation",
|
|
3572
|
+
"aria-label": t("Section navigation"),
|
|
3298
3573
|
children: items.map((item) => {
|
|
3299
3574
|
const Icon = item.icon;
|
|
3300
3575
|
const active = item.href === activeHref;
|
|
@@ -3330,6 +3605,7 @@ function DesktopStrip({ items, activeHref, LinkComponent }) {
|
|
|
3330
3605
|
);
|
|
3331
3606
|
}
|
|
3332
3607
|
function MobileDropdown({ items, activeHref, LinkComponent }) {
|
|
3608
|
+
const { t } = useCboxLocalization();
|
|
3333
3609
|
const [open, setOpen] = useState(false);
|
|
3334
3610
|
const ref = useRef(null);
|
|
3335
3611
|
const listId = useId();
|
|
@@ -3389,7 +3665,7 @@ function MobileDropdown({ items, activeHref, LinkComponent }) {
|
|
|
3389
3665
|
{
|
|
3390
3666
|
id: listId,
|
|
3391
3667
|
role: "listbox",
|
|
3392
|
-
"aria-label": "Section navigation",
|
|
3668
|
+
"aria-label": t("Section navigation"),
|
|
3393
3669
|
className: "absolute left-0 right-0 top-full z-30 mt-1 overflow-hidden rounded-md border border-border bg-popover shadow-lg",
|
|
3394
3670
|
children: items.map((item) => {
|
|
3395
3671
|
const Icon = item.icon;
|
|
@@ -3590,7 +3866,7 @@ function StatTile({
|
|
|
3590
3866
|
function urlString(defaultValue = "") {
|
|
3591
3867
|
return {
|
|
3592
3868
|
parse: (raw) => raw ?? defaultValue,
|
|
3593
|
-
serialize: (value) => value
|
|
3869
|
+
serialize: (value) => value,
|
|
3594
3870
|
isDefault: (value) => value === defaultValue
|
|
3595
3871
|
};
|
|
3596
3872
|
}
|
|
@@ -3665,7 +3941,152 @@ function buildUrlSearch(state, schema) {
|
|
|
3665
3941
|
}
|
|
3666
3942
|
return params.toString();
|
|
3667
3943
|
}
|
|
3944
|
+
function navMatchLength(path, href) {
|
|
3945
|
+
if (href === "/") {
|
|
3946
|
+
return path === "/" ? 1 : -1;
|
|
3947
|
+
}
|
|
3948
|
+
return path === href || path.startsWith(href + "/") ? href.length : -1;
|
|
3949
|
+
}
|
|
3950
|
+
function activeNavItem(path, items) {
|
|
3951
|
+
let best;
|
|
3952
|
+
let length = -1;
|
|
3953
|
+
for (const item of items) {
|
|
3954
|
+
const match = navMatchLength(path, item.href);
|
|
3955
|
+
if (match > length) {
|
|
3956
|
+
best = item;
|
|
3957
|
+
length = match;
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
return best;
|
|
3961
|
+
}
|
|
3962
|
+
function activeNavArea(path, areas) {
|
|
3963
|
+
let best;
|
|
3964
|
+
let length = -1;
|
|
3965
|
+
for (const area of areas) {
|
|
3966
|
+
const candidates = [area.href, ...(area.items ?? []).map((item) => item.href)];
|
|
3967
|
+
const match = Math.max(...candidates.map((href) => navMatchLength(path, href)));
|
|
3968
|
+
if (match > length) {
|
|
3969
|
+
best = area;
|
|
3970
|
+
length = match;
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
return best;
|
|
3974
|
+
}
|
|
3975
|
+
function useControllable(value, fallback, onChange) {
|
|
3976
|
+
const [own, setOwn] = useState(fallback);
|
|
3977
|
+
const current = value ?? own;
|
|
3978
|
+
const ref = useRef(current);
|
|
3979
|
+
ref.current = current;
|
|
3980
|
+
const set = useCallback((next) => {
|
|
3981
|
+
const resolved = typeof next === "function" ? next(ref.current) : next;
|
|
3982
|
+
if (value === void 0) setOwn(resolved);
|
|
3983
|
+
onChange?.(resolved);
|
|
3984
|
+
}, [value, onChange]);
|
|
3985
|
+
return [current, set];
|
|
3986
|
+
}
|
|
3987
|
+
function CboxWorkspaceShell({ appKey, brandSubmark, areas, currentPath, LinkComponent: Link, context, tools, account, children, brandHref = "/", mainId = "main-content", pinned: pinnedProp, defaultPinned = false, onPinnedChange, collapsed: collapsedProp, defaultCollapsed = false, onCollapsedChange }) {
|
|
3988
|
+
const { t } = useCboxLocalization();
|
|
3989
|
+
const [pinned, setPinned] = useControllable(pinnedProp, defaultPinned, onPinnedChange);
|
|
3990
|
+
const [collapsed, setCollapsed] = useControllable(collapsedProp, defaultCollapsed, onCollapsedChange);
|
|
3991
|
+
const [expanded, setExpanded] = useState(false);
|
|
3992
|
+
const [mobile, setMobile] = useState(false);
|
|
3993
|
+
const dialog = useRef(null);
|
|
3994
|
+
const path = currentPath.split(/[?#]/)[0] ?? "/";
|
|
3995
|
+
const area = activeNavArea(path, areas);
|
|
3996
|
+
const subnav = area?.items && area.items.length > 1 ? area : void 0;
|
|
3997
|
+
useEffect(() => {
|
|
3998
|
+
setMobile(false);
|
|
3999
|
+
}, [currentPath]);
|
|
4000
|
+
useEffect(() => {
|
|
4001
|
+
const el = dialog.current;
|
|
4002
|
+
if (mobile && el && !el.open) el.showModal();
|
|
4003
|
+
if (!mobile && el?.open) el.close();
|
|
4004
|
+
}, [mobile]);
|
|
4005
|
+
useEffect(() => {
|
|
4006
|
+
const key = (event) => {
|
|
4007
|
+
if ((event.metaKey || event.ctrlKey) && event.key === ".") {
|
|
4008
|
+
event.preventDefault();
|
|
4009
|
+
setCollapsed((value) => !value);
|
|
4010
|
+
}
|
|
4011
|
+
};
|
|
4012
|
+
addEventListener("keydown", key);
|
|
4013
|
+
return () => removeEventListener("keydown", key);
|
|
4014
|
+
}, [setCollapsed]);
|
|
4015
|
+
const open = pinned || expanded;
|
|
4016
|
+
const links = (items) => {
|
|
4017
|
+
const active = activeNavItem(path, items);
|
|
4018
|
+
return items.map((item) => /* @__PURE__ */ jsxs(Link, { href: item.href, className: `cbx-workspace-link ${item === active ? "is-active" : ""}`, "aria-label": item.label, "aria-current": item === active ? "page" : void 0, children: [
|
|
4019
|
+
/* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 shrink-0", strokeWidth: 1.7 }),
|
|
4020
|
+
/* @__PURE__ */ jsx("span", { children: item.label })
|
|
4021
|
+
] }, item.href));
|
|
4022
|
+
};
|
|
4023
|
+
return /* @__PURE__ */ jsxs("div", { className: "cbx-workspace", children: [
|
|
4024
|
+
/* @__PURE__ */ jsx("div", { className: "cbx-rail-slot", "data-pinned": pinned, children: /* @__PURE__ */ jsxs("aside", { className: "cbx-rail", "data-open": open, onMouseEnter: () => setExpanded(true), onMouseLeave: () => setExpanded(false), onFocusCapture: () => setExpanded(true), onBlurCapture: (event) => {
|
|
4025
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setExpanded(false);
|
|
4026
|
+
}, children: [
|
|
4027
|
+
/* @__PURE__ */ jsxs("div", { className: "cbx-rail-brand", children: [
|
|
4028
|
+
/* @__PURE__ */ jsxs(Link, { href: brandHref, "aria-label": t("Home"), children: [
|
|
4029
|
+
/* @__PURE__ */ jsx("span", { className: "dark:hidden", children: /* @__PURE__ */ jsx(CboxBrand, { appKey, submark: brandSubmark, collapsed: !open }) }),
|
|
4030
|
+
/* @__PURE__ */ jsx("span", { className: "hidden dark:block", children: /* @__PURE__ */ jsx(CboxBrand, { appKey, submark: brandSubmark, collapsed: !open, tone: "light" }) })
|
|
4031
|
+
] }),
|
|
4032
|
+
open && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setPinned((value) => !value), "aria-label": t(pinned ? "Unpin navigation" : "Pin navigation"), "aria-pressed": pinned, children: pinned ? /* @__PURE__ */ jsx(PinOff, { size: 16 }) : /* @__PURE__ */ jsx(Pin, { size: 16 }) })
|
|
4033
|
+
] }),
|
|
4034
|
+
/* @__PURE__ */ jsx("nav", { "aria-label": brandSubmark, children: areas.map((item) => /* @__PURE__ */ jsxs(Link, { href: item.href, "aria-label": item.label, "aria-current": area === item ? "page" : void 0, title: open ? void 0 : item.label, className: `cbx-workspace-link ${area === item ? "is-active" : ""}`, children: [
|
|
4035
|
+
/* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 shrink-0", strokeWidth: 1.7 }),
|
|
4036
|
+
open && /* @__PURE__ */ jsx("span", { children: item.label })
|
|
4037
|
+
] }, item.href)) }),
|
|
4038
|
+
/* @__PURE__ */ jsx("div", { className: "cbx-rail-account", children: account })
|
|
4039
|
+
] }) }),
|
|
4040
|
+
/* @__PURE__ */ jsxs("div", { className: "cbx-workspace-column", children: [
|
|
4041
|
+
/* @__PURE__ */ jsxs("header", { className: "cbx-workspace-header", children: [
|
|
4042
|
+
/* @__PURE__ */ jsx("button", { className: "cbx-mobile-trigger", type: "button", onClick: () => setMobile(true), "aria-label": t("Open navigation"), children: /* @__PURE__ */ jsx(Menu, { size: 18 }) }),
|
|
4043
|
+
/* @__PURE__ */ jsx("div", { className: "cbx-workspace-context", children: context }),
|
|
4044
|
+
/* @__PURE__ */ jsx("div", { className: "cbx-workspace-tools", children: tools })
|
|
4045
|
+
] }),
|
|
4046
|
+
/* @__PURE__ */ jsxs("div", { className: "cbx-workspace-body", children: [
|
|
4047
|
+
subnav && /* @__PURE__ */ jsx("aside", { className: "cbx-subnav", "data-collapsed": collapsed, children: collapsed ? /* @__PURE__ */ jsxs("button", { className: "cbx-subnav-strip", type: "button", onClick: () => setCollapsed(false), "aria-label": t("Expand sidebar"), children: [
|
|
4048
|
+
/* @__PURE__ */ jsx(PanelLeftOpen, { size: 16 }),
|
|
4049
|
+
/* @__PURE__ */ jsx("span", { children: subnav.label })
|
|
4050
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4051
|
+
/* @__PURE__ */ jsxs("div", { className: "cbx-subnav-header", children: [
|
|
4052
|
+
/* @__PURE__ */ jsx("strong", { children: subnav.label }),
|
|
4053
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCollapsed(true), "aria-label": t("Collapse sidebar"), title: "\u2318.", children: /* @__PURE__ */ jsx(PanelLeftClose, { size: 16 }) })
|
|
4054
|
+
] }),
|
|
4055
|
+
/* @__PURE__ */ jsx("nav", { "aria-label": subnav.label, children: links(subnav.items ?? []) })
|
|
4056
|
+
] }) }),
|
|
4057
|
+
/* @__PURE__ */ jsx("main", { id: mainId, className: "canvas-gradient cbx-workspace-main", children })
|
|
4058
|
+
] })
|
|
4059
|
+
] }),
|
|
4060
|
+
/* @__PURE__ */ jsxs("dialog", { ref: dialog, className: "cbx-workspace-mobile", onClose: () => setMobile(false), onClick: (event) => {
|
|
4061
|
+
if (event.target === event.currentTarget) setMobile(false);
|
|
4062
|
+
}, children: [
|
|
4063
|
+
/* @__PURE__ */ jsxs("div", { className: "cbx-subnav-header", children: [
|
|
4064
|
+
/* @__PURE__ */ jsx("strong", { children: brandSubmark }),
|
|
4065
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setMobile(false), "aria-label": t("Close navigation"), children: /* @__PURE__ */ jsx(X, { size: 18 }) })
|
|
4066
|
+
] }),
|
|
4067
|
+
/* @__PURE__ */ jsx("nav", { "aria-label": t("Navigation"), children: areas.map((item) => /* @__PURE__ */ jsxs("section", { children: [
|
|
4068
|
+
links([item]),
|
|
4069
|
+
item.items && /* @__PURE__ */ jsx("div", { className: "pl-4", children: links(item.items) })
|
|
4070
|
+
] }, item.href)) }),
|
|
4071
|
+
/* @__PURE__ */ jsx("div", { className: "cbx-rail-account", children: account })
|
|
4072
|
+
] })
|
|
4073
|
+
] });
|
|
4074
|
+
}
|
|
4075
|
+
var CHART_PALETTE = ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
|
|
4076
|
+
function Sparkline({ values, color = "var(--chart-1)", width = 90, height = 20 }) {
|
|
4077
|
+
if (values.length === 0) {
|
|
4078
|
+
return null;
|
|
4079
|
+
}
|
|
4080
|
+
const clean = values.map((value) => Number.isFinite(value) ? value : 0);
|
|
4081
|
+
const max = Math.max(1e-9, ...clean);
|
|
4082
|
+
const points = clean.map((value, index) => [index / Math.max(1, clean.length - 1) * (width - 2) + 1, height - 1 - value / max * (height - 3)]);
|
|
4083
|
+
const d = points.map(([px, py], index) => `${index === 0 ? "M" : "L"}${px.toFixed(1)} ${py.toFixed(1)}`).join("");
|
|
4084
|
+
return /* @__PURE__ */ jsxs("svg", { width, height, viewBox: `0 0 ${width} ${height}`, "aria-hidden": "true", children: [
|
|
4085
|
+
/* @__PURE__ */ jsx("path", { d: `${d}L${width - 1} ${height}L1 ${height}Z`, fill: color, opacity: 0.14 }),
|
|
4086
|
+
/* @__PURE__ */ jsx("path", { d, fill: "none", stroke: color, strokeWidth: 1.5, strokeLinejoin: "round", strokeLinecap: "round" })
|
|
4087
|
+
] });
|
|
4088
|
+
}
|
|
3668
4089
|
|
|
3669
|
-
export { AppContent, AppLauncher, AppShell, AppSwitcher, CboxAppShell, CboxAppSidebar, CboxBrand, CboxIdProvider, CboxMobileNavDrawer, CboxOrgContextBar, CboxSurfaces, Combobox, ContextAvatarChip, ContextBar, DataPanel, EmptyState, Heading, KeyValue, KeyValueList, NotificationsBell, OrgSwitcher, PageHeader, PageShell, PageTabs, PanelBody, RowLink, SearchTrigger, SectionHeader, StatTile, StatusPill, TopBar, UserMenu,
|
|
4090
|
+
export { AppContent, AppLauncher, AppShell, AppSwitcher, CHART_PALETTE, CboxAppCatalog, CboxAppIcon, CboxAppShell, CboxAppSidebar, CboxBrand, CboxIdProvider, CboxLocalizationProvider, CboxMobileNavDrawer, CboxOrgContextBar, CboxServiceMark, CboxSurfaces, CboxWorkspaceShell, Combobox, ContextAvatarChip, ContextBar, DataPanel, EmptyState, Heading, KeyValue, KeyValueList, NotificationsBell, OrgSwitcher, PageHeader, PageShell, PageTabs, PanelBody, RowLink, SearchTrigger, SectionHeader, Sparkline, StatTile, StatusPill, TopBar, UserMenu, activeNavArea, activeNavItem, authorizeHref, buildUrlSearch, defineUrlSchema, interpolate, navMatchLength, parseAppCatalog, parseUrlSearch, rememberAppCatalog, secureIconUrl, urlBoolean, urlEnum, urlNumber, urlString, urlStringArray, useCboxId, useCboxIdOptional, useCboxLocalization, useCboxSurfaces, useCboxSurfacesOptional, useKnownAppCatalog };
|
|
3670
4091
|
//# sourceMappingURL=index.js.map
|
|
3671
4092
|
//# sourceMappingURL=index.js.map
|