@burdenoff/microfe-devportal 2026.1101.1 → 2026.1107.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/DevportalRoutes.d.ts.map +1 -1
- package/dist/devportal/DevportalRoutes.js +13 -9
- package/dist/devportal/DevportalRoutes.js.map +1 -1
- package/dist/devportal/components/OnboardingChecklist.d.ts +16 -0
- package/dist/devportal/components/OnboardingChecklist.d.ts.map +1 -0
- package/dist/devportal/components/OnboardingChecklist.js +151 -0
- package/dist/devportal/components/OnboardingChecklist.js.map +1 -0
- package/dist/devportal/constants/developerLinks.d.ts +47 -0
- package/dist/devportal/constants/developerLinks.d.ts.map +1 -0
- package/dist/devportal/constants/developerLinks.js +27 -0
- package/dist/devportal/constants/developerLinks.js.map +1 -0
- package/dist/devportal/hooks/useGraphQL.d.ts.map +1 -1
- package/dist/devportal/hooks/useGraphQL.js +2 -1
- package/dist/devportal/hooks/useGraphQL.js.map +1 -1
- package/dist/devportal/hooks/usePackageUpload.d.ts +3 -0
- package/dist/devportal/hooks/usePackageUpload.d.ts.map +1 -1
- package/dist/devportal/hooks/usePackageUpload.js +41 -32
- package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
- package/dist/devportal/pages/DashboardPage.d.ts.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +107 -105
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/DocumentationPage.d.ts.map +1 -1
- package/dist/devportal/pages/DocumentationPage.js +114 -148
- package/dist/devportal/pages/DocumentationPage.js.map +1 -1
- package/dist/devportal/pages/PublisherProfilePage.d.ts.map +1 -1
- package/dist/devportal/pages/PublisherProfilePage.js +170 -160
- package/dist/devportal/pages/PublisherProfilePage.js.map +1 -1
- package/dist/devportal/pages/ResourcesPage.js +2 -2
- package/dist/devportal/pages/ResourcesPage.js.map +1 -1
- package/dist/devportal/pages/SetupPage.d.ts.map +1 -1
- package/dist/devportal/pages/SetupPage.js +363 -218
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/utils/developerOnboarding.d.ts +85 -0
- package/dist/devportal/utils/developerOnboarding.d.ts.map +1 -0
- package/dist/devportal/utils/developerOnboarding.js +104 -0
- package/dist/devportal/utils/developerOnboarding.js.map +1 -0
- package/dist/devportal/utils/uploadErrors.d.ts +46 -0
- package/dist/devportal/utils/uploadErrors.d.ts.map +1 -0
- package/dist/devportal/utils/uploadErrors.js +62 -0
- package/dist/devportal/utils/uploadErrors.js.map +1 -0
- package/dist/generated/global-operations.d.ts +53 -0
- package/dist/generated/global-operations.d.ts.map +1 -1
- package/dist/generated/global-operations.js +170 -134
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/translations/en.d.ts +50 -0
- package/dist/translations/en.d.ts.map +1 -1
- package/dist/translations/en.js +51 -1
- package/dist/translations/en.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,135 +6,137 @@ import { PageLayout as i } from "../components/PageLayout.js";
|
|
|
6
6
|
import { AppCard as a } from "../components/AppCard.js";
|
|
7
7
|
import { PermissionGuard as o } from "../components/PermissionGuard.js";
|
|
8
8
|
import "../components/index.js";
|
|
9
|
+
import { OnboardingChecklist as s } from "../components/OnboardingChecklist.js";
|
|
9
10
|
import "../hooks/index.js";
|
|
10
|
-
import { useEffect as
|
|
11
|
-
import { AppWindow as
|
|
12
|
-
import { Fragment as
|
|
13
|
-
import { GlassCard as
|
|
11
|
+
import { useEffect as c } from "react";
|
|
12
|
+
import { AppWindow as l, ArrowRight as u, Building2 as d, DollarSign as f, Download as p, Loader2 as m, Plus as h } from "lucide-react";
|
|
13
|
+
import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
14
|
+
import { GlassCard as y, GradientText as b } from "@burdenoff/fe-libs/ui";
|
|
14
15
|
//#region src/devportal/pages/DashboardPage.tsx
|
|
15
|
-
var
|
|
16
|
-
let { navigate:
|
|
16
|
+
var x = () => {
|
|
17
|
+
let { navigate: f, basePath: p, activeOrgId: y, setActiveOrgId: x } = e(), T = t(), { data: E, loading: D } = r({
|
|
17
18
|
variables: {},
|
|
18
19
|
fetchPolicy: "network-only"
|
|
19
|
-
}),
|
|
20
|
-
if (
|
|
20
|
+
}), O = (E?.myDeveloperOrganizations ?? []).filter((e) => e.kind !== "PHYSICAL"), k = (() => {
|
|
21
|
+
if (O.length) return y && O.some((e) => e.id === y) ? y : O[0]?.id;
|
|
21
22
|
})();
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
c(() => {
|
|
24
|
+
k && k !== y && x(k);
|
|
24
25
|
}, [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
k,
|
|
27
|
+
y,
|
|
28
|
+
x
|
|
28
29
|
]);
|
|
29
|
-
let { data:
|
|
30
|
-
variables: { organizationId:
|
|
31
|
-
skip: !
|
|
30
|
+
let { data: A, loading: j } = r({
|
|
31
|
+
variables: { organizationId: k },
|
|
32
|
+
skip: !k,
|
|
32
33
|
fetchPolicy: "cache-and-network"
|
|
33
|
-
}),
|
|
34
|
-
|
|
35
|
-
if (!(
|
|
34
|
+
}), M = D || j, N = A ?? E;
|
|
35
|
+
c(() => {
|
|
36
|
+
if (!(D || !E) && !(O.length > 0)) {
|
|
36
37
|
try {
|
|
37
38
|
if (localStorage.getItem("devportal_setup_dismissed")) return;
|
|
38
39
|
} catch {
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
+
f?.(`${p}/setup`);
|
|
42
43
|
}
|
|
43
44
|
}, [
|
|
45
|
+
D,
|
|
44
46
|
E,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
f
|
|
47
|
+
O.length,
|
|
48
|
+
f,
|
|
49
|
+
p
|
|
49
50
|
]);
|
|
50
|
-
let
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
return /* @__PURE__ */
|
|
51
|
+
let P = A?.applications?.edges?.map((e) => e.node) ?? [], F = N?.applications?.totalCount ?? 0, I = O.length, L = (e) => {
|
|
52
|
+
f?.(`${p}${e}`);
|
|
53
|
+
}, R = M, z = !D && O.length > 0;
|
|
54
|
+
return /* @__PURE__ */ _(o, {
|
|
54
55
|
authOnly: !0,
|
|
55
|
-
children: /* @__PURE__ */
|
|
56
|
-
title: /* @__PURE__ */
|
|
57
|
-
|
|
56
|
+
children: /* @__PURE__ */ _(i, {
|
|
57
|
+
title: /* @__PURE__ */ v(g, { children: [
|
|
58
|
+
T("devportal.developer", "Developer"),
|
|
58
59
|
" ",
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ _(b, { children: T("devportal.portal", "Portal") })
|
|
60
61
|
] }),
|
|
61
|
-
actions:
|
|
62
|
+
actions: z ? /* @__PURE__ */ v("button", {
|
|
62
63
|
"data-tour": "devportal-dashboard-create-app-button",
|
|
63
|
-
onClick: () =>
|
|
64
|
+
onClick: () => L("/apps/new"),
|
|
64
65
|
className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
|
|
65
|
-
children: [/* @__PURE__ */
|
|
66
|
-
}) :
|
|
66
|
+
children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createApp", "Create App")]
|
|
67
|
+
}) : R ? null : /* @__PURE__ */ v("button", {
|
|
67
68
|
"data-tour": "devportal-dashboard-create-org-button",
|
|
68
|
-
onClick: () =>
|
|
69
|
+
onClick: () => L("/organizations/new"),
|
|
69
70
|
className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
|
|
70
|
-
children: [/* @__PURE__ */
|
|
71
|
+
children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createOrganization", "Create Organization")]
|
|
71
72
|
}),
|
|
72
|
-
children: /* @__PURE__ */
|
|
73
|
+
children: /* @__PURE__ */ v("div", {
|
|
73
74
|
className: "flex flex-col gap-8",
|
|
74
75
|
children: [
|
|
75
|
-
/* @__PURE__ */
|
|
76
|
+
/* @__PURE__ */ _(s, { onNavigate: L }),
|
|
77
|
+
/* @__PURE__ */ _("div", {
|
|
76
78
|
className: "flex items-center gap-6 border-b border-border-seam pb-6",
|
|
77
|
-
children:
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
icon: /* @__PURE__ */
|
|
80
|
-
value: P,
|
|
81
|
-
label: w("devportal.apps", "Apps")
|
|
82
|
-
}),
|
|
83
|
-
/* @__PURE__ */ g("div", { className: "h-4 w-px bg-border-seam" }),
|
|
84
|
-
/* @__PURE__ */ g(x, {
|
|
85
|
-
icon: /* @__PURE__ */ g(u, { size: 14 }),
|
|
79
|
+
children: R ? /* @__PURE__ */ _(m, { className: "h-4 w-4 animate-spin text-text-secondary" }) : /* @__PURE__ */ v(g, { children: [
|
|
80
|
+
/* @__PURE__ */ _(S, {
|
|
81
|
+
icon: /* @__PURE__ */ _(l, { size: 14 }),
|
|
86
82
|
value: F,
|
|
87
|
-
label:
|
|
83
|
+
label: T("devportal.apps", "Apps")
|
|
84
|
+
}),
|
|
85
|
+
/* @__PURE__ */ _("div", { className: "h-4 w-px bg-border-seam" }),
|
|
86
|
+
/* @__PURE__ */ _(S, {
|
|
87
|
+
icon: /* @__PURE__ */ _(d, { size: 14 }),
|
|
88
|
+
value: I,
|
|
89
|
+
label: T("devportal.organizations", "Organizations")
|
|
88
90
|
})
|
|
89
91
|
] })
|
|
90
92
|
}),
|
|
91
|
-
/* @__PURE__ */
|
|
92
|
-
title:
|
|
93
|
-
action:
|
|
94
|
-
onClick: () =>
|
|
93
|
+
/* @__PURE__ */ _(C, {
|
|
94
|
+
title: T("devportal.recentApplications", "Recent Applications"),
|
|
95
|
+
action: P.length > 0 ? /* @__PURE__ */ v("button", {
|
|
96
|
+
onClick: () => L("/apps"),
|
|
95
97
|
className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
96
|
-
children: [
|
|
98
|
+
children: [T("devportal.viewAll", "View all"), /* @__PURE__ */ _(u, { size: 14 })]
|
|
97
99
|
}) : void 0,
|
|
98
|
-
children:
|
|
100
|
+
children: R ? /* @__PURE__ */ _("div", {
|
|
99
101
|
className: "flex items-center justify-center py-12",
|
|
100
|
-
children: /* @__PURE__ */
|
|
101
|
-
}) :
|
|
102
|
+
children: /* @__PURE__ */ _(m, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
103
|
+
}) : P.length > 0 ? /* @__PURE__ */ _("div", {
|
|
102
104
|
className: "grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
103
|
-
children:
|
|
105
|
+
children: P.slice(0, 3).map((e) => /* @__PURE__ */ _(a, {
|
|
104
106
|
app: e,
|
|
105
|
-
onClick: () =>
|
|
107
|
+
onClick: () => L(`/apps/${e.id}`),
|
|
106
108
|
showActions: !1
|
|
107
109
|
}, e.id))
|
|
108
|
-
}) : /* @__PURE__ */
|
|
110
|
+
}) : /* @__PURE__ */ _(n, {
|
|
109
111
|
illustration: "empty-generic",
|
|
110
|
-
title:
|
|
111
|
-
description:
|
|
112
|
-
action:
|
|
113
|
-
onClick: () =>
|
|
112
|
+
title: T("devportal.noApplicationsYet", "No applications yet"),
|
|
113
|
+
description: z ? T("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : T("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
|
|
114
|
+
action: z ? /* @__PURE__ */ v("button", {
|
|
115
|
+
onClick: () => L("/apps/new"),
|
|
114
116
|
className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
|
|
115
|
-
children: [/* @__PURE__ */
|
|
116
|
-
}) : /* @__PURE__ */
|
|
117
|
-
onClick: () =>
|
|
117
|
+
children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createApp", "Create App")]
|
|
118
|
+
}) : /* @__PURE__ */ v("button", {
|
|
119
|
+
onClick: () => L("/organizations/new"),
|
|
118
120
|
className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
|
|
119
|
-
children: [/* @__PURE__ */
|
|
121
|
+
children: [/* @__PURE__ */ _(h, { size: 16 }), T("devportal.createOrganization", "Create Organization")]
|
|
120
122
|
})
|
|
121
123
|
})
|
|
122
124
|
}),
|
|
123
|
-
(
|
|
124
|
-
title:
|
|
125
|
-
action:
|
|
126
|
-
onClick: () =>
|
|
125
|
+
(R || O.length > 0) && /* @__PURE__ */ _(C, {
|
|
126
|
+
title: T("devportal.yourOrganizations", "Your Organizations"),
|
|
127
|
+
action: O.length > 0 ? /* @__PURE__ */ v("button", {
|
|
128
|
+
onClick: () => L("/organizations"),
|
|
127
129
|
className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
128
|
-
children: [
|
|
130
|
+
children: [T("devportal.viewAll", "View all"), /* @__PURE__ */ _(u, { size: 14 })]
|
|
129
131
|
}) : void 0,
|
|
130
|
-
children:
|
|
132
|
+
children: R ? /* @__PURE__ */ _("div", {
|
|
131
133
|
className: "flex items-center justify-center py-12",
|
|
132
|
-
children: /* @__PURE__ */
|
|
133
|
-
}) : /* @__PURE__ */
|
|
134
|
+
children: /* @__PURE__ */ _(m, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
135
|
+
}) : /* @__PURE__ */ _("div", {
|
|
134
136
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
135
|
-
children:
|
|
137
|
+
children: O.slice(0, 3).map((e) => /* @__PURE__ */ _(w, {
|
|
136
138
|
org: e,
|
|
137
|
-
onClick: () =>
|
|
139
|
+
onClick: () => L(`/organizations/${e.id}`)
|
|
138
140
|
}, e.id))
|
|
139
141
|
})
|
|
140
142
|
})
|
|
@@ -142,34 +144,34 @@ var b = () => {
|
|
|
142
144
|
})
|
|
143
145
|
})
|
|
144
146
|
});
|
|
145
|
-
},
|
|
147
|
+
}, S = ({ icon: e, value: t, label: n }) => /* @__PURE__ */ v("div", {
|
|
146
148
|
className: "flex items-center gap-1.5",
|
|
147
149
|
children: [
|
|
148
|
-
/* @__PURE__ */
|
|
150
|
+
/* @__PURE__ */ _("span", {
|
|
149
151
|
className: "text-text-muted",
|
|
150
152
|
children: e
|
|
151
153
|
}),
|
|
152
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ _("span", {
|
|
153
155
|
className: "text-sm font-semibold tabular-nums",
|
|
154
156
|
children: t
|
|
155
157
|
}),
|
|
156
|
-
/* @__PURE__ */
|
|
158
|
+
/* @__PURE__ */ _("span", {
|
|
157
159
|
className: "text-sm text-text-secondary",
|
|
158
160
|
children: n
|
|
159
161
|
})
|
|
160
162
|
]
|
|
161
|
-
}),
|
|
163
|
+
}), C = ({ title: e, action: t, children: n }) => /* @__PURE__ */ v("section", {
|
|
162
164
|
className: "flex flex-col gap-4",
|
|
163
|
-
children: [/* @__PURE__ */
|
|
165
|
+
children: [/* @__PURE__ */ v("div", {
|
|
164
166
|
className: "flex items-center justify-between",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
167
|
+
children: [/* @__PURE__ */ _("h2", {
|
|
166
168
|
className: "text-sm font-semibold uppercase tracking-wider text-text-secondary",
|
|
167
169
|
children: e
|
|
168
170
|
}), t]
|
|
169
171
|
}), n]
|
|
170
|
-
}),
|
|
172
|
+
}), w = ({ org: e, onClick: n }) => {
|
|
171
173
|
let r = t(), i = e.name.split(" ").map((e) => e[0]).join("").slice(0, 2).toUpperCase();
|
|
172
|
-
return /* @__PURE__ */
|
|
174
|
+
return /* @__PURE__ */ v(y, {
|
|
173
175
|
treatment: "insight",
|
|
174
176
|
role: "button",
|
|
175
177
|
tabIndex: 0,
|
|
@@ -178,23 +180,23 @@ var b = () => {
|
|
|
178
180
|
onKeyDown: (e) => {
|
|
179
181
|
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), n());
|
|
180
182
|
},
|
|
181
|
-
children: [/* @__PURE__ */
|
|
183
|
+
children: [/* @__PURE__ */ v("div", {
|
|
182
184
|
className: "flex items-center gap-3 mb-4",
|
|
183
185
|
children: [
|
|
184
|
-
e.logoUrl ? /* @__PURE__ */
|
|
186
|
+
e.logoUrl ? /* @__PURE__ */ _("img", {
|
|
185
187
|
src: e.logoUrl,
|
|
186
188
|
alt: e.name,
|
|
187
189
|
className: "h-10 w-10 rounded-xl object-cover"
|
|
188
|
-
}) : /* @__PURE__ */
|
|
190
|
+
}) : /* @__PURE__ */ _("div", {
|
|
189
191
|
className: "flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary text-sm font-bold",
|
|
190
192
|
children: i
|
|
191
193
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
194
|
+
/* @__PURE__ */ v("div", {
|
|
193
195
|
className: "min-w-0 flex-1",
|
|
194
|
-
children: [/* @__PURE__ */
|
|
196
|
+
children: [/* @__PURE__ */ _("p", {
|
|
195
197
|
className: "font-semibold truncate text-text-primary",
|
|
196
198
|
children: e.name
|
|
197
|
-
}), /* @__PURE__ */
|
|
199
|
+
}), /* @__PURE__ */ v("p", {
|
|
198
200
|
className: "text-xs text-text-secondary mt-0.5",
|
|
199
201
|
children: [
|
|
200
202
|
e.publishedApps ?? 0,
|
|
@@ -203,34 +205,34 @@ var b = () => {
|
|
|
203
205
|
]
|
|
204
206
|
})]
|
|
205
207
|
}),
|
|
206
|
-
/* @__PURE__ */
|
|
208
|
+
/* @__PURE__ */ _(u, {
|
|
207
209
|
size: 15,
|
|
208
210
|
className: "text-text-muted shrink-0"
|
|
209
211
|
})
|
|
210
212
|
]
|
|
211
|
-
}), /* @__PURE__ */
|
|
213
|
+
}), /* @__PURE__ */ v("div", {
|
|
212
214
|
className: "grid grid-cols-2 gap-3",
|
|
213
|
-
children: [/* @__PURE__ */
|
|
215
|
+
children: [/* @__PURE__ */ v("div", {
|
|
214
216
|
className: "flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2",
|
|
215
|
-
children: [/* @__PURE__ */
|
|
217
|
+
children: [/* @__PURE__ */ _(p, {
|
|
216
218
|
size: 12,
|
|
217
219
|
className: "text-text-muted shrink-0"
|
|
218
|
-
}), /* @__PURE__ */
|
|
220
|
+
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
219
221
|
className: "text-[11px] text-text-secondary leading-none mb-0.5",
|
|
220
222
|
children: r("devportal.installs", "Installs")
|
|
221
|
-
}), /* @__PURE__ */
|
|
223
|
+
}), /* @__PURE__ */ _("p", {
|
|
222
224
|
className: "text-sm font-semibold tabular-nums",
|
|
223
225
|
children: (e.totalInstalls || 0).toLocaleString()
|
|
224
226
|
})] })]
|
|
225
|
-
}), /* @__PURE__ */
|
|
227
|
+
}), /* @__PURE__ */ v("div", {
|
|
226
228
|
className: "flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2",
|
|
227
|
-
children: [/* @__PURE__ */
|
|
229
|
+
children: [/* @__PURE__ */ _(f, {
|
|
228
230
|
size: 12,
|
|
229
231
|
className: "text-text-muted shrink-0"
|
|
230
|
-
}), /* @__PURE__ */
|
|
232
|
+
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
231
233
|
className: "text-[11px] text-text-secondary leading-none mb-0.5",
|
|
232
234
|
children: r("devportal.revenue", "Revenue")
|
|
233
|
-
}), /* @__PURE__ */
|
|
235
|
+
}), /* @__PURE__ */ v("p", {
|
|
234
236
|
className: "text-sm font-semibold tabular-nums",
|
|
235
237
|
children: ["$", (e.totalRevenue || 0).toLocaleString()]
|
|
236
238
|
})] })]
|
|
@@ -239,6 +241,6 @@ var b = () => {
|
|
|
239
241
|
});
|
|
240
242
|
};
|
|
241
243
|
//#endregion
|
|
242
|
-
export {
|
|
244
|
+
export { x as DashboardPage };
|
|
243
245
|
|
|
244
246
|
//# sourceMappingURL=DashboardPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/devportal/pages/DashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect } from \"react\";\nimport {\n AppWindow,\n Building2,\n Plus,\n ArrowRight,\n Loader2,\n Download,\n DollarSign,\n} from \"lucide-react\";\nimport { GlassCard, GradientText } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { AppCard } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { useTr } from \"../utils/i18n\";\nimport type { Application, DeveloperOrganization } from \"../types\";\nimport {\n useGetDevportalDashboardBootstrapQuery,\n type OrganizationFieldsFragment,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\nexport const DashboardPage: FC = () => {\n const { navigate, basePath, activeOrgId, setActiveOrgId } = useDevportal();\n const tr = useTr();\n\n // network-only prevents a stale empty-orgs cache entry from\n // firing the /setup redirect before the real response arrives.\n const { data: orgsData, loading: orgsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: {},\n fetchPolicy: \"network-only\",\n });\n\n // This dashboard is only ever mounted at the plain (non-seller) devportal\n // route — exclude PHYSICAL orgs so a seller-portal org never shows up\n // here. See DeveloperOrgKind (BOFF-7285).\n const organizations: OrganizationFieldsFragment[] = (\n orgsData?.myDeveloperOrganizations ?? []\n ).filter((org: OrganizationFieldsFragment) => org.kind !== \"PHYSICAL\");\n\n const resolvedOrgId: string | undefined = (() => {\n if (!organizations.length) return undefined;\n if (activeOrgId && organizations.some((o) => o.id === activeOrgId))\n return activeOrgId;\n return organizations[0]?.id;\n })();\n\n useEffect(() => {\n if (resolvedOrgId && resolvedOrgId !== activeOrgId) {\n setActiveOrgId(resolvedOrgId);\n }\n }, [resolvedOrgId, activeOrgId, setActiveOrgId]);\n\n const { data: appsData, loading: appsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: { organizationId: resolvedOrgId },\n skip: !resolvedOrgId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const loading = orgsLoading || appsLoading;\n const data = appsData ?? orgsData;\n\n useEffect(() => {\n if (orgsLoading || !orgsData) return;\n if (organizations.length > 0) return;\n try {\n if (localStorage.getItem(\"devportal_setup_dismissed\")) return;\n } catch {\n return;\n }\n navigate?.(`${basePath}/setup`);\n }, [orgsLoading, orgsData, organizations.length, navigate, basePath]);\n\n const applications: ApplicationFieldsFragment[] =\n appsData?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n const totalApps = data?.applications?.totalCount ?? 0;\n const totalOrgs = organizations.length;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const isLoading = loading;\n // hasOrgs depends only on the orgs query — not on appsLoading — so the\n // header action doesn't flash \"Create Organization\" while apps are loading.\n const hasOrgs = !orgsLoading && organizations.length > 0;\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={\n <>\n {tr(\"devportal.developer\", \"Developer\")}{\" \"}\n <GradientText>{tr(\"devportal.portal\", \"Portal\")}</GradientText>\n </>\n }\n actions={\n hasOrgs ? (\n <button\n data-tour=\"devportal-dashboard-create-app-button\"\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n ) : !isLoading ? (\n <button\n data-tour=\"devportal-dashboard-create-org-button\"\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createOrganization\", \"Create Organization\")}\n </button>\n ) : null\n }\n >\n <div className=\"flex flex-col gap-8\">\n {/* Compact inline stats */}\n <div className=\"flex items-center gap-6 border-b border-border-seam pb-6\">\n {isLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n ) : (\n <>\n <StatPill\n icon={<AppWindow size={14} />}\n value={totalApps}\n label={tr(\"devportal.apps\", \"Apps\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Building2 size={14} />}\n value={totalOrgs}\n label={tr(\"devportal.organizations\", \"Organizations\")}\n />\n </>\n )}\n </div>\n\n {/* Recent Apps */}\n <DashSection\n title={tr(\"devportal.recentApplications\", \"Recent Applications\")}\n action={\n applications.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/apps\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : applications.length > 0 ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {applications\n .slice(0, 3)\n .map((app: ApplicationFieldsFragment) => (\n <AppCard\n key={app.id}\n app={app as Application}\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n showActions={false}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title={tr(\"devportal.noApplicationsYet\", \"No applications yet\")}\n description={\n hasOrgs\n ? tr(\n \"devportal.createFirstApplicationToGetStarted\",\n \"Create your first application to get started\",\n )\n : tr(\n \"devportal.createOrgFirstToPublishApps\",\n \"Create an organization first to start publishing apps\",\n )\n }\n action={\n hasOrgs ? (\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n ) : (\n <button\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n </button>\n )\n }\n />\n )}\n </DashSection>\n\n {/* Organizations */}\n {(isLoading || organizations.length > 0) && (\n <DashSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n action={\n organizations.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {organizations\n .slice(0, 3)\n .map((org: OrganizationFieldsFragment) => (\n <OrgCard\n key={org.id}\n org={org as DeveloperOrganization}\n onClick={() =>\n handleNavigate(`/organizations/${org.id}`)\n }\n />\n ))}\n </div>\n )}\n </DashSection>\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n};\n\nconst StatPill: FC<{ icon: ReactNode; value: number; label: string }> = ({\n icon,\n value,\n label,\n}) => (\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-muted\">{icon}</span>\n <span className=\"text-sm font-semibold tabular-nums\">{value}</span>\n <span className=\"text-sm text-text-secondary\">{label}</span>\n </div>\n);\n\nconst DashSection: FC<{\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}> = ({ title, action, children }) => (\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold uppercase tracking-wider text-text-secondary\">\n {title}\n </h2>\n {action}\n </div>\n {children}\n </section>\n);\n\nconst OrgCard: FC<{ org: DeveloperOrganization; onClick: () => void }> = ({\n org,\n onClick,\n}) => {\n const tr = useTr();\n\n const initials = org.name\n .split(\" \")\n .map((w: string) => w[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n\n return (\n <GlassCard\n treatment=\"insight\"\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n }}\n >\n <div className=\"flex items-center gap-3 mb-4\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-xl object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary text-sm font-bold\">\n {initials}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold truncate text-text-primary\">{org.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {org.publishedApps ?? 0}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n <ArrowRight size={15} className=\"text-text-muted shrink-0\" />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <Download size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.installs\", \"Installs\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n {(org.totalInstalls || 0).toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <DollarSign size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.revenue\", \"Revenue\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n ${(org.totalRevenue || 0).toLocaleString()}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AAyBA,IAAa,UAA0B;CACrC,IAAM,EAAE,aAAU,aAAU,gBAAa,sBAAmB,GAAc,EACpE,IAAK,GAAO,EAIZ,EAAE,MAAM,GAAU,SAAS,MAC/B,EAAuC;EACrC,WAAW,EAAE;EACb,aAAa;EACd,CAAC,EAKE,KACJ,GAAU,4BAA4B,EAAE,EACxC,QAAQ,MAAoC,EAAI,SAAS,WAAW,EAEhE,WAA2C;AAC1C,QAAc,OAGnB,QAFI,KAAe,EAAc,MAAM,MAAM,EAAE,OAAO,EAAY,GACzD,IACF,EAAc,IAAI;KACvB;AAEJ,SAAgB;AACd,EAAI,KAAiB,MAAkB,KACrC,EAAe,EAAc;IAE9B;EAAC;EAAe;EAAa;EAAe,CAAC;CAEhD,IAAM,EAAE,MAAM,GAAU,SAAS,MAC/B,EAAuC;EACrC,WAAW,EAAE,gBAAgB,GAAe;EAC5C,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEE,IAAU,KAAe,GACzB,IAAO,KAAY;AAEzB,SAAgB;AACV,aAAe,CAAC,MAChB,IAAc,SAAS,IAC3B;OAAI;AACF,QAAI,aAAa,QAAQ,4BAA4B,CAAE;WACjD;AACN;;AAEF,OAAW,GAAG,EAAS,QAAQ;;IAC9B;EAAC;EAAa;EAAU,EAAc;EAAQ;EAAU;EAAS,CAAC;CAErE,IAAM,IACJ,GAAU,cAAc,OAAO,KAC5B,MAA2C,EAAE,KAC/C,IAAI,EAAE,EAEH,IAAY,GAAM,cAAc,cAAc,GAC9C,IAAY,EAAc,QAE1B,KAAkB,MAAiB;AACvC,MAAW,GAAG,IAAW,IAAO;IAG5B,IAAY,GAGZ,IAAU,CAAC,KAAe,EAAc,SAAS;AAEvD,QACE,kBAAC,GAAD;EAAiB,UAAA;YACf,kBAAC,GAAD;GACE,OACE,kBAAA,GAAA,EAAA,UAAA;IACG,EAAG,uBAAuB,YAAY;IAAE;IACzC,kBAAC,GAAD,EAAA,UAAe,EAAG,oBAAoB,SAAS,EAAgB,CAAA;IAC9D,EAAA,CAAA;GAEL,SACE,IACE,kBAAC,UAAD;IACE,aAAU;IACV,eAAe,EAAe,YAAY;IAC1C,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,aAAa,CACjC;QACN,IASD,OARF,kBAAC,UAAD;IACE,aAAU;IACV,eAAe,EAAe,qBAAqB;IACnD,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,gCAAgC,sBAAsB,CACnD;;aAIb,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,OAAD;MAAK,WAAU;gBACZ,IACC,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA,GAEhE,kBAAA,GAAA,EAAA,UAAA;OACE,kBAAC,GAAD;QACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;QAC7B,OAAO;QACP,OAAO,EAAG,kBAAkB,OAAO;QACnC,CAAA;OACF,kBAAC,OAAD,EAAK,WAAU,2BAA4B,CAAA;OAC3C,kBAAC,GAAD;QACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;QAC7B,OAAO;QACP,OAAO,EAAG,2BAA2B,gBAAgB;QACrD,CAAA;OACD,EAAA,CAAA;MAED,CAAA;KAGN,kBAAC,GAAD;MACE,OAAO,EAAG,gCAAgC,sBAAsB;MAChE,QACE,EAAa,SAAS,IACpB,kBAAC,UAAD;OACE,eAAe,EAAe,QAAQ;OACtC,WAAU;iBAFZ,CAIG,EAAG,qBAAqB,WAAW,EACpC,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CACjB;WACP,KAAA;gBAGL,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;OAC5D,CAAA,GACJ,EAAa,SAAS,IACxB,kBAAC,OAAD;OAAK,WAAU;iBACZ,EACE,MAAM,GAAG,EAAE,CACX,KAAK,MACJ,kBAAC,GAAD;QAEO;QACL,eAAe,EAAe,SAAS,EAAI,KAAK;QAChD,aAAa;QACb,EAJK,EAAI,GAIT,CACF;OACA,CAAA,GAEN,kBAAC,GAAD;OACE,cAAa;OACb,OAAO,EAAG,+BAA+B,sBAAsB;OAC/D,aACE,IACI,EACE,gDACA,+CACD,GACD,EACE,yCACA,wDACD;OAEP,QACE,IACE,kBAAC,UAAD;QACE,eAAe,EAAe,YAAY;QAC1C,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,aAAa,CACjC;YAET,kBAAC,UAAD;QACE,eAAe,EAAe,qBAAqB;QACnD,WAAU;kBAFZ,CAIE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EACC,gCACA,sBACD,CACM;;OAGb,CAAA;MAEQ,CAAA;MAGZ,KAAa,EAAc,SAAS,MACpC,kBAAC,GAAD;MACE,OAAO,EAAG,+BAA+B,qBAAqB;MAC9D,QACE,EAAc,SAAS,IACrB,kBAAC,UAAD;OACE,eAAe,EAAe,iBAAiB;OAC/C,WAAU;iBAFZ,CAIG,EAAG,qBAAqB,WAAW,EACpC,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA,CACjB;WACP,KAAA;gBAGL,IACC,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAS,WAAU,4CAA6C,CAAA;OAC5D,CAAA,GAEN,kBAAC,OAAD;OAAK,WAAU;iBACZ,EACE,MAAM,GAAG,EAAE,CACX,KAAK,MACJ,kBAAC,GAAD;QAEO;QACL,eACE,EAAe,kBAAkB,EAAI,KAAK;QAE5C,EALK,EAAI,GAKT,CACF;OACA,CAAA;MAEI,CAAA;KAEZ;;GACK,CAAA;EACG,CAAA;GAIhB,KAAmE,EACvE,SACA,UACA,eAEA,kBAAC,OAAD;CAAK,WAAU;WAAf;EACE,kBAAC,QAAD;GAAM,WAAU;aAAmB;GAAY,CAAA;EAC/C,kBAAC,QAAD;GAAM,WAAU;aAAsC;GAAa,CAAA;EACnE,kBAAC,QAAD;GAAM,WAAU;aAA+B;GAAa,CAAA;EACxD;IAGF,KAIA,EAAE,UAAO,WAAQ,kBACrB,kBAAC,WAAD;CAAS,WAAU;WAAnB,CACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aACX;GACE,CAAA,EACJ,EACG;KACL,EACO;IAGN,KAAoE,EACxE,QACA,iBACI;CACJ,IAAM,IAAK,GAAO,EAEZ,IAAW,EAAI,KAClB,MAAM,IAAI,CACV,KAAK,MAAc,EAAE,GAAG,CACxB,KAAK,GAAG,CACR,MAAM,GAAG,EAAE,CACX,aAAa;AAEhB,QACE,kBAAC,GAAD;EACE,WAAU;EACV,MAAK;EACL,UAAU;EACV,WAAU;EACD;EACT,YAAY,MAAM;AAChB,IAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EAClB,GAAS;;YATf,CAaE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,EAAI,UACH,kBAAC,OAAD;KACE,KAAK,EAAI;KACT,KAAK,EAAI;KACT,WAAU;KACV,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA;IAER,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAA4C,EAAI;MAAS,CAAA,EACtE,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG,EAAI,iBAAiB;OAAG;OACxB,EAAG,2BAA2B,iBAAiB;OAC9C;QACA;;IACN,kBAAC,GAAD;KAAY,MAAM;KAAI,WAAU;KAA6B,CAAA;IACzD;MAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAU,MAAM;KAAI,WAAU;KAA6B,CAAA,EAC3D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,sBAAsB,WAAW;KACnC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;gBACT,EAAI,iBAAiB,GAAG,gBAAgB;KACxC,CAAA,CACA,EAAA,CAAA,CACF;OACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAY,MAAM;KAAI,WAAU;KAA6B,CAAA,EAC7D,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,qBAAqB,UAAU;KACjC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;eAAb,CAAkD,MAC7C,EAAI,gBAAgB,GAAG,gBAAgB,CACxC;OACA,EAAA,CAAA,CACF;MACF;KACI"}
|
|
1
|
+
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/devportal/pages/DashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect } from \"react\";\nimport {\n AppWindow,\n Building2,\n Plus,\n ArrowRight,\n Loader2,\n Download,\n DollarSign,\n} from \"lucide-react\";\nimport { GlassCard, GradientText } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { AppCard } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { OnboardingChecklist } from \"../components/OnboardingChecklist\";\nimport { useTr } from \"../utils/i18n\";\nimport type { Application, DeveloperOrganization } from \"../types\";\nimport {\n useGetDevportalDashboardBootstrapQuery,\n type OrganizationFieldsFragment,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\nexport const DashboardPage: FC = () => {\n const { navigate, basePath, activeOrgId, setActiveOrgId } = useDevportal();\n const tr = useTr();\n\n // network-only prevents a stale empty-orgs cache entry from\n // firing the /setup redirect before the real response arrives.\n const { data: orgsData, loading: orgsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: {},\n fetchPolicy: \"network-only\",\n });\n\n // This dashboard is only ever mounted at the plain (non-seller) devportal\n // route — exclude PHYSICAL orgs so a seller-portal org never shows up\n // here. See DeveloperOrgKind (BOFF-7285).\n const organizations: OrganizationFieldsFragment[] = (\n orgsData?.myDeveloperOrganizations ?? []\n ).filter((org: OrganizationFieldsFragment) => org.kind !== \"PHYSICAL\");\n\n const resolvedOrgId: string | undefined = (() => {\n if (!organizations.length) return undefined;\n if (activeOrgId && organizations.some((o) => o.id === activeOrgId))\n return activeOrgId;\n return organizations[0]?.id;\n })();\n\n useEffect(() => {\n if (resolvedOrgId && resolvedOrgId !== activeOrgId) {\n setActiveOrgId(resolvedOrgId);\n }\n }, [resolvedOrgId, activeOrgId, setActiveOrgId]);\n\n const { data: appsData, loading: appsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: { organizationId: resolvedOrgId },\n skip: !resolvedOrgId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const loading = orgsLoading || appsLoading;\n const data = appsData ?? orgsData;\n\n useEffect(() => {\n if (orgsLoading || !orgsData) return;\n if (organizations.length > 0) return;\n try {\n if (localStorage.getItem(\"devportal_setup_dismissed\")) return;\n } catch {\n return;\n }\n navigate?.(`${basePath}/setup`);\n }, [orgsLoading, orgsData, organizations.length, navigate, basePath]);\n\n const applications: ApplicationFieldsFragment[] =\n appsData?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n const totalApps = data?.applications?.totalCount ?? 0;\n const totalOrgs = organizations.length;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const isLoading = loading;\n // hasOrgs depends only on the orgs query — not on appsLoading — so the\n // header action doesn't flash \"Create Organization\" while apps are loading.\n const hasOrgs = !orgsLoading && organizations.length > 0;\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={\n <>\n {tr(\"devportal.developer\", \"Developer\")}{\" \"}\n <GradientText>{tr(\"devportal.portal\", \"Portal\")}</GradientText>\n </>\n }\n actions={\n hasOrgs ? (\n <button\n data-tour=\"devportal-dashboard-create-app-button\"\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n ) : !isLoading ? (\n <button\n data-tour=\"devportal-dashboard-create-org-button\"\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createOrganization\", \"Create Organization\")}\n </button>\n ) : null\n }\n >\n <div className=\"flex flex-col gap-8\">\n {/* Developer onboarding checklist — hides itself once complete */}\n <OnboardingChecklist onNavigate={handleNavigate} />\n\n {/* Compact inline stats */}\n <div className=\"flex items-center gap-6 border-b border-border-seam pb-6\">\n {isLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n ) : (\n <>\n <StatPill\n icon={<AppWindow size={14} />}\n value={totalApps}\n label={tr(\"devportal.apps\", \"Apps\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Building2 size={14} />}\n value={totalOrgs}\n label={tr(\"devportal.organizations\", \"Organizations\")}\n />\n </>\n )}\n </div>\n\n {/* Recent Apps */}\n <DashSection\n title={tr(\"devportal.recentApplications\", \"Recent Applications\")}\n action={\n applications.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/apps\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : applications.length > 0 ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {applications\n .slice(0, 3)\n .map((app: ApplicationFieldsFragment) => (\n <AppCard\n key={app.id}\n app={app as Application}\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n showActions={false}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n title={tr(\"devportal.noApplicationsYet\", \"No applications yet\")}\n description={\n hasOrgs\n ? tr(\n \"devportal.createFirstApplicationToGetStarted\",\n \"Create your first application to get started\",\n )\n : tr(\n \"devportal.createOrgFirstToPublishApps\",\n \"Create an organization first to start publishing apps\",\n )\n }\n action={\n hasOrgs ? (\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n ) : (\n <button\n onClick={() => handleNavigate(\"/organizations/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <Plus size={16} />\n {tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n </button>\n )\n }\n />\n )}\n </DashSection>\n\n {/* Organizations */}\n {(isLoading || organizations.length > 0) && (\n <DashSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n action={\n organizations.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n {tr(\"devportal.viewAll\", \"View all\")}\n <ArrowRight size={14} />\n </button>\n ) : undefined\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : (\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {organizations\n .slice(0, 3)\n .map((org: OrganizationFieldsFragment) => (\n <OrgCard\n key={org.id}\n org={org as DeveloperOrganization}\n onClick={() =>\n handleNavigate(`/organizations/${org.id}`)\n }\n />\n ))}\n </div>\n )}\n </DashSection>\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n};\n\nconst StatPill: FC<{ icon: ReactNode; value: number; label: string }> = ({\n icon,\n value,\n label,\n}) => (\n <div className=\"flex items-center gap-1.5\">\n <span className=\"text-text-muted\">{icon}</span>\n <span className=\"text-sm font-semibold tabular-nums\">{value}</span>\n <span className=\"text-sm text-text-secondary\">{label}</span>\n </div>\n);\n\nconst DashSection: FC<{\n title: string;\n action?: ReactNode;\n children: ReactNode;\n}> = ({ title, action, children }) => (\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-sm font-semibold uppercase tracking-wider text-text-secondary\">\n {title}\n </h2>\n {action}\n </div>\n {children}\n </section>\n);\n\nconst OrgCard: FC<{ org: DeveloperOrganization; onClick: () => void }> = ({\n org,\n onClick,\n}) => {\n const tr = useTr();\n\n const initials = org.name\n .split(\" \")\n .map((w: string) => w[0])\n .join(\"\")\n .slice(0, 2)\n .toUpperCase();\n\n return (\n <GlassCard\n treatment=\"insight\"\n role=\"button\"\n tabIndex={0}\n className=\"cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" || e.key === \" \") {\n e.preventDefault();\n onClick();\n }\n }}\n >\n <div className=\"flex items-center gap-3 mb-4\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-xl object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary text-sm font-bold\">\n {initials}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold truncate text-text-primary\">{org.name}</p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {org.publishedApps ?? 0}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n <ArrowRight size={15} className=\"text-text-muted shrink-0\" />\n </div>\n\n <div className=\"grid grid-cols-2 gap-3\">\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <Download size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.installs\", \"Installs\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n {(org.totalInstalls || 0).toLocaleString()}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2\">\n <DollarSign size={12} className=\"text-text-muted shrink-0\" />\n <div>\n <p className=\"text-[11px] text-text-secondary leading-none mb-0.5\">\n {tr(\"devportal.revenue\", \"Revenue\")}\n </p>\n <p className=\"text-sm font-semibold tabular-nums\">\n ${(org.totalRevenue || 0).toLocaleString()}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentationPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/DocumentationPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"DocumentationPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/DocumentationPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAwRhC,eAAO,MAAM,iBAAiB,EAAE,EAgL/B,CAAC"}
|