@burdenoff/microfe-devportal 2026.1112.2 → 2026.1113.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/DevportalRoot.js +65 -119
- package/dist/devportal/DevportalRoot.js.map +1 -1
- package/dist/devportal/components/AppCard.js +72 -69
- package/dist/devportal/components/AppCard.js.map +1 -1
- package/dist/devportal/components/DeveloperSettingsHub.d.ts +6 -0
- package/dist/devportal/components/DeveloperSettingsHub.js +59 -0
- package/dist/devportal/components/DeveloperSettingsHub.js.map +1 -0
- package/dist/devportal/components/ImageUploadField.js +82 -80
- package/dist/devportal/components/ImageUploadField.js.map +1 -1
- package/dist/devportal/components/SectionNav.d.ts +1 -1
- package/dist/devportal/components/SectionNav.js +60 -36
- package/dist/devportal/components/SectionNav.js.map +1 -1
- package/dist/devportal/components/StoreImage.d.ts +15 -0
- package/dist/devportal/components/StoreImage.js +30 -0
- package/dist/devportal/components/StoreImage.js.map +1 -0
- package/dist/devportal/components/index.d.ts +1 -0
- package/dist/devportal/components/index.js +17 -16
- package/dist/devportal/hooks/usePackageUpload.d.ts +46 -4
- package/dist/devportal/hooks/usePackageUpload.js +90 -36
- package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
- package/dist/devportal/pages/AdminReviewPage.js +205 -202
- package/dist/devportal/pages/AdminReviewPage.js.map +1 -1
- package/dist/devportal/pages/AnalyticsDashboardPage.js +186 -179
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +113 -111
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +80 -84
- package/dist/devportal/pages/AppLogsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +529 -504
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +186 -183
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +163 -166
- package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
- package/dist/devportal/pages/CreateOAuthClientPage.js +134 -130
- package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
- package/dist/devportal/pages/CreatePhysicalItemPage.js +2 -2
- package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +11 -11
- package/dist/devportal/pages/DocumentationPage.js +100 -94
- package/dist/devportal/pages/DocumentationPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +379 -377
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/EditPhysicalItemPage.js +312 -307
- package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -1
- package/dist/devportal/pages/InvitationDecisionPage.js +83 -79
- package/dist/devportal/pages/InvitationDecisionPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +175 -171
- package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientDetailPage.js +76 -76
- package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js +126 -124
- package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationDetailPage.js +19 -19
- package/dist/devportal/pages/OrganizationMembersPage.js +223 -204
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/ResourcesPage.d.ts +6 -0
- package/dist/devportal/pages/ResourcesPage.js +171 -155
- package/dist/devportal/pages/ResourcesPage.js.map +1 -1
- package/dist/devportal/pages/SellerDashboardPage.js +117 -115
- package/dist/devportal/pages/SellerDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SellerItemsPage.js +202 -199
- package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
- package/dist/devportal/pages/SettingsPage.js +330 -315
- package/dist/devportal/pages/SettingsPage.js.map +1 -1
- package/dist/devportal/pages/SetupPage.js +5 -5
- package/dist/devportal/pages/StoreSubmissionPage.js +133 -131
- package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +6 -6
- package/dist/devportal/pages/WebhooksPage.js +39 -39
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/utils/appDetailTabs.d.ts +73 -0
- package/dist/devportal/utils/appDetailTabs.js +62 -0
- package/dist/devportal/utils/appDetailTabs.js.map +1 -0
- package/dist/devportal/utils/developerNav.d.ts +39 -0
- package/dist/devportal/utils/developerNav.js +304 -0
- package/dist/devportal/utils/developerNav.js.map +1 -0
- package/package.json +2 -2
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { PageLayout as e } from "../components/PageLayout.js";
|
|
2
2
|
import { EmptyState as t } from "../components/EmptyState.js";
|
|
3
3
|
import { useDevportal as n } from "../providers/DevportalProvider.js";
|
|
4
|
-
import { PermissionGuard as r } from "../components/PermissionGuard.js";
|
|
5
4
|
import "../components/index.js";
|
|
5
|
+
import { PermissionGuard as r } from "../components/PermissionGuard.js";
|
|
6
6
|
import { formatCurrency as i } from "../utils/currency.js";
|
|
7
7
|
import { resolveGlobalGraphqlEndpoint as a } from "../utils/graphqlEndpoint.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { StoreImage as o } from "../components/StoreImage.js";
|
|
9
|
+
import { useEffect as s, useState as c } from "react";
|
|
10
|
+
import { ArrowRight as l, Building2 as u, CheckCircle2 as d, Clock as f, DollarSign as p, Loader2 as m, Package as h, Plus as g } from "lucide-react";
|
|
11
|
+
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
12
12
|
import { GlassCard as b, GradientText as x } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
13
14
|
// src/devportal/pages/SellerDashboardPage.tsx
|
|
14
|
-
var
|
|
15
|
+
var C = {
|
|
15
16
|
PUBLISHED: {
|
|
16
17
|
label: "Published",
|
|
17
18
|
className: "text-status-success-text bg-status-success-bg-subtle"
|
|
@@ -33,7 +34,7 @@ var S = {
|
|
|
33
34
|
className: "text-text-muted bg-bg-muted"
|
|
34
35
|
}
|
|
35
36
|
};
|
|
36
|
-
function
|
|
37
|
+
function w(e, t) {
|
|
37
38
|
let n = t || "USD";
|
|
38
39
|
return `${new Intl.NumberFormat("en-US", {
|
|
39
40
|
style: "currency",
|
|
@@ -42,26 +43,26 @@ function C(e, t) {
|
|
|
42
43
|
maximumFractionDigits: 2
|
|
43
44
|
}).format(e)} ${n}`;
|
|
44
45
|
}
|
|
45
|
-
var
|
|
46
|
-
function
|
|
46
|
+
var T = "\n query SellerDashboardBootstrap {\n myDeveloperOrganizations {\n id\n name\n slug\n logoUrl\n isVerified\n publishedApps\n totalInstalls\n totalRevenue\n kind\n }\n myPublisher {\n id\n }\n }\n";
|
|
47
|
+
function E(e) {
|
|
47
48
|
return `query SellerDashboardOrgCurrencies {\n ${e.map((e, t) => `o${t}: developerOrgRevenueSummary(organizationId: ${JSON.stringify(e)}, period: "monthly") { currency }`).join("\n ")}\n }`;
|
|
48
49
|
}
|
|
49
|
-
var
|
|
50
|
-
let { navigate: i, basePath:
|
|
50
|
+
var D = "\n query SellerDashboardProducts($publisherId: String!) {\n products(filter: { publisherId: $publisherId, nature: PHYSICAL }, limit: 3, offset: 0) {\n id\n name\n slug\n type\n status\n price\n currency\n stockQuantity\n icon\n }\n }\n", O = () => {
|
|
51
|
+
let { navigate: i, basePath: o, apiGatewayUrl: p, authToken: b } = n(), { t: C } = S(), w = (e, t) => {
|
|
51
52
|
let n = C(e);
|
|
52
53
|
return n === e ? t : n;
|
|
53
|
-
},
|
|
54
|
-
|
|
54
|
+
}, O = a(p), [N, P] = c(!0), [F, I] = c(!1), [L, R] = c([]), [z, B] = c(null), [V, H] = c([]), [U, W] = c({});
|
|
55
|
+
s(() => {
|
|
55
56
|
let e = !1;
|
|
56
57
|
return P(!0), (async () => {
|
|
57
58
|
try {
|
|
58
|
-
let t = await fetch(
|
|
59
|
+
let t = await fetch(O, {
|
|
59
60
|
method: "POST",
|
|
60
61
|
headers: {
|
|
61
62
|
"Content-Type": "application/json",
|
|
62
|
-
Authorization: `Bearer ${
|
|
63
|
+
Authorization: `Bearer ${b ?? ""}`
|
|
63
64
|
},
|
|
64
|
-
body: JSON.stringify({ query:
|
|
65
|
+
body: JSON.stringify({ query: T })
|
|
65
66
|
});
|
|
66
67
|
if (e) return;
|
|
67
68
|
let n = await t.json();
|
|
@@ -73,19 +74,19 @@ var E = "\n query SellerDashboardProducts($publisherId: String!) {\n product
|
|
|
73
74
|
})(), () => {
|
|
74
75
|
e = !0;
|
|
75
76
|
};
|
|
76
|
-
}, [
|
|
77
|
+
}, [O, b]), s(() => {
|
|
77
78
|
if (!z) return;
|
|
78
79
|
let e = !1;
|
|
79
80
|
return I(!0), (async () => {
|
|
80
81
|
try {
|
|
81
|
-
let t = await fetch(
|
|
82
|
+
let t = await fetch(O, {
|
|
82
83
|
method: "POST",
|
|
83
84
|
headers: {
|
|
84
85
|
"Content-Type": "application/json",
|
|
85
|
-
Authorization: `Bearer ${
|
|
86
|
+
Authorization: `Bearer ${b ?? ""}`
|
|
86
87
|
},
|
|
87
88
|
body: JSON.stringify({
|
|
88
|
-
query:
|
|
89
|
+
query: D,
|
|
89
90
|
variables: { publisherId: z }
|
|
90
91
|
})
|
|
91
92
|
});
|
|
@@ -109,21 +110,21 @@ var E = "\n query SellerDashboardProducts($publisherId: String!) {\n product
|
|
|
109
110
|
};
|
|
110
111
|
}, [
|
|
111
112
|
z,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
]),
|
|
113
|
+
O,
|
|
114
|
+
b
|
|
115
|
+
]), s(() => {
|
|
115
116
|
let e = L.slice(0, 3).map((e) => e.id);
|
|
116
117
|
if (e.length === 0) return;
|
|
117
118
|
let t = !1;
|
|
118
119
|
return (async () => {
|
|
119
120
|
try {
|
|
120
|
-
let n = await fetch(
|
|
121
|
+
let n = await fetch(O, {
|
|
121
122
|
method: "POST",
|
|
122
123
|
headers: {
|
|
123
124
|
"Content-Type": "application/json",
|
|
124
|
-
Authorization: `Bearer ${
|
|
125
|
+
Authorization: `Bearer ${b ?? ""}`
|
|
125
126
|
},
|
|
126
|
-
body: JSON.stringify({ query:
|
|
127
|
+
body: JSON.stringify({ query: E(e) })
|
|
127
128
|
});
|
|
128
129
|
if (t) return;
|
|
129
130
|
let r = await n.json();
|
|
@@ -139,96 +140,96 @@ var E = "\n query SellerDashboardProducts($publisherId: String!) {\n product
|
|
|
139
140
|
};
|
|
140
141
|
}, [
|
|
141
142
|
L.map((e) => e.id).join(","),
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
O,
|
|
144
|
+
b
|
|
144
145
|
]);
|
|
145
146
|
let G = N, K = N || F, q = V.length, J = V.filter((e) => e.status === "PENDING_REVIEW").length, Y = V.filter((e) => e.status === "PUBLISHED").length, X = (e) => {
|
|
146
|
-
i?.(`${
|
|
147
|
+
i?.(`${o}${e}`);
|
|
147
148
|
};
|
|
148
|
-
return /* @__PURE__ */
|
|
149
|
+
return /* @__PURE__ */ v(r, {
|
|
149
150
|
authOnly: !0,
|
|
150
|
-
children: /* @__PURE__ */
|
|
151
|
-
title: /* @__PURE__ */
|
|
152
|
-
|
|
151
|
+
children: /* @__PURE__ */ v(e, {
|
|
152
|
+
title: /* @__PURE__ */ y(_, { children: [
|
|
153
|
+
w("devportal.seller", "Seller"),
|
|
153
154
|
" ",
|
|
154
|
-
/* @__PURE__ */
|
|
155
|
+
/* @__PURE__ */ v(x, { children: w("devportal.dashboard", "Dashboard") })
|
|
155
156
|
] }),
|
|
156
|
-
actions: G ? null : /* @__PURE__ */
|
|
157
|
+
actions: G ? null : /* @__PURE__ */ y("button", {
|
|
157
158
|
onClick: () => X("/seller/items/new"),
|
|
158
159
|
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",
|
|
159
|
-
children: [/* @__PURE__ */
|
|
160
|
+
children: [/* @__PURE__ */ v(g, { size: 16 }), w("devportal.listAnItem", "List an Item")]
|
|
160
161
|
}),
|
|
161
|
-
children: /* @__PURE__ */
|
|
162
|
+
children: /* @__PURE__ */ y("div", {
|
|
162
163
|
className: "flex flex-col gap-8",
|
|
163
164
|
children: [
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ v("div", {
|
|
165
166
|
className: "flex items-center gap-6 border-b border-border-seam pb-6",
|
|
166
|
-
children: K ? /* @__PURE__ */
|
|
167
|
-
/* @__PURE__ */
|
|
168
|
-
icon: /* @__PURE__ */
|
|
167
|
+
children: K ? /* @__PURE__ */ v(m, { className: "h-4 w-4 animate-spin text-text-secondary" }) : /* @__PURE__ */ y(_, { children: [
|
|
168
|
+
/* @__PURE__ */ v(k, {
|
|
169
|
+
icon: /* @__PURE__ */ v(h, { size: 14 }),
|
|
169
170
|
value: q,
|
|
170
|
-
label:
|
|
171
|
+
label: w("devportal.items", "Items")
|
|
171
172
|
}),
|
|
172
|
-
/* @__PURE__ */
|
|
173
|
-
/* @__PURE__ */
|
|
174
|
-
icon: /* @__PURE__ */
|
|
173
|
+
/* @__PURE__ */ v("div", { className: "h-4 w-px bg-border-seam" }),
|
|
174
|
+
/* @__PURE__ */ v(k, {
|
|
175
|
+
icon: /* @__PURE__ */ v(f, { size: 14 }),
|
|
175
176
|
value: J,
|
|
176
|
-
label:
|
|
177
|
+
label: w("devportal.pendingReview", "Pending Review")
|
|
177
178
|
}),
|
|
178
|
-
/* @__PURE__ */
|
|
179
|
-
/* @__PURE__ */
|
|
180
|
-
icon: /* @__PURE__ */
|
|
179
|
+
/* @__PURE__ */ v("div", { className: "h-4 w-px bg-border-seam" }),
|
|
180
|
+
/* @__PURE__ */ v(k, {
|
|
181
|
+
icon: /* @__PURE__ */ v(d, { size: 14 }),
|
|
181
182
|
value: Y,
|
|
182
|
-
label:
|
|
183
|
+
label: w("devportal.published", "Published")
|
|
183
184
|
}),
|
|
184
|
-
/* @__PURE__ */
|
|
185
|
-
/* @__PURE__ */
|
|
186
|
-
icon: /* @__PURE__ */
|
|
185
|
+
/* @__PURE__ */ v("div", { className: "h-4 w-px bg-border-seam" }),
|
|
186
|
+
/* @__PURE__ */ v(k, {
|
|
187
|
+
icon: /* @__PURE__ */ v(u, { size: 14 }),
|
|
187
188
|
value: L.length,
|
|
188
|
-
label:
|
|
189
|
+
label: w("devportal.organizations", "Organizations")
|
|
189
190
|
})
|
|
190
191
|
] })
|
|
191
192
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
193
|
-
title:
|
|
194
|
-
action: V.length > 0 ? /* @__PURE__ */
|
|
193
|
+
/* @__PURE__ */ v(A, {
|
|
194
|
+
title: w("devportal.recentListings", "Recent Listings"),
|
|
195
|
+
action: V.length > 0 ? /* @__PURE__ */ y("button", {
|
|
195
196
|
onClick: () => X("/seller/items"),
|
|
196
197
|
className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
197
|
-
children: [
|
|
198
|
+
children: [w("devportal.viewAll", "View all"), /* @__PURE__ */ v(l, { size: 14 })]
|
|
198
199
|
}) : void 0,
|
|
199
|
-
children: G || F ? /* @__PURE__ */
|
|
200
|
+
children: G || F ? /* @__PURE__ */ v("div", {
|
|
200
201
|
className: "flex items-center justify-center py-12",
|
|
201
|
-
children: /* @__PURE__ */
|
|
202
|
-
}) : V.length > 0 ? /* @__PURE__ */
|
|
202
|
+
children: /* @__PURE__ */ v(m, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
203
|
+
}) : V.length > 0 ? /* @__PURE__ */ v("div", {
|
|
203
204
|
className: "grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
204
|
-
children: V.slice(0, 3).map((e) => /* @__PURE__ */
|
|
205
|
+
children: V.slice(0, 3).map((e) => /* @__PURE__ */ v(j, {
|
|
205
206
|
product: e,
|
|
206
207
|
onClick: () => X("/seller/items")
|
|
207
208
|
}, e.id))
|
|
208
|
-
}) : /* @__PURE__ */
|
|
209
|
+
}) : /* @__PURE__ */ v(t, {
|
|
209
210
|
illustration: "empty-data",
|
|
210
|
-
title:
|
|
211
|
-
description:
|
|
212
|
-
action: /* @__PURE__ */
|
|
211
|
+
title: w("devportal.listYourFirstItem", "List your first item"),
|
|
212
|
+
description: w("devportal.addPhysicalProductsToYourStore", "Add physical products to your store and they will appear here. Your seller organization is set up automatically the first time you list an item."),
|
|
213
|
+
action: /* @__PURE__ */ y("button", {
|
|
213
214
|
onClick: () => X("/seller/items/new"),
|
|
214
215
|
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",
|
|
215
|
-
children: [/* @__PURE__ */
|
|
216
|
+
children: [/* @__PURE__ */ v(g, { size: 16 }), w("devportal.listAnItem", "List an Item")]
|
|
216
217
|
})
|
|
217
218
|
})
|
|
218
219
|
}),
|
|
219
|
-
(G || L.length > 0) && /* @__PURE__ */
|
|
220
|
-
title:
|
|
221
|
-
action: L.length > 0 ? /* @__PURE__ */
|
|
220
|
+
(G || L.length > 0) && /* @__PURE__ */ v(A, {
|
|
221
|
+
title: w("devportal.yourOrganizations", "Your Organizations"),
|
|
222
|
+
action: L.length > 0 ? /* @__PURE__ */ y("button", {
|
|
222
223
|
onClick: () => X("/seller/organizations"),
|
|
223
224
|
className: "inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline",
|
|
224
|
-
children: [
|
|
225
|
+
children: [w("devportal.viewAll", "View all"), /* @__PURE__ */ v(l, { size: 14 })]
|
|
225
226
|
}) : void 0,
|
|
226
|
-
children: G ? /* @__PURE__ */
|
|
227
|
+
children: G ? /* @__PURE__ */ v("div", {
|
|
227
228
|
className: "flex items-center justify-center py-12",
|
|
228
|
-
children: /* @__PURE__ */
|
|
229
|
-
}) : /* @__PURE__ */
|
|
229
|
+
children: /* @__PURE__ */ v(m, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
230
|
+
}) : /* @__PURE__ */ v("div", {
|
|
230
231
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
231
|
-
children: L.slice(0, 3).map((e) => /* @__PURE__ */
|
|
232
|
+
children: L.slice(0, 3).map((e) => /* @__PURE__ */ v(M, {
|
|
232
233
|
org: e,
|
|
233
234
|
currency: U[e.id] ?? "USD",
|
|
234
235
|
onClick: () => X(`/seller/organizations/${e.id}`)
|
|
@@ -239,34 +240,34 @@ var E = "\n query SellerDashboardProducts($publisherId: String!) {\n product
|
|
|
239
240
|
})
|
|
240
241
|
})
|
|
241
242
|
});
|
|
242
|
-
},
|
|
243
|
+
}, k = ({ icon: e, value: t, label: n }) => /* @__PURE__ */ y("div", {
|
|
243
244
|
className: "flex items-center gap-1.5",
|
|
244
245
|
children: [
|
|
245
|
-
/* @__PURE__ */
|
|
246
|
+
/* @__PURE__ */ v("span", {
|
|
246
247
|
className: "text-text-muted",
|
|
247
248
|
children: e
|
|
248
249
|
}),
|
|
249
|
-
/* @__PURE__ */
|
|
250
|
+
/* @__PURE__ */ v("span", {
|
|
250
251
|
className: "text-sm font-semibold tabular-nums",
|
|
251
252
|
children: t
|
|
252
253
|
}),
|
|
253
|
-
/* @__PURE__ */
|
|
254
|
+
/* @__PURE__ */ v("span", {
|
|
254
255
|
className: "text-sm text-text-secondary",
|
|
255
256
|
children: n
|
|
256
257
|
})
|
|
257
258
|
]
|
|
258
|
-
}),
|
|
259
|
+
}), A = ({ title: e, action: t, children: n }) => /* @__PURE__ */ y("section", {
|
|
259
260
|
className: "flex flex-col gap-4",
|
|
260
|
-
children: [/* @__PURE__ */
|
|
261
|
+
children: [/* @__PURE__ */ y("div", {
|
|
261
262
|
className: "flex items-center justify-between",
|
|
262
|
-
children: [/* @__PURE__ */
|
|
263
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
263
264
|
className: "text-sm font-semibold uppercase tracking-wider text-text-secondary",
|
|
264
265
|
children: e
|
|
265
266
|
}), t]
|
|
266
267
|
}), n]
|
|
267
|
-
}),
|
|
268
|
-
let n =
|
|
269
|
-
return /* @__PURE__ */
|
|
268
|
+
}), j = ({ product: e, onClick: t }) => {
|
|
269
|
+
let n = C[e.status] ?? C.DRAFT;
|
|
270
|
+
return /* @__PURE__ */ y(b, {
|
|
270
271
|
treatment: "insight",
|
|
271
272
|
role: "button",
|
|
272
273
|
tabIndex: 0,
|
|
@@ -275,49 +276,50 @@ var E = "\n query SellerDashboardProducts($publisherId: String!) {\n product
|
|
|
275
276
|
onKeyDown: (e) => {
|
|
276
277
|
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), t());
|
|
277
278
|
},
|
|
278
|
-
children: [/* @__PURE__ */
|
|
279
|
+
children: [/* @__PURE__ */ y("div", {
|
|
279
280
|
className: "flex items-center gap-3 mb-4",
|
|
280
281
|
children: [
|
|
281
|
-
|
|
282
|
+
/* @__PURE__ */ v(o, {
|
|
282
283
|
src: e.icon,
|
|
283
284
|
alt: e.name,
|
|
284
|
-
className: "h-10 w-10 rounded-xl object-cover shrink-0"
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
285
|
+
className: "h-10 w-10 rounded-xl object-cover shrink-0",
|
|
286
|
+
fallback: /* @__PURE__ */ v("div", {
|
|
287
|
+
className: "flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary shrink-0",
|
|
288
|
+
children: /* @__PURE__ */ v(h, { size: 20 })
|
|
289
|
+
})
|
|
288
290
|
}),
|
|
289
|
-
/* @__PURE__ */
|
|
291
|
+
/* @__PURE__ */ y("div", {
|
|
290
292
|
className: "min-w-0 flex-1",
|
|
291
|
-
children: [/* @__PURE__ */
|
|
293
|
+
children: [/* @__PURE__ */ v("p", {
|
|
292
294
|
className: "font-semibold truncate text-text-primary",
|
|
293
295
|
children: e.name
|
|
294
|
-
}), /* @__PURE__ */
|
|
296
|
+
}), /* @__PURE__ */ v("p", {
|
|
295
297
|
className: "text-xs text-text-secondary mt-0.5",
|
|
296
|
-
children:
|
|
298
|
+
children: w(e.price, e.currency)
|
|
297
299
|
})]
|
|
298
300
|
}),
|
|
299
|
-
/* @__PURE__ */
|
|
301
|
+
/* @__PURE__ */ v("span", {
|
|
300
302
|
className: `shrink-0 rounded-full px-2.5 py-0.5 text-xs font-medium ${n.className}`,
|
|
301
303
|
children: n.label
|
|
302
304
|
})
|
|
303
305
|
]
|
|
304
|
-
}), /* @__PURE__ */
|
|
306
|
+
}), /* @__PURE__ */ y("div", {
|
|
305
307
|
className: "flex items-center gap-2 text-sm",
|
|
306
|
-
children: [/* @__PURE__ */
|
|
308
|
+
children: [/* @__PURE__ */ v(h, {
|
|
307
309
|
size: 12,
|
|
308
310
|
className: "text-text-muted shrink-0"
|
|
309
|
-
}), /* @__PURE__ */
|
|
311
|
+
}), /* @__PURE__ */ v("span", {
|
|
310
312
|
className: e.stockQuantity === 0 ? "text-status-error-text" : "text-text-secondary",
|
|
311
313
|
children: e.stockQuantity === 0 ? "Out of stock" : `${e.stockQuantity} in stock`
|
|
312
314
|
})]
|
|
313
315
|
})]
|
|
314
316
|
});
|
|
315
|
-
},
|
|
316
|
-
let { t: r } =
|
|
317
|
+
}, M = ({ org: e, currency: t, onClick: n }) => {
|
|
318
|
+
let { t: r } = S(), a = (e, t) => {
|
|
317
319
|
let n = r(e);
|
|
318
320
|
return n === e ? t : n;
|
|
319
321
|
}, o = e.name.split(" ").map((e) => e[0]).join("").slice(0, 2).toUpperCase();
|
|
320
|
-
return /* @__PURE__ */
|
|
322
|
+
return /* @__PURE__ */ y(b, {
|
|
321
323
|
treatment: "insight",
|
|
322
324
|
role: "button",
|
|
323
325
|
tabIndex: 0,
|
|
@@ -326,23 +328,23 @@ var E = "\n query SellerDashboardProducts($publisherId: String!) {\n product
|
|
|
326
328
|
onKeyDown: (e) => {
|
|
327
329
|
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), n());
|
|
328
330
|
},
|
|
329
|
-
children: [/* @__PURE__ */
|
|
331
|
+
children: [/* @__PURE__ */ y("div", {
|
|
330
332
|
className: "flex items-center gap-3 mb-4",
|
|
331
333
|
children: [
|
|
332
|
-
e.logoUrl ? /* @__PURE__ */
|
|
334
|
+
e.logoUrl ? /* @__PURE__ */ v("img", {
|
|
333
335
|
src: e.logoUrl,
|
|
334
336
|
alt: e.name,
|
|
335
337
|
className: "h-10 w-10 rounded-xl object-cover"
|
|
336
|
-
}) : /* @__PURE__ */
|
|
338
|
+
}) : /* @__PURE__ */ v("div", {
|
|
337
339
|
className: "flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary text-sm font-bold",
|
|
338
340
|
children: o
|
|
339
341
|
}),
|
|
340
|
-
/* @__PURE__ */
|
|
342
|
+
/* @__PURE__ */ y("div", {
|
|
341
343
|
className: "min-w-0 flex-1",
|
|
342
|
-
children: [/* @__PURE__ */
|
|
344
|
+
children: [/* @__PURE__ */ v("p", {
|
|
343
345
|
className: "font-semibold truncate text-text-primary",
|
|
344
346
|
children: e.name
|
|
345
|
-
}), /* @__PURE__ */
|
|
347
|
+
}), /* @__PURE__ */ y("p", {
|
|
346
348
|
className: "text-xs text-text-secondary mt-0.5",
|
|
347
349
|
children: [
|
|
348
350
|
e.publishedApps ?? 0,
|
|
@@ -351,22 +353,22 @@ var E = "\n query SellerDashboardProducts($publisherId: String!) {\n product
|
|
|
351
353
|
]
|
|
352
354
|
})]
|
|
353
355
|
}),
|
|
354
|
-
/* @__PURE__ */
|
|
356
|
+
/* @__PURE__ */ v(l, {
|
|
355
357
|
size: 15,
|
|
356
358
|
className: "text-text-muted shrink-0"
|
|
357
359
|
})
|
|
358
360
|
]
|
|
359
|
-
}), /* @__PURE__ */
|
|
361
|
+
}), /* @__PURE__ */ v("div", {
|
|
360
362
|
className: "grid grid-cols-1 gap-3",
|
|
361
|
-
children: /* @__PURE__ */
|
|
363
|
+
children: /* @__PURE__ */ y("div", {
|
|
362
364
|
className: "flex items-center gap-2 rounded-lg bg-bg-muted px-3 py-2",
|
|
363
|
-
children: [/* @__PURE__ */
|
|
365
|
+
children: [/* @__PURE__ */ v(p, {
|
|
364
366
|
size: 12,
|
|
365
367
|
className: "text-text-muted shrink-0"
|
|
366
|
-
}), /* @__PURE__ */
|
|
368
|
+
}), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
367
369
|
className: "text-[11px] text-text-secondary leading-none mb-0.5",
|
|
368
370
|
children: a("devportal.revenue", "Revenue")
|
|
369
|
-
}), /* @__PURE__ */
|
|
371
|
+
}), /* @__PURE__ */ v("p", {
|
|
370
372
|
className: "text-sm font-semibold tabular-nums",
|
|
371
373
|
children: i(e.totalRevenue ?? 0, t)
|
|
372
374
|
})] })]
|
|
@@ -374,6 +376,6 @@ var E = "\n query SellerDashboardProducts($publisherId: String!) {\n product
|
|
|
374
376
|
})]
|
|
375
377
|
});
|
|
376
378
|
};
|
|
377
|
-
export {
|
|
379
|
+
export { O as SellerDashboardPage };
|
|
378
380
|
|
|
379
381
|
//# sourceMappingURL=SellerDashboardPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SellerDashboardPage.js","names":["_Fragment","EmptyState","EmptyState$1","STATUS_BADGE_STYLES","formatPrice","curr","currency","price","BOOTSTRAP_QUERY","buildOrgCurrencyBatchQuery","orgIds","i","id","SELLER_PRODUCTS_QUERY","SellerDashboardPage","useDevportal","useI18n","tr","value","t","key","fallback","gqlUrl","resolveGlobalGraphqlEndpoint","apiGatewayUrl","bootstrapLoading","setBootstrapLoading","useState","productsLoading","setProductsLoading","orgs","setOrgs","publisherId","setPublisherId","products","setProducts","orgCurrencies","setOrgCurrencies","useEffect","cancelled","res","authToken","json","org","DIGITAL_TYPES","p","visibleOrgIds","next","o","isLoading","statsLoading","totalItems","pendingCount","publishedCount","handleNavigate","basePath","path","PermissionGuard","PageLayout","Fragment","GradientText","Plus","Loader2","StatPill","Package","Clock","CheckCircle2","Building2","DashSection","ArrowRight","ProductCard","product","OrgCard","icon","label","title","action","children","badge","GlassCard","onClick","initials","w","DollarSign","formatCurrency"],"sources":["../../../src/devportal/pages/SellerDashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect, useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n Package,\n Building2,\n Plus,\n ArrowRight,\n Loader2,\n DollarSign,\n Clock,\n CheckCircle2,\n} from \"lucide-react\";\nimport { GlassCard, GradientText } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { formatCurrency } from \"../utils/currency\";\nimport { resolveGlobalGraphqlEndpoint } from \"../utils/graphqlEndpoint\";\n\n// ---------------------------------------------------------------------------\n// Local types\n// ---------------------------------------------------------------------------\n\ntype ProductStatus =\n \"DRAFT\" | \"PENDING_REVIEW\" | \"PUBLISHED\" | \"REJECTED\" | \"INACTIVE\";\n\ninterface PhysicalProduct {\n id: string;\n name: string;\n slug: string;\n type: string;\n status: ProductStatus;\n price: number;\n currency: string;\n stockQuantity: number;\n icon?: string | null;\n}\n\ninterface OrgSummary {\n id: string;\n name: string;\n slug: string;\n logoUrl?: string | null;\n isVerified: boolean;\n publishedApps?: number | null;\n totalInstalls?: number | null;\n totalRevenue?: number | null;\n kind: \"DIGITAL\" | \"PHYSICAL\";\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst STATUS_BADGE_STYLES: Record<\n ProductStatus,\n { label: string; className: string }\n> = {\n PUBLISHED: {\n label: \"Published\",\n className: \"text-status-success-text bg-status-success-bg-subtle\",\n },\n PENDING_REVIEW: {\n label: \"Pending Review\",\n className: \"text-status-warning-text bg-status-warning-bg-subtle\",\n },\n DRAFT: {\n label: \"Draft\",\n className: \"text-text-muted bg-bg-muted\",\n },\n REJECTED: {\n label: \"Rejected\",\n className: \"text-status-error-text bg-status-error-bg/10\",\n },\n INACTIVE: {\n label: \"Inactive\",\n className: \"text-text-muted bg-bg-muted\",\n },\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction formatPrice(price: number, currency: string): string {\n const curr = currency || \"USD\";\n const formatted = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: curr,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(price);\n return `${formatted} ${curr}`;\n}\n\n// ---------------------------------------------------------------------------\n// GraphQL query strings\n// ---------------------------------------------------------------------------\n\nconst BOOTSTRAP_QUERY = `\n query SellerDashboardBootstrap {\n myDeveloperOrganizations {\n id\n name\n slug\n logoUrl\n isVerified\n publishedApps\n totalInstalls\n totalRevenue\n kind\n }\n myPublisher {\n id\n }\n }\n`;\n\n/**\n * org.totalRevenue has no currency field of its own on the backend model —\n * one aliased developerOrgRevenueSummary lookup per visible org card (at\n * most 3 here) gets the org's real dominant currency to label it with,\n * batched into a single request the same way buildIssueFlagsBatchQuery does\n * in SellerOrdersPage.tsx.\n */\nfunction buildOrgCurrencyBatchQuery(orgIds: string[]): string {\n const fields = orgIds\n .map(\n (id, i) =>\n `o${i}: developerOrgRevenueSummary(organizationId: ${JSON.stringify(id)}, period: \"monthly\") { currency }`,\n )\n .join(\"\\n \");\n return `query SellerDashboardOrgCurrencies {\\n ${fields}\\n }`;\n}\n\nconst SELLER_PRODUCTS_QUERY = `\n query SellerDashboardProducts($publisherId: String!) {\n products(filter: { publisherId: $publisherId, nature: PHYSICAL }, limit: 3, offset: 0) {\n id\n name\n slug\n type\n status\n price\n currency\n stockQuantity\n icon\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Page component\n// ---------------------------------------------------------------------------\n\nexport const SellerDashboardPage: FC = () => {\n const { navigate, basePath, apiGatewayUrl, authToken } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const gqlUrl = resolveGlobalGraphqlEndpoint(apiGatewayUrl);\n\n const [bootstrapLoading, setBootstrapLoading] = useState(true);\n const [productsLoading, setProductsLoading] = useState(false);\n const [orgs, setOrgs] = useState<OrgSummary[]>([]);\n const [publisherId, setPublisherId] = useState<string | null>(null);\n const [products, setProducts] = useState<PhysicalProduct[]>([]);\n const [orgCurrencies, setOrgCurrencies] = useState<Record<string, string>>(\n {},\n );\n\n // Fetch orgs + publisher in one round-trip\n useEffect(() => {\n let cancelled = false;\n setBootstrapLoading(true);\n\n const run = async () => {\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authToken ?? \"\"}`,\n },\n body: JSON.stringify({ query: BOOTSTRAP_QUERY }),\n });\n if (cancelled) return;\n const json = (await res.json()) as {\n data?: {\n myDeveloperOrganizations?: OrgSummary[];\n myPublisher?: { id: string } | null;\n };\n };\n if (cancelled) return;\n // Seller portal only ever shows PHYSICAL-kind orgs — a DIGITAL org\n // (regular app-publishing) has no business here. See DeveloperOrgKind\n // (BOFF-7285).\n setOrgs(\n (json.data?.myDeveloperOrganizations ?? []).filter(\n (org) => org.kind === \"PHYSICAL\",\n ),\n );\n setPublisherId(json.data?.myPublisher?.id ?? null);\n } finally {\n if (!cancelled) setBootstrapLoading(false);\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n }, [gqlUrl, authToken]);\n\n // Fetch recent physical products once publisher is known\n useEffect(() => {\n if (!publisherId) return;\n let cancelled = false;\n setProductsLoading(true);\n\n const run = async () => {\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authToken ?? \"\"}`,\n },\n body: JSON.stringify({\n query: SELLER_PRODUCTS_QUERY,\n variables: { publisherId },\n }),\n });\n if (cancelled) return;\n const json = (await res.json()) as {\n data?: {\n products?: Array<\n Omit<PhysicalProduct, \"status\"> & { status: string }\n >;\n };\n };\n if (cancelled) return;\n const DIGITAL_TYPES = new Set([\"APP\", \"NODE\", \"WORKFLOW\"]);\n setProducts(\n (json.data?.products ?? [])\n .filter((p) => !DIGITAL_TYPES.has(p.type))\n .map((p) => ({\n ...p,\n status: p.status as ProductStatus,\n })),\n );\n } finally {\n if (!cancelled) setProductsLoading(false);\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n }, [publisherId, gqlUrl, authToken]);\n\n // Fetch each visible org card's real currency once orgs are known\n useEffect(() => {\n const visibleOrgIds = orgs.slice(0, 3).map((org) => org.id);\n if (visibleOrgIds.length === 0) return;\n let cancelled = false;\n\n const run = async () => {\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authToken ?? \"\"}`,\n },\n body: JSON.stringify({\n query: buildOrgCurrencyBatchQuery(visibleOrgIds),\n }),\n });\n if (cancelled) return;\n const json = (await res.json()) as {\n data?: Record<string, { currency: string } | null>;\n };\n if (cancelled || !json.data) return;\n const next: Record<string, string> = {};\n visibleOrgIds.forEach((id, i) => {\n const currency = json.data?.[`o${i}`]?.currency;\n if (currency) next[id] = currency;\n });\n setOrgCurrencies(next);\n } catch {\n // Non-critical — cards fall back to USD if this fails.\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- refetch keyed on org id membership, not array identity\n }, [orgs.map((o) => o.id).join(\",\"), gqlUrl, authToken]);\n\n const isLoading = bootstrapLoading;\n const statsLoading = bootstrapLoading || productsLoading;\n\n const totalItems = products.length;\n const pendingCount = products.filter(\n (p) => p.status === \"PENDING_REVIEW\",\n ).length;\n const publishedCount = products.filter(\n (p) => p.status === \"PUBLISHED\",\n ).length;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={\n <>\n {tr(\"devportal.seller\", \"Seller\")}{\" \"}\n <GradientText>\n {tr(\"devportal.dashboard\", \"Dashboard\")}\n </GradientText>\n </>\n }\n actions={\n // Whether or not a PHYSICAL org exists yet, this always goes to the\n // same place — CreatePhysicalItemPage auto-provisions one via\n // createSellerOrganization on first listing (see\n // useSellerOrganization, BOFF-7285). Hidden only while loading.\n !isLoading ? (\n <button\n onClick={() => handleNavigate(\"/seller/items/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.listAnItem\", \"List an Item\")}\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 {statsLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n ) : (\n <>\n <StatPill\n icon={<Package size={14} />}\n value={totalItems}\n label={tr(\"devportal.items\", \"Items\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Clock size={14} />}\n value={pendingCount}\n label={tr(\"devportal.pendingReview\", \"Pending Review\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<CheckCircle2 size={14} />}\n value={publishedCount}\n label={tr(\"devportal.published\", \"Published\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Building2 size={14} />}\n value={orgs.length}\n label={tr(\"devportal.organizations\", \"Organizations\")}\n />\n </>\n )}\n </div>\n\n {/* Recent Listings */}\n <DashSection\n title={tr(\"devportal.recentListings\", \"Recent Listings\")}\n action={\n products.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/seller/items\")}\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 || productsLoading ? (\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 ) : products.length > 0 ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {products.slice(0, 3).map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n onClick={() => handleNavigate(\"/seller/items\")}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n title={tr(\n \"devportal.listYourFirstItem\",\n \"List your first item\",\n )}\n description={tr(\n \"devportal.addPhysicalProductsToYourStore\",\n \"Add physical products to your store and they will appear here. Your seller organization is set up automatically the first time you list an item.\",\n )}\n action={\n <button\n onClick={() => handleNavigate(\"/seller/items/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.listAnItem\", \"List an Item\")}\n </button>\n }\n />\n )}\n </DashSection>\n\n {/* Organizations */}\n {(isLoading || orgs.length > 0) && (\n <DashSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n action={\n orgs.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/seller/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 {orgs.slice(0, 3).map((org) => (\n <OrgCard\n key={org.id}\n org={org}\n currency={orgCurrencies[org.id] ?? \"USD\"}\n onClick={() =>\n handleNavigate(`/seller/organizations/${org.id}`)\n }\n />\n ))}\n </div>\n )}\n </DashSection>\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Sub-components\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 ProductCard: FC<{\n product: PhysicalProduct;\n onClick: () => void;\n}> = ({ product, onClick }) => {\n const badge =\n STATUS_BADGE_STYLES[product.status] ?? STATUS_BADGE_STYLES.DRAFT;\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 {product.icon ? (\n <img\n src={product.icon}\n alt={product.name}\n className=\"h-10 w-10 rounded-xl object-cover shrink-0\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary shrink-0\">\n <Package size={20} />\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold truncate text-text-primary\">\n {product.name}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {formatPrice(product.price, product.currency)}\n </p>\n </div>\n <span\n className={`shrink-0 rounded-full px-2.5 py-0.5 text-xs font-medium ${badge.className}`}\n >\n {badge.label}\n </span>\n </div>\n <div className=\"flex items-center gap-2 text-sm\">\n <Package size={12} className=\"text-text-muted shrink-0\" />\n <span\n className={\n product.stockQuantity === 0\n ? \"text-status-error-text\"\n : \"text-text-secondary\"\n }\n >\n {product.stockQuantity === 0\n ? \"Out of stock\"\n : `${product.stockQuantity} in stock`}\n </span>\n </div>\n </GlassCard>\n );\n};\n\nconst OrgCard: FC<{\n org: OrgSummary;\n currency: string;\n onClick: () => void;\n}> = ({ org, currency, onClick }) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const 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 {/* Every org on this page is PHYSICAL (seller-only dashboard) —\n \"Installs\" is an app-marketplace concept that doesn't apply, so\n Revenue gets the full-width cell instead of sharing a 2-col grid. */}\n <div className=\"grid grid-cols-1 gap-3\">\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 {formatCurrency(org.totalRevenue ?? 0, currency)}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SellerDashboardPage.js","names":["_Fragment","EmptyState","EmptyState$1","STATUS_BADGE_STYLES","formatPrice","curr","currency","price","BOOTSTRAP_QUERY","buildOrgCurrencyBatchQuery","orgIds","i","id","SELLER_PRODUCTS_QUERY","SellerDashboardPage","useDevportal","useI18n","tr","value","t","key","fallback","gqlUrl","resolveGlobalGraphqlEndpoint","apiGatewayUrl","bootstrapLoading","setBootstrapLoading","useState","productsLoading","setProductsLoading","orgs","setOrgs","publisherId","setPublisherId","products","setProducts","orgCurrencies","setOrgCurrencies","useEffect","cancelled","res","authToken","json","org","DIGITAL_TYPES","p","visibleOrgIds","next","o","isLoading","statsLoading","totalItems","pendingCount","publishedCount","handleNavigate","basePath","path","PermissionGuard","PageLayout","Fragment","GradientText","Plus","Loader2","StatPill","Package","Clock","CheckCircle2","Building2","DashSection","ArrowRight","ProductCard","product","OrgCard","icon","label","title","action","children","badge","GlassCard","onClick","StoreImage","initials","w","DollarSign","formatCurrency"],"sources":["../../../src/devportal/pages/SellerDashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect, useState } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n Package,\n Building2,\n Plus,\n ArrowRight,\n Loader2,\n DollarSign,\n Clock,\n CheckCircle2,\n} from \"lucide-react\";\nimport { GlassCard, GradientText } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { formatCurrency } from \"../utils/currency\";\nimport { resolveGlobalGraphqlEndpoint } from \"../utils/graphqlEndpoint\";\nimport { StoreImage } from \"../components/StoreImage\";\n\n// ---------------------------------------------------------------------------\n// Local types\n// ---------------------------------------------------------------------------\n\ntype ProductStatus =\n \"DRAFT\" | \"PENDING_REVIEW\" | \"PUBLISHED\" | \"REJECTED\" | \"INACTIVE\";\n\ninterface PhysicalProduct {\n id: string;\n name: string;\n slug: string;\n type: string;\n status: ProductStatus;\n price: number;\n currency: string;\n stockQuantity: number;\n icon?: string | null;\n}\n\ninterface OrgSummary {\n id: string;\n name: string;\n slug: string;\n logoUrl?: string | null;\n isVerified: boolean;\n publishedApps?: number | null;\n totalInstalls?: number | null;\n totalRevenue?: number | null;\n kind: \"DIGITAL\" | \"PHYSICAL\";\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst STATUS_BADGE_STYLES: Record<\n ProductStatus,\n { label: string; className: string }\n> = {\n PUBLISHED: {\n label: \"Published\",\n className: \"text-status-success-text bg-status-success-bg-subtle\",\n },\n PENDING_REVIEW: {\n label: \"Pending Review\",\n className: \"text-status-warning-text bg-status-warning-bg-subtle\",\n },\n DRAFT: {\n label: \"Draft\",\n className: \"text-text-muted bg-bg-muted\",\n },\n REJECTED: {\n label: \"Rejected\",\n className: \"text-status-error-text bg-status-error-bg/10\",\n },\n INACTIVE: {\n label: \"Inactive\",\n className: \"text-text-muted bg-bg-muted\",\n },\n};\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction formatPrice(price: number, currency: string): string {\n const curr = currency || \"USD\";\n const formatted = new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: curr,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(price);\n return `${formatted} ${curr}`;\n}\n\n// ---------------------------------------------------------------------------\n// GraphQL query strings\n// ---------------------------------------------------------------------------\n\nconst BOOTSTRAP_QUERY = `\n query SellerDashboardBootstrap {\n myDeveloperOrganizations {\n id\n name\n slug\n logoUrl\n isVerified\n publishedApps\n totalInstalls\n totalRevenue\n kind\n }\n myPublisher {\n id\n }\n }\n`;\n\n/**\n * org.totalRevenue has no currency field of its own on the backend model —\n * one aliased developerOrgRevenueSummary lookup per visible org card (at\n * most 3 here) gets the org's real dominant currency to label it with,\n * batched into a single request the same way buildIssueFlagsBatchQuery does\n * in SellerOrdersPage.tsx.\n */\nfunction buildOrgCurrencyBatchQuery(orgIds: string[]): string {\n const fields = orgIds\n .map(\n (id, i) =>\n `o${i}: developerOrgRevenueSummary(organizationId: ${JSON.stringify(id)}, period: \"monthly\") { currency }`,\n )\n .join(\"\\n \");\n return `query SellerDashboardOrgCurrencies {\\n ${fields}\\n }`;\n}\n\nconst SELLER_PRODUCTS_QUERY = `\n query SellerDashboardProducts($publisherId: String!) {\n products(filter: { publisherId: $publisherId, nature: PHYSICAL }, limit: 3, offset: 0) {\n id\n name\n slug\n type\n status\n price\n currency\n stockQuantity\n icon\n }\n }\n`;\n\n// ---------------------------------------------------------------------------\n// Page component\n// ---------------------------------------------------------------------------\n\nexport const SellerDashboardPage: FC = () => {\n const { navigate, basePath, apiGatewayUrl, authToken } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const gqlUrl = resolveGlobalGraphqlEndpoint(apiGatewayUrl);\n\n const [bootstrapLoading, setBootstrapLoading] = useState(true);\n const [productsLoading, setProductsLoading] = useState(false);\n const [orgs, setOrgs] = useState<OrgSummary[]>([]);\n const [publisherId, setPublisherId] = useState<string | null>(null);\n const [products, setProducts] = useState<PhysicalProduct[]>([]);\n const [orgCurrencies, setOrgCurrencies] = useState<Record<string, string>>(\n {},\n );\n\n // Fetch orgs + publisher in one round-trip\n useEffect(() => {\n let cancelled = false;\n setBootstrapLoading(true);\n\n const run = async () => {\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authToken ?? \"\"}`,\n },\n body: JSON.stringify({ query: BOOTSTRAP_QUERY }),\n });\n if (cancelled) return;\n const json = (await res.json()) as {\n data?: {\n myDeveloperOrganizations?: OrgSummary[];\n myPublisher?: { id: string } | null;\n };\n };\n if (cancelled) return;\n // Seller portal only ever shows PHYSICAL-kind orgs — a DIGITAL org\n // (regular app-publishing) has no business here. See DeveloperOrgKind\n // (BOFF-7285).\n setOrgs(\n (json.data?.myDeveloperOrganizations ?? []).filter(\n (org) => org.kind === \"PHYSICAL\",\n ),\n );\n setPublisherId(json.data?.myPublisher?.id ?? null);\n } finally {\n if (!cancelled) setBootstrapLoading(false);\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n }, [gqlUrl, authToken]);\n\n // Fetch recent physical products once publisher is known\n useEffect(() => {\n if (!publisherId) return;\n let cancelled = false;\n setProductsLoading(true);\n\n const run = async () => {\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authToken ?? \"\"}`,\n },\n body: JSON.stringify({\n query: SELLER_PRODUCTS_QUERY,\n variables: { publisherId },\n }),\n });\n if (cancelled) return;\n const json = (await res.json()) as {\n data?: {\n products?: Array<\n Omit<PhysicalProduct, \"status\"> & { status: string }\n >;\n };\n };\n if (cancelled) return;\n const DIGITAL_TYPES = new Set([\"APP\", \"NODE\", \"WORKFLOW\"]);\n setProducts(\n (json.data?.products ?? [])\n .filter((p) => !DIGITAL_TYPES.has(p.type))\n .map((p) => ({\n ...p,\n status: p.status as ProductStatus,\n })),\n );\n } finally {\n if (!cancelled) setProductsLoading(false);\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n }, [publisherId, gqlUrl, authToken]);\n\n // Fetch each visible org card's real currency once orgs are known\n useEffect(() => {\n const visibleOrgIds = orgs.slice(0, 3).map((org) => org.id);\n if (visibleOrgIds.length === 0) return;\n let cancelled = false;\n\n const run = async () => {\n try {\n const res = await fetch(gqlUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${authToken ?? \"\"}`,\n },\n body: JSON.stringify({\n query: buildOrgCurrencyBatchQuery(visibleOrgIds),\n }),\n });\n if (cancelled) return;\n const json = (await res.json()) as {\n data?: Record<string, { currency: string } | null>;\n };\n if (cancelled || !json.data) return;\n const next: Record<string, string> = {};\n visibleOrgIds.forEach((id, i) => {\n const currency = json.data?.[`o${i}`]?.currency;\n if (currency) next[id] = currency;\n });\n setOrgCurrencies(next);\n } catch {\n // Non-critical — cards fall back to USD if this fails.\n }\n };\n\n void run();\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- refetch keyed on org id membership, not array identity\n }, [orgs.map((o) => o.id).join(\",\"), gqlUrl, authToken]);\n\n const isLoading = bootstrapLoading;\n const statsLoading = bootstrapLoading || productsLoading;\n\n const totalItems = products.length;\n const pendingCount = products.filter(\n (p) => p.status === \"PENDING_REVIEW\",\n ).length;\n const publishedCount = products.filter(\n (p) => p.status === \"PUBLISHED\",\n ).length;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={\n <>\n {tr(\"devportal.seller\", \"Seller\")}{\" \"}\n <GradientText>\n {tr(\"devportal.dashboard\", \"Dashboard\")}\n </GradientText>\n </>\n }\n actions={\n // Whether or not a PHYSICAL org exists yet, this always goes to the\n // same place — CreatePhysicalItemPage auto-provisions one via\n // createSellerOrganization on first listing (see\n // useSellerOrganization, BOFF-7285). Hidden only while loading.\n !isLoading ? (\n <button\n onClick={() => handleNavigate(\"/seller/items/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.listAnItem\", \"List an Item\")}\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 {statsLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n ) : (\n <>\n <StatPill\n icon={<Package size={14} />}\n value={totalItems}\n label={tr(\"devportal.items\", \"Items\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Clock size={14} />}\n value={pendingCount}\n label={tr(\"devportal.pendingReview\", \"Pending Review\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<CheckCircle2 size={14} />}\n value={publishedCount}\n label={tr(\"devportal.published\", \"Published\")}\n />\n <div className=\"h-4 w-px bg-border-seam\" />\n <StatPill\n icon={<Building2 size={14} />}\n value={orgs.length}\n label={tr(\"devportal.organizations\", \"Organizations\")}\n />\n </>\n )}\n </div>\n\n {/* Recent Listings */}\n <DashSection\n title={tr(\"devportal.recentListings\", \"Recent Listings\")}\n action={\n products.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/seller/items\")}\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 || productsLoading ? (\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 ) : products.length > 0 ? (\n <div className=\"grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3\">\n {products.slice(0, 3).map((product) => (\n <ProductCard\n key={product.id}\n product={product}\n onClick={() => handleNavigate(\"/seller/items\")}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n title={tr(\n \"devportal.listYourFirstItem\",\n \"List your first item\",\n )}\n description={tr(\n \"devportal.addPhysicalProductsToYourStore\",\n \"Add physical products to your store and they will appear here. Your seller organization is set up automatically the first time you list an item.\",\n )}\n action={\n <button\n onClick={() => handleNavigate(\"/seller/items/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.listAnItem\", \"List an Item\")}\n </button>\n }\n />\n )}\n </DashSection>\n\n {/* Organizations */}\n {(isLoading || orgs.length > 0) && (\n <DashSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n action={\n orgs.length > 0 ? (\n <button\n onClick={() => handleNavigate(\"/seller/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 {orgs.slice(0, 3).map((org) => (\n <OrgCard\n key={org.id}\n org={org}\n currency={orgCurrencies[org.id] ?? \"USD\"}\n onClick={() =>\n handleNavigate(`/seller/organizations/${org.id}`)\n }\n />\n ))}\n </div>\n )}\n </DashSection>\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n};\n\n// ---------------------------------------------------------------------------\n// Sub-components\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 ProductCard: FC<{\n product: PhysicalProduct;\n onClick: () => void;\n}> = ({ product, onClick }) => {\n const badge =\n STATUS_BADGE_STYLES[product.status] ?? STATUS_BADGE_STYLES.DRAFT;\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 <StoreImage\n src={product.icon}\n alt={product.name}\n className=\"h-10 w-10 rounded-xl object-cover shrink-0\"\n fallback={\n <div className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-action-primary-bg/15 text-primary shrink-0\">\n <Package size={20} />\n </div>\n }\n />\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold truncate text-text-primary\">\n {product.name}\n </p>\n <p className=\"text-xs text-text-secondary mt-0.5\">\n {formatPrice(product.price, product.currency)}\n </p>\n </div>\n <span\n className={`shrink-0 rounded-full px-2.5 py-0.5 text-xs font-medium ${badge.className}`}\n >\n {badge.label}\n </span>\n </div>\n <div className=\"flex items-center gap-2 text-sm\">\n <Package size={12} className=\"text-text-muted shrink-0\" />\n <span\n className={\n product.stockQuantity === 0\n ? \"text-status-error-text\"\n : \"text-text-secondary\"\n }\n >\n {product.stockQuantity === 0\n ? \"Out of stock\"\n : `${product.stockQuantity} in stock`}\n </span>\n </div>\n </GlassCard>\n );\n};\n\nconst OrgCard: FC<{\n org: OrgSummary;\n currency: string;\n onClick: () => void;\n}> = ({ org, currency, onClick }) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const 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 {/* Every org on this page is PHYSICAL (seller-only dashboard) —\n \"Installs\" is an app-marketplace concept that doesn't apply, so\n Revenue gets the full-width cell instead of sharing a 2-col grid. */}\n <div className=\"grid grid-cols-1 gap-3\">\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 {formatCurrency(org.totalRevenue ?? 0, currency)}\n </p>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n};\n"],"mappings":"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"}
|