@burdenoff/microfe-devportal 2026.828.1 → 2026.830.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/AdminDevportalRoot.d.ts.map +1 -1
- package/dist/devportal/AdminDevportalRoot.js +16 -19
- package/dist/devportal/AdminDevportalRoot.js.map +1 -1
- package/dist/devportal/DevportalRoot.d.ts.map +1 -1
- package/dist/devportal/DevportalRoot.js +36 -39
- package/dist/devportal/DevportalRoot.js.map +1 -1
- package/dist/devportal/components/AppCard.js +41 -41
- package/dist/devportal/components/ConfirmDialog.d.ts.map +1 -1
- package/dist/devportal/components/ConfirmDialog.js +19 -22
- package/dist/devportal/components/ConfirmDialog.js.map +1 -1
- package/dist/devportal/components/DataTable.d.ts.map +1 -1
- package/dist/devportal/components/DataTable.js +31 -34
- package/dist/devportal/components/DataTable.js.map +1 -1
- package/dist/devportal/components/FeatureFlaggedRoute.js +8 -8
- package/dist/devportal/components/FormField.d.ts.map +1 -1
- package/dist/devportal/components/FormField.js +26 -32
- package/dist/devportal/components/FormField.js.map +1 -1
- package/dist/devportal/components/GrantDelegationModal.js +64 -64
- package/dist/devportal/components/GrantDelegationModal.js.map +1 -1
- package/dist/devportal/components/LoadingState.d.ts.map +1 -1
- package/dist/devportal/components/LoadingState.js +5 -8
- package/dist/devportal/components/LoadingState.js.map +1 -1
- package/dist/devportal/components/PageLayout.d.ts.map +1 -1
- package/dist/devportal/components/PageLayout.js +10 -13
- package/dist/devportal/components/PageLayout.js.map +1 -1
- package/dist/devportal/components/PermissionGuard.d.ts.map +1 -1
- package/dist/devportal/components/PermissionGuard.js +11 -14
- package/dist/devportal/components/PermissionGuard.js.map +1 -1
- package/dist/devportal/components/SecretDisplay.d.ts.map +1 -1
- package/dist/devportal/components/SecretDisplay.js +24 -30
- package/dist/devportal/components/SecretDisplay.js.map +1 -1
- package/dist/devportal/components/SectionNav.d.ts.map +1 -1
- package/dist/devportal/components/SectionNav.js +24 -27
- package/dist/devportal/components/SectionNav.js.map +1 -1
- package/dist/devportal/components/StatusBadge.d.ts.map +1 -1
- package/dist/devportal/components/StatusBadge.js +24 -33
- package/dist/devportal/components/StatusBadge.js.map +1 -1
- package/dist/devportal/index.d.ts +1 -0
- package/dist/devportal/index.d.ts.map +1 -1
- package/dist/devportal/index.js +1 -0
- package/dist/devportal/pages/AdminReviewPage.js +172 -172
- package/dist/devportal/pages/AnalyticsDashboardPage.js +43 -43
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +119 -119
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +45 -45
- package/dist/devportal/pages/ApplicationDetailPage.js +607 -607
- package/dist/devportal/pages/ApplicationVersionsPage.js +13 -13
- package/dist/devportal/pages/ApplicationsPage.d.ts.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +104 -101
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +45 -45
- package/dist/devportal/pages/CreateApplicationPage.js +38 -38
- package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
- package/dist/devportal/pages/CreateOAuthClientPage.js +52 -52
- package/dist/devportal/pages/CreateOrganizationPage.js +26 -26
- package/dist/devportal/pages/CreateWebhookPage.d.ts.map +1 -1
- package/dist/devportal/pages/CreateWebhookPage.js +48 -48
- package/dist/devportal/pages/CreateWebhookPage.js.map +1 -1
- package/dist/devportal/pages/DashboardPage.d.ts.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +66 -72
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +174 -174
- package/dist/devportal/pages/DocumentationPage.js +93 -93
- package/dist/devportal/pages/EditApplicationPage.js +14 -14
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +76 -76
- package/dist/devportal/pages/OAuthClientDetailPage.js +75 -75
- package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js +64 -64
- package/dist/devportal/pages/OrganizationDetailPage.js +189 -189
- package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationMembersPage.js +10 -10
- package/dist/devportal/pages/OrganizationsPage.d.ts.map +1 -1
- package/dist/devportal/pages/OrganizationsPage.js +163 -154
- package/dist/devportal/pages/OrganizationsPage.js.map +1 -1
- package/dist/devportal/pages/PublishPage.js +104 -104
- package/dist/devportal/pages/PublishPage.js.map +1 -1
- package/dist/devportal/pages/PublisherProfilePage.js +55 -55
- package/dist/devportal/pages/ResourcesPage.js +60 -60
- package/dist/devportal/pages/RevenueDashboardPage.js +60 -60
- package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SellerDashboardPage.js +83 -83
- package/dist/devportal/pages/SellerItemsPage.js +163 -163
- package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
- package/dist/devportal/pages/SellerOrdersPage.js +63 -63
- package/dist/devportal/pages/SettingsPage.js +21 -21
- package/dist/devportal/pages/SetupPage.js +183 -183
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/pages/StoreSubmissionPage.js +5 -5
- package/dist/devportal/pages/WebhookDetailPage.js +123 -123
- package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
- package/dist/devportal/pages/WebhooksPage.js +118 -118
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/utils/i18n.d.ts +9 -2
- package/dist/devportal/utils/i18n.d.ts.map +1 -1
- package/dist/devportal/utils/i18n.js +13 -10
- package/dist/devportal/utils/i18n.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/translations/en.d.ts +1251 -0
- package/dist/translations/en.d.ts.map +1 -0
- package/dist/translations/en.js +1235 -0
- package/dist/translations/en.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoadingState.js","names":[],"sources":["../../../src/devportal/components/LoadingState.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { Loader2 } from \"lucide-react\";\nimport {
|
|
1
|
+
{"version":3,"file":"LoadingState.js","names":[],"sources":["../../../src/devportal/components/LoadingState.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { Loader2 } from \"lucide-react\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\n\ninterface LoadingStateProps {\n message?: string;\n className?: string;\n size?: \"sm\" | \"md\" | \"lg\";\n}\n\n/**\n * Loading state component with spinner and optional message\n */\nexport const LoadingState: FC<LoadingStateProps> = ({\n message,\n className,\n size = \"md\",\n}) => {\n const tr = useTr();\n const resolvedMessage = message ?? tr(\"devportal.loadingDots\", \"Loading...\");\n const sizeClasses = {\n sm: \"h-4 w-4\",\n md: \"h-8 w-8\",\n lg: \"h-12 w-12\",\n };\n\n return (\n <div\n className={cn(\n \"flex flex-col items-center justify-center gap-4 p-8\",\n className,\n )}\n >\n <Loader2 className={cn(\"animate-spin text-primary\", sizeClasses[size])} />\n {resolvedMessage && (\n <p className=\"text-sm text-text-secondary\">{resolvedMessage}</p>\n )}\n </div>\n );\n};\n\n/**\n * Full page loading state\n */\nexport const PageLoadingState: FC<{ message?: string }> = ({ message }) => (\n <div className=\"flex min-h-[400px] w-full items-center justify-center\">\n <LoadingState message={message} size=\"lg\" />\n </div>\n);\n\n/**\n * Card loading skeleton\n */\nexport const CardSkeleton: FC<{ className?: string }> = ({ className }) => (\n <div\n className={cn(\n \"rounded-card border border-border-seam bg-bg-surface p-6\",\n className,\n )}\n >\n <div className=\"h-4 w-3/4 overflow-hidden rounded bg-bg-sunken animate-vc-shimmer\" />\n <div className=\"mt-4 h-3 w-1/2 overflow-hidden rounded bg-bg-sunken animate-vc-shimmer\" />\n <div className=\"mt-2 h-3 w-2/3 overflow-hidden rounded bg-bg-sunken animate-vc-shimmer\" />\n </div>\n);\n\n/**\n * Table row skeleton\n */\nexport const TableRowSkeleton: FC<{ columns?: number }> = ({ columns = 4 }) => (\n <tr>\n {Array.from({ length: columns }).map((_, i) => (\n <td key={i} className=\"px-4 py-3\">\n <div className=\"h-4 overflow-hidden rounded bg-bg-sunken animate-vc-shimmer\" />\n </td>\n ))}\n </tr>\n);\n"],"mappings":";;;;;AAcA,IAAa,KAAuC,EAClD,YACA,cACA,UAAO,WACH;CACJ,IAAM,IAAK,GAAO,EACZ,IAAkB,KAAW,EAAG,yBAAyB,aAAa;AAO5E,QACE,kBAAC,OAAD;EACE,WAAW,EACT,uDACA,EACD;YAJH,CAME,kBAAC,GAAD,EAAS,WAAW,EAAG,6BAbP;GAClB,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,CASmE,GAAM,EAAI,CAAA,EACzE,KACC,kBAAC,KAAD;GAAG,WAAU;aAA+B;GAAoB,CAAA,CAE9D;;GAOG,KAA8C,EAAE,iBAC3D,kBAAC,OAAD;CAAK,WAAU;WACb,kBAAC,GAAD;EAAuB;EAAS,MAAK;EAAO,CAAA;CACxC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageLayout.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/PageLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAO3D,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,0EAA0E;IAC1E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,
|
|
1
|
+
{"version":3,"file":"PageLayout.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/PageLayout.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAO3D,UAAU,eAAe;IACvB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,0EAA0E;IAC1E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,EAAE,CAAC,eAAe,CAoF1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAC1B;IACE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,cAAc,CAAC,WAAW,CAAC,CAgBhC,CAAC"}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useTr as t } from "../utils/i18n.js";
|
|
3
|
+
import { cn as n } from "../utils/cn.js";
|
|
4
4
|
import { ArrowLeft as r } from "lucide-react";
|
|
5
5
|
import { jsx as i, jsxs as a } from "react/jsx-runtime";
|
|
6
6
|
import { AuroraBackground as o, PagePurpose as s } from "@burdenoff/fe-libs/ui";
|
|
7
7
|
//#region src/devportal/components/PageLayout.tsx
|
|
8
8
|
var c = ({ title: c, description: l, backLink: u, backLabel: d, actions: f, children: p, className: m, fullWidth: h = !1, purpose: g, purposeLearnMoreHref: _, purposeLearnMoreLabel: v, heroBackdrop: y = !1 }) => {
|
|
9
|
-
let { navigate: b, basePath: x } = e(),
|
|
10
|
-
let n = S(e);
|
|
11
|
-
return n === e ? t : n;
|
|
12
|
-
})("devportal.back", "Back");
|
|
9
|
+
let { navigate: b, basePath: x } = e(), S = t(), C = d ?? S("devportal.back", "Back");
|
|
13
10
|
return /* @__PURE__ */ a("div", {
|
|
14
|
-
className:
|
|
11
|
+
className: n("flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-10 xl:px-12", m),
|
|
15
12
|
children: [/* @__PURE__ */ a("div", {
|
|
16
|
-
className:
|
|
13
|
+
className: n("flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", y && "relative overflow-hidden rounded-card border border-border-seam/60 px-5 py-5 sm:px-6 sm:py-6"),
|
|
17
14
|
children: [
|
|
18
15
|
y && /* @__PURE__ */ i(o, { intensity: "subtle" }),
|
|
19
16
|
/* @__PURE__ */ a("div", {
|
|
@@ -50,21 +47,21 @@ var c = ({ title: c, description: l, backLink: u, backLabel: d, actions: f, chil
|
|
|
50
47
|
})
|
|
51
48
|
]
|
|
52
49
|
}), /* @__PURE__ */ i("div", {
|
|
53
|
-
className:
|
|
50
|
+
className: n(!h && "max-w-7xl"),
|
|
54
51
|
children: p
|
|
55
52
|
})]
|
|
56
53
|
});
|
|
57
|
-
}, l = ({ title: e, description:
|
|
58
|
-
className:
|
|
54
|
+
}, l = ({ title: e, description: t, actions: r, children: o, className: s, ...c }) => /* @__PURE__ */ a("section", {
|
|
55
|
+
className: n("space-y-4", s),
|
|
59
56
|
...c,
|
|
60
57
|
children: [(e || r) && /* @__PURE__ */ a("div", {
|
|
61
58
|
className: "flex items-center justify-between",
|
|
62
59
|
children: [/* @__PURE__ */ a("div", { children: [e && /* @__PURE__ */ i("h2", {
|
|
63
60
|
className: "text-lg font-semibold",
|
|
64
61
|
children: e
|
|
65
|
-
}),
|
|
62
|
+
}), t && /* @__PURE__ */ i("p", {
|
|
66
63
|
className: "text-sm text-text-secondary",
|
|
67
|
-
children:
|
|
64
|
+
children: t
|
|
68
65
|
})] }), r && /* @__PURE__ */ i("div", {
|
|
69
66
|
className: "flex items-center gap-2",
|
|
70
67
|
children: r
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/devportal/components/PageLayout.tsx"],"sourcesContent":["import type { FC, HTMLAttributes, ReactNode } from \"react\";\nimport { ArrowLeft } from \"lucide-react\";\nimport {
|
|
1
|
+
{"version":3,"file":"PageLayout.js","names":[],"sources":["../../../src/devportal/components/PageLayout.tsx"],"sourcesContent":["import type { FC, HTMLAttributes, ReactNode } from \"react\";\nimport { ArrowLeft } from \"lucide-react\";\nimport { AuroraBackground, PagePurpose } from \"@burdenoff/fe-libs/ui\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\n\ninterface PageLayoutProps {\n title: ReactNode;\n description?: string;\n backLink?: string;\n backLabel?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n fullWidth?: boolean;\n /**\n * Plain-language \"story\" for the page — WHAT it is for and the real use-case\n * — rendered as a calm {@link PagePurpose} under the header so every page is\n * self-explanatory for a new user. Keep it to one or two sentences.\n */\n purpose?: ReactNode;\n /** Optional \"Learn more\" link rendered at the end of the purpose line. */\n purposeLearnMoreHref?: string;\n purposeLearnMoreLabel?: string;\n /**\n * When true the page header sits over a subtle living-glass aurora backdrop\n * (Boff UI 2.0). Reserve this for the MFE's primary/overview page — one\n * aurora per MFE. The backdrop is decorative (-z-10) and never moves content.\n */\n heroBackdrop?: boolean;\n}\n\n/**\n * Standard page layout with header, optional back link, and actions\n */\nexport const PageLayout: FC<PageLayoutProps> = ({\n title,\n description,\n backLink,\n backLabel,\n actions,\n children,\n className,\n fullWidth = false,\n purpose,\n purposeLearnMoreHref,\n purposeLearnMoreLabel,\n heroBackdrop = false,\n}) => {\n const { navigate, basePath } = useDevportal();\n const tr = useTr();\n const resolvedBackLabel = backLabel ?? tr(\"devportal.back\", \"Back\");\n\n const handleBack = () => {\n if (!navigate || !backLink) return;\n\n const normalizedBasePath = basePath?.endsWith(\"/\")\n ? basePath.slice(0, -1)\n : basePath || \"\";\n const normalizedBackLink = backLink.startsWith(\"/\")\n ? backLink\n : `/${backLink}`;\n const alreadyIncludesBasePath =\n normalizedBasePath.length > 0 &&\n (normalizedBackLink === normalizedBasePath ||\n normalizedBackLink.startsWith(`${normalizedBasePath}/`));\n\n navigate(\n alreadyIncludesBasePath\n ? normalizedBackLink\n : `${normalizedBasePath}${normalizedBackLink}`,\n );\n };\n\n return (\n <div\n className={cn(\n \"flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-10 xl:px-12\",\n className,\n )}\n >\n {/* Header — optionally over a subtle living-glass aurora backdrop */}\n <div\n className={cn(\n \"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\",\n heroBackdrop &&\n \"relative overflow-hidden rounded-card border border-border-seam/60 px-5 py-5 sm:px-6 sm:py-6\",\n )}\n >\n {heroBackdrop && <AuroraBackground intensity=\"subtle\" />}\n <div className=\"flex flex-col gap-1\">\n {backLink && (\n <button\n onClick={handleBack}\n className=\"mb-2 flex items-center gap-1 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n <ArrowLeft size={16} />\n {resolvedBackLabel}\n </button>\n )}\n <h1 className=\"text-2xl font-bold tracking-tight\">{title}</h1>\n {description && <p className=\"text-text-secondary\">{description}</p>}\n {purpose && (\n <PagePurpose\n className=\"mt-1 max-w-2xl\"\n learnMoreHref={purposeLearnMoreHref}\n learnMoreLabel={purposeLearnMoreLabel}\n >\n {purpose}\n </PagePurpose>\n )}\n </div>\n {actions && <div className=\"flex items-center gap-2\">{actions}</div>}\n </div>\n\n {/* Content */}\n <div className={cn(!fullWidth && \"max-w-7xl\")}>{children}</div>\n </div>\n );\n};\n\n/**\n * Section within a page\n */\nexport const PageSection: FC<\n {\n title?: string;\n description?: string;\n actions?: ReactNode;\n children: ReactNode;\n className?: string;\n } & HTMLAttributes<HTMLElement>\n> = ({ title, description, actions, children, className, ...rest }) => (\n <section className={cn(\"space-y-4\", className)} {...rest}>\n {(title || actions) && (\n <div className=\"flex items-center justify-between\">\n <div>\n {title && <h2 className=\"text-lg font-semibold\">{title}</h2>}\n {description && (\n <p className=\"text-sm text-text-secondary\">{description}</p>\n )}\n </div>\n {actions && <div className=\"flex items-center gap-2\">{actions}</div>}\n </div>\n )}\n {children}\n </section>\n);\n"],"mappings":";;;;;;;AAoCA,IAAa,KAAmC,EAC9C,UACA,gBACA,aACA,cACA,YACA,aACA,cACA,eAAY,IACZ,YACA,yBACA,0BACA,kBAAe,SACX;CACJ,IAAM,EAAE,aAAU,gBAAa,GAAc,EACvC,IAAK,GAAO,EACZ,IAAoB,KAAa,EAAG,kBAAkB,OAAO;AAuBnE,QACE,kBAAC,OAAD;EACE,WAAW,EACT,2EACA,EACD;YAJH,CAOE,kBAAC,OAAD;GACE,WAAW,EACT,sEACA,KACE,+FACH;aALH;IAOG,KAAgB,kBAAC,GAAD,EAAkB,WAAU,UAAW,CAAA;IACxD,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,KACC,kBAAC,UAAD;OACE,eAxCa;AACvB,YAAI,CAAC,KAAY,CAAC,EAAU;QAE5B,IAAM,IAAqB,GAAU,SAAS,IAAI,GAC9C,EAAS,MAAM,GAAG,GAAG,GACrB,KAAY,IACV,IAAqB,EAAS,WAAW,IAAI,GAC/C,IACA,IAAI;AAMR,UAJE,EAAmB,SAAS,MAC3B,MAAuB,KACtB,EAAmB,WAAW,GAAG,EAAmB,GAAG,IAIrD,IACA,GAAG,IAAqB,IAC7B;;OAuBS,WAAU;iBAFZ,CAIE,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA,EACtB,EACM;;MAEX,kBAAC,MAAD;OAAI,WAAU;iBAAqC;OAAW,CAAA;MAC7D,KAAe,kBAAC,KAAD;OAAG,WAAU;iBAAuB;OAAgB,CAAA;MACnE,KACC,kBAAC,GAAD;OACE,WAAU;OACV,eAAe;OACf,gBAAgB;iBAEf;OACW,CAAA;MAEZ;;IACL,KAAW,kBAAC,OAAD;KAAK,WAAU;eAA2B;KAAc,CAAA;IAChE;MAGN,kBAAC,OAAD;GAAK,WAAW,EAAG,CAAC,KAAa,YAAY;GAAG;GAAe,CAAA,CAC3D;;GAOG,KAQR,EAAE,UAAO,gBAAa,YAAS,aAAU,cAAW,GAAG,QAC1D,kBAAC,WAAD;CAAS,WAAW,EAAG,aAAa,EAAU;CAAE,GAAI;WAApD,EACI,KAAS,MACT,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACG,KAAS,kBAAC,MAAD;GAAI,WAAU;aAAyB;GAAW,CAAA,EAC3D,KACC,kBAAC,KAAD;GAAG,WAAU;aAA+B;GAAgB,CAAA,CAE1D,EAAA,CAAA,EACL,KAAW,kBAAC,OAAD;GAAK,WAAU;aAA2B;GAAc,CAAA,CAChE;KAEP,EACO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionGuard.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/PermissionGuard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAM3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,UAAU,oBAAoB;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,EAAE,CAAC,oBAAoB,
|
|
1
|
+
{"version":3,"file":"PermissionGuard.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/PermissionGuard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAM3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,UAAU,oBAAoB;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,EAAE,CAAC,oBAAoB,CAyFpD,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useTr as e } from "../utils/i18n.js";
|
|
2
|
+
import { PageLoadingState as t } from "./LoadingState.js";
|
|
3
|
+
import { AccessDenied as n } from "@burdenoff/fe-libs/shared/components";
|
|
4
4
|
import { usePermissions as r } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
5
5
|
import { Fragment as i, jsx as a } from "react/jsx-runtime";
|
|
6
6
|
import { useAuth as o } from "@burdenoff/fe-libs/shared/providers/shell/AuthProvider";
|
|
@@ -18,19 +18,16 @@ var s = ({ permission: s, authOnly: c = !1, children: l }) => {
|
|
|
18
18
|
} catch {
|
|
19
19
|
p = !1;
|
|
20
20
|
}
|
|
21
|
-
let m = typeof window < "u" && (window.location.hostname === "localhost" || window.location.hostname.endsWith(".local.burdenoff.com")) && p,
|
|
22
|
-
let n = h(e);
|
|
23
|
-
return n === e ? t : n;
|
|
24
|
-
};
|
|
21
|
+
let m = typeof window < "u" && (window.location.hostname === "localhost" || window.location.hostname.endsWith(".local.burdenoff.com")) && p, h = e();
|
|
25
22
|
return d ? /* @__PURE__ */ a("div", {
|
|
26
23
|
className: "flex min-h-screen items-center justify-center bg-bg-canvas",
|
|
27
|
-
children: /* @__PURE__ */ a(
|
|
28
|
-
}) : c ? u !== null && !p && !m ? /* @__PURE__ */ a(
|
|
29
|
-
title:
|
|
30
|
-
description:
|
|
31
|
-
}) : /* @__PURE__ */ a(i, { children: l }) : s && u && !u(s) && !f && !m ? /* @__PURE__ */ a(
|
|
32
|
-
title:
|
|
33
|
-
description:
|
|
24
|
+
children: /* @__PURE__ */ a(t, {})
|
|
25
|
+
}) : c ? u !== null && !p && !m ? /* @__PURE__ */ a(n, {
|
|
26
|
+
title: h("devportal.accessDenied", "Access Denied"),
|
|
27
|
+
description: h("devportal.noPermissionView", "You don't have permission to view this page.")
|
|
28
|
+
}) : /* @__PURE__ */ a(i, { children: l }) : s && u && !u(s) && !f && !m ? /* @__PURE__ */ a(n, {
|
|
29
|
+
title: h("devportal.accessDenied", "Access Denied"),
|
|
30
|
+
description: h("devportal.noPermissionView", "You don't have permission to view this page.")
|
|
34
31
|
}) : /* @__PURE__ */ a(i, { children: l });
|
|
35
32
|
};
|
|
36
33
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/devportal/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { usePermissions } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useAuth } from \"@burdenoff/fe-libs/shared/providers/shell/AuthProvider\";\nimport { AccessDenied } from \"@burdenoff/fe-libs/shared/components\";\nimport {
|
|
1
|
+
{"version":3,"file":"PermissionGuard.js","names":[],"sources":["../../../src/devportal/components/PermissionGuard.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { usePermissions } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport { useAuth } from \"@burdenoff/fe-libs/shared/providers/shell/AuthProvider\";\nimport { AccessDenied } from \"@burdenoff/fe-libs/shared/components\";\nimport { PageLoadingState } from \"./LoadingState\";\nimport { useTr } from \"../utils/i18n\";\nimport type { DevportalPermission } from \"../constants/permissions\";\n\ninterface PermissionGuardProps {\n /**\n * Required RBAC permission. Optional when `authOnly` is set — self-scoped\n * developer surfaces gate on authentication only (see `authOnly`).\n */\n permission?: DevportalPermission;\n /**\n * Self-scoped mode. The corresponding backend fields are gated with\n * `@rbac(scopeType: \"self\")` — an authentication gate whose resolvers filter\n * strictly to the caller's own resources. Such fields carry no RBAC\n * permission in `myPermissions`, so a permission check would wrongly deny\n * every normal member. In this mode we render for any authenticated user and\n * let the backend self-enforce per resource.\n */\n authOnly?: boolean;\n children: ReactNode;\n}\n\n/**\n * Wraps page content with a permission check.\n * Shows a loading state while permissions resolve, then either\n * renders children (if permitted) or an AccessDenied screen.\n */\nexport const PermissionGuard: FC<PermissionGuardProps> = ({\n permission,\n authOnly = false,\n children,\n}) => {\n let hasPermission: ((permission: DevportalPermission) => boolean) | null =\n null;\n let isLoading = false;\n let isRbacAdmin = false;\n let isAuthenticated = false;\n\n try {\n const permissions = usePermissions();\n hasPermission = permissions.hasPermission as (\n permission: DevportalPermission,\n ) => boolean;\n isLoading = permissions.isLoading;\n isRbacAdmin =\n permissions.permissions?.permissions?.some(\n (entry) => entry.resourceType === \"rbac\" && entry.level === \"ADMIN\",\n ) ?? false;\n } catch {\n // Standalone/dev mode intentionally allows rendering without shell RBAC context.\n hasPermission = null;\n isLoading = false;\n isRbacAdmin = false;\n }\n\n try {\n const auth = useAuth();\n isAuthenticated = auth.isAuthenticated;\n } catch {\n isAuthenticated = false;\n }\n\n const isLocalShellDev =\n typeof window !== \"undefined\" &&\n (window.location.hostname === \"localhost\" ||\n window.location.hostname.endsWith(\".local.burdenoff.com\"));\n\n const allowLocalShellBypass = isLocalShellDev && isAuthenticated;\n const tr = useTr();\n\n if (isLoading) {\n return (\n <div className=\"flex min-h-screen items-center justify-center bg-bg-canvas\">\n <PageLoadingState />\n </div>\n );\n }\n\n // Self-scoped surface: gate on authentication only. The backend\n // (@rbac scopeType: \"self\") filters to the caller's own resources, so we\n // must not require an RBAC permission the member legitimately lacks.\n if (authOnly) {\n // When the shell RBAC context is unavailable (standalone/dev), render.\n if (hasPermission !== null && !isAuthenticated && !allowLocalShellBypass) {\n return (\n <AccessDenied\n title={tr(\"devportal.accessDenied\", \"Access Denied\")}\n description={tr(\n \"devportal.noPermissionView\",\n \"You don't have permission to view this page.\",\n )}\n />\n );\n }\n return <>{children}</>;\n }\n\n if (\n permission &&\n hasPermission &&\n !hasPermission(permission) &&\n !isRbacAdmin &&\n !allowLocalShellBypass\n ) {\n return (\n <AccessDenied\n title={tr(\"devportal.accessDenied\", \"Access Denied\")}\n description={tr(\n \"devportal.noPermissionView\",\n \"You don't have permission to view this page.\",\n )}\n />\n );\n }\n\n return <>{children}</>;\n};\n"],"mappings":";;;;;;;AA+BA,IAAa,KAA6C,EACxD,eACA,cAAW,IACX,kBACI;CACJ,IAAI,IACF,MACE,IAAY,IACZ,IAAc,IACd,IAAkB;AAEtB,KAAI;EACF,IAAM,IAAc,GAAgB;AAKpC,EAJA,IAAgB,EAAY,eAG5B,IAAY,EAAY,WACxB,IACE,EAAY,aAAa,aAAa,MACnC,MAAU,EAAM,iBAAiB,UAAU,EAAM,UAAU,QAC7D,IAAI;SACD;AAIN,EAFA,IAAgB,MAChB,IAAY,IACZ,IAAc;;AAGhB,KAAI;AAEF,MADa,GAAS,CACC;SACjB;AACN,MAAkB;;CAQpB,IAAM,IAJJ,OAAO,SAAW,QACjB,OAAO,SAAS,aAAa,eAC5B,OAAO,SAAS,SAAS,SAAS,uBAAuB,KAEZ,GAC3C,IAAK,GAAO;AA+ClB,QA7CI,IAEA,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAoB,CAAA;EAChB,CAAA,GAON,IAEE,MAAkB,QAAQ,CAAC,KAAmB,CAAC,IAE/C,kBAAC,GAAD;EACE,OAAO,EAAG,0BAA0B,gBAAgB;EACpD,aAAa,EACX,8BACA,+CACD;EACD,CAAA,GAGC,kBAAA,GAAA,EAAG,aAAY,CAAA,GAItB,KACA,KACA,CAAC,EAAc,EAAW,IAC1B,CAAC,KACD,CAAC,IAGC,kBAAC,GAAD;EACE,OAAO,EAAG,0BAA0B,gBAAgB;EACpD,aAAa,EACX,8BACA,+CACD;EACD,CAAA,GAIC,kBAAA,GAAA,EAAG,aAAY,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecretDisplay.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/SecretDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAY,MAAM,OAAO,CAAC;AAU1C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,
|
|
1
|
+
{"version":3,"file":"SecretDisplay.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/SecretDisplay.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAY,MAAM,OAAO,CAAC;AAU1C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAqEhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CA2CA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB,CAsDA,CAAC"}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { useTr as e } from "../utils/i18n.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { nativeCopyText as n, nativeImpact as r, nativeNotify as i } from "../../utils/nativeBridge.js";
|
|
4
|
+
import { useState as a } from "react";
|
|
5
5
|
import { Check as o, Copy as s, Eye as c, EyeOff as l } from "lucide-react";
|
|
6
6
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
7
7
|
//#region src/devportal/components/SecretDisplay.tsx
|
|
8
8
|
var f = ({ value: f, label: p, className: m, showCopy: h = !0, defaultHidden: g = !0, visible: _ }) => {
|
|
9
|
-
let
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}, [b, x] = i(_ === void 0 ? !g : _), [S, C] = i(!1), w = async () => {
|
|
13
|
-
n("light"), await t(f) ? (C(!0), r("success"), setTimeout(() => C(!1), 2e3)) : r("error");
|
|
14
|
-
}, T = "•".repeat(Math.min(f.length, 40));
|
|
9
|
+
let v = e(), [y, b] = a(_ === void 0 ? !g : _), [x, S] = a(!1), C = async () => {
|
|
10
|
+
r("light"), await n(f) ? (S(!0), i("success"), setTimeout(() => S(!1), 2e3)) : i("error");
|
|
11
|
+
}, w = "•".repeat(Math.min(f.length, 40));
|
|
15
12
|
return /* @__PURE__ */ d("div", {
|
|
16
|
-
className:
|
|
13
|
+
className: t("space-y-1", m),
|
|
17
14
|
children: [p && /* @__PURE__ */ u("label", {
|
|
18
15
|
className: "text-sm font-medium text-text-secondary",
|
|
19
16
|
children: p
|
|
@@ -22,21 +19,21 @@ var f = ({ value: f, label: p, className: m, showCopy: h = !0, defaultHidden: g
|
|
|
22
19
|
children: [
|
|
23
20
|
/* @__PURE__ */ u("code", {
|
|
24
21
|
className: "flex-1 rounded-md bg-bg-sunken px-3 py-2 font-mono text-sm break-all",
|
|
25
|
-
children:
|
|
22
|
+
children: y ? f : w
|
|
26
23
|
}),
|
|
27
24
|
/* @__PURE__ */ u("button", {
|
|
28
25
|
type: "button",
|
|
29
|
-
onClick: () =>
|
|
26
|
+
onClick: () => b(!y),
|
|
30
27
|
className: "rounded-md p-2 hover:bg-bg-sunken transition-colors",
|
|
31
|
-
title:
|
|
32
|
-
children: u(
|
|
28
|
+
title: y ? v("devportal.hide", "Hide") : v("devportal.show", "Show"),
|
|
29
|
+
children: u(y ? l : c, { size: 16 })
|
|
33
30
|
}),
|
|
34
31
|
h && /* @__PURE__ */ u("button", {
|
|
35
32
|
type: "button",
|
|
36
|
-
onClick:
|
|
33
|
+
onClick: C,
|
|
37
34
|
className: "rounded-md p-2 hover:bg-bg-sunken transition-colors",
|
|
38
|
-
title:
|
|
39
|
-
children:
|
|
35
|
+
title: v("devportal.copyToClipboard", "Copy to clipboard"),
|
|
36
|
+
children: x ? /* @__PURE__ */ u(o, {
|
|
40
37
|
size: 16,
|
|
41
38
|
className: "text-status-success-text"
|
|
42
39
|
}) : /* @__PURE__ */ u(s, { size: 16 })
|
|
@@ -44,8 +41,8 @@ var f = ({ value: f, label: p, className: m, showCopy: h = !0, defaultHidden: g
|
|
|
44
41
|
]
|
|
45
42
|
})]
|
|
46
43
|
});
|
|
47
|
-
}, p = ({ title:
|
|
48
|
-
let
|
|
44
|
+
}, p = ({ title: t, secret: n, description: r, onDismiss: i }) => {
|
|
45
|
+
let a = e();
|
|
49
46
|
return /* @__PURE__ */ u("div", {
|
|
50
47
|
className: "rounded-lg border border-border-subtle bg-status-warning-bg-subtle p-4",
|
|
51
48
|
children: /* @__PURE__ */ d("div", {
|
|
@@ -69,31 +66,28 @@ var f = ({ value: f, label: p, className: m, showCopy: h = !0, defaultHidden: g
|
|
|
69
66
|
children: [
|
|
70
67
|
/* @__PURE__ */ u("h4", {
|
|
71
68
|
className: "font-medium text-status-warning-text",
|
|
72
|
-
children:
|
|
69
|
+
children: t
|
|
73
70
|
}),
|
|
74
|
-
|
|
71
|
+
r && /* @__PURE__ */ u("p", {
|
|
75
72
|
className: "mt-1 text-sm text-status-warning-text",
|
|
76
|
-
children:
|
|
73
|
+
children: r
|
|
77
74
|
}),
|
|
78
75
|
/* @__PURE__ */ u("div", {
|
|
79
76
|
className: "mt-3",
|
|
80
77
|
children: /* @__PURE__ */ u(f, {
|
|
81
|
-
value:
|
|
78
|
+
value: n,
|
|
82
79
|
defaultHidden: !1
|
|
83
80
|
})
|
|
84
81
|
}),
|
|
85
82
|
/* @__PURE__ */ u("p", {
|
|
86
83
|
className: "mt-2 text-xs text-status-warning-text",
|
|
87
|
-
children: (
|
|
88
|
-
let n = i(e);
|
|
89
|
-
return n === e ? t : n;
|
|
90
|
-
})("devportal.copySecretNow", "Make sure to copy this secret now. You won't be able to see it again!")
|
|
84
|
+
children: a("devportal.copySecretNow", "Make sure to copy this secret now. You won't be able to see it again!")
|
|
91
85
|
})
|
|
92
86
|
]
|
|
93
87
|
}),
|
|
94
|
-
|
|
88
|
+
i && /* @__PURE__ */ d("button", {
|
|
95
89
|
type: "button",
|
|
96
|
-
onClick:
|
|
90
|
+
onClick: i,
|
|
97
91
|
className: "text-status-warning-text hover:text-status-warning-text",
|
|
98
92
|
children: [/* @__PURE__ */ u("span", {
|
|
99
93
|
className: "sr-only",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecretDisplay.js","names":[],"sources":["../../../src/devportal/components/SecretDisplay.tsx"],"sourcesContent":["import { type FC, useState } from \"react\";\nimport { Eye, EyeOff, Copy, Check } from \"lucide-react\";\nimport {
|
|
1
|
+
{"version":3,"file":"SecretDisplay.js","names":[],"sources":["../../../src/devportal/components/SecretDisplay.tsx"],"sourcesContent":["import { type FC, useState } from \"react\";\nimport { Eye, EyeOff, Copy, Check } from \"lucide-react\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\n\nexport interface SecretDisplayProps {\n value: string;\n label?: string;\n className?: string;\n showCopy?: boolean;\n defaultHidden?: boolean;\n /** Alias for defaultHidden=false - shows the secret by default */\n visible?: boolean;\n}\n\n/**\n * Component for displaying secrets with show/hide and copy functionality\n */\nexport const SecretDisplay: FC<SecretDisplayProps> = ({\n value,\n label,\n className,\n showCopy = true,\n defaultHidden = true,\n visible,\n}) => {\n const tr = useTr();\n // If visible is explicitly set, use it; otherwise use defaultHidden\n const [isVisible, setIsVisible] = useState(\n visible !== undefined ? visible : !defaultHidden,\n );\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(value);\n if (ok) {\n setCopied(true);\n void nativeNotify(\"success\");\n setTimeout(() => setCopied(false), 2000);\n } else {\n void nativeNotify(\"error\");\n }\n };\n\n const maskedValue = \"•\".repeat(Math.min(value.length, 40));\n\n return (\n <div className={cn(\"space-y-1\", className)}>\n {label && (\n <label className=\"text-sm font-medium text-text-secondary\">\n {label}\n </label>\n )}\n <div className=\"flex items-center gap-2\">\n <code className=\"flex-1 rounded-md bg-bg-sunken px-3 py-2 font-mono text-sm break-all\">\n {isVisible ? value : maskedValue}\n </code>\n <button\n type=\"button\"\n onClick={() => setIsVisible(!isVisible)}\n className=\"rounded-md p-2 hover:bg-bg-sunken transition-colors\"\n title={\n isVisible\n ? tr(\"devportal.hide\", \"Hide\")\n : tr(\"devportal.show\", \"Show\")\n }\n >\n {isVisible ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n {showCopy && (\n <button\n type=\"button\"\n onClick={handleCopy}\n className=\"rounded-md p-2 hover:bg-bg-sunken transition-colors\"\n title={tr(\"devportal.copyToClipboard\", \"Copy to clipboard\")}\n >\n {copied ? (\n <Check size={16} className=\"text-status-success-text\" />\n ) : (\n <Copy size={16} />\n )}\n </button>\n )}\n </div>\n </div>\n );\n};\n\n/**\n * Compact inline secret display\n */\nexport const InlineSecret: FC<{\n value: string;\n className?: string;\n}> = ({ value, className }) => {\n const [isVisible, setIsVisible] = useState(false);\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(value);\n if (ok) {\n setCopied(true);\n void nativeNotify(\"success\");\n setTimeout(() => setCopied(false), 2000);\n } else {\n void nativeNotify(\"error\");\n }\n };\n\n const maskedValue = value.substring(0, 8) + \"•\".repeat(20);\n\n return (\n <span className={cn(\"inline-flex items-center gap-1\", className)}>\n <code className=\"font-mono text-sm\">\n {isVisible ? value : maskedValue}\n </code>\n <button\n type=\"button\"\n onClick={() => setIsVisible(!isVisible)}\n className=\"p-1 hover:bg-bg-sunken rounded\"\n >\n {isVisible ? <EyeOff size={12} /> : <Eye size={12} />}\n </button>\n <button\n type=\"button\"\n onClick={handleCopy}\n className=\"p-1 hover:bg-bg-sunken rounded\"\n >\n {copied ? (\n <Check size={12} className=\"text-status-success-text\" />\n ) : (\n <Copy size={12} />\n )}\n </button>\n </span>\n );\n};\n\n/**\n * New secret alert - shown once when a new secret is generated\n */\nexport const NewSecretAlert: FC<{\n title: string;\n secret: string;\n description?: string;\n onDismiss?: () => void;\n}> = ({ title, secret, description, onDismiss }) => {\n const tr = useTr();\n return (\n <div className=\"rounded-lg border border-border-subtle bg-status-warning-bg-subtle p-4\">\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0\">\n <svg\n className=\"h-5 w-5 text-status-warning-text\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </div>\n <div className=\"flex-1\">\n <h4 className=\"font-medium text-status-warning-text\">{title}</h4>\n {description && (\n <p className=\"mt-1 text-sm text-status-warning-text\">\n {description}\n </p>\n )}\n <div className=\"mt-3\">\n <SecretDisplay value={secret} defaultHidden={false} />\n </div>\n <p className=\"mt-2 text-xs text-status-warning-text\">\n {tr(\n \"devportal.copySecretNow\",\n \"Make sure to copy this secret now. You won't be able to see it again!\",\n )}\n </p>\n </div>\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"text-status-warning-text hover:text-status-warning-text\"\n >\n <span className=\"sr-only\">Dismiss</span>\n <svg className=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fillRule=\"evenodd\"\n d=\"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;AAuBA,IAAa,KAAyC,EACpD,UACA,UACA,cACA,cAAW,IACX,mBAAgB,IAChB,iBACI;CACJ,IAAM,IAAK,GAAO,EAEZ,CAAC,GAAW,KAAgB,EAChC,MAAY,KAAA,IAAsB,CAAC,IAAX,EACzB,EACK,CAAC,GAAQ,KAAa,EAAS,GAAM,EAErC,IAAa,YAAY;AAG7B,EAFK,EAAa,QAAQ,EACf,MAAM,EAAe,EAAM,IAEpC,EAAU,GAAK,EACV,EAAa,UAAU,EAC5B,iBAAiB,EAAU,GAAM,EAAE,IAAK,IAEnC,EAAa,QAAQ;IAIxB,IAAc,IAAI,OAAO,KAAK,IAAI,EAAM,QAAQ,GAAG,CAAC;AAE1D,QACE,kBAAC,OAAD;EAAK,WAAW,EAAG,aAAa,EAAU;YAA1C,CACG,KACC,kBAAC,SAAD;GAAO,WAAU;aACd;GACK,CAAA,EAEV,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,QAAD;KAAM,WAAU;eACb,IAAY,IAAQ;KAChB,CAAA;IACP,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAa,CAAC,EAAU;KACvC,WAAU;KACV,OACE,IACI,EAAG,kBAAkB,OAAO,GAC5B,EAAG,kBAAkB,OAAO;eAGrB,EAAZ,IAAa,IAAuB,GAAxB,EAAQ,MAAM,IAAM,CAAoB;KAC9C,CAAA;IACR,KACC,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;KACV,OAAO,EAAG,6BAA6B,oBAAoB;eAE1D,IACC,kBAAC,GAAD;MAAO,MAAM;MAAI,WAAU;MAA6B,CAAA,GAExD,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;KAEb,CAAA;IAEP;KACF;;GA0DG,KAKP,EAAE,UAAO,WAAQ,gBAAa,mBAAgB;CAClD,IAAM,IAAK,GAAO;AAClB,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MACE,WAAU;MACV,SAAQ;MACR,MAAK;gBAEL,kBAAC,QAAD;OACE,UAAS;OACT,GAAE;OACF,UAAS;OACT,CAAA;MACE,CAAA;KACF,CAAA;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAwC;OAAW,CAAA;MAChE,KACC,kBAAC,KAAD;OAAG,WAAU;iBACV;OACC,CAAA;MAEN,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD;QAAe,OAAO;QAAQ,eAAe;QAAS,CAAA;OAClD,CAAA;MACN,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,2BACA,wEACD;OACC,CAAA;MACA;;IACL,KACC,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAHZ,CAKE,kBAAC,QAAD;MAAM,WAAU;gBAAU;MAAc,CAAA,EACxC,kBAAC,OAAD;MAAK,WAAU;MAAU,SAAQ;MAAY,MAAK;gBAChD,kBAAC,QAAD;OACE,UAAS;OACT,GAAE;OACF,UAAS;OACT,CAAA;MACE,CAAA,CACC;;IAEP;;EACF,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionNav.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/SectionNav.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAGhC,OAAO,EACL,eAAe,EAWhB,MAAM,cAAc,CAAC;AAWtB,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,eAAe,CAAC;IAC7B,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAoFD,8EAA8E;AAC9E,eAAO,MAAM,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"SectionNav.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/SectionNav.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAGhC,OAAO,EACL,eAAe,EAWhB,MAAM,cAAc,CAAC;AAWtB,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,eAAe,CAAC;IAC7B,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAoFD,8EAA8E;AAC9E,eAAO,MAAM,WAAW,EAAE,EAoFzB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,EA8CxB,CAAC;AAGF,eAAO,MAAM,sBAAsB,uBAAW,CAAC"}
|
|
@@ -1,39 +1,36 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useTr as t } from "../utils/i18n.js";
|
|
3
|
+
import { cn as n } from "../utils/cn.js";
|
|
4
|
+
import { useGetMyOrganizationsQuery as r } from "../../generated/global-operations.js";
|
|
4
5
|
import "../hooks/useGraphQL.js";
|
|
5
|
-
import { useEffect as
|
|
6
|
-
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
6
|
+
import { useEffect as i, useRef as a, useState as o } from "react";
|
|
7
7
|
import { Building2 as s, Check as c, ChevronDown as l } from "lucide-react";
|
|
8
8
|
import "react-router";
|
|
9
9
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
10
10
|
//#region src/devportal/components/SectionNav.tsx
|
|
11
11
|
var f = () => {
|
|
12
|
-
let { activeOrgId: f, setActiveOrgId: p } = e(),
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}, [g, _] = a(!1), v = i(null), { data: y } = n({ fetchPolicy: "cache-first" }), b = y?.myDeveloperOrganizations ?? [];
|
|
16
|
-
if (r(() => {
|
|
17
|
-
!f && b.length === 1 && b[0]?.id && p(b[0].id);
|
|
12
|
+
let { activeOrgId: f, setActiveOrgId: p } = e(), m = t(), [h, g] = o(!1), _ = a(null), { data: v } = r({ fetchPolicy: "cache-first" }), y = v?.myDeveloperOrganizations ?? [];
|
|
13
|
+
if (i(() => {
|
|
14
|
+
!f && y.length === 1 && y[0]?.id && p(y[0].id);
|
|
18
15
|
}, [
|
|
19
|
-
|
|
16
|
+
y,
|
|
20
17
|
f,
|
|
21
18
|
p
|
|
22
|
-
]),
|
|
19
|
+
]), i(() => {
|
|
23
20
|
let e = (e) => {
|
|
24
|
-
|
|
21
|
+
_.current && !_.current.contains(e.target) && g(!1);
|
|
25
22
|
};
|
|
26
|
-
return
|
|
27
|
-
}, [
|
|
28
|
-
let
|
|
23
|
+
return h && document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
24
|
+
}, [h]), y.length === 0) return null;
|
|
25
|
+
let b = y.find((e) => e.id === f)?.name ?? m("devportal.nav.selectOrg", "Select org");
|
|
29
26
|
return /* @__PURE__ */ d("div", {
|
|
30
|
-
ref:
|
|
27
|
+
ref: _,
|
|
31
28
|
className: "relative flex-shrink-0",
|
|
32
29
|
children: [/* @__PURE__ */ d("button", {
|
|
33
30
|
type: "button",
|
|
34
|
-
onClick: () =>
|
|
35
|
-
className:
|
|
36
|
-
"aria-label":
|
|
31
|
+
onClick: () => g((e) => !e),
|
|
32
|
+
className: n("inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors", "bg-bg-surface text-text-primary border-border-subtle hover:bg-accent"),
|
|
33
|
+
"aria-label": m("devportal.nav.switchOrg", "Switch organization"),
|
|
37
34
|
children: [
|
|
38
35
|
/* @__PURE__ */ u(s, {
|
|
39
36
|
size: 13,
|
|
@@ -41,24 +38,24 @@ var f = () => {
|
|
|
41
38
|
}),
|
|
42
39
|
/* @__PURE__ */ u("span", {
|
|
43
40
|
className: "max-w-[140px] truncate",
|
|
44
|
-
children:
|
|
41
|
+
children: b
|
|
45
42
|
}),
|
|
46
43
|
/* @__PURE__ */ u(l, {
|
|
47
44
|
size: 13,
|
|
48
|
-
className:
|
|
45
|
+
className: n("transition-transform", h && "rotate-180")
|
|
49
46
|
})
|
|
50
47
|
]
|
|
51
|
-
}),
|
|
48
|
+
}), h && /* @__PURE__ */ u("div", {
|
|
52
49
|
className: "absolute right-0 top-full z-50 mt-1 min-w-[200px] rounded-md border border-border-subtle bg-popover shadow-lg py-1",
|
|
53
|
-
children:
|
|
50
|
+
children: y.map((e) => /* @__PURE__ */ d("button", {
|
|
54
51
|
type: "button",
|
|
55
52
|
onClick: () => {
|
|
56
|
-
p(e.id),
|
|
53
|
+
p(e.id), g(!1);
|
|
57
54
|
},
|
|
58
|
-
className:
|
|
55
|
+
className: n("flex w-full items-center gap-2 px-3 py-2 text-sm text-left transition-colors hover:bg-accent", e.id === f ? "text-primary font-medium" : "text-text-primary"),
|
|
59
56
|
children: [/* @__PURE__ */ u(c, {
|
|
60
57
|
size: 13,
|
|
61
|
-
className:
|
|
58
|
+
className: n("flex-shrink-0", e.id === f ? "opacity-100 text-primary" : "opacity-0")
|
|
62
59
|
}), /* @__PURE__ */ u("span", {
|
|
63
60
|
className: "truncate",
|
|
64
61
|
children: e.name
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionNav.js","names":[],"sources":["../../../src/devportal/components/SectionNav.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useLocation } from \"react-router\";\nimport {\n LayoutDashboard,\n AppWindow,\n Building2,\n Share2,\n DollarSign,\n BookOpen,\n Library,\n User,\n Settings,\n ChevronDown,\n Check,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport {\n useGetMyOrganizationsQuery,\n type GetMyOrganizationsQuery,\n} from \"../hooks/useGraphQL\";\nimport { cn } from \"../utils/cn\";\n\ntype OrgItem = GetMyOrganizationsQuery[\"myDeveloperOrganizations\"][number];\n\ninterface SectionDef {\n path: string;\n i18nKey: string;\n fallback: string;\n icon: typeof LayoutDashboard;\n /** Additional sub-paths that should mark this section as active. */\n matchPrefixes?: string[];\n}\n\nconst SECTIONS: ReadonlyArray<SectionDef> = [\n {\n path: \"\",\n i18nKey: \"devportal.nav.dashboard\",\n fallback: \"Dashboard\",\n icon: LayoutDashboard,\n },\n {\n path: \"apps\",\n i18nKey: \"devportal.nav.apps\",\n fallback: \"Apps\",\n icon: AppWindow,\n },\n {\n path: \"organizations\",\n i18nKey: \"devportal.nav.organizations\",\n fallback: \"Organizations\",\n icon: Building2,\n },\n {\n path: \"delegations\",\n i18nKey: \"devportal.nav.delegations\",\n fallback: \"Delegations\",\n icon: Share2,\n },\n {\n path: \"revenue\",\n i18nKey: \"devportal.nav.revenue\",\n fallback: \"Revenue\",\n icon: DollarSign,\n },\n {\n path: \"docs\",\n i18nKey: \"devportal.nav.docs\",\n fallback: \"Docs\",\n icon: BookOpen,\n },\n {\n path: \"resources\",\n i18nKey: \"devportal.nav.resources\",\n fallback: \"Resources\",\n icon: Library,\n },\n {\n path: \"publisher/profile\",\n i18nKey: \"devportal.nav.publisher\",\n fallback: \"Publisher\",\n icon: User,\n matchPrefixes: [\"publisher\"],\n },\n {\n path: \"settings\",\n i18nKey: \"devportal.nav.settings\",\n fallback: \"Settings\",\n icon: Settings,\n },\n];\n\nfunction joinBaseAndPath(basePath: string | undefined, path: string): string {\n const base = (basePath ?? \"\").replace(/\\/$/, \"\");\n if (!path) return base || \"/\";\n const suffix = path.startsWith(\"/\") ? path : `/${path}`;\n return `${base}${suffix}`;\n}\n\nfunction isSectionActive(\n pathname: string,\n basePath: string | undefined,\n section: SectionDef,\n): boolean {\n const sectionUrl = joinBaseAndPath(basePath, section.path);\n // Index/dashboard is active only when exactly matched (or with empty extra).\n if (section.path === \"\") {\n return pathname === sectionUrl || pathname === `${sectionUrl}/`;\n }\n const candidates = [section.path, ...(section.matchPrefixes ?? [])];\n return candidates.some((prefix) => {\n const url = joinBaseAndPath(basePath, prefix);\n return pathname === url || pathname.startsWith(`${url}/`);\n });\n}\n\n/** Org context switcher — can be embedded standalone or inside SectionNav. */\nexport const OrgSwitcher: FC = () => {\n const { activeOrgId, setActiveOrgId } = 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 const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n const { data } = useGetMyOrganizationsQuery({ fetchPolicy: \"cache-first\" });\n const orgs: OrgItem[] = data?.myDeveloperOrganizations ?? [];\n\n // Auto-select when there's exactly one org and nothing is stored yet\n useEffect(() => {\n if (!activeOrgId && orgs.length === 1 && orgs[0]?.id) {\n setActiveOrgId(orgs[0].id);\n }\n }, [orgs, activeOrgId, setActiveOrgId]);\n\n // Close on outside click\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n if (open) document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n if (orgs.length === 0) return null;\n\n const activeOrg = orgs.find((o) => o.id === activeOrgId);\n const label = activeOrg?.name ?? tr(\"devportal.nav.selectOrg\", \"Select org\");\n\n return (\n <div ref={ref} className=\"relative flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors\",\n \"bg-bg-surface text-text-primary border-border-subtle hover:bg-accent\",\n )}\n aria-label={tr(\"devportal.nav.switchOrg\", \"Switch organization\")}\n >\n <Building2 size={13} className=\"text-primary flex-shrink-0\" />\n <span className=\"max-w-[140px] truncate\">{label}</span>\n <ChevronDown\n size={13}\n className={cn(\"transition-transform\", open && \"rotate-180\")}\n />\n </button>\n\n {open && (\n <div className=\"absolute right-0 top-full z-50 mt-1 min-w-[200px] rounded-md border border-border-subtle bg-popover shadow-lg py-1\">\n {orgs.map((org) => (\n <button\n key={org.id}\n type=\"button\"\n onClick={() => {\n setActiveOrgId(org.id);\n setOpen(false);\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 text-sm text-left transition-colors hover:bg-accent\",\n org.id === activeOrgId\n ? \"text-primary font-medium\"\n : \"text-text-primary\",\n )}\n >\n <Check\n size={13}\n className={cn(\n \"flex-shrink-0\",\n org.id === activeOrgId\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <span className=\"truncate\">{org.name}</span>\n </button>\n ))}\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Horizontal section nav rendered above the page content on every devportal\n * route. Gives developers a one-click jump between all top-level sections so\n * routes like OAuth, Delegations, Analytics, Revenue, Docs, Resources,\n * Publisher Profile, and Settings are reachable without typing URLs.\n *\n * Renders nothing when `basePath` is undefined (standalone/dev mode) so it\n * doesn't conflict with whatever harness wraps the MFE there.\n */\nexport const SectionNav: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { pathname } = useLocation();\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 if (!basePath || !navigate) return null;\n\n const handleClick = (path: string) => {\n navigate(joinBaseAndPath(basePath, path));\n };\n\n return (\n <nav\n aria-label={tr(\"devportal.nav.sections\", \"Developer portal sections\")}\n className=\"border-b border-border-subtle bg-bg-surface/40 px-4 py-2\"\n data-testid=\"devportal-section-nav\"\n >\n <div className=\"flex items-center gap-2\">\n <ul className=\"flex flex-1 flex-wrap gap-1\">\n {SECTIONS.map((section) => {\n const active = isSectionActive(pathname, basePath, section);\n const Icon = section.icon;\n return (\n <li key={section.path}>\n <button\n type=\"button\"\n onClick={() => handleClick(section.path)}\n aria-current={active ? \"page\" : undefined}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors\",\n active\n ? \"bg-action-primary-bg text-action-primary-text\"\n : \"text-text-secondary hover:bg-accent hover:text-text-primary\",\n )}\n >\n <Icon size={14} />\n <span>{tr(section.i18nKey, section.fallback)}</span>\n </button>\n </li>\n );\n })}\n </ul>\n <OrgSwitcher />\n </div>\n </nav>\n );\n};\n\n// Exported for unit tests that want to assert against the canonical list.\nexport const __SECTION_NAV_SECTIONS = SECTIONS;\n"],"mappings":";;;;;;;;;;AAsHA,IAAa,UAAwB;CACnC,IAAM,EAAE,gBAAa,sBAAmB,GAAc,EAChD,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAE9B,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,IAAM,EAAuB,KAAK,EAElC,EAAE,YAAS,EAA2B,EAAE,aAAa,eAAe,CAAC,EACrE,IAAkB,GAAM,4BAA4B,EAAE;AAoB5D,KAjBA,QAAgB;AACd,EAAI,CAAC,KAAe,EAAK,WAAW,KAAK,EAAK,IAAI,MAChD,EAAe,EAAK,GAAG,GAAG;IAE3B;EAAC;EAAM;EAAa;EAAe,CAAC,EAGvC,QAAgB;EACd,IAAM,KAAW,MAAkB;AACjC,GAAI,EAAI,WAAW,CAAC,EAAI,QAAQ,SAAS,EAAE,OAAe,IACxD,EAAQ,GAAM;;AAIlB,SADI,KAAM,SAAS,iBAAiB,aAAa,EAAQ,QAC5C,SAAS,oBAAoB,aAAa,EAAQ;IAC9D,CAAC,EAAK,CAAC,EAEN,EAAK,WAAW,EAAG,QAAO;CAG9B,IAAM,IADY,EAAK,MAAM,MAAM,EAAE,OAAO,EAAY,EAC/B,QAAQ,EAAG,2BAA2B,aAAa;AAE5E,QACE,kBAAC,OAAD;EAAU;EAAK,WAAU;YAAzB,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,GAAS,MAAM,CAAC,EAAE;GACjC,WAAW,EACT,wGACA,uEACD;GACD,cAAY,EAAG,2BAA2B,sBAAsB;aAPlE;IASE,kBAAC,GAAD;KAAW,MAAM;KAAI,WAAU;KAA+B,CAAA;IAC9D,kBAAC,QAAD;KAAM,WAAU;eAA0B;KAAa,CAAA;IACvD,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,EAAG,wBAAwB,KAAQ,aAAa;KAC3D,CAAA;IACK;MAER,KACC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAK,KAAK,MACT,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe;AAEb,KADA,EAAe,EAAI,GAAG,EACtB,EAAQ,GAAM;;IAEhB,WAAW,EACT,gGACA,EAAI,OAAO,IACP,6BACA,oBACL;cAZH,CAcE,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,EACT,iBACA,EAAI,OAAO,IACP,6BACA,YACL;KACD,CAAA,EACF,kBAAC,QAAD;KAAM,WAAU;eAAY,EAAI;KAAY,CAAA,CACrC;MAvBF,EAAI,GAuBF,CACT;GACE,CAAA,CAEJ"}
|
|
1
|
+
{"version":3,"file":"SectionNav.js","names":[],"sources":["../../../src/devportal/components/SectionNav.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useLocation } from \"react-router\";\nimport {\n LayoutDashboard,\n AppWindow,\n Building2,\n Share2,\n DollarSign,\n BookOpen,\n Library,\n User,\n Settings,\n ChevronDown,\n Check,\n} from \"lucide-react\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport {\n useGetMyOrganizationsQuery,\n type GetMyOrganizationsQuery,\n} from \"../hooks/useGraphQL\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\n\ntype OrgItem = GetMyOrganizationsQuery[\"myDeveloperOrganizations\"][number];\n\ninterface SectionDef {\n path: string;\n i18nKey: string;\n fallback: string;\n icon: typeof LayoutDashboard;\n /** Additional sub-paths that should mark this section as active. */\n matchPrefixes?: string[];\n}\n\nconst SECTIONS: ReadonlyArray<SectionDef> = [\n {\n path: \"\",\n i18nKey: \"devportal.nav.dashboard\",\n fallback: \"Dashboard\",\n icon: LayoutDashboard,\n },\n {\n path: \"apps\",\n i18nKey: \"devportal.nav.apps\",\n fallback: \"Apps\",\n icon: AppWindow,\n },\n {\n path: \"organizations\",\n i18nKey: \"devportal.nav.organizations\",\n fallback: \"Organizations\",\n icon: Building2,\n },\n {\n path: \"delegations\",\n i18nKey: \"devportal.nav.delegations\",\n fallback: \"Delegations\",\n icon: Share2,\n },\n {\n path: \"revenue\",\n i18nKey: \"devportal.nav.revenue\",\n fallback: \"Revenue\",\n icon: DollarSign,\n },\n {\n path: \"docs\",\n i18nKey: \"devportal.nav.docs\",\n fallback: \"Docs\",\n icon: BookOpen,\n },\n {\n path: \"resources\",\n i18nKey: \"devportal.nav.resources\",\n fallback: \"Resources\",\n icon: Library,\n },\n {\n path: \"publisher/profile\",\n i18nKey: \"devportal.nav.publisher\",\n fallback: \"Publisher\",\n icon: User,\n matchPrefixes: [\"publisher\"],\n },\n {\n path: \"settings\",\n i18nKey: \"devportal.nav.settings\",\n fallback: \"Settings\",\n icon: Settings,\n },\n];\n\nfunction joinBaseAndPath(basePath: string | undefined, path: string): string {\n const base = (basePath ?? \"\").replace(/\\/$/, \"\");\n if (!path) return base || \"/\";\n const suffix = path.startsWith(\"/\") ? path : `/${path}`;\n return `${base}${suffix}`;\n}\n\nfunction isSectionActive(\n pathname: string,\n basePath: string | undefined,\n section: SectionDef,\n): boolean {\n const sectionUrl = joinBaseAndPath(basePath, section.path);\n // Index/dashboard is active only when exactly matched (or with empty extra).\n if (section.path === \"\") {\n return pathname === sectionUrl || pathname === `${sectionUrl}/`;\n }\n const candidates = [section.path, ...(section.matchPrefixes ?? [])];\n return candidates.some((prefix) => {\n const url = joinBaseAndPath(basePath, prefix);\n return pathname === url || pathname.startsWith(`${url}/`);\n });\n}\n\n/** Org context switcher — can be embedded standalone or inside SectionNav. */\nexport const OrgSwitcher: FC = () => {\n const { activeOrgId, setActiveOrgId } = useDevportal();\n const tr = useTr();\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n const { data } = useGetMyOrganizationsQuery({ fetchPolicy: \"cache-first\" });\n const orgs: OrgItem[] = data?.myDeveloperOrganizations ?? [];\n\n // Auto-select when there's exactly one org and nothing is stored yet\n useEffect(() => {\n if (!activeOrgId && orgs.length === 1 && orgs[0]?.id) {\n setActiveOrgId(orgs[0].id);\n }\n }, [orgs, activeOrgId, setActiveOrgId]);\n\n // Close on outside click\n useEffect(() => {\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n if (open) document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n if (orgs.length === 0) return null;\n\n const activeOrg = orgs.find((o) => o.id === activeOrgId);\n const label = activeOrg?.name ?? tr(\"devportal.nav.selectOrg\", \"Select org\");\n\n return (\n <div ref={ref} className=\"relative flex-shrink-0\">\n <button\n type=\"button\"\n onClick={() => setOpen((v) => !v)}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors\",\n \"bg-bg-surface text-text-primary border-border-subtle hover:bg-accent\",\n )}\n aria-label={tr(\"devportal.nav.switchOrg\", \"Switch organization\")}\n >\n <Building2 size={13} className=\"text-primary flex-shrink-0\" />\n <span className=\"max-w-[140px] truncate\">{label}</span>\n <ChevronDown\n size={13}\n className={cn(\"transition-transform\", open && \"rotate-180\")}\n />\n </button>\n\n {open && (\n <div className=\"absolute right-0 top-full z-50 mt-1 min-w-[200px] rounded-md border border-border-subtle bg-popover shadow-lg py-1\">\n {orgs.map((org) => (\n <button\n key={org.id}\n type=\"button\"\n onClick={() => {\n setActiveOrgId(org.id);\n setOpen(false);\n }}\n className={cn(\n \"flex w-full items-center gap-2 px-3 py-2 text-sm text-left transition-colors hover:bg-accent\",\n org.id === activeOrgId\n ? \"text-primary font-medium\"\n : \"text-text-primary\",\n )}\n >\n <Check\n size={13}\n className={cn(\n \"flex-shrink-0\",\n org.id === activeOrgId\n ? \"opacity-100 text-primary\"\n : \"opacity-0\",\n )}\n />\n <span className=\"truncate\">{org.name}</span>\n </button>\n ))}\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Horizontal section nav rendered above the page content on every devportal\n * route. Gives developers a one-click jump between all top-level sections so\n * routes like OAuth, Delegations, Analytics, Revenue, Docs, Resources,\n * Publisher Profile, and Settings are reachable without typing URLs.\n *\n * Renders nothing when `basePath` is undefined (standalone/dev mode) so it\n * doesn't conflict with whatever harness wraps the MFE there.\n */\nexport const SectionNav: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { pathname } = useLocation();\n const tr = useTr();\n\n if (!basePath || !navigate) return null;\n\n const handleClick = (path: string) => {\n navigate(joinBaseAndPath(basePath, path));\n };\n\n return (\n <nav\n aria-label={tr(\"devportal.nav.sections\", \"Developer portal sections\")}\n className=\"border-b border-border-subtle bg-bg-surface/40 px-4 py-2\"\n data-testid=\"devportal-section-nav\"\n >\n <div className=\"flex items-center gap-2\">\n <ul className=\"flex flex-1 flex-wrap gap-1\">\n {SECTIONS.map((section) => {\n const active = isSectionActive(pathname, basePath, section);\n const Icon = section.icon;\n return (\n <li key={section.path}>\n <button\n type=\"button\"\n onClick={() => handleClick(section.path)}\n aria-current={active ? \"page\" : undefined}\n className={cn(\n \"inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors\",\n active\n ? \"bg-action-primary-bg text-action-primary-text\"\n : \"text-text-secondary hover:bg-accent hover:text-text-primary\",\n )}\n >\n <Icon size={14} />\n <span>{tr(section.i18nKey, section.fallback)}</span>\n </button>\n </li>\n );\n })}\n </ul>\n <OrgSwitcher />\n </div>\n </nav>\n );\n};\n\n// Exported for unit tests that want to assert against the canonical list.\nexport const __SECTION_NAV_SECTIONS = SECTIONS;\n"],"mappings":";;;;;;;;;;AAsHA,IAAa,UAAwB;CACnC,IAAM,EAAE,gBAAa,sBAAmB,GAAc,EAChD,IAAK,GAAO,EACZ,CAAC,GAAM,KAAW,EAAS,GAAM,EACjC,IAAM,EAAuB,KAAK,EAElC,EAAE,YAAS,EAA2B,EAAE,aAAa,eAAe,CAAC,EACrE,IAAkB,GAAM,4BAA4B,EAAE;AAoB5D,KAjBA,QAAgB;AACd,EAAI,CAAC,KAAe,EAAK,WAAW,KAAK,EAAK,IAAI,MAChD,EAAe,EAAK,GAAG,GAAG;IAE3B;EAAC;EAAM;EAAa;EAAe,CAAC,EAGvC,QAAgB;EACd,IAAM,KAAW,MAAkB;AACjC,GAAI,EAAI,WAAW,CAAC,EAAI,QAAQ,SAAS,EAAE,OAAe,IACxD,EAAQ,GAAM;;AAIlB,SADI,KAAM,SAAS,iBAAiB,aAAa,EAAQ,QAC5C,SAAS,oBAAoB,aAAa,EAAQ;IAC9D,CAAC,EAAK,CAAC,EAEN,EAAK,WAAW,EAAG,QAAO;CAG9B,IAAM,IADY,EAAK,MAAM,MAAM,EAAE,OAAO,EAAY,EAC/B,QAAQ,EAAG,2BAA2B,aAAa;AAE5E,QACE,kBAAC,OAAD;EAAU;EAAK,WAAU;YAAzB,CACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,GAAS,MAAM,CAAC,EAAE;GACjC,WAAW,EACT,wGACA,uEACD;GACD,cAAY,EAAG,2BAA2B,sBAAsB;aAPlE;IASE,kBAAC,GAAD;KAAW,MAAM;KAAI,WAAU;KAA+B,CAAA;IAC9D,kBAAC,QAAD;KAAM,WAAU;eAA0B;KAAa,CAAA;IACvD,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,EAAG,wBAAwB,KAAQ,aAAa;KAC3D,CAAA;IACK;MAER,KACC,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAK,KAAK,MACT,kBAAC,UAAD;IAEE,MAAK;IACL,eAAe;AAEb,KADA,EAAe,EAAI,GAAG,EACtB,EAAQ,GAAM;;IAEhB,WAAW,EACT,gGACA,EAAI,OAAO,IACP,6BACA,oBACL;cAZH,CAcE,kBAAC,GAAD;KACE,MAAM;KACN,WAAW,EACT,iBACA,EAAI,OAAO,IACP,6BACA,YACL;KACD,CAAA,EACF,kBAAC,QAAD;KAAM,WAAU;eAAY,EAAI;KAAY,CAAA,CACrC;MAvBF,EAAI,GAuBF,CACT;GACE,CAAA,CAEJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/StatusBadge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAYhC,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CA+C5C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"StatusBadge.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/StatusBadge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAYhC,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,KAAK,GAAG,SAAS,GAAG,UAAU,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CA+C5C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAoBvE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAYpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,EAAE,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAqBvE,CAAC"}
|
|
@@ -1,46 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { useTr as e } from "../utils/i18n.js";
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { APP_STATUS_COLORS as n, DELIVERY_STATUS_COLORS as r, INVITATION_STATUS_COLORS as i, ORG_TIER_COLORS as a, PARTNER_TIER_COLORS as o, WEBHOOK_STATUS_COLORS as s } from "../utils/constants.js";
|
|
4
4
|
import { jsx as c } from "react/jsx-runtime";
|
|
5
5
|
//#region src/devportal/components/StatusBadge.tsx
|
|
6
|
-
var l = ({ status:
|
|
7
|
-
className:
|
|
6
|
+
var l = ({ status: e, type: l = "app", className: u }) => /* @__PURE__ */ c("span", {
|
|
7
|
+
className: t("inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium", (() => {
|
|
8
8
|
switch (l) {
|
|
9
|
-
case "webhook": return
|
|
10
|
-
case "delivery": return
|
|
11
|
-
case "invitation": return
|
|
12
|
-
case "tier": return
|
|
13
|
-
case "partner": return
|
|
14
|
-
default: return
|
|
9
|
+
case "webhook": return s[e] || "bg-bg-sunken text-text-primary";
|
|
10
|
+
case "delivery": return r[e] || "bg-bg-sunken text-text-primary";
|
|
11
|
+
case "invitation": return i[e] || "bg-bg-sunken text-text-primary";
|
|
12
|
+
case "tier": return a[e] || "bg-bg-sunken text-text-primary";
|
|
13
|
+
case "partner": return o[e] || "bg-bg-sunken text-text-primary";
|
|
14
|
+
default: return n[e] || "bg-bg-sunken text-text-primary";
|
|
15
15
|
}
|
|
16
16
|
})(), u),
|
|
17
|
-
children: ((e) => e.replace(/_/g, " ").toLowerCase().replace(/\b\w/g, (e) => e.toUpperCase()))(
|
|
18
|
-
}), u = ({ verified:
|
|
19
|
-
let
|
|
20
|
-
let n = r(e);
|
|
21
|
-
return n === e ? t : n;
|
|
22
|
-
};
|
|
17
|
+
children: ((e) => e.replace(/_/g, " ").toLowerCase().replace(/\b\w/g, (e) => e.toUpperCase()))(e)
|
|
18
|
+
}), u = ({ verified: n, className: r }) => {
|
|
19
|
+
let i = e();
|
|
23
20
|
return /* @__PURE__ */ c("span", {
|
|
24
|
-
className:
|
|
25
|
-
children:
|
|
21
|
+
className: t("inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium", n ? "bg-status-info-bg-subtle text-status-info-text" : "bg-bg-sunken text-text-secondary", r),
|
|
22
|
+
children: n ? i("devportal.verifiedBadge", "✓ Verified") : i("devportal.unverified", "Unverified")
|
|
26
23
|
});
|
|
27
|
-
}, d = ({ className:
|
|
28
|
-
let
|
|
24
|
+
}, d = ({ className: n }) => {
|
|
25
|
+
let r = e();
|
|
29
26
|
return /* @__PURE__ */ c("span", {
|
|
30
|
-
className:
|
|
31
|
-
children: (
|
|
32
|
-
let r = n(e);
|
|
33
|
-
return r === e ? t : r;
|
|
34
|
-
})("devportal.featuredBadge", "★ Featured")
|
|
27
|
+
className: t("inline-flex items-center gap-1 rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs font-medium text-status-warning-text", n),
|
|
28
|
+
children: r("devportal.featuredBadge", "★ Featured")
|
|
35
29
|
});
|
|
36
|
-
}, f = ({ active:
|
|
37
|
-
let
|
|
38
|
-
let n = r(e);
|
|
39
|
-
return n === e ? t : n;
|
|
40
|
-
};
|
|
30
|
+
}, f = ({ active: n, className: r }) => {
|
|
31
|
+
let i = e();
|
|
41
32
|
return /* @__PURE__ */ c("span", {
|
|
42
|
-
className:
|
|
43
|
-
title:
|
|
33
|
+
className: t("inline-block h-2 w-2 rounded-full", n ? "bg-status-success-bg-subtle motion-safe:animate-vc-glow-pulse" : "bg-text-tertiary", r),
|
|
34
|
+
title: n ? i("devportal.active", "Active") : i("devportal.inactive", "Inactive")
|
|
44
35
|
});
|
|
45
36
|
};
|
|
46
37
|
//#endregion
|