@burdenoff/microfe-devportal 2026.1112.2 → 2026.1113.2
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/dist/devportal/DevportalRoot.js +65 -119
- package/dist/devportal/DevportalRoot.js.map +1 -1
- package/dist/devportal/components/AppCard.js +72 -69
- package/dist/devportal/components/AppCard.js.map +1 -1
- package/dist/devportal/components/DeveloperSettingsHub.d.ts +6 -0
- package/dist/devportal/components/DeveloperSettingsHub.js +59 -0
- package/dist/devportal/components/DeveloperSettingsHub.js.map +1 -0
- package/dist/devportal/components/ImageUploadField.js +82 -80
- package/dist/devportal/components/ImageUploadField.js.map +1 -1
- package/dist/devportal/components/SectionNav.d.ts +1 -1
- package/dist/devportal/components/SectionNav.js +60 -36
- package/dist/devportal/components/SectionNav.js.map +1 -1
- package/dist/devportal/components/StoreImage.d.ts +15 -0
- package/dist/devportal/components/StoreImage.js +30 -0
- package/dist/devportal/components/StoreImage.js.map +1 -0
- package/dist/devportal/components/index.d.ts +1 -0
- package/dist/devportal/components/index.js +17 -16
- package/dist/devportal/hooks/usePackageUpload.d.ts +46 -4
- package/dist/devportal/hooks/usePackageUpload.js +90 -36
- package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
- package/dist/devportal/pages/AdminReviewPage.js +205 -202
- package/dist/devportal/pages/AdminReviewPage.js.map +1 -1
- package/dist/devportal/pages/AnalyticsDashboardPage.js +186 -179
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +113 -111
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +80 -84
- package/dist/devportal/pages/AppLogsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +529 -504
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +186 -183
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +163 -166
- package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
- package/dist/devportal/pages/CreateOAuthClientPage.js +134 -130
- package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
- package/dist/devportal/pages/CreatePhysicalItemPage.js +2 -2
- package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +11 -11
- package/dist/devportal/pages/DocumentationPage.js +100 -94
- package/dist/devportal/pages/DocumentationPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +379 -377
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/EditPhysicalItemPage.js +312 -307
- package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -1
- package/dist/devportal/pages/InvitationDecisionPage.js +83 -79
- package/dist/devportal/pages/InvitationDecisionPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +175 -171
- package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientDetailPage.js +76 -76
- package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js +126 -124
- package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationDetailPage.js +19 -19
- package/dist/devportal/pages/OrganizationMembersPage.js +223 -204
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/ResourcesPage.d.ts +6 -0
- package/dist/devportal/pages/ResourcesPage.js +171 -155
- package/dist/devportal/pages/ResourcesPage.js.map +1 -1
- package/dist/devportal/pages/SellerDashboardPage.js +117 -115
- package/dist/devportal/pages/SellerDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SellerItemsPage.js +202 -199
- package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
- package/dist/devportal/pages/SettingsPage.js +330 -315
- package/dist/devportal/pages/SettingsPage.js.map +1 -1
- package/dist/devportal/pages/SetupPage.js +5 -5
- package/dist/devportal/pages/StoreSubmissionPage.js +133 -131
- package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +6 -6
- package/dist/devportal/pages/WebhooksPage.js +39 -39
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/utils/appDetailTabs.d.ts +73 -0
- package/dist/devportal/utils/appDetailTabs.js +62 -0
- package/dist/devportal/utils/appDetailTabs.js.map +1 -0
- package/dist/devportal/utils/developerNav.d.ts +39 -0
- package/dist/devportal/utils/developerNav.js +304 -0
- package/dist/devportal/utils/developerNav.js.map +1 -0
- package/package.json +2 -2
|
@@ -2,149 +2,95 @@ import { DevportalProvider as e } from "./providers/DevportalProvider.js";
|
|
|
2
2
|
import { useTr as t } from "./utils/i18n.js";
|
|
3
3
|
import { DevportalRoutes as n } from "./DevportalRoutes.js";
|
|
4
4
|
import { setDevportalEventBusBridge as r } from "./events/index.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
5
|
+
import { getMenuEntries as i } from "./utils/developerNav.js";
|
|
6
|
+
import { useEffect as a } from "react";
|
|
7
|
+
import { ErrorBoundary as o } from "@burdenoff/fe-libs/shared/components";
|
|
8
|
+
import { AppWindow as s, BarChart3 as c, Blocks as l, BookOpen as u, Building2 as d, Code2 as f, DollarSign as p, Key as m, LayoutDashboard as h, Settings as g, Shield as _, UserCircle as v, Webhook as y, Wrench as b } from "lucide-react";
|
|
9
|
+
import { jsx as x } from "react/jsx-runtime";
|
|
10
|
+
import { FeatureFlagProvider as S } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
11
|
+
import { useEventBus as C } from "@burdenoff/fe-libs/shared/events";
|
|
12
|
+
import { useHeadingDocumentTitle as w } from "@burdenoff/fe-libs/ui";
|
|
11
13
|
// src/devportal/DevportalRoot.tsx
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
var T = {
|
|
15
|
+
"devportal-dashboard": h,
|
|
16
|
+
"devportal-organizations": d,
|
|
17
|
+
"devportal-apps": s,
|
|
18
|
+
"devportal-timecampus-applets": l,
|
|
19
|
+
"devportal-oauth": _,
|
|
20
|
+
"devportal-api-keys": m,
|
|
21
|
+
"devportal-webhooks": y,
|
|
22
|
+
"devportal-analytics": c,
|
|
23
|
+
"devportal-revenue": p,
|
|
24
|
+
"devportal-docs": u,
|
|
25
|
+
"devportal-resources": b,
|
|
26
|
+
"devportal-settings": g,
|
|
27
|
+
"devportal-publisher": v
|
|
28
|
+
}, E = ({ id: e }) => {
|
|
29
|
+
let t = T[e] ?? f;
|
|
30
|
+
return /* @__PURE__ */ x(t, { size: 16 });
|
|
31
|
+
}, D = (s) => {
|
|
32
|
+
let { registerNavItems: c, registerFooterItems: l, basePath: u = "/" } = s, d = t(), p = s.variant === "fluidgrids";
|
|
33
|
+
w({
|
|
34
|
+
enabled: s.variant === "fluidgrids",
|
|
35
|
+
suffix: "FluidGrids"
|
|
36
|
+
});
|
|
37
|
+
let m = (e, t) => (e.endsWith("/") ? e.slice(0, -1) : e) + (t.startsWith("/") ? t : `/${t}`);
|
|
38
|
+
a(() => {
|
|
39
|
+
if (!c) return;
|
|
16
40
|
let e = {
|
|
17
41
|
id: "devportal",
|
|
18
|
-
label:
|
|
19
|
-
path:
|
|
20
|
-
icon: /* @__PURE__ */
|
|
42
|
+
label: d("devportal.developerPortal", "Developer Portal"),
|
|
43
|
+
path: u,
|
|
44
|
+
icon: /* @__PURE__ */ x(f, { size: 20 }),
|
|
21
45
|
order: 75,
|
|
22
46
|
group: "global",
|
|
23
|
-
children:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: "devportal-organizations",
|
|
33
|
-
label: E("devportal.organizations", "Organizations"),
|
|
34
|
-
path: D(T, "organizations"),
|
|
35
|
-
icon: /* @__PURE__ */ y(u, { size: 16 }),
|
|
36
|
-
order: 20
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
id: "devportal-apps",
|
|
40
|
-
label: E("devportal.myApps", "My Apps"),
|
|
41
|
-
path: D(T, "apps"),
|
|
42
|
-
icon: /* @__PURE__ */ y(o, { size: 16 }),
|
|
43
|
-
order: 30
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
id: "devportal-timecampus-applets",
|
|
47
|
-
label: E("devportal.nav.timecampusApplets", "TimeCampus Applets"),
|
|
48
|
-
path: D(T, "timecampus-applets"),
|
|
49
|
-
icon: /* @__PURE__ */ y(c, { size: 16 }),
|
|
50
|
-
order: 35
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
id: "devportal-oauth",
|
|
54
|
-
label: E("devportal.oauthClients", "OAuth Clients"),
|
|
55
|
-
path: D(T, "oauth"),
|
|
56
|
-
icon: /* @__PURE__ */ y(g, { size: 16 }),
|
|
57
|
-
order: 40
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: "devportal-api-keys",
|
|
61
|
-
label: E("devportal.apiKeys", "API Keys"),
|
|
62
|
-
path: D(T, "api-keys"),
|
|
63
|
-
icon: /* @__PURE__ */ y(p, { size: 16 }),
|
|
64
|
-
order: 50
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
id: "devportal-webhooks",
|
|
68
|
-
label: E("devportal.webhooks", "Webhooks"),
|
|
69
|
-
path: D(T, "webhooks"),
|
|
70
|
-
icon: /* @__PURE__ */ y(_, { size: 16 }),
|
|
71
|
-
order: 60
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
id: "devportal-analytics",
|
|
75
|
-
label: E("devportal.analytics", "Analytics"),
|
|
76
|
-
path: D(T, "analytics"),
|
|
77
|
-
icon: /* @__PURE__ */ y(s, { size: 16 }),
|
|
78
|
-
order: 70
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
id: "devportal-revenue",
|
|
82
|
-
label: E("devportal.revenue", "Revenue"),
|
|
83
|
-
path: "/billing/earnings",
|
|
84
|
-
icon: /* @__PURE__ */ y(f, { size: 16 }),
|
|
85
|
-
order: 80
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
id: "devportal-docs",
|
|
89
|
-
label: E("devportal.documentation", "Documentation"),
|
|
90
|
-
path: D(T, "docs"),
|
|
91
|
-
icon: /* @__PURE__ */ y(l, { size: 16 }),
|
|
92
|
-
order: 90
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
id: "devportal-resources",
|
|
96
|
-
label: E("devportal.resources", "Resources"),
|
|
97
|
-
path: D(T, "resources"),
|
|
98
|
-
icon: /* @__PURE__ */ y(v, { size: 16 }),
|
|
99
|
-
order: 100
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
id: "devportal-settings",
|
|
103
|
-
label: E("devportal.settings", "Settings"),
|
|
104
|
-
path: D(T, "settings"),
|
|
105
|
-
icon: /* @__PURE__ */ y(h, { size: 16 }),
|
|
106
|
-
order: 110
|
|
107
|
-
}
|
|
108
|
-
]
|
|
47
|
+
children: i(p).map((e) => ({
|
|
48
|
+
id: e.id,
|
|
49
|
+
label: d(e.i18nKey, e.fallback),
|
|
50
|
+
path: e.absolutePath ? e.absolutePath : e.path ? m(u, e.path) : u,
|
|
51
|
+
icon: /* @__PURE__ */ x(E, { id: e.id }),
|
|
52
|
+
order: e.order
|
|
53
|
+
}))
|
|
109
54
|
};
|
|
110
|
-
return
|
|
55
|
+
return c(e);
|
|
111
56
|
}, [
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
57
|
+
c,
|
|
58
|
+
u,
|
|
59
|
+
d,
|
|
60
|
+
p
|
|
61
|
+
]), a(() => {
|
|
62
|
+
if (!l) return;
|
|
117
63
|
let e = [{
|
|
118
64
|
id: "devportal-status",
|
|
119
|
-
label:
|
|
65
|
+
label: d("devportal.developerPortalReady", "Developer Portal Ready"),
|
|
120
66
|
type: "status",
|
|
121
67
|
order: 10,
|
|
122
68
|
section: "left"
|
|
123
69
|
}];
|
|
124
|
-
return
|
|
125
|
-
}, [
|
|
70
|
+
return l(e);
|
|
71
|
+
}, [l, d]), a(() => {
|
|
126
72
|
let e = window.__breadcrumbRegistry;
|
|
127
|
-
if (e) return e.register("oauth",
|
|
73
|
+
if (e) return e.register("oauth", p ? d("devportal.nav.oauthApps", "OAuth apps") : d("devportal.nav.oauth", "OAuth Clients")), e.register("api-keys", d("devportal.nav.apiKeys", "API Keys")), () => {
|
|
128
74
|
e.unregister("oauth"), e.unregister("api-keys");
|
|
129
75
|
};
|
|
130
|
-
}, [
|
|
131
|
-
let
|
|
132
|
-
return
|
|
133
|
-
fallback: /* @__PURE__ */
|
|
76
|
+
}, [d, p]);
|
|
77
|
+
let h = C();
|
|
78
|
+
return a(() => (r(h), () => r(null)), [h]), /* @__PURE__ */ x(o, {
|
|
79
|
+
fallback: /* @__PURE__ */ x("div", {
|
|
134
80
|
className: "p-6 text-center text-text-secondary",
|
|
135
|
-
children:
|
|
81
|
+
children: d("devportal.error.moduleLoadFailed", "Something went wrong loading this module. Please refresh the page.")
|
|
136
82
|
}),
|
|
137
|
-
children: /* @__PURE__ */
|
|
83
|
+
children: /* @__PURE__ */ x(S, {
|
|
138
84
|
workspaceId: null,
|
|
139
85
|
gateway: "global",
|
|
140
86
|
defaultEnabled: !0,
|
|
141
|
-
children: /* @__PURE__ */
|
|
142
|
-
...
|
|
143
|
-
children: /* @__PURE__ */
|
|
87
|
+
children: /* @__PURE__ */ x(e, {
|
|
88
|
+
...s,
|
|
89
|
+
children: /* @__PURE__ */ x(n, {})
|
|
144
90
|
})
|
|
145
91
|
})
|
|
146
92
|
});
|
|
147
93
|
};
|
|
148
|
-
export {
|
|
94
|
+
export { D as DevportalRoot };
|
|
149
95
|
|
|
150
96
|
//# sourceMappingURL=DevportalRoot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DevportalRoot.js","names":["DevportalRoot","props","tr","useTr","joinPath","base","path","useEffect","registerNavItems","devportalNavItem","basePath","Code2","LayoutDashboard","Building2","AppWindow","Blocks","Shield","Key","Webhook","BarChart3","DollarSign","BookOpen","Wrench","Settings","registerFooterItems","footerItems","reg","sharedBus","useEventBus","setDevportalEventBusBridge","ErrorBoundary","FeatureFlagProvider","DevportalProvider","DevportalRoutes"],"sources":["../../src/devportal/DevportalRoot.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect } from \"react\";\nimport { ErrorBoundary } from \"@burdenoff/fe-libs/shared/components\";\nimport {\n Code2,\n LayoutDashboard,\n Building2,\n AppWindow,\n Blocks,\n BookOpen,\n Settings,\n Webhook,\n Key,\n Shield,\n BarChart3,\n DollarSign,\n Wrench,\n} from \"lucide-react\";\nimport { DevportalProvider } from \"./providers/DevportalProvider\";\nimport { FeatureFlagProvider } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport { useTr } from \"./utils/i18n\";\nimport type { NavItem, DevportalRootProps } from \"./types\";\nimport { DevportalRoutes } from \"./DevportalRoutes\";\nimport { setDevportalEventBusBridge } from \"./events\";\n\n/**\n * Root component for the Developer Portal microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { DevportalRoot, type DevportalRootProps } from '@burdenoff/microfe-devportal';\n *\n * function App() {\n * const props: DevportalRootProps = {\n * basePath: '/devportal',\n * navigate: (path) => router.push(path),\n * currentUser: { id: '1', email: 'user@example.com' },\n * tenantId: 'tenant-123',\n * apiGatewayUrl: 'https://api.example.com',\n * authToken: 'token',\n * };\n *\n * return <DevportalRoot {...props} />;\n * }\n * ```\n */\nexport const DevportalRoot: FC<DevportalRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = \"/\" } = props;\n const tr = useTr();\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n // Create a parent \"Developer Portal\" item with children for dropdown behavior\n const devportalNavItem: NavItem = {\n id: \"devportal\",\n label: tr(\"devportal.developerPortal\", \"Developer Portal\"),\n path: basePath,\n icon: <Code2 size={20} />,\n order: 75,\n group: \"global\",\n children: [\n {\n id: \"devportal-dashboard\",\n label: tr(\"devportal.dashboard\", \"Dashboard\"),\n path: basePath,\n icon: <LayoutDashboard size={16} />,\n order: 10,\n },\n {\n id: \"devportal-organizations\",\n label: tr(\"devportal.organizations\", \"Organizations\"),\n path: joinPath(basePath, \"organizations\"),\n icon: <Building2 size={16} />,\n order: 20,\n },\n {\n id: \"devportal-apps\",\n label: tr(\"devportal.myApps\", \"My Apps\"),\n path: joinPath(basePath, \"apps\"),\n icon: <AppWindow size={16} />,\n order: 30,\n },\n {\n // TimeCampus applets (BOFF-11005): the third-party applet developer\n // flow for the active workspace.\n id: \"devportal-timecampus-applets\",\n label: tr(\"devportal.nav.timecampusApplets\", \"TimeCampus Applets\"),\n path: joinPath(basePath, \"timecampus-applets\"),\n icon: <Blocks size={16} />,\n order: 35,\n },\n {\n id: \"devportal-oauth\",\n label: tr(\"devportal.oauthClients\", \"OAuth Clients\"),\n path: joinPath(basePath, \"oauth\"),\n icon: <Shield size={16} />,\n order: 40,\n },\n {\n id: \"devportal-api-keys\",\n label: tr(\"devportal.apiKeys\", \"API Keys\"),\n path: joinPath(basePath, \"api-keys\"),\n icon: <Key size={16} />,\n order: 50,\n },\n {\n id: \"devportal-webhooks\",\n label: tr(\"devportal.webhooks\", \"Webhooks\"),\n path: joinPath(basePath, \"webhooks\"),\n icon: <Webhook size={16} />,\n order: 60,\n },\n {\n id: \"devportal-analytics\",\n label: tr(\"devportal.analytics\", \"Analytics\"),\n path: joinPath(basePath, \"analytics\"),\n icon: <BarChart3 size={16} />,\n order: 70,\n },\n {\n id: \"devportal-revenue\",\n label: tr(\"devportal.revenue\", \"Revenue\"),\n path: \"/billing/earnings\",\n icon: <DollarSign size={16} />,\n order: 80,\n },\n {\n id: \"devportal-docs\",\n label: tr(\"devportal.documentation\", \"Documentation\"),\n path: joinPath(basePath, \"docs\"),\n icon: <BookOpen size={16} />,\n order: 90,\n },\n {\n id: \"devportal-resources\",\n label: tr(\"devportal.resources\", \"Resources\"),\n path: joinPath(basePath, \"resources\"),\n icon: <Wrench size={16} />,\n order: 100,\n },\n {\n id: \"devportal-settings\",\n label: tr(\"devportal.settings\", \"Settings\"),\n path: joinPath(basePath, \"settings\"),\n icon: <Settings size={16} />,\n order: 110,\n },\n ],\n };\n\n // Register and get cleanup function\n const cleanup = registerNavItems(devportalNavItem);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath, tr]);\n\n // Register footer items\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: \"devportal-status\",\n label: tr(\"devportal.developerPortalReady\", \"Developer Portal Ready\"),\n type: \"status\" as const,\n order: 10,\n section: \"left\" as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n // Register static breadcrumb labels for URL segments that need pretty names.\n // The AutoBreadcrumbs component reads window.__breadcrumbRegistry for all\n // segments (not just UUIDs), so segments like 'oauth' and 'api-keys' get\n // human-readable labels instead of the auto-cased URL segment.\n useEffect(() => {\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n if (!reg) return;\n reg.register(\"oauth\", tr(\"devportal.nav.oauth\", \"OAuth Clients\"));\n reg.register(\"api-keys\", tr(\"devportal.nav.apiKeys\", \"API Keys\"));\n return () => {\n reg.unregister(\"oauth\");\n reg.unregister(\"api-keys\");\n };\n }, [tr]);\n\n // Bridge fe-libs event bus -> devportal event bus so MFE pages can emit\n // domain events (`devportal.app.created`, etc.) and the shell sinks\n // (RybbitSink / BackendAuditSink / OtelBrowserSink) consume them centrally.\n // Per memory rule + spec 47: MFEs must not call Rybbit/OTel/fetch directly.\n const sharedBus = useEventBus();\n useEffect(() => {\n setDevportalEventBusBridge(\n sharedBus as unknown as { emit: (n: string, p: unknown) => void },\n );\n return () => setDevportalEventBusBridge(null);\n }, [sharedBus]);\n\n // NOTE: When running in the shell (workspaces-app), the shell's MultiGatewayProvider\n // already provides an ApolloProvider via GatewayRoute. The devportal uses that context.\n // When running standalone, the main.tsx wraps with DevPortalProvider (ApolloProvider).\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n {tr(\n \"devportal.error.moduleLoadFailed\",\n \"Something went wrong loading this module. Please refresh the page.\",\n )}\n </div>\n }\n >\n <FeatureFlagProvider\n workspaceId={null}\n gateway=\"global\"\n defaultEnabled={true}\n >\n <DevportalProvider {...props}>\n <DevportalRoutes />\n </DevportalProvider>\n </FeatureFlagProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DevportalRoot.js","names":["MENU_ICONS","LayoutDashboard","Building2","AppWindow","Blocks","Shield","Key","Webhook","BarChart3","DollarSign","BookOpen","Wrench","Settings","UserCircle","MenuIcon","Icon","id","Code2","DevportalRoot","props","tr","useTr","isFluidgrids","joinPath","base","path","useEffect","registerNavItems","devportalNavItem","basePath","getMenuEntries","entry","registerFooterItems","footerItems","reg","sharedBus","useEventBus","setDevportalEventBusBridge","ErrorBoundary","FeatureFlagProvider","DevportalProvider","DevportalRoutes"],"sources":["../../src/devportal/DevportalRoot.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect } from \"react\";\nimport { ErrorBoundary } from \"@burdenoff/fe-libs/shared/components\";\nimport {\n Code2,\n LayoutDashboard,\n Building2,\n AppWindow,\n Blocks,\n BookOpen,\n Settings,\n Webhook,\n Key,\n Shield,\n BarChart3,\n DollarSign,\n Wrench,\n UserCircle,\n} from \"lucide-react\";\nimport { DevportalProvider } from \"./providers/DevportalProvider\";\nimport { FeatureFlagProvider } from \"@burdenoff/fe-libs/shared/feature-flags\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport { useHeadingDocumentTitle } from \"@burdenoff/fe-libs/ui\";\nimport { useTr } from \"./utils/i18n\";\nimport type { NavItem, DevportalRootProps } from \"./types\";\nimport { DevportalRoutes } from \"./DevportalRoutes\";\nimport { setDevportalEventBusBridge } from \"./events\";\nimport { getMenuEntries } from \"./utils/developerNav\";\n\nconst MENU_ICONS: Record<string, typeof Code2> = {\n \"devportal-dashboard\": LayoutDashboard,\n \"devportal-organizations\": Building2,\n \"devportal-apps\": AppWindow,\n \"devportal-timecampus-applets\": Blocks,\n \"devportal-oauth\": Shield,\n \"devportal-api-keys\": Key,\n \"devportal-webhooks\": Webhook,\n \"devportal-analytics\": BarChart3,\n \"devportal-revenue\": DollarSign,\n \"devportal-docs\": BookOpen,\n \"devportal-resources\": Wrench,\n \"devportal-settings\": Settings,\n \"devportal-publisher\": UserCircle,\n};\n\nconst MenuIcon: FC<{ id: string }> = ({ id }) => {\n const Icon = MENU_ICONS[id] ?? Code2;\n return <Icon size={16} />;\n};\n\n/**\n * Root component for the Developer Portal microfrontend.\n *\n * This is the main entry point that host applications import and render.\n * It sets up the necessary providers and renders the internal routing.\n * Automatically registers navigation items with the shell if registration functions are provided.\n *\n * @example\n * ```tsx\n * import { DevportalRoot, type DevportalRootProps } from '@burdenoff/microfe-devportal';\n *\n * function App() {\n * const props: DevportalRootProps = {\n * basePath: '/devportal',\n * navigate: (path) => router.push(path),\n * currentUser: { id: '1', email: 'user@example.com' },\n * tenantId: 'tenant-123',\n * apiGatewayUrl: 'https://api.example.com',\n * authToken: 'token',\n * };\n *\n * return <DevportalRoot {...props} />;\n * }\n * ```\n */\nexport const DevportalRoot: FC<DevportalRootProps> = (props) => {\n const { registerNavItems, registerFooterItems, basePath = \"/\" } = props;\n const tr = useTr();\n const isFluidgrids = props.variant === \"fluidgrids\";\n\n // FluidGrids only (BOFF-11862, BOFF-11863): each page's browser tab title follows its main heading.\n useHeadingDocumentTitle({\n enabled: props.variant === \"fluidgrids\",\n suffix: \"FluidGrids\",\n });\n\n // Helper to join paths correctly, avoiding double slashes\n const joinPath = (base: string, path: string): string => {\n const cleanBase = base.endsWith(\"/\") ? base.slice(0, -1) : base;\n const cleanPath = path.startsWith(\"/\") ? path : `/${path}`;\n return cleanBase + cleanPath;\n };\n\n // Register navigation items with the shell\n useEffect(() => {\n if (!registerNavItems) return;\n\n // Create a parent \"Developer Portal\" item with children for dropdown behavior\n const devportalNavItem: NavItem = {\n id: \"devportal\",\n label: tr(\"devportal.developerPortal\", \"Developer Portal\"),\n path: basePath,\n icon: <Code2 size={20} />,\n order: 75,\n group: \"global\",\n children: getMenuEntries(isFluidgrids).map((entry) => ({\n id: entry.id,\n label: tr(entry.i18nKey, entry.fallback),\n path: entry.absolutePath\n ? entry.absolutePath\n : entry.path\n ? joinPath(basePath, entry.path)\n : basePath,\n icon: <MenuIcon id={entry.id} />,\n order: entry.order,\n })),\n };\n\n // Register and get cleanup function\n const cleanup = registerNavItems(devportalNavItem);\n\n // Return cleanup function to unregister on unmount\n return cleanup;\n }, [registerNavItems, basePath, tr, isFluidgrids]);\n\n // Register footer items\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems = [\n {\n id: \"devportal-status\",\n label: tr(\"devportal.developerPortalReady\", \"Developer Portal Ready\"),\n type: \"status\" as const,\n order: 10,\n section: \"left\" as const,\n },\n ];\n\n const cleanup = registerFooterItems(footerItems);\n return cleanup;\n }, [registerFooterItems, tr]);\n\n // Register static breadcrumb labels for URL segments that need pretty names.\n // The AutoBreadcrumbs component reads window.__breadcrumbRegistry for all\n // segments (not just UUIDs), so segments like 'oauth' and 'api-keys' get\n // human-readable labels instead of the auto-cased URL segment.\n useEffect(() => {\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n if (!reg) return;\n reg.register(\n \"oauth\",\n isFluidgrids\n ? tr(\"devportal.nav.oauthApps\", \"OAuth apps\")\n : tr(\"devportal.nav.oauth\", \"OAuth Clients\"),\n );\n reg.register(\"api-keys\", tr(\"devportal.nav.apiKeys\", \"API Keys\"));\n return () => {\n reg.unregister(\"oauth\");\n reg.unregister(\"api-keys\");\n };\n }, [tr, isFluidgrids]);\n\n // Bridge fe-libs event bus -> devportal event bus so MFE pages can emit\n // domain events (`devportal.app.created`, etc.) and the shell sinks\n // (RybbitSink / BackendAuditSink / OtelBrowserSink) consume them centrally.\n // Per memory rule + spec 47: MFEs must not call Rybbit/OTel/fetch directly.\n const sharedBus = useEventBus();\n useEffect(() => {\n setDevportalEventBusBridge(\n sharedBus as unknown as { emit: (n: string, p: unknown) => void },\n );\n return () => setDevportalEventBusBridge(null);\n }, [sharedBus]);\n\n // NOTE: When running in the shell (workspaces-app), the shell's MultiGatewayProvider\n // already provides an ApolloProvider via GatewayRoute. The devportal uses that context.\n // When running standalone, the main.tsx wraps with DevPortalProvider (ApolloProvider).\n return (\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n {tr(\n \"devportal.error.moduleLoadFailed\",\n \"Something went wrong loading this module. Please refresh the page.\",\n )}\n </div>\n }\n >\n <FeatureFlagProvider\n workspaceId={null}\n gateway=\"global\"\n defaultEnabled={true}\n >\n <DevportalProvider {...props}>\n <DevportalRoutes />\n </DevportalProvider>\n </FeatureFlagProvider>\n </ErrorBoundary>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AA6BA,IAAMA,IAA2C;CAC/C,uBAAuBC;CACvB,2BAA2BC;CAC3B,kBAAkBC;CAClB,gCAAgCC;CAChC,mBAAmBC;CACnB,sBAAsBC;CACtB,sBAAsBC;CACtB,uBAAuBC;CACvB,qBAAqBC;CACrB,kBAAkBC;CAClB,uBAAuBC;CACvB,sBAAsBC;CACtB,uBAAuBC;AACzB,GAEMC,KAAgC,EAAE,YAAS;CAC/C,IAAMC,IAAOf,EAAWgB,MAAOC;CAC/B,OAAO,kBAACF,GAAD,EAAM,MAAM,GAAK,CAAA;AAC1B,GA2BaG,KAAyC,MAAU;CAC9D,IAAM,EAAE,qBAAkB,wBAAqB,cAAW,QAAQC,GAC5DC,IAAKC,EAAM,GACXC,IAAeH,EAAM,YAAY;CAGvC,EAAwB;EACtB,SAASA,EAAM,YAAY;EAC3B,QAAQ;CACV,CAAC;CAGD,IAAMI,KAAY,GAAc,OACZC,EAAK,SAAS,GAAG,IAAIA,EAAK,MAAM,GAAG,EAAE,IAAIA,MACzCC,EAAK,WAAW,GAAG,IAAIA,IAAO,IAAIA;CA0DtD,AArDAC,QAAgB;EACd,IAAI,CAACC,GAAkB;EAGvB,IAAMC,IAA4B;GAChC,IAAI;GACJ,OAAOR,EAAG,6BAA6B,kBAAkB;GACzD,MAAMS;GACN,MAAM,kBAACZ,GAAD,EAAO,MAAM,GAAK,CAAA;GACxB,OAAO;GACP,OAAO;GACP,UAAUa,EAAeR,CAAY,CAAC,CAAC,KAAK,OAAW;IACrD,IAAIS,EAAM;IACV,OAAOX,EAAGW,EAAM,SAASA,EAAM,QAAQ;IACvC,MAAMA,EAAM,eACRA,EAAM,eACNA,EAAM,OACJR,EAASM,GAAUE,EAAM,IAAI,IAC7BF;IACN,MAAM,kBAACf,GAAD,EAAU,IAAIiB,EAAM,GAAK,CAAA;IAC/B,OAAOA,EAAM;GACf,EAAE;EACJ;EAMA,OAHgBJ,EAAiBC,CAG1B;CACT,GAAG;EAACD;EAAkBE;EAAUT;EAAIE;CAAY,CAAC,GAGjDI,QAAgB;EACd,IAAI,CAACM,GAAqB;EAE1B,IAAMC,IAAc,CAClB;GACE,IAAI;GACJ,OAAOb,EAAG,kCAAkC,wBAAwB;GACpE,MAAM;GACN,OAAO;GACP,SAAS;EACX,CACF;EAGA,OADgBY,EAAoBC,CAC7B;CACT,GAAG,CAACD,GAAqBZ,CAAE,CAAC,GAM5BM,QAAgB;EACd,IAAMQ,IACJ,OAMA;EACF,IAAKA,GAQL,OAPAA,EAAI,SACF,SACAZ,IACIF,EAAG,2BAA2B,YAAY,IAC1CA,EAAG,uBAAuB,eAAe,CAC/C,GACAc,EAAI,SAAS,YAAYd,EAAG,yBAAyB,UAAU,CAAC,SACnD;GAEX,AADAc,EAAI,WAAW,OAAO,GACtBA,EAAI,WAAW,UAAU;EAC3B;CACF,GAAG,CAACd,GAAIE,CAAY,CAAC;CAMrB,IAAMa,IAAYC,EAAY;CAW9B,OAVAV,SACEW,EACEF,CACF,SACaE,EAA2B,IAAI,IAC3C,CAACF,CAAS,CAAC,GAMZ,kBAACG,GAAD;EACE,UACE,kBAAC,OAAD;GAAK,WAAU;GACZ,UAAAlB,EACC,oCACA,oEACF;EACG,CAAA;EAGP,UAAA,kBAACmB,GAAD;GACE,aAAa;GACb,SAAQ;GACR,gBAAgB;GAEhB,UAAA,kBAACC,GAAD;IAAmB,GAAIrB;IACrB,UAAA,kBAACsB,GAAD,CAAkB,CAAA;GACD,CAAA;EACA,CAAA;CACR,CAAA;AAEnB"}
|
|
@@ -3,144 +3,147 @@ import { FeaturedBadge as t, StatusBadge as n, VerifiedBadge as r } from "./Stat
|
|
|
3
3
|
import { APP_TYPE_LABELS as i } from "../utils/constants.js";
|
|
4
4
|
import { useDevportal as a } from "../providers/DevportalProvider.js";
|
|
5
5
|
import { SelectionCheckbox as o } from "./SelectionCheckbox.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
6
|
+
import { StoreImage as s } from "./StoreImage.js";
|
|
7
|
+
import { AppWindow as c, Download as l, ExternalLink as u, Star as d } from "lucide-react";
|
|
8
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
9
9
|
import { GlassCard as m } from "@burdenoff/fe-libs/ui";
|
|
10
|
+
import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
11
|
// src/devportal/components/AppCard.tsx
|
|
11
|
-
var
|
|
12
|
-
let { navigate:
|
|
13
|
-
let n =
|
|
12
|
+
var g = ({ app: g, onClick: _, className: v, showActions: y = !0, selected: b = !1, onToggleSelect: x }) => {
|
|
13
|
+
let { navigate: S, basePath: C } = a(), { t: w } = h(), T = (e, t) => {
|
|
14
|
+
let n = w(e);
|
|
14
15
|
return n === e ? t : n;
|
|
15
|
-
},
|
|
16
|
-
|
|
16
|
+
}, E = () => {
|
|
17
|
+
_ ? _() : S && S(`${C}/apps/${g.id}`);
|
|
17
18
|
};
|
|
18
|
-
return /* @__PURE__ */
|
|
19
|
+
return /* @__PURE__ */ p(m, {
|
|
19
20
|
treatment: "insight",
|
|
20
21
|
role: "button",
|
|
21
22
|
tabIndex: 0,
|
|
22
|
-
className: e("group cursor-pointer border-border-seam p-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
23
|
-
onClick:
|
|
23
|
+
className: e("group cursor-pointer border-border-seam p-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", b && "border-primary bg-action-primary-bg/5", v),
|
|
24
|
+
onClick: E,
|
|
24
25
|
onKeyDown: (e) => {
|
|
25
|
-
(e.key === "Enter" || e.key === " ") && (e.preventDefault(),
|
|
26
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), E());
|
|
26
27
|
},
|
|
27
28
|
children: [
|
|
28
|
-
/* @__PURE__ */
|
|
29
|
+
/* @__PURE__ */ p("div", {
|
|
29
30
|
className: "flex items-start gap-3",
|
|
30
31
|
children: [
|
|
31
|
-
|
|
32
|
+
x && /* @__PURE__ */ f("div", {
|
|
32
33
|
onClick: (e) => e.stopPropagation(),
|
|
33
|
-
children: /* @__PURE__ */
|
|
34
|
-
checked:
|
|
35
|
-
onChange:
|
|
36
|
-
ariaLabel: `Select application ${
|
|
34
|
+
children: /* @__PURE__ */ f(o, {
|
|
35
|
+
checked: b,
|
|
36
|
+
onChange: x,
|
|
37
|
+
ariaLabel: `Select application ${g.name}`,
|
|
37
38
|
className: "mt-1"
|
|
38
39
|
})
|
|
39
40
|
}),
|
|
40
|
-
|
|
41
|
-
src:
|
|
42
|
-
alt:
|
|
43
|
-
className: "h-12 w-12 rounded-lg object-cover"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
/* @__PURE__ */ f(s, {
|
|
42
|
+
src: g.iconUrl,
|
|
43
|
+
alt: g.name,
|
|
44
|
+
className: "h-12 w-12 rounded-lg object-cover",
|
|
45
|
+
fallback: /* @__PURE__ */ f("div", {
|
|
46
|
+
className: "flex h-12 w-12 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
|
|
47
|
+
children: /* @__PURE__ */ f(c, { size: 24 })
|
|
48
|
+
})
|
|
47
49
|
}),
|
|
48
|
-
/* @__PURE__ */
|
|
50
|
+
/* @__PURE__ */ p("div", {
|
|
49
51
|
className: "flex-1 min-w-0",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
52
|
+
children: [/* @__PURE__ */ p("div", {
|
|
51
53
|
className: "flex items-center gap-2",
|
|
52
|
-
children: [/* @__PURE__ */
|
|
54
|
+
children: [/* @__PURE__ */ f("h3", {
|
|
53
55
|
className: "font-semibold truncate",
|
|
54
|
-
children:
|
|
55
|
-
}),
|
|
56
|
-
}), /* @__PURE__ */
|
|
56
|
+
children: g.name
|
|
57
|
+
}), g.isVerified && /* @__PURE__ */ f(r, { verified: !0 })]
|
|
58
|
+
}), /* @__PURE__ */ f("p", {
|
|
57
59
|
className: "text-sm text-text-secondary",
|
|
58
|
-
children: i[
|
|
60
|
+
children: i[g.appType] || g.appType
|
|
59
61
|
})]
|
|
60
62
|
})
|
|
61
63
|
]
|
|
62
64
|
}),
|
|
63
|
-
|
|
65
|
+
g.shortDescription && /* @__PURE__ */ f("p", {
|
|
64
66
|
className: "mt-3 text-sm text-text-secondary line-clamp-2",
|
|
65
|
-
children:
|
|
67
|
+
children: g.shortDescription
|
|
66
68
|
}),
|
|
67
|
-
/* @__PURE__ */
|
|
69
|
+
/* @__PURE__ */ p("div", {
|
|
68
70
|
className: "mt-3 flex flex-wrap items-center gap-2",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
71
|
+
children: [/* @__PURE__ */ f(n, { status: g.status }), g.isFeatured && /* @__PURE__ */ f(t, {})]
|
|
70
72
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
73
|
+
/* @__PURE__ */ p("div", {
|
|
72
74
|
className: "mt-4 flex items-center gap-4 text-sm text-text-secondary",
|
|
73
|
-
children: [
|
|
75
|
+
children: [g.averageRating !== void 0 && g.averageRating > 0 && /* @__PURE__ */ p("span", {
|
|
74
76
|
className: "flex items-center gap-1",
|
|
75
|
-
children: [/* @__PURE__ */ d
|
|
77
|
+
children: [/* @__PURE__ */ f(d, {
|
|
76
78
|
size: 14,
|
|
77
79
|
className: "text-status-warning-text fill-status-warning-text"
|
|
78
|
-
}),
|
|
79
|
-
}), /* @__PURE__ */
|
|
80
|
+
}), g.averageRating.toFixed(1)]
|
|
81
|
+
}), /* @__PURE__ */ p("span", {
|
|
80
82
|
className: "flex items-center gap-1",
|
|
81
83
|
children: [
|
|
82
|
-
/* @__PURE__ */
|
|
83
|
-
|
|
84
|
+
/* @__PURE__ */ f(l, { size: 14 }),
|
|
85
|
+
g.installCount.toLocaleString(),
|
|
84
86
|
" ",
|
|
85
|
-
|
|
87
|
+
T("devportal.installs", "installs")
|
|
86
88
|
]
|
|
87
89
|
})]
|
|
88
90
|
}),
|
|
89
|
-
|
|
91
|
+
y && /* @__PURE__ */ f("div", {
|
|
90
92
|
className: "mt-4 flex items-center justify-between opacity-0 group-hover:opacity-100 transition-opacity",
|
|
91
|
-
children: /* @__PURE__ */
|
|
93
|
+
children: /* @__PURE__ */ p("button", {
|
|
92
94
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
93
95
|
onClick: (e) => {
|
|
94
|
-
e.stopPropagation(),
|
|
96
|
+
e.stopPropagation(), E();
|
|
95
97
|
},
|
|
96
|
-
children: [
|
|
98
|
+
children: [T("devportal.viewDetails", "View Details"), /* @__PURE__ */ f(u, { size: 14 })]
|
|
97
99
|
})
|
|
98
100
|
})
|
|
99
101
|
]
|
|
100
102
|
});
|
|
101
|
-
},
|
|
102
|
-
let { navigate:
|
|
103
|
-
r ? r() :
|
|
103
|
+
}, _ = ({ app: t, onClick: r, className: l, selected: u = !1, onToggleSelect: d }) => {
|
|
104
|
+
let { navigate: m, basePath: h } = a(), g = () => {
|
|
105
|
+
r ? r() : m && m(`${h}/apps/${t.id}`);
|
|
104
106
|
};
|
|
105
|
-
return /* @__PURE__ */
|
|
107
|
+
return /* @__PURE__ */ p("div", {
|
|
106
108
|
role: "button",
|
|
107
109
|
tabIndex: 0,
|
|
108
|
-
className: e("flex items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-3 cursor-pointer transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
109
|
-
onClick:
|
|
110
|
+
className: e("flex items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-3 cursor-pointer transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", u && "border-primary bg-action-primary-bg/5", l),
|
|
111
|
+
onClick: g,
|
|
110
112
|
onKeyDown: (e) => {
|
|
111
|
-
(e.key === "Enter" || e.key === " ") && (e.preventDefault(),
|
|
113
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), g());
|
|
112
114
|
},
|
|
113
115
|
children: [
|
|
114
|
-
|
|
116
|
+
d && /* @__PURE__ */ f("div", {
|
|
115
117
|
onClick: (e) => e.stopPropagation(),
|
|
116
|
-
children: /* @__PURE__ */
|
|
117
|
-
checked:
|
|
118
|
-
onChange:
|
|
118
|
+
children: /* @__PURE__ */ f(o, {
|
|
119
|
+
checked: u,
|
|
120
|
+
onChange: d,
|
|
119
121
|
ariaLabel: `Select application ${t.name}`
|
|
120
122
|
})
|
|
121
123
|
}),
|
|
122
|
-
|
|
124
|
+
/* @__PURE__ */ f(s, {
|
|
123
125
|
src: t.iconUrl,
|
|
124
126
|
alt: t.name,
|
|
125
|
-
className: "h-10 w-10 rounded-lg object-cover"
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
className: "h-10 w-10 rounded-lg object-cover",
|
|
128
|
+
fallback: /* @__PURE__ */ f("div", {
|
|
129
|
+
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
|
|
130
|
+
children: /* @__PURE__ */ f(c, { size: 20 })
|
|
131
|
+
})
|
|
129
132
|
}),
|
|
130
|
-
/* @__PURE__ */
|
|
133
|
+
/* @__PURE__ */ p("div", {
|
|
131
134
|
className: "flex-1 min-w-0",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
135
|
+
children: [/* @__PURE__ */ f("p", {
|
|
133
136
|
className: "font-medium truncate",
|
|
134
137
|
children: t.name
|
|
135
|
-
}), /* @__PURE__ */
|
|
138
|
+
}), /* @__PURE__ */ f("p", {
|
|
136
139
|
className: "text-sm text-text-secondary truncate",
|
|
137
140
|
children: t.shortDescription || i[t.appType]
|
|
138
141
|
})]
|
|
139
142
|
}),
|
|
140
|
-
/* @__PURE__ */
|
|
143
|
+
/* @__PURE__ */ f(n, { status: t.status })
|
|
141
144
|
]
|
|
142
145
|
});
|
|
143
146
|
};
|
|
144
|
-
export {
|
|
147
|
+
export { g as AppCard, _ as AppListItem };
|
|
145
148
|
|
|
146
149
|
//# sourceMappingURL=AppCard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppCard.js","names":["AppCard","useDevportal","useI18n","tr","value","t","key","fallback","handleClick","onClick","navigate","basePath","app","GlassCard","cn","selected","className","event","onToggleSelect","SelectionCheckbox","AppWindow","VerifiedBadge","APP_TYPE_LABELS","StatusBadge","FeaturedBadge","Star","Download","showActions","ExternalLink","AppListItem"],"sources":["../../../src/devportal/components/AppCard.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { AppWindow, Star, Download, ExternalLink } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { cn } from \"../utils/cn\";\nimport { StatusBadge, VerifiedBadge, FeaturedBadge } from \"./StatusBadge\";\nimport type { Application } from \"../types\";\nimport { APP_TYPE_LABELS } from \"../utils/constants\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { SelectionCheckbox } from \"./SelectionCheckbox\";\n\ninterface AppCardProps {\n app: Application;\n onClick?: () => void;\n className?: string;\n showActions?: boolean;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n}\n\n/**\n * Application card for displaying app information\n */\nexport const AppCard: FC<AppCardProps> = ({\n app,\n onClick,\n className,\n showActions = true,\n selected = false,\n onToggleSelect,\n}) => {\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const handleClick = () => {\n if (onClick) {\n onClick();\n } else if (navigate) {\n navigate(`${basePath}/apps/${app.id}`);\n }\n };\n\n return (\n <GlassCard\n treatment=\"insight\"\n role=\"button\"\n tabIndex={0}\n className={cn(\n \"group cursor-pointer border-border-seam p-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n selected && \"border-primary bg-action-primary-bg/5\",\n className,\n )}\n onClick={handleClick}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n handleClick();\n }\n }}\n >\n {/* Header */}\n <div className=\"flex items-start gap-3\">\n {onToggleSelect && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={`Select application ${app.name}`}\n className=\"mt-1\"\n />\n </div>\n )}\n {app.iconUrl ? (\n <img\n src={app.iconUrl}\n alt={app.name}\n className=\"h-12 w-12 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex h-12 w-12 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <AppWindow size={24} />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold truncate\">{app.name}</h3>\n {app.isVerified && <VerifiedBadge verified />}\n </div>\n <p className=\"text-sm text-text-secondary\">\n {APP_TYPE_LABELS[app.appType] || app.appType}\n </p>\n </div>\n </div>\n\n {/* Description */}\n {app.shortDescription && (\n <p className=\"mt-3 text-sm text-text-secondary line-clamp-2\">\n {app.shortDescription}\n </p>\n )}\n\n {/* Badges */}\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <StatusBadge status={app.status} />\n {app.isFeatured && <FeaturedBadge />}\n </div>\n\n {/* Stats */}\n <div className=\"mt-4 flex items-center gap-4 text-sm text-text-secondary\">\n {app.averageRating !== undefined && app.averageRating > 0 && (\n <span className=\"flex items-center gap-1\">\n <Star\n size={14}\n className=\"text-status-warning-text fill-status-warning-text\"\n />\n {app.averageRating.toFixed(1)}\n </span>\n )}\n <span className=\"flex items-center gap-1\">\n <Download size={14} />\n {app.installCount.toLocaleString()}{\" \"}\n {tr(\"devportal.installs\", \"installs\")}\n </span>\n </div>\n\n {/* Actions */}\n {showActions && (\n <div className=\"mt-4 flex items-center justify-between opacity-0 group-hover:opacity-100 transition-opacity\">\n <button\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n onClick={(e) => {\n e.stopPropagation();\n handleClick();\n }}\n >\n {tr(\"devportal.viewDetails\", \"View Details\")}\n <ExternalLink size={14} />\n </button>\n </div>\n )}\n </GlassCard>\n );\n};\n\n/**\n * Grid of app cards\n */\nexport const AppCardGrid: FC<{\n apps: Application[];\n onAppClick?: (app: Application) => void;\n className?: string;\n}> = ({ apps, onAppClick, className }) => (\n <div\n className={cn(\n \"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\",\n className,\n )}\n >\n {apps.map((app) => (\n <AppCard\n key={app.id}\n app={app}\n onClick={onAppClick ? () => onAppClick(app) : undefined}\n />\n ))}\n </div>\n);\n\n/**\n * Compact app list item\n */\nexport const AppListItem: FC<{\n app: Application;\n onClick?: () => void;\n className?: string;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n}> = ({ app, onClick, className, selected = false, onToggleSelect }) => {\n const { navigate, basePath } = useDevportal();\n\n const handleClick = () => {\n if (onClick) {\n onClick();\n } else if (navigate) {\n navigate(`${basePath}/apps/${app.id}`);\n }\n };\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={cn(\n \"flex items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-3 cursor-pointer transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n selected && \"border-primary bg-action-primary-bg/5\",\n className,\n )}\n onClick={handleClick}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n handleClick();\n }\n }}\n >\n {onToggleSelect && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={`Select application ${app.name}`}\n />\n </div>\n )}\n {app.iconUrl ? (\n <img\n src={app.iconUrl}\n alt={app.name}\n className=\"h-10 w-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <AppWindow size={20} />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium truncate\">{app.name}</p>\n <p className=\"text-sm text-text-secondary truncate\">\n {app.shortDescription || APP_TYPE_LABELS[app.appType]}\n </p>\n </div>\n <StatusBadge status={app.status} />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AppCard.js","names":["AppCard","useDevportal","useI18n","tr","value","t","key","fallback","handleClick","onClick","navigate","basePath","app","GlassCard","cn","selected","className","event","onToggleSelect","SelectionCheckbox","StoreImage","AppWindow","VerifiedBadge","APP_TYPE_LABELS","StatusBadge","FeaturedBadge","Star","Download","showActions","ExternalLink","AppListItem"],"sources":["../../../src/devportal/components/AppCard.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { AppWindow, Star, Download, ExternalLink } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { cn } from \"../utils/cn\";\nimport { StatusBadge, VerifiedBadge, FeaturedBadge } from \"./StatusBadge\";\nimport type { Application } from \"../types\";\nimport { APP_TYPE_LABELS } from \"../utils/constants\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { SelectionCheckbox } from \"./SelectionCheckbox\";\nimport { StoreImage } from \"./StoreImage\";\n\ninterface AppCardProps {\n app: Application;\n onClick?: () => void;\n className?: string;\n showActions?: boolean;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n}\n\n/**\n * Application card for displaying app information\n */\nexport const AppCard: FC<AppCardProps> = ({\n app,\n onClick,\n className,\n showActions = true,\n selected = false,\n onToggleSelect,\n}) => {\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const handleClick = () => {\n if (onClick) {\n onClick();\n } else if (navigate) {\n navigate(`${basePath}/apps/${app.id}`);\n }\n };\n\n return (\n <GlassCard\n treatment=\"insight\"\n role=\"button\"\n tabIndex={0}\n className={cn(\n \"group cursor-pointer border-border-seam p-4 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n selected && \"border-primary bg-action-primary-bg/5\",\n className,\n )}\n onClick={handleClick}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n handleClick();\n }\n }}\n >\n {/* Header */}\n <div className=\"flex items-start gap-3\">\n {onToggleSelect && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={`Select application ${app.name}`}\n className=\"mt-1\"\n />\n </div>\n )}\n <StoreImage\n src={app.iconUrl}\n alt={app.name}\n className=\"h-12 w-12 rounded-lg object-cover\"\n fallback={\n <div className=\"flex h-12 w-12 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <AppWindow size={24} />\n </div>\n }\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold truncate\">{app.name}</h3>\n {app.isVerified && <VerifiedBadge verified />}\n </div>\n <p className=\"text-sm text-text-secondary\">\n {APP_TYPE_LABELS[app.appType] || app.appType}\n </p>\n </div>\n </div>\n\n {/* Description */}\n {app.shortDescription && (\n <p className=\"mt-3 text-sm text-text-secondary line-clamp-2\">\n {app.shortDescription}\n </p>\n )}\n\n {/* Badges */}\n <div className=\"mt-3 flex flex-wrap items-center gap-2\">\n <StatusBadge status={app.status} />\n {app.isFeatured && <FeaturedBadge />}\n </div>\n\n {/* Stats */}\n <div className=\"mt-4 flex items-center gap-4 text-sm text-text-secondary\">\n {app.averageRating !== undefined && app.averageRating > 0 && (\n <span className=\"flex items-center gap-1\">\n <Star\n size={14}\n className=\"text-status-warning-text fill-status-warning-text\"\n />\n {app.averageRating.toFixed(1)}\n </span>\n )}\n <span className=\"flex items-center gap-1\">\n <Download size={14} />\n {app.installCount.toLocaleString()}{\" \"}\n {tr(\"devportal.installs\", \"installs\")}\n </span>\n </div>\n\n {/* Actions */}\n {showActions && (\n <div className=\"mt-4 flex items-center justify-between opacity-0 group-hover:opacity-100 transition-opacity\">\n <button\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n onClick={(e) => {\n e.stopPropagation();\n handleClick();\n }}\n >\n {tr(\"devportal.viewDetails\", \"View Details\")}\n <ExternalLink size={14} />\n </button>\n </div>\n )}\n </GlassCard>\n );\n};\n\n/**\n * Grid of app cards\n */\nexport const AppCardGrid: FC<{\n apps: Application[];\n onAppClick?: (app: Application) => void;\n className?: string;\n}> = ({ apps, onAppClick, className }) => (\n <div\n className={cn(\n \"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\",\n className,\n )}\n >\n {apps.map((app) => (\n <AppCard\n key={app.id}\n app={app}\n onClick={onAppClick ? () => onAppClick(app) : undefined}\n />\n ))}\n </div>\n);\n\n/**\n * Compact app list item\n */\nexport const AppListItem: FC<{\n app: Application;\n onClick?: () => void;\n className?: string;\n selected?: boolean;\n onToggleSelect?: (checked: boolean) => void;\n}> = ({ app, onClick, className, selected = false, onToggleSelect }) => {\n const { navigate, basePath } = useDevportal();\n\n const handleClick = () => {\n if (onClick) {\n onClick();\n } else if (navigate) {\n navigate(`${basePath}/apps/${app.id}`);\n }\n };\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n className={cn(\n \"flex items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-3 cursor-pointer transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n selected && \"border-primary bg-action-primary-bg/5\",\n className,\n )}\n onClick={handleClick}\n onKeyDown={(event) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n handleClick();\n }\n }}\n >\n {onToggleSelect && (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selected}\n onChange={onToggleSelect}\n ariaLabel={`Select application ${app.name}`}\n />\n </div>\n )}\n <StoreImage\n src={app.iconUrl}\n alt={app.name}\n className=\"h-10 w-10 rounded-lg object-cover\"\n fallback={\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <AppWindow size={20} />\n </div>\n }\n />\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium truncate\">{app.name}</p>\n <p className=\"text-sm text-text-secondary truncate\">\n {app.shortDescription || APP_TYPE_LABELS[app.appType]}\n </p>\n </div>\n <StatusBadge status={app.status} />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
|
+
import { useTr as t } from "../utils/i18n.js";
|
|
3
|
+
import { DEVELOPER_SETTINGS_ENTRIES as n, resolveEntryPath as r } from "../utils/developerNav.js";
|
|
4
|
+
import { BarChart3 as i, BookOpen as a, ChevronRight as o, FileClock as s, KeyRound as c, Share2 as l, Shield as u, Webhook as d } from "lucide-react";
|
|
5
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
6
|
+
// src/devportal/components/DeveloperSettingsHub.tsx
|
|
7
|
+
var m = {
|
|
8
|
+
"api-keys": c,
|
|
9
|
+
oauth: u,
|
|
10
|
+
webhooks: d,
|
|
11
|
+
delegations: l,
|
|
12
|
+
"app-logs": s,
|
|
13
|
+
analytics: i,
|
|
14
|
+
docs: a
|
|
15
|
+
}, h = () => {
|
|
16
|
+
let { navigate: i, basePath: a } = e(), s = t();
|
|
17
|
+
return /* @__PURE__ */ f("nav", {
|
|
18
|
+
"aria-label": s("devportal.developerSettings.title", "Developer settings"),
|
|
19
|
+
"data-testid": "developer-settings-hub",
|
|
20
|
+
children: /* @__PURE__ */ f("ul", {
|
|
21
|
+
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
22
|
+
children: n.map((e) => {
|
|
23
|
+
let t = m[e.id] ?? c;
|
|
24
|
+
return /* @__PURE__ */ f("li", { children: /* @__PURE__ */ p("button", {
|
|
25
|
+
type: "button",
|
|
26
|
+
onClick: () => i?.(r(a, e)),
|
|
27
|
+
className: "flex h-full w-full items-start gap-3 rounded-card border border-border-seam bg-bg-surface p-4 text-left transition-colors hover:bg-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ f("span", {
|
|
30
|
+
className: "rounded-lg bg-action-primary-bg/10 p-2.5 text-primary",
|
|
31
|
+
children: /* @__PURE__ */ f(t, {
|
|
32
|
+
size: 20,
|
|
33
|
+
"aria-hidden": "true"
|
|
34
|
+
})
|
|
35
|
+
}),
|
|
36
|
+
/* @__PURE__ */ p("span", {
|
|
37
|
+
className: "flex-1",
|
|
38
|
+
children: [/* @__PURE__ */ f("span", {
|
|
39
|
+
className: "block font-medium",
|
|
40
|
+
children: s(e.titleKey, e.titleFallback)
|
|
41
|
+
}), /* @__PURE__ */ f("span", {
|
|
42
|
+
className: "mt-1 block text-sm text-text-secondary",
|
|
43
|
+
children: s(e.descriptionKey, e.descriptionFallback)
|
|
44
|
+
})]
|
|
45
|
+
}),
|
|
46
|
+
/* @__PURE__ */ f(o, {
|
|
47
|
+
size: 16,
|
|
48
|
+
"aria-hidden": "true",
|
|
49
|
+
className: "mt-1 text-text-secondary"
|
|
50
|
+
})
|
|
51
|
+
]
|
|
52
|
+
}) }, e.id);
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
export { h as DeveloperSettingsHub };
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=DeveloperSettingsHub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeveloperSettingsHub.js","names":["ICONS","KeyRound","Shield","Webhook","Share2","FileClock","BarChart3","BookOpen","DeveloperSettingsHub","useDevportal","tr","useTr","DEVELOPER_SETTINGS_ENTRIES","Icon","entry","navigate","resolveEntryPath","basePath","ChevronRight"],"sources":["../../../src/devportal/components/DeveloperSettingsHub.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport {\n BarChart3,\n BookOpen,\n ChevronRight,\n FileClock,\n KeyRound,\n Share2,\n Shield,\n Webhook,\n} from \"lucide-react\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { useTr } from \"../utils/i18n\";\nimport {\n DEVELOPER_SETTINGS_ENTRIES,\n resolveEntryPath,\n} from \"../utils/developerNav\";\n\nconst ICONS: Record<string, typeof KeyRound> = {\n \"api-keys\": KeyRound,\n oauth: Shield,\n webhooks: Webhook,\n delegations: Share2,\n \"app-logs\": FileClock,\n analytics: BarChart3,\n docs: BookOpen,\n};\n\n/**\n * FluidGrids only: the single home for the rarer developer pages. Each card\n * opens the page it names; the old URLs keep working.\n */\nexport const DeveloperSettingsHub: FC = () => {\n const { navigate, basePath } = useDevportal();\n const tr = useTr();\n\n return (\n <nav\n aria-label={tr(\"devportal.developerSettings.title\", \"Developer settings\")}\n data-testid=\"developer-settings-hub\"\n >\n <ul className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {DEVELOPER_SETTINGS_ENTRIES.map((entry) => {\n const Icon = ICONS[entry.id] ?? KeyRound;\n return (\n <li key={entry.id}>\n <button\n type=\"button\"\n onClick={() => navigate?.(resolveEntryPath(basePath, entry))}\n className=\"flex h-full w-full items-start gap-3 rounded-card border border-border-seam bg-bg-surface p-4 text-left transition-colors hover:bg-accent focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n <span className=\"rounded-lg bg-action-primary-bg/10 p-2.5 text-primary\">\n <Icon size={20} aria-hidden=\"true\" />\n </span>\n <span className=\"flex-1\">\n <span className=\"block font-medium\">\n {tr(entry.titleKey, entry.titleFallback)}\n </span>\n <span className=\"mt-1 block text-sm text-text-secondary\">\n {tr(entry.descriptionKey, entry.descriptionFallback)}\n </span>\n </span>\n <ChevronRight\n size={16}\n aria-hidden=\"true\"\n className=\"mt-1 text-text-secondary\"\n />\n </button>\n </li>\n );\n })}\n </ul>\n </nav>\n );\n};\n"],"mappings":";;;;;;AAkBA,IAAMA,IAAyC;CAC7C,YAAYC;CACZ,OAAOC;CACP,UAAUC;CACV,aAAaC;CACb,YAAYC;CACZ,WAAWC;CACX,MAAMC;AACR,GAMaC,UAAiC;CAC5C,IAAM,EAAE,aAAU,gBAAaC,EAAa,GACtCC,IAAKC,EAAM;CAEjB,OACE,kBAAC,OAAD;EACE,cAAYD,EAAG,qCAAqC,oBAAoB;EACxE,eAAY;EAEZ,UAAA,kBAAC,MAAD;GAAI,WAAU;GACX,UAAAE,EAA2B,KAAK,MAAU;IACzC,IAAMC,IAAOb,EAAMc,EAAM,OAAOb;IAChC,OACE,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAec,IAAWC,EAAiBC,GAAUH,CAAK,CAAC;KAC3D,WAAU;KAHZ,UAAA;MAKE,kBAAC,QAAD;OAAM,WAAU;OACd,UAAA,kBAACD,GAAD;QAAM,MAAM;QAAI,eAAY;OAAQ,CAAA;MAChC,CAAA;MACN,kBAAC,QAAD;OAAM,WAAU;OAAhB,UAAA,CACE,kBAAC,QAAD;QAAM,WAAU;QACb,UAAAH,EAAGI,EAAM,UAAUA,EAAM,aAAa;OACnC,CAAA,GACN,kBAAC,QAAD;QAAM,WAAU;QACb,UAAAJ,EAAGI,EAAM,gBAAgBA,EAAM,mBAAmB;OAC/C,CAAA,CACF;;MACN,kBAACI,GAAD;OACE,MAAM;OACN,eAAY;OACZ,WAAU;MACX,CAAA;KACK;IACN,CAAA,EAAA,GAvBKJ,EAAM,EAuBX;GAER,CAAC;EACC,CAAA;CACD,CAAA;AAET"}
|