@burdenoff/microfe-devportal 2026.611.2 → 2026.616.1
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/components/SectionNav.js +105 -41
- package/dist/devportal/components/SectionNav.js.map +1 -1
- package/dist/devportal/hooks/useGraphQL.js +1 -1
- package/dist/devportal/pages/AdminReviewPage.js +36 -36
- package/dist/devportal/pages/AnalyticsDashboardPage.js +150 -150
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +184 -172
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +9 -9
- package/dist/devportal/pages/ApplicationDetailPage.js +414 -414
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +44 -44
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +118 -118
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +20 -20
- package/dist/devportal/pages/CreateApplicationPage.js +46 -66
- package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
- package/dist/devportal/pages/CreateOAuthClientPage.js +20 -20
- package/dist/devportal/pages/CreateOrganizationPage.js +125 -442
- package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
- package/dist/devportal/pages/CreateWebhookPage.js +25 -25
- package/dist/devportal/pages/DashboardPage.js +160 -145
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +22 -22
- package/dist/devportal/pages/EditApplicationPage.js +297 -317
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +9 -9
- package/dist/devportal/pages/OAuthClientDetailPage.js +17 -17
- package/dist/devportal/pages/OAuthClientsPage.js +16 -16
- package/dist/devportal/pages/OrganizationDetailPage.js +78 -78
- package/dist/devportal/pages/OrganizationMembersPage.js +260 -260
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationsPage.js +18 -18
- package/dist/devportal/pages/PublishPage.js +16 -16
- package/dist/devportal/pages/PublisherProfilePage.js +156 -146
- package/dist/devportal/pages/PublisherProfilePage.js.map +1 -1
- package/dist/devportal/pages/RevenueDashboardPage.js +512 -362
- package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SettingsPage.js +17 -17
- package/dist/devportal/pages/SetupPage.js +99 -99
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +143 -143
- package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
- package/dist/devportal/pages/WebhooksPage.js +140 -140
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/providers/DevportalProvider.js +46 -33
- package/dist/devportal/providers/DevportalProvider.js.map +1 -1
- package/dist/generated/global-operations.js +69 -55
- package/dist/generated/global-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,131 +1,195 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
2
|
import { cn as t } from "../utils/cn.js";
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { useGetMyOrganizationsQuery as n } from "../../generated/global-operations.js";
|
|
4
|
+
import "../hooks/useGraphQL.js";
|
|
5
|
+
import { useEffect as r, useRef as i, useState as a } from "react";
|
|
6
|
+
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
7
|
+
import { AppWindow as s, BarChart3 as c, BookOpen as l, Building2 as u, Check as d, ChevronDown as f, DollarSign as p, Key as m, KeyRound as h, LayoutDashboard as g, Library as _, Settings as v, Share2 as y, User as b, Webhook as x } from "lucide-react";
|
|
8
|
+
import { useLocation as S } from "react-router-dom";
|
|
9
|
+
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
7
10
|
//#region src/devportal/components/SectionNav.tsx
|
|
8
|
-
var
|
|
11
|
+
var T = [
|
|
9
12
|
{
|
|
10
13
|
path: "",
|
|
11
14
|
i18nKey: "devportal.nav.dashboard",
|
|
12
15
|
fallback: "Dashboard",
|
|
13
|
-
icon:
|
|
16
|
+
icon: g
|
|
14
17
|
},
|
|
15
18
|
{
|
|
16
19
|
path: "apps",
|
|
17
20
|
i18nKey: "devportal.nav.apps",
|
|
18
21
|
fallback: "Apps",
|
|
19
|
-
icon:
|
|
22
|
+
icon: s
|
|
20
23
|
},
|
|
21
24
|
{
|
|
22
25
|
path: "organizations",
|
|
23
26
|
i18nKey: "devportal.nav.organizations",
|
|
24
27
|
fallback: "Organizations",
|
|
25
|
-
icon:
|
|
28
|
+
icon: u
|
|
26
29
|
},
|
|
27
30
|
{
|
|
28
31
|
path: "oauth",
|
|
29
32
|
i18nKey: "devportal.nav.oauth",
|
|
30
33
|
fallback: "OAuth Clients",
|
|
31
|
-
icon:
|
|
34
|
+
icon: h
|
|
32
35
|
},
|
|
33
36
|
{
|
|
34
37
|
path: "api-keys",
|
|
35
38
|
i18nKey: "devportal.nav.apiKeys",
|
|
36
39
|
fallback: "API Keys",
|
|
37
|
-
icon:
|
|
40
|
+
icon: m
|
|
38
41
|
},
|
|
39
42
|
{
|
|
40
43
|
path: "webhooks",
|
|
41
44
|
i18nKey: "devportal.nav.webhooks",
|
|
42
45
|
fallback: "Webhooks",
|
|
43
|
-
icon:
|
|
46
|
+
icon: x
|
|
44
47
|
},
|
|
45
48
|
{
|
|
46
49
|
path: "delegations",
|
|
47
50
|
i18nKey: "devportal.nav.delegations",
|
|
48
51
|
fallback: "Delegations",
|
|
49
|
-
icon:
|
|
52
|
+
icon: y
|
|
50
53
|
},
|
|
51
54
|
{
|
|
52
55
|
path: "analytics",
|
|
53
56
|
i18nKey: "devportal.nav.analytics",
|
|
54
57
|
fallback: "Analytics",
|
|
55
|
-
icon:
|
|
58
|
+
icon: c
|
|
56
59
|
},
|
|
57
60
|
{
|
|
58
61
|
path: "revenue",
|
|
59
62
|
i18nKey: "devportal.nav.revenue",
|
|
60
63
|
fallback: "Revenue",
|
|
61
|
-
icon:
|
|
64
|
+
icon: p
|
|
62
65
|
},
|
|
63
66
|
{
|
|
64
67
|
path: "docs",
|
|
65
68
|
i18nKey: "devportal.nav.docs",
|
|
66
69
|
fallback: "Docs",
|
|
67
|
-
icon:
|
|
70
|
+
icon: l
|
|
68
71
|
},
|
|
69
72
|
{
|
|
70
73
|
path: "resources",
|
|
71
74
|
i18nKey: "devportal.nav.resources",
|
|
72
75
|
fallback: "Resources",
|
|
73
|
-
icon:
|
|
76
|
+
icon: _
|
|
74
77
|
},
|
|
75
78
|
{
|
|
76
79
|
path: "publisher/profile",
|
|
77
80
|
i18nKey: "devportal.nav.publisher",
|
|
78
81
|
fallback: "Publisher",
|
|
79
|
-
icon:
|
|
82
|
+
icon: b,
|
|
80
83
|
matchPrefixes: ["publisher"]
|
|
81
84
|
},
|
|
82
85
|
{
|
|
83
86
|
path: "settings",
|
|
84
87
|
i18nKey: "devportal.nav.settings",
|
|
85
88
|
fallback: "Settings",
|
|
86
|
-
icon:
|
|
89
|
+
icon: v
|
|
87
90
|
}
|
|
88
91
|
];
|
|
89
|
-
function
|
|
92
|
+
function E(e, t) {
|
|
90
93
|
let n = (e ?? "").replace(/\/$/, "");
|
|
91
94
|
return t ? `${n}${t.startsWith("/") ? t : `/${t}`}` : n || "/";
|
|
92
95
|
}
|
|
93
|
-
function
|
|
94
|
-
let r =
|
|
96
|
+
function D(e, t, n) {
|
|
97
|
+
let r = E(t, n.path);
|
|
95
98
|
return n.path === "" ? e === r || e === `${r}/` : [n.path, ...n.matchPrefixes ?? []].some((n) => {
|
|
96
|
-
let r =
|
|
99
|
+
let r = E(t, n);
|
|
97
100
|
return e === r || e.startsWith(`${r}/`);
|
|
98
101
|
});
|
|
99
102
|
}
|
|
100
|
-
var
|
|
101
|
-
let {
|
|
102
|
-
let n =
|
|
103
|
+
var O = () => {
|
|
104
|
+
let { activeOrgId: s, setActiveOrgId: c } = e(), { t: l } = o(), p = (e, t) => {
|
|
105
|
+
let n = l(e);
|
|
106
|
+
return n === e ? t : n;
|
|
107
|
+
}, [m, h] = a(!1), g = i(null), { data: _ } = n({ fetchPolicy: "cache-first" }), v = _?.myDeveloperOrganizations ?? [];
|
|
108
|
+
if (r(() => {
|
|
109
|
+
!s && v.length === 1 && v[0]?.id && c(v[0].id);
|
|
110
|
+
}, [
|
|
111
|
+
v,
|
|
112
|
+
s,
|
|
113
|
+
c
|
|
114
|
+
]), r(() => {
|
|
115
|
+
let e = (e) => {
|
|
116
|
+
g.current && !g.current.contains(e.target) && h(!1);
|
|
117
|
+
};
|
|
118
|
+
return m && document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
119
|
+
}, [m]), v.length === 0) return null;
|
|
120
|
+
let y = v.find((e) => e.id === s)?.name ?? p("devportal.nav.selectOrg", "Select org");
|
|
121
|
+
return /* @__PURE__ */ w("div", {
|
|
122
|
+
ref: g,
|
|
123
|
+
className: "relative flex-shrink-0",
|
|
124
|
+
children: [/* @__PURE__ */ w("button", {
|
|
125
|
+
type: "button",
|
|
126
|
+
onClick: () => h((e) => !e),
|
|
127
|
+
className: t("inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm font-medium transition-colors", "bg-card text-foreground border-border hover:bg-accent"),
|
|
128
|
+
"aria-label": p("devportal.nav.switchOrg", "Switch organization"),
|
|
129
|
+
children: [
|
|
130
|
+
/* @__PURE__ */ C(u, {
|
|
131
|
+
size: 13,
|
|
132
|
+
className: "text-primary flex-shrink-0"
|
|
133
|
+
}),
|
|
134
|
+
/* @__PURE__ */ C("span", {
|
|
135
|
+
className: "max-w-[140px] truncate",
|
|
136
|
+
children: y
|
|
137
|
+
}),
|
|
138
|
+
/* @__PURE__ */ C(f, {
|
|
139
|
+
size: 13,
|
|
140
|
+
className: t("transition-transform", m && "rotate-180")
|
|
141
|
+
})
|
|
142
|
+
]
|
|
143
|
+
}), m && /* @__PURE__ */ C("div", {
|
|
144
|
+
className: "absolute right-0 top-full z-50 mt-1 min-w-[200px] rounded-md border border-border bg-popover shadow-lg py-1",
|
|
145
|
+
children: v.map((e) => /* @__PURE__ */ w("button", {
|
|
146
|
+
type: "button",
|
|
147
|
+
onClick: () => {
|
|
148
|
+
c(e.id), h(!1);
|
|
149
|
+
},
|
|
150
|
+
className: t("flex w-full items-center gap-2 px-3 py-2 text-sm text-left transition-colors hover:bg-accent", e.id === s ? "text-primary font-medium" : "text-foreground"),
|
|
151
|
+
children: [/* @__PURE__ */ C(d, {
|
|
152
|
+
size: 13,
|
|
153
|
+
className: t("flex-shrink-0", e.id === s ? "opacity-100 text-primary" : "opacity-0")
|
|
154
|
+
}), /* @__PURE__ */ C("span", {
|
|
155
|
+
className: "truncate",
|
|
156
|
+
children: e.name
|
|
157
|
+
})]
|
|
158
|
+
}, e.id))
|
|
159
|
+
})]
|
|
160
|
+
});
|
|
161
|
+
}, k = () => {
|
|
162
|
+
let { navigate: n, basePath: r } = e(), { pathname: i } = S(), { t: a } = o(), s = (e, t) => {
|
|
163
|
+
let n = a(e);
|
|
103
164
|
return n === e ? t : n;
|
|
104
165
|
};
|
|
105
|
-
if (!
|
|
166
|
+
if (!r || !n) return null;
|
|
106
167
|
let c = (e) => {
|
|
107
|
-
|
|
168
|
+
n(E(r, e));
|
|
108
169
|
};
|
|
109
|
-
return /* @__PURE__ */
|
|
170
|
+
return /* @__PURE__ */ C("nav", {
|
|
110
171
|
"aria-label": s("devportal.nav.sections", "Developer portal sections"),
|
|
111
172
|
className: "border-b border-border bg-card/40 px-4 py-2",
|
|
112
173
|
"data-testid": "devportal-section-nav",
|
|
113
|
-
children: /* @__PURE__ */
|
|
114
|
-
className: "flex
|
|
115
|
-
children:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
174
|
+
children: /* @__PURE__ */ w("div", {
|
|
175
|
+
className: "flex items-center gap-2",
|
|
176
|
+
children: [/* @__PURE__ */ C("ul", {
|
|
177
|
+
className: "flex flex-1 flex-wrap gap-1",
|
|
178
|
+
children: T.map((e) => {
|
|
179
|
+
let n = D(i, r, e), a = e.icon;
|
|
180
|
+
return /* @__PURE__ */ C("li", { children: /* @__PURE__ */ w("button", {
|
|
181
|
+
type: "button",
|
|
182
|
+
onClick: () => c(e.path),
|
|
183
|
+
"aria-current": n ? "page" : void 0,
|
|
184
|
+
className: t("inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium transition-colors", n ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-accent hover:text-foreground"),
|
|
185
|
+
children: [/* @__PURE__ */ C(a, { size: 14 }), /* @__PURE__ */ C("span", { children: s(e.i18nKey, e.fallback) })]
|
|
186
|
+
}) }, e.path);
|
|
187
|
+
})
|
|
188
|
+
}), /* @__PURE__ */ C(O, {})]
|
|
125
189
|
})
|
|
126
190
|
});
|
|
127
191
|
};
|
|
128
192
|
//#endregion
|
|
129
|
-
export {
|
|
193
|
+
export { k as SectionNav };
|
|
130
194
|
|
|
131
195
|
//# sourceMappingURL=SectionNav.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SectionNav.js","names":[],"sources":["../../../src/devportal/components/SectionNav.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useLocation } from \"react-router-dom\";\nimport {\n LayoutDashboard,\n AppWindow,\n Building2,\n KeyRound,\n Key,\n Webhook,\n Share2,\n BarChart3,\n DollarSign,\n BookOpen,\n Library,\n User,\n Settings,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { cn } from \"../utils/cn\";\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: \"oauth\",\n i18nKey: \"devportal.nav.oauth\",\n fallback: \"OAuth Clients\",\n icon: KeyRound,\n },\n {\n path: \"api-keys\",\n i18nKey: \"devportal.nav.apiKeys\",\n fallback: \"API Keys\",\n icon: Key,\n },\n {\n path: \"webhooks\",\n i18nKey: \"devportal.nav.webhooks\",\n fallback: \"Webhooks\",\n icon: Webhook,\n },\n {\n path: \"delegations\",\n i18nKey: \"devportal.nav.delegations\",\n fallback: \"Delegations\",\n icon: Share2,\n },\n {\n path: \"analytics\",\n i18nKey: \"devportal.nav.analytics\",\n fallback: \"Analytics\",\n icon: BarChart3,\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/**\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 bg-card/40 px-4 py-2\"\n data-testid=\"devportal-section-nav\"\n >\n <ul className=\"flex 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-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\n )}\n >\n <Icon size={14} />\n <span>{tr(section.i18nKey, section.fallback)}</span>\n </button>\n </li>\n );\n })}\n </ul>\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":";;;;;;;AA8BA,IAAM,IAAsC;CAC1C;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACN,eAAe,CAAC,YAAY;EAC7B;CACD;EACE,MAAM;EACN,SAAS;EACT,UAAU;EACV,MAAM;EACP;CACF;AAED,SAAS,EAAgB,GAA8B,GAAsB;CAC3E,IAAM,KAAQ,KAAY,IAAI,QAAQ,OAAO,GAAG;AAGhD,QAFK,IAEE,GAAG,IADK,EAAK,WAAW,IAAI,GAAG,IAAO,IAAI,QAD/B,KAAQ;;AAK5B,SAAS,EACP,GACA,GACA,GACS;CACT,IAAM,IAAa,EAAgB,GAAU,EAAQ,KAAK;AAM1D,QAJI,EAAQ,SAAS,KACZ,MAAa,KAAc,MAAa,GAAG,EAAW,KAE5C,CAAC,EAAQ,MAAM,GAAI,EAAQ,iBAAiB,EAAE,CAAE,CACjD,MAAM,MAAW;EACjC,IAAM,IAAM,EAAgB,GAAU,EAAO;AAC7C,SAAO,MAAa,KAAO,EAAS,WAAW,GAAG,EAAI,GAAG;GACzD;;AAYJ,IAAa,UAAuB;CAClC,IAAM,EAAE,aAAU,gBAAa,GAAc,EACvC,EAAE,gBAAa,GAAa,EAC5B,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;;AAGpC,KAAI,CAAC,KAAY,CAAC,EAAU,QAAO;CAEnC,IAAM,KAAe,MAAiB;AACpC,IAAS,EAAgB,GAAU,EAAK,CAAC;;AAG3C,QACE,kBAAC,OAAD;EACE,cAAY,EAAG,0BAA0B,4BAA4B;EACrE,WAAU;EACV,eAAY;YAEZ,kBAAC,MAAD;GAAI,WAAU;aACX,EAAS,KAAK,MAAY;IACzB,IAAM,IAAS,EAAgB,GAAU,GAAU,EAAQ,EACrD,IAAO,EAAQ;AACrB,WACE,kBAAC,MAAD,EAAA,UACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAY,EAAQ,KAAK;KACxC,gBAAc,IAAS,SAAS,KAAA;KAChC,WAAW,EACT,iGACA,IACI,uCACA,8DACL;eATH,CAWE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAClB,kBAAC,QAAD,EAAA,UAAO,EAAG,EAAQ,SAAS,EAAQ,SAAS,EAAQ,CAAA,CAC7C;QACN,EAfI,EAAQ,KAeZ;KAEP;GACC,CAAA;EACD,CAAA"}
|
|
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-dom\";\nimport {\n LayoutDashboard,\n AppWindow,\n Building2,\n KeyRound,\n Key,\n Webhook,\n Share2,\n BarChart3,\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: \"oauth\",\n i18nKey: \"devportal.nav.oauth\",\n fallback: \"OAuth Clients\",\n icon: KeyRound,\n },\n {\n path: \"api-keys\",\n i18nKey: \"devportal.nav.apiKeys\",\n fallback: \"API Keys\",\n icon: Key,\n },\n {\n path: \"webhooks\",\n i18nKey: \"devportal.nav.webhooks\",\n fallback: \"Webhooks\",\n icon: Webhook,\n },\n {\n path: \"delegations\",\n i18nKey: \"devportal.nav.delegations\",\n fallback: \"Delegations\",\n icon: Share2,\n },\n {\n path: \"analytics\",\n i18nKey: \"devportal.nav.analytics\",\n fallback: \"Analytics\",\n icon: BarChart3,\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 shown on the right side of the section nav. */\nconst 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-card text-foreground border-border 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 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-foreground\",\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 bg-card/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-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-accent hover:text-foreground\",\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":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import "../../generated/global-operations.js";
|
|
2
1
|
import { CombinedGraphQLErrors as e } from "../../node_modules/@apollo/client/errors/CombinedGraphQLErrors.js";
|
|
3
2
|
import { ServerError as t } from "../../node_modules/@apollo/client/errors/ServerError.js";
|
|
4
3
|
import { getMainDefinition as n } from "../../node_modules/@apollo/client/utilities/internal/getMainDefinition.js";
|
|
5
4
|
import { GraphQLWsLink as r } from "../../node_modules/@apollo/client/link/subscriptions/index.js";
|
|
6
5
|
import { createClient as i } from "../../node_modules/graphql-ws/dist/client.js";
|
|
6
|
+
import "../../generated/global-operations.js";
|
|
7
7
|
import { ApolloClient as a, InMemoryCache as o, createHttpLink as s, from as c, split as l } from "@apollo/client/core";
|
|
8
8
|
import { setContext as u } from "@apollo/client/link/context";
|
|
9
9
|
import { onError as d } from "@apollo/client/link/error";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
2
|
import { EmptyState as t } from "../components/EmptyState.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { useApproveApplicationMutation as n, useGetApplicationsQuery as r, useRejectApplicationMutation as i } from "../../generated/global-operations.js";
|
|
4
|
+
import { getErrorMessage as a } from "../hooks/useGraphQL.js";
|
|
5
|
+
import { PageLayout as o, PageSection as s } from "../components/PageLayout.js";
|
|
6
|
+
import { PermissionGuard as c } from "../components/PermissionGuard.js";
|
|
6
7
|
import "../components/index.js";
|
|
7
|
-
import { DEVPORTAL_PERMISSIONS as
|
|
8
|
-
import { getErrorMessage as l } from "../hooks/useGraphQL.js";
|
|
8
|
+
import { DEVPORTAL_PERMISSIONS as l } from "../constants/permissions.js";
|
|
9
9
|
import { useDevportalPermission as u } from "../hooks/usePermission.js";
|
|
10
10
|
import "../hooks/index.js";
|
|
11
11
|
import { useState as d } from "react";
|
|
@@ -14,10 +14,10 @@ import { AlertCircle as p, CheckCircle as m, ChevronDown as h, ChevronUp as g, C
|
|
|
14
14
|
import { jsx as S, jsxs as C } from "react/jsx-runtime";
|
|
15
15
|
//#region src/devportal/pages/AdminReviewPage.tsx
|
|
16
16
|
var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
17
|
-
let { navigate: a, basePath: o } = e(), { t: s } = f(),
|
|
17
|
+
let { navigate: a, basePath: o } = e(), { t: s } = f(), c = (e, t) => {
|
|
18
18
|
let n = s(e);
|
|
19
19
|
return n === e ? t : n;
|
|
20
|
-
}, [p, _] = d(!1), [b, w] = d(""), T = u(
|
|
20
|
+
}, [p, _] = d(!1), [b, w] = d(""), T = u(l.APP_APPROVE), E = u(l.APP_REJECT);
|
|
21
21
|
return /* @__PURE__ */ C("div", {
|
|
22
22
|
className: "rounded-lg border bg-card overflow-hidden",
|
|
23
23
|
children: [/* @__PURE__ */ C("div", {
|
|
@@ -56,19 +56,19 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
56
56
|
}),
|
|
57
57
|
/* @__PURE__ */ S("p", {
|
|
58
58
|
className: "text-sm text-muted-foreground truncate",
|
|
59
|
-
children: t.shortDescription ?? t.description ??
|
|
59
|
+
children: t.shortDescription ?? t.description ?? c("devportal.noDescription", "No description")
|
|
60
60
|
}),
|
|
61
61
|
/* @__PURE__ */ C("p", {
|
|
62
62
|
className: "text-xs text-muted-foreground mt-0.5",
|
|
63
63
|
children: [
|
|
64
|
-
|
|
64
|
+
c("devportal.submitted", "Submitted"),
|
|
65
65
|
":",
|
|
66
66
|
" ",
|
|
67
67
|
t.submittedAt ? new Date(t.submittedAt).toLocaleDateString(void 0, {
|
|
68
68
|
year: "numeric",
|
|
69
69
|
month: "short",
|
|
70
70
|
day: "numeric"
|
|
71
|
-
}) :
|
|
71
|
+
}) : c("devportal.unknown", "Unknown")
|
|
72
72
|
]
|
|
73
73
|
})
|
|
74
74
|
]
|
|
@@ -78,12 +78,12 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
78
78
|
children: [/* @__PURE__ */ C("button", {
|
|
79
79
|
onClick: () => a?.(`${o}/apps/${t.id}`),
|
|
80
80
|
className: "inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm hover:bg-muted",
|
|
81
|
-
title:
|
|
82
|
-
children: [/* @__PURE__ */ S(v, { size: 14 }),
|
|
81
|
+
title: c("devportal.viewAppDetails", "View app details"),
|
|
82
|
+
children: [/* @__PURE__ */ S(v, { size: 14 }), c("devportal.view", "View")]
|
|
83
83
|
}), /* @__PURE__ */ C("button", {
|
|
84
84
|
onClick: () => _((e) => !e),
|
|
85
85
|
className: "inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm hover:bg-muted",
|
|
86
|
-
children: [S(p ? g : h, { size: 14 }),
|
|
86
|
+
children: [S(p ? g : h, { size: 14 }), c("devportal.review", "Review")]
|
|
87
87
|
})]
|
|
88
88
|
})
|
|
89
89
|
]
|
|
@@ -94,7 +94,7 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
94
94
|
className: "rounded-md border bg-background p-3",
|
|
95
95
|
children: [/* @__PURE__ */ S("p", {
|
|
96
96
|
className: "text-xs font-medium text-muted-foreground mb-1",
|
|
97
|
-
children:
|
|
97
|
+
children: c("devportal.developerNotes", "Developer Notes")
|
|
98
98
|
}), /* @__PURE__ */ S("p", {
|
|
99
99
|
className: "text-sm",
|
|
100
100
|
children: t.reviewNotes
|
|
@@ -106,7 +106,7 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
106
106
|
/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ C("span", {
|
|
107
107
|
className: "text-muted-foreground",
|
|
108
108
|
children: [
|
|
109
|
-
|
|
109
|
+
c("devportal.category", "Category"),
|
|
110
110
|
":",
|
|
111
111
|
" "
|
|
112
112
|
]
|
|
@@ -114,7 +114,7 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
114
114
|
/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ C("span", {
|
|
115
115
|
className: "text-muted-foreground",
|
|
116
116
|
children: [
|
|
117
|
-
|
|
117
|
+
c("devportal.pricing", "Pricing"),
|
|
118
118
|
":",
|
|
119
119
|
" "
|
|
120
120
|
]
|
|
@@ -122,7 +122,7 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
122
122
|
/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ C("span", {
|
|
123
123
|
className: "text-muted-foreground",
|
|
124
124
|
children: [
|
|
125
|
-
|
|
125
|
+
c("devportal.website", "Website"),
|
|
126
126
|
":",
|
|
127
127
|
" "
|
|
128
128
|
]
|
|
@@ -131,12 +131,12 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
131
131
|
target: "_blank",
|
|
132
132
|
rel: "noopener noreferrer",
|
|
133
133
|
className: "text-primary underline hover:no-underline",
|
|
134
|
-
children:
|
|
134
|
+
children: c("devportal.link", "Link")
|
|
135
135
|
}) : /* @__PURE__ */ S("span", { children: "—" })] }),
|
|
136
136
|
/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ C("span", {
|
|
137
137
|
className: "text-muted-foreground",
|
|
138
138
|
children: [
|
|
139
|
-
|
|
139
|
+
c("devportal.privacyPolicy", "Privacy Policy"),
|
|
140
140
|
":",
|
|
141
141
|
" "
|
|
142
142
|
]
|
|
@@ -145,20 +145,20 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
145
145
|
target: "_blank",
|
|
146
146
|
rel: "noopener noreferrer",
|
|
147
147
|
className: "text-primary underline hover:no-underline",
|
|
148
|
-
children:
|
|
148
|
+
children: c("devportal.link", "Link")
|
|
149
149
|
}) : /* @__PURE__ */ S("span", {
|
|
150
150
|
className: "text-status-error-text",
|
|
151
|
-
children:
|
|
151
|
+
children: c("devportal.missing", "Missing")
|
|
152
152
|
})] })
|
|
153
153
|
]
|
|
154
154
|
}),
|
|
155
155
|
/* @__PURE__ */ C("div", { children: [/* @__PURE__ */ S("label", {
|
|
156
156
|
className: "text-sm font-medium",
|
|
157
|
-
children:
|
|
157
|
+
children: c("devportal.reviewNotes", "Review Notes")
|
|
158
158
|
}), /* @__PURE__ */ S("textarea", {
|
|
159
159
|
value: b,
|
|
160
160
|
onChange: (e) => w(e.target.value),
|
|
161
|
-
placeholder:
|
|
161
|
+
placeholder: c("devportal.reviewNotesPlaceholder", "Add feedback for the developer (required for rejection)..."),
|
|
162
162
|
rows: 3,
|
|
163
163
|
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring resize-none"
|
|
164
164
|
})] }),
|
|
@@ -168,7 +168,7 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
168
168
|
onClick: () => n(t.id, b),
|
|
169
169
|
disabled: i || !T.allowed,
|
|
170
170
|
"aria-disabled": i || !T.allowed,
|
|
171
|
-
title: T.allowed ? void 0 :
|
|
171
|
+
title: T.allowed ? void 0 : c("devportal.common.noPermission", "You do not have permission to perform this action"),
|
|
172
172
|
className: "inline-flex items-center gap-2 rounded-md bg-status-success-bg px-4 py-2 text-sm font-medium text-white hover:bg-status-success-bg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
173
173
|
children: [i ? /* @__PURE__ */ S(y, {
|
|
174
174
|
size: 14,
|
|
@@ -177,13 +177,13 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
177
177
|
}) : /* @__PURE__ */ S(m, {
|
|
178
178
|
size: 14,
|
|
179
179
|
"aria-hidden": "true"
|
|
180
|
-
}),
|
|
180
|
+
}), c("devportal.approve", "Approve")]
|
|
181
181
|
}), /* @__PURE__ */ C("button", {
|
|
182
182
|
onClick: () => r(t.id, b),
|
|
183
183
|
disabled: i || !b.trim() || !E.allowed,
|
|
184
184
|
"aria-disabled": i || !b.trim() || !E.allowed,
|
|
185
185
|
className: "inline-flex items-center gap-2 rounded-md bg-destructive px-4 py-2 text-sm font-medium text-destructive-foreground hover:bg-destructive/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
186
|
-
title: E.allowed ? b.trim() ? void 0 :
|
|
186
|
+
title: E.allowed ? b.trim() ? void 0 : c("devportal.notesRequiredForRejection", "Notes required for rejection") : c("devportal.common.noPermission", "You do not have permission to perform this action"),
|
|
187
187
|
children: [i ? /* @__PURE__ */ S(y, {
|
|
188
188
|
size: 14,
|
|
189
189
|
className: "animate-spin",
|
|
@@ -191,7 +191,7 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
191
191
|
}) : /* @__PURE__ */ S(x, {
|
|
192
192
|
size: 14,
|
|
193
193
|
"aria-hidden": "true"
|
|
194
|
-
}),
|
|
194
|
+
}), c("devportal.reject", "Reject")]
|
|
195
195
|
})]
|
|
196
196
|
})
|
|
197
197
|
]
|
|
@@ -204,13 +204,13 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
204
204
|
}, [h, g] = d(""), [v, x] = d(/* @__PURE__ */ new Set()), [T, E] = d(null), [D, O] = d(null), k = (e) => x((t) => new Set(t).add(e)), A = (e) => x((t) => {
|
|
205
205
|
let n = new Set(t);
|
|
206
206
|
return n.delete(e), n;
|
|
207
|
-
}), { data: j, loading: M, error: N, refetch: P } =
|
|
207
|
+
}), { data: j, loading: M, error: N, refetch: P } = r({
|
|
208
208
|
variables: {
|
|
209
209
|
status: "SUBMITTED",
|
|
210
210
|
limit: 100
|
|
211
211
|
},
|
|
212
212
|
fetchPolicy: "cache-and-network"
|
|
213
|
-
}), [F] =
|
|
213
|
+
}), [F] = n(), [I] = i(), L = j?.applications?.edges?.map((e) => e.node) ?? [], R = h ? L.filter((e) => e.name.toLowerCase().includes(h.toLowerCase()) || (e.shortDescription ?? "").toLowerCase().includes(h.toLowerCase())) : L, z = async (e, t) => {
|
|
214
214
|
k(e), E(null), O(null);
|
|
215
215
|
try {
|
|
216
216
|
let n = await F({ variables: {
|
|
@@ -241,14 +241,14 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
};
|
|
244
|
-
return M && !j ? /* @__PURE__ */ S(
|
|
244
|
+
return M && !j ? /* @__PURE__ */ S(o, {
|
|
245
245
|
title: u("devportal.appReviewQueue", "App Review Queue"),
|
|
246
246
|
description: u("devportal.appReviewQueueDescription", "Review submitted applications for approval"),
|
|
247
247
|
children: /* @__PURE__ */ S("div", {
|
|
248
248
|
className: "flex items-center justify-center py-12",
|
|
249
249
|
children: /* @__PURE__ */ S(y, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
250
250
|
})
|
|
251
|
-
}) : N ? /* @__PURE__ */ S(
|
|
251
|
+
}) : N ? /* @__PURE__ */ S(o, {
|
|
252
252
|
title: u("devportal.appReviewQueue", "App Review Queue"),
|
|
253
253
|
description: u("devportal.appReviewQueueDescription", "Review submitted applications for approval"),
|
|
254
254
|
children: /* @__PURE__ */ C("div", {
|
|
@@ -257,7 +257,7 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
257
257
|
/* @__PURE__ */ S(p, { className: "mx-auto h-8 w-8 text-destructive" }),
|
|
258
258
|
/* @__PURE__ */ S("p", {
|
|
259
259
|
className: "mt-2 text-sm text-muted-foreground",
|
|
260
|
-
children:
|
|
260
|
+
children: a(N)
|
|
261
261
|
}),
|
|
262
262
|
/* @__PURE__ */ S("button", {
|
|
263
263
|
onClick: () => P(),
|
|
@@ -266,9 +266,9 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
266
266
|
})
|
|
267
267
|
]
|
|
268
268
|
})
|
|
269
|
-
}) : /* @__PURE__ */ S(
|
|
270
|
-
permission:
|
|
271
|
-
children: /* @__PURE__ */ C(
|
|
269
|
+
}) : /* @__PURE__ */ S(c, {
|
|
270
|
+
permission: l.REVIEW_MANAGE,
|
|
271
|
+
children: /* @__PURE__ */ C(o, {
|
|
272
272
|
title: u("devportal.appReviewQueue", "App Review Queue"),
|
|
273
273
|
description: u("devportal.appReviewQueueFullDescription", "Review submitted applications for approval or rejection"),
|
|
274
274
|
actions: /* @__PURE__ */ S("button", {
|
|
@@ -321,7 +321,7 @@ var w = ({ app: t, onApprove: n, onReject: r, processing: i }) => {
|
|
|
321
321
|
className: "w-full rounded-md border bg-background pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
|
322
322
|
})]
|
|
323
323
|
}),
|
|
324
|
-
/* @__PURE__ */ S(
|
|
324
|
+
/* @__PURE__ */ S(s, { children: R.length === 0 ? /* @__PURE__ */ S(t, {
|
|
325
325
|
icon: /* @__PURE__ */ S(m, { size: 32 }),
|
|
326
326
|
title: u("devportal.noAppsPendingReview", "No apps pending review"),
|
|
327
327
|
description: h ? u("devportal.noAppsMatchSearch", "No apps match your search") : u("devportal.allApplicationsReviewed", "All submitted applications have been reviewed")
|