@burdenoff/microfe-devportal 2026.625.2 → 2026.625.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/components/EmptyState.js +14 -8
- package/dist/devportal/components/EmptyState.js.map +1 -1
- package/dist/devportal/components/LoadingState.js.map +1 -1
- package/dist/devportal/components/PageLayout.js +39 -34
- package/dist/devportal/components/PageLayout.js.map +1 -1
- package/dist/devportal/components/StatCard.js +4 -3
- package/dist/devportal/components/StatCard.js.map +1 -1
- package/dist/devportal/components/StatusBadge.js +1 -1
- package/dist/devportal/components/StatusBadge.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +1 -1
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +85 -85
- package/dist/devportal/pages/AppLogsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +7 -7
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +157 -151
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +2 -2
- package/dist/devportal/pages/DelegationsPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationMembersPage.js +225 -225
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationsPage.js +25 -25
- package/dist/devportal/pages/OrganizationsPage.js.map +1 -1
- package/dist/devportal/pages/WebhooksPage.js +1 -1
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -11,265 +11,271 @@ import "../hooks/index.js";
|
|
|
11
11
|
import { useEffect as u } from "react";
|
|
12
12
|
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
13
13
|
import { Activity as f, AppWindow as p, ArrowRight as m, Building2 as h, DollarSign as g, Key as _, Loader2 as v, Plus as y, Store as b, TrendingUp as x, Users as S, Webhook as C } from "lucide-react";
|
|
14
|
-
import {
|
|
15
|
-
import { EmphasisPanel as
|
|
14
|
+
import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
15
|
+
import { EmphasisPanel as D, GradientText as O } from "@burdenoff/fe-libs/ui";
|
|
16
16
|
//#region src/devportal/pages/DashboardPage.tsx
|
|
17
|
-
var
|
|
18
|
-
let { navigate:
|
|
19
|
-
let n =
|
|
17
|
+
var k = () => {
|
|
18
|
+
let { navigate: k, basePath: M } = e(), { t: N } = d(), P = (e, t) => {
|
|
19
|
+
let n = N(e);
|
|
20
20
|
return n === e ? t : n;
|
|
21
|
-
}, { data:
|
|
21
|
+
}, { data: F, loading: I } = n({ fetchPolicy: "cache-and-network" }), L = F?.myDeveloperOrganizations ?? [];
|
|
22
22
|
u(() => {
|
|
23
|
-
if (!(
|
|
23
|
+
if (!(I || !F) && !(L.length > 0)) {
|
|
24
24
|
try {
|
|
25
25
|
if (localStorage.getItem("devportal_setup_dismissed")) return;
|
|
26
26
|
} catch {
|
|
27
27
|
return;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
k?.(`${M}/setup`);
|
|
30
30
|
}
|
|
31
31
|
}, [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
I,
|
|
33
|
+
F,
|
|
34
|
+
L.length,
|
|
35
|
+
k,
|
|
36
|
+
M
|
|
37
37
|
]);
|
|
38
|
-
let
|
|
39
|
-
totalApps:
|
|
40
|
-
totalOrgs:
|
|
38
|
+
let R = F?.applications?.edges?.map((e) => e.node) ?? [], z = {
|
|
39
|
+
totalApps: F?.applications?.totalCount ?? 0,
|
|
40
|
+
totalOrgs: L.length,
|
|
41
41
|
totalApiKeys: 0,
|
|
42
42
|
totalWebhooks: 0,
|
|
43
|
-
totalApiCalls:
|
|
44
|
-
activeInstalls:
|
|
45
|
-
monthlyRevenue:
|
|
43
|
+
totalApiCalls: L.reduce((e, t) => e + parseInt(t.totalApiCalls || "0"), 0),
|
|
44
|
+
activeInstalls: L.reduce((e, t) => e + (t.activeInstalls || 0), 0),
|
|
45
|
+
monthlyRevenue: L.reduce((e, t) => e + (t.totalRevenue || 0), 0),
|
|
46
46
|
errorRate: 0
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
return /* @__PURE__ */
|
|
47
|
+
}, B = (e) => {
|
|
48
|
+
k?.(`${M}${e}`);
|
|
49
|
+
}, V = I, H = !V && L.length > 0;
|
|
50
|
+
return /* @__PURE__ */ T(c, {
|
|
51
51
|
permission: l.APP_READ,
|
|
52
|
-
children: /* @__PURE__ */
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
children: /* @__PURE__ */ E(r, {
|
|
53
|
+
heroBackdrop: !0,
|
|
54
|
+
title: /* @__PURE__ */ E(w, { children: [
|
|
55
|
+
P("devportal.developer", "Developer"),
|
|
56
|
+
" ",
|
|
57
|
+
/* @__PURE__ */ T(O, { children: P("devportal.portal", "Portal") })
|
|
58
|
+
] }),
|
|
59
|
+
description: P("devportal.buildManageMonitor", "Build, manage, and monitor your applications"),
|
|
60
|
+
actions: H ? /* @__PURE__ */ E("button", {
|
|
56
61
|
"data-tour": "devportal-dashboard-create-app-button",
|
|
57
|
-
onClick: () =>
|
|
62
|
+
onClick: () => B("/apps/new"),
|
|
58
63
|
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",
|
|
59
|
-
children: [/* @__PURE__ */
|
|
60
|
-
}) :
|
|
64
|
+
children: [/* @__PURE__ */ T(y, { size: 16 }), P("devportal.createApp", "Create App")]
|
|
65
|
+
}) : V ? null : /* @__PURE__ */ E("button", {
|
|
61
66
|
"data-tour": "devportal-dashboard-create-org-button",
|
|
62
|
-
onClick: () =>
|
|
67
|
+
onClick: () => B("/organizations/new"),
|
|
63
68
|
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",
|
|
64
|
-
children: [/* @__PURE__ */
|
|
69
|
+
children: [/* @__PURE__ */ T(y, { size: 16 }), P("devportal.createOrganization", "Create Organization")]
|
|
65
70
|
}),
|
|
66
71
|
children: [
|
|
67
|
-
/* @__PURE__ */
|
|
72
|
+
/* @__PURE__ */ T(D, {
|
|
68
73
|
"data-tour": "devportal-dashboard-stats-overview",
|
|
69
|
-
children: /* @__PURE__ */
|
|
74
|
+
children: /* @__PURE__ */ E(o, {
|
|
70
75
|
columns: 4,
|
|
71
76
|
children: [
|
|
72
|
-
/* @__PURE__ */
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
77
|
+
/* @__PURE__ */ T(a, {
|
|
78
|
+
featured: !0,
|
|
79
|
+
title: P("devportal.totalApps", "Total Apps"),
|
|
80
|
+
value: V ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : z.totalApps,
|
|
81
|
+
icon: /* @__PURE__ */ T(p, { size: 20 }),
|
|
76
82
|
trend: void 0
|
|
77
83
|
}),
|
|
78
|
-
/* @__PURE__ */
|
|
79
|
-
title:
|
|
80
|
-
value:
|
|
81
|
-
icon: /* @__PURE__ */
|
|
84
|
+
/* @__PURE__ */ T(a, {
|
|
85
|
+
title: P("devportal.organizations", "Organizations"),
|
|
86
|
+
value: V ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : z.totalOrgs,
|
|
87
|
+
icon: /* @__PURE__ */ T(h, { size: 20 })
|
|
82
88
|
}),
|
|
83
|
-
/* @__PURE__ */
|
|
84
|
-
title:
|
|
85
|
-
value:
|
|
86
|
-
icon: /* @__PURE__ */
|
|
87
|
-
description:
|
|
89
|
+
/* @__PURE__ */ T(a, {
|
|
90
|
+
title: P("devportal.apiKeys", "API Keys"),
|
|
91
|
+
value: z.totalApiKeys,
|
|
92
|
+
icon: /* @__PURE__ */ T(_, { size: 20 }),
|
|
93
|
+
description: P("devportal.activeKeys", "Active keys")
|
|
88
94
|
}),
|
|
89
|
-
/* @__PURE__ */
|
|
90
|
-
title:
|
|
91
|
-
value:
|
|
92
|
-
icon: /* @__PURE__ */
|
|
93
|
-
description:
|
|
95
|
+
/* @__PURE__ */ T(a, {
|
|
96
|
+
title: P("devportal.webhooks", "Webhooks"),
|
|
97
|
+
value: z.totalWebhooks,
|
|
98
|
+
icon: /* @__PURE__ */ T(C, { size: 20 }),
|
|
99
|
+
description: P("devportal.configured", "Configured")
|
|
94
100
|
})
|
|
95
101
|
]
|
|
96
102
|
})
|
|
97
103
|
}),
|
|
98
|
-
/* @__PURE__ */
|
|
99
|
-
title:
|
|
104
|
+
/* @__PURE__ */ T(i, {
|
|
105
|
+
title: P("devportal.usageOverview", "Usage Overview"),
|
|
100
106
|
"data-tour": "devportal-dashboard-usage-overview",
|
|
101
|
-
children: /* @__PURE__ */
|
|
107
|
+
children: /* @__PURE__ */ E(o, {
|
|
102
108
|
columns: 4,
|
|
103
109
|
children: [
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
-
title:
|
|
106
|
-
value:
|
|
107
|
-
icon: /* @__PURE__ */
|
|
110
|
+
/* @__PURE__ */ T(a, {
|
|
111
|
+
title: P("devportal.apiCalls", "API Calls"),
|
|
112
|
+
value: V ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : z.totalApiCalls > 1e6 ? `${(z.totalApiCalls / 1e6).toFixed(1)}M` : z.totalApiCalls > 1e3 ? `${(z.totalApiCalls / 1e3).toFixed(1)}K` : z.totalApiCalls.toString(),
|
|
113
|
+
icon: /* @__PURE__ */ T(f, { size: 20 }),
|
|
108
114
|
trend: void 0
|
|
109
115
|
}),
|
|
110
|
-
/* @__PURE__ */
|
|
111
|
-
title:
|
|
112
|
-
value:
|
|
113
|
-
icon: /* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ T(a, {
|
|
117
|
+
title: P("devportal.activeInstalls", "Active Installs"),
|
|
118
|
+
value: V ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : z.activeInstalls > 1e3 ? `${(z.activeInstalls / 1e3).toFixed(1)}K` : z.activeInstalls.toString(),
|
|
119
|
+
icon: /* @__PURE__ */ T(S, { size: 20 }),
|
|
114
120
|
trend: void 0
|
|
115
121
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
-
title:
|
|
118
|
-
value:
|
|
119
|
-
icon: /* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ T(a, {
|
|
123
|
+
title: P("devportal.monthlyRevenue", "Monthly Revenue"),
|
|
124
|
+
value: V ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : `$${z.monthlyRevenue.toLocaleString()}`,
|
|
125
|
+
icon: /* @__PURE__ */ T(g, { size: 20 }),
|
|
120
126
|
trend: void 0
|
|
121
127
|
}),
|
|
122
|
-
/* @__PURE__ */
|
|
123
|
-
title:
|
|
124
|
-
value: `${
|
|
125
|
-
icon: /* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ T(a, {
|
|
129
|
+
title: P("devportal.errorRate", "Error Rate"),
|
|
130
|
+
value: `${z.errorRate}%`,
|
|
131
|
+
icon: /* @__PURE__ */ T(x, { size: 20 }),
|
|
126
132
|
trend: void 0
|
|
127
133
|
})
|
|
128
134
|
]
|
|
129
135
|
})
|
|
130
136
|
}),
|
|
131
|
-
/* @__PURE__ */
|
|
137
|
+
/* @__PURE__ */ E("div", {
|
|
132
138
|
className: "grid gap-6 lg:grid-cols-2",
|
|
133
|
-
children: [/* @__PURE__ */
|
|
139
|
+
children: [/* @__PURE__ */ T(i, {
|
|
134
140
|
"data-tour": "devportal-dashboard-recent-apps",
|
|
135
|
-
title:
|
|
136
|
-
actions: /* @__PURE__ */
|
|
137
|
-
onClick: () =>
|
|
141
|
+
title: P("devportal.recentApplications", "Recent Applications"),
|
|
142
|
+
actions: /* @__PURE__ */ E("button", {
|
|
143
|
+
onClick: () => B("/apps"),
|
|
138
144
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
139
145
|
children: [
|
|
140
|
-
|
|
146
|
+
P("devportal.viewAll", "View all"),
|
|
141
147
|
" ",
|
|
142
|
-
/* @__PURE__ */
|
|
148
|
+
/* @__PURE__ */ T(m, { size: 14 })
|
|
143
149
|
]
|
|
144
150
|
}),
|
|
145
|
-
children:
|
|
151
|
+
children: V ? /* @__PURE__ */ T("div", {
|
|
146
152
|
className: "flex items-center justify-center py-8",
|
|
147
|
-
children: /* @__PURE__ */
|
|
148
|
-
}) :
|
|
153
|
+
children: /* @__PURE__ */ T(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
154
|
+
}) : R.length > 0 ? /* @__PURE__ */ T("div", {
|
|
149
155
|
className: "space-y-2",
|
|
150
|
-
children:
|
|
156
|
+
children: R.slice(0, 3).map((e) => /* @__PURE__ */ T(s, {
|
|
151
157
|
app: e,
|
|
152
|
-
onClick: () =>
|
|
158
|
+
onClick: () => B(`/apps/${e.id}`)
|
|
153
159
|
}, e.id))
|
|
154
|
-
}) : /* @__PURE__ */
|
|
155
|
-
|
|
156
|
-
title:
|
|
157
|
-
description:
|
|
158
|
-
action:
|
|
159
|
-
onClick: () =>
|
|
160
|
+
}) : /* @__PURE__ */ T(t, {
|
|
161
|
+
illustration: "empty-generic",
|
|
162
|
+
title: P("devportal.noApplicationsYet", "No applications yet"),
|
|
163
|
+
description: H ? P("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : P("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
|
|
164
|
+
action: H ? /* @__PURE__ */ E("button", {
|
|
165
|
+
onClick: () => B("/apps/new"),
|
|
160
166
|
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",
|
|
161
|
-
children: [/* @__PURE__ */
|
|
162
|
-
}) : /* @__PURE__ */
|
|
163
|
-
onClick: () =>
|
|
167
|
+
children: [/* @__PURE__ */ T(y, { size: 16 }), P("devportal.createApp", "Create App")]
|
|
168
|
+
}) : /* @__PURE__ */ E("button", {
|
|
169
|
+
onClick: () => B("/organizations/new"),
|
|
164
170
|
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",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
171
|
+
children: [/* @__PURE__ */ T(y, { size: 16 }), P("devportal.createOrganization", "Create Organization")]
|
|
166
172
|
})
|
|
167
173
|
})
|
|
168
|
-
}), /* @__PURE__ */
|
|
169
|
-
title:
|
|
174
|
+
}), /* @__PURE__ */ T(i, {
|
|
175
|
+
title: P("devportal.quickActions", "Quick Actions"),
|
|
170
176
|
"data-tour": "devportal-dashboard-quick-actions",
|
|
171
|
-
children: /* @__PURE__ */
|
|
177
|
+
children: /* @__PURE__ */ E("div", {
|
|
172
178
|
className: "grid gap-3",
|
|
173
179
|
children: [
|
|
174
|
-
|
|
175
|
-
title:
|
|
176
|
-
description:
|
|
177
|
-
icon: /* @__PURE__ */
|
|
178
|
-
onClick: () =>
|
|
179
|
-
}) : /* @__PURE__ */
|
|
180
|
-
title:
|
|
181
|
-
description:
|
|
182
|
-
icon: /* @__PURE__ */
|
|
183
|
-
onClick: () =>
|
|
180
|
+
H ? /* @__PURE__ */ T(A, {
|
|
181
|
+
title: P("devportal.createNewApp", "Create New App"),
|
|
182
|
+
description: P("devportal.startBuildingANewApplication", "Start building a new application"),
|
|
183
|
+
icon: /* @__PURE__ */ T(p, { size: 20 }),
|
|
184
|
+
onClick: () => B("/apps/new")
|
|
185
|
+
}) : /* @__PURE__ */ T(A, {
|
|
186
|
+
title: P("devportal.createOrganization", "Create Organization"),
|
|
187
|
+
description: P("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
|
|
188
|
+
icon: /* @__PURE__ */ T(h, { size: 20 }),
|
|
189
|
+
onClick: () => B("/organizations/new"),
|
|
184
190
|
primary: !0
|
|
185
191
|
}),
|
|
186
|
-
|
|
187
|
-
title:
|
|
188
|
-
description:
|
|
189
|
-
icon: /* @__PURE__ */
|
|
190
|
-
onClick: () =>
|
|
192
|
+
H && /* @__PURE__ */ T(A, {
|
|
193
|
+
title: P("devportal.publisherProfile", "Publisher Profile"),
|
|
194
|
+
description: P("devportal.manageYourStorePresence", "Manage your store publisher presence"),
|
|
195
|
+
icon: /* @__PURE__ */ T(b, { size: 20 }),
|
|
196
|
+
onClick: () => B("/publisher/profile")
|
|
191
197
|
}),
|
|
192
|
-
/* @__PURE__ */
|
|
193
|
-
title:
|
|
194
|
-
description:
|
|
195
|
-
icon: /* @__PURE__ */
|
|
196
|
-
onClick: () =>
|
|
198
|
+
/* @__PURE__ */ T(A, {
|
|
199
|
+
title: P("devportal.generateApiKey", "Generate API Key"),
|
|
200
|
+
description: P("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
|
|
201
|
+
icon: /* @__PURE__ */ T(_, { size: 20 }),
|
|
202
|
+
onClick: () => B("/api-keys")
|
|
197
203
|
}),
|
|
198
|
-
/* @__PURE__ */
|
|
199
|
-
title:
|
|
200
|
-
description:
|
|
201
|
-
icon: /* @__PURE__ */
|
|
202
|
-
onClick: () =>
|
|
204
|
+
/* @__PURE__ */ T(A, {
|
|
205
|
+
title: P("devportal.configureWebhooks", "Configure Webhooks"),
|
|
206
|
+
description: P("devportal.setUpEventNotifications", "Set up event notifications"),
|
|
207
|
+
icon: /* @__PURE__ */ T(C, { size: 20 }),
|
|
208
|
+
onClick: () => B("/webhooks")
|
|
203
209
|
})
|
|
204
210
|
]
|
|
205
211
|
})
|
|
206
212
|
})]
|
|
207
213
|
}),
|
|
208
|
-
|
|
214
|
+
V ? /* @__PURE__ */ T(i, {
|
|
209
215
|
"data-tour": "devportal-dashboard-organizations",
|
|
210
|
-
title:
|
|
211
|
-
children: /* @__PURE__ */
|
|
216
|
+
title: P("devportal.yourOrganizations", "Your Organizations"),
|
|
217
|
+
children: /* @__PURE__ */ T("div", {
|
|
212
218
|
className: "flex items-center justify-center py-8",
|
|
213
|
-
children: /* @__PURE__ */
|
|
219
|
+
children: /* @__PURE__ */ T(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
|
|
214
220
|
})
|
|
215
|
-
}) :
|
|
216
|
-
title:
|
|
217
|
-
actions: /* @__PURE__ */
|
|
218
|
-
onClick: () =>
|
|
221
|
+
}) : L.length > 0 ? /* @__PURE__ */ T(i, {
|
|
222
|
+
title: P("devportal.yourOrganizations", "Your Organizations"),
|
|
223
|
+
actions: /* @__PURE__ */ E("button", {
|
|
224
|
+
onClick: () => B("/organizations"),
|
|
219
225
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
220
226
|
children: [
|
|
221
|
-
|
|
227
|
+
P("devportal.viewAll", "View all"),
|
|
222
228
|
" ",
|
|
223
|
-
/* @__PURE__ */
|
|
229
|
+
/* @__PURE__ */ T(m, { size: 14 })
|
|
224
230
|
]
|
|
225
231
|
}),
|
|
226
|
-
children: /* @__PURE__ */
|
|
232
|
+
children: /* @__PURE__ */ T("div", {
|
|
227
233
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
228
|
-
children:
|
|
234
|
+
children: L.slice(0, 3).map((e) => /* @__PURE__ */ T(j, {
|
|
229
235
|
org: e,
|
|
230
|
-
onClick: () =>
|
|
236
|
+
onClick: () => B(`/organizations/${e.id}`)
|
|
231
237
|
}, e.id))
|
|
232
238
|
})
|
|
233
239
|
}) : null
|
|
234
240
|
]
|
|
235
241
|
})
|
|
236
242
|
});
|
|
237
|
-
},
|
|
243
|
+
}, A = ({ title: e, description: t, icon: n, onClick: r, primary: i }) => /* @__PURE__ */ E("button", {
|
|
238
244
|
onClick: r,
|
|
239
245
|
className: ["flex w-full items-center gap-4 rounded-card border p-4 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", i ? "border-primary bg-action-primary-bg/5 hover:bg-action-primary-bg/10" : "border-border-seam bg-bg-surface shadow-[var(--shadow-pop)] hover:bg-accent"].join(" "),
|
|
240
|
-
children: [/* @__PURE__ */
|
|
246
|
+
children: [/* @__PURE__ */ T("div", {
|
|
241
247
|
className: ["rounded-lg p-2", i ? "bg-action-primary-bg/20 text-primary" : "bg-action-primary-bg/10 text-primary"].join(" "),
|
|
242
248
|
children: n
|
|
243
|
-
}), /* @__PURE__ */
|
|
249
|
+
}), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("p", {
|
|
244
250
|
className: "font-medium",
|
|
245
251
|
children: e
|
|
246
|
-
}), /* @__PURE__ */
|
|
252
|
+
}), /* @__PURE__ */ T("p", {
|
|
247
253
|
className: "text-sm text-text-secondary",
|
|
248
254
|
children: t
|
|
249
255
|
})] })]
|
|
250
|
-
}),
|
|
256
|
+
}), j = ({ org: e, onClick: t }) => {
|
|
251
257
|
let { t: n } = d(), r = (e, t) => {
|
|
252
258
|
let r = n(e);
|
|
253
259
|
return r === e ? t : r;
|
|
254
260
|
};
|
|
255
|
-
return /* @__PURE__ */
|
|
261
|
+
return /* @__PURE__ */ E("button", {
|
|
256
262
|
onClick: t,
|
|
257
263
|
className: "w-full rounded-card border border-border-seam bg-bg-surface p-4 text-left shadow-[var(--shadow-pop)] transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
258
|
-
children: [/* @__PURE__ */
|
|
264
|
+
children: [/* @__PURE__ */ E("div", {
|
|
259
265
|
className: "flex items-center gap-3",
|
|
260
|
-
children: [e.logoUrl ? /* @__PURE__ */
|
|
266
|
+
children: [e.logoUrl ? /* @__PURE__ */ T("img", {
|
|
261
267
|
src: e.logoUrl,
|
|
262
268
|
alt: e.name,
|
|
263
269
|
className: "h-10 w-10 rounded-lg object-cover"
|
|
264
|
-
}) : /* @__PURE__ */
|
|
270
|
+
}) : /* @__PURE__ */ T("div", {
|
|
265
271
|
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
|
|
266
|
-
children: /* @__PURE__ */
|
|
267
|
-
}), /* @__PURE__ */
|
|
272
|
+
children: /* @__PURE__ */ T(h, { size: 20 })
|
|
273
|
+
}), /* @__PURE__ */ E("div", {
|
|
268
274
|
className: "flex-1 min-w-0",
|
|
269
|
-
children: [/* @__PURE__ */
|
|
275
|
+
children: [/* @__PURE__ */ T("p", {
|
|
270
276
|
className: "font-medium truncate",
|
|
271
277
|
children: e.name
|
|
272
|
-
}), /* @__PURE__ */
|
|
278
|
+
}), /* @__PURE__ */ E("p", {
|
|
273
279
|
className: "text-sm text-text-secondary",
|
|
274
280
|
children: [
|
|
275
281
|
e.publishedApps,
|
|
@@ -278,13 +284,13 @@ var D = () => {
|
|
|
278
284
|
]
|
|
279
285
|
})]
|
|
280
286
|
})]
|
|
281
|
-
}), /* @__PURE__ */
|
|
287
|
+
}), /* @__PURE__ */ E("div", {
|
|
282
288
|
className: "mt-3 flex gap-4 text-sm text-text-secondary",
|
|
283
|
-
children: [/* @__PURE__ */
|
|
289
|
+
children: [/* @__PURE__ */ E("span", { children: [
|
|
284
290
|
(e.totalInstalls || 0).toLocaleString(),
|
|
285
291
|
" ",
|
|
286
292
|
r("devportal.installs", "installs")
|
|
287
|
-
] }), /* @__PURE__ */
|
|
293
|
+
] }), /* @__PURE__ */ E("span", { children: [
|
|
288
294
|
"$",
|
|
289
295
|
(e.totalRevenue || 0).toLocaleString(),
|
|
290
296
|
" ",
|
|
@@ -294,6 +300,6 @@ var D = () => {
|
|
|
294
300
|
});
|
|
295
301
|
};
|
|
296
302
|
//#endregion
|
|
297
|
-
export {
|
|
303
|
+
export { k as DashboardPage };
|
|
298
304
|
|
|
299
305
|
//# 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 { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n AppWindow,\n Building2,\n Key,\n Webhook,\n TrendingUp,\n Users,\n DollarSign,\n Activity,\n Plus,\n ArrowRight,\n Loader2,\n Store,\n} from \"lucide-react\";\nimport { EmphasisPanel } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { AppListItem } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport type { Application, DeveloperOrganization } from \"../types\";\nimport {\n useGetDevportalDashboardBootstrapQuery,\n type OrganizationFieldsFragment,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\n/**\n * Developer Portal Dashboard Page\n *\n * Shows an overview of the developer's applications, organizations,\n * and key metrics using real data from the GraphQL API.\n */\nexport const DashboardPage: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Fetch dashboard bootstrap in a single global query\n const { data, loading } = useGetDevportalDashboardBootstrapQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const organizations: OrganizationFieldsFragment[] =\n data?.myDeveloperOrganizations ?? [];\n\n // First-time users: redirect to setup wizard when no org exists and they\n // haven't already dismissed the wizard in this browser.\n useEffect(() => {\n if (loading || !data) return;\n if (organizations.length > 0) return;\n try {\n if (localStorage.getItem(\"devportal_setup_dismissed\")) return;\n } catch {\n // localStorage unavailable — don't redirect\n return;\n }\n navigate?.(`${basePath}/setup`);\n }, [loading, data, organizations.length, navigate, basePath]);\n const applications: ApplicationFieldsFragment[] =\n data?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n // Calculate aggregate stats\n const stats = {\n totalApps: data?.applications?.totalCount ?? 0,\n totalOrgs: organizations.length,\n totalApiKeys: 0,\n totalWebhooks: 0,\n totalApiCalls: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + parseInt(org.totalApiCalls || \"0\"),\n 0,\n ),\n activeInstalls: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + (org.activeInstalls || 0),\n 0,\n ),\n monthlyRevenue: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + (org.totalRevenue || 0),\n 0,\n ),\n errorRate: 0,\n };\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const isLoading = loading;\n\n const hasOrgs = !isLoading && organizations.length > 0;\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n title={tr(\"devportal.developerPortal\", \"Developer Portal\")}\n description={tr(\n \"devportal.buildManageMonitor\",\n \"Build, manage, and monitor your applications\",\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 {/* Stats Overview — primary emphasis zone */}\n <EmphasisPanel data-tour=\"devportal-dashboard-stats-overview\">\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.totalApps\", \"Total Apps\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n stats.totalApps\n )\n }\n icon={<AppWindow size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.organizations\", \"Organizations\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n stats.totalOrgs\n )\n }\n icon={<Building2 size={20} />}\n />\n <StatCard\n title={tr(\"devportal.apiKeys\", \"API Keys\")}\n value={stats.totalApiKeys}\n icon={<Key size={20} />}\n description={tr(\"devportal.activeKeys\", \"Active keys\")}\n />\n <StatCard\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n value={stats.totalWebhooks}\n icon={<Webhook size={20} />}\n description={tr(\"devportal.configured\", \"Configured\")}\n />\n </StatCardGrid>\n </EmphasisPanel>\n\n {/* Usage Stats */}\n <PageSection\n title={tr(\"devportal.usageOverview\", \"Usage Overview\")}\n data-tour=\"devportal-dashboard-usage-overview\"\n >\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.apiCalls\", \"API Calls\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : stats.totalApiCalls > 1000000 ? (\n `${(stats.totalApiCalls / 1000000).toFixed(1)}M`\n ) : stats.totalApiCalls > 1000 ? (\n `${(stats.totalApiCalls / 1000).toFixed(1)}K`\n ) : (\n stats.totalApiCalls.toString()\n )\n }\n icon={<Activity size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.activeInstalls\", \"Active Installs\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : stats.activeInstalls > 1000 ? (\n `${(stats.activeInstalls / 1000).toFixed(1)}K`\n ) : (\n stats.activeInstalls.toString()\n )\n }\n icon={<Users size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.monthlyRevenue\", \"Monthly Revenue\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n `$${stats.monthlyRevenue.toLocaleString()}`\n )\n }\n icon={<DollarSign size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.errorRate\", \"Error Rate\")}\n value={`${stats.errorRate}%`}\n icon={<TrendingUp size={20} />}\n trend={undefined}\n />\n </StatCardGrid>\n </PageSection>\n\n {/* Quick Actions & Recent Items */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Recent Apps */}\n <PageSection\n data-tour=\"devportal-dashboard-recent-apps\"\n title={tr(\"devportal.recentApplications\", \"Recent Applications\")}\n actions={\n <button\n onClick={() => handleNavigate(\"/apps\")}\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n >\n {tr(\"devportal.viewAll\", \"View all\")} <ArrowRight size={14} />\n </button>\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : applications.length > 0 ? (\n <div className=\"space-y-2\">\n {applications\n .slice(0, 3)\n .map((app: ApplicationFieldsFragment) => (\n <AppListItem\n key={app.id}\n app={app as Application}\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<AppWindow size={24} />}\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 </PageSection>\n\n {/* Quick Actions */}\n <PageSection\n title={tr(\"devportal.quickActions\", \"Quick Actions\")}\n data-tour=\"devportal-dashboard-quick-actions\"\n >\n <div className=\"grid gap-3\">\n {!hasOrgs ? (\n <QuickActionCard\n title={tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n description={tr(\n \"devportal.setUpATeamToCollaborate\",\n \"Set up a team to collaborate\",\n )}\n icon={<Building2 size={20} />}\n onClick={() => handleNavigate(\"/organizations/new\")}\n primary\n />\n ) : (\n <QuickActionCard\n title={tr(\"devportal.createNewApp\", \"Create New App\")}\n description={tr(\n \"devportal.startBuildingANewApplication\",\n \"Start building a new application\",\n )}\n icon={<AppWindow size={20} />}\n onClick={() => handleNavigate(\"/apps/new\")}\n />\n )}\n {hasOrgs && (\n <QuickActionCard\n title={tr(\"devportal.publisherProfile\", \"Publisher Profile\")}\n description={tr(\n \"devportal.manageYourStorePresence\",\n \"Manage your store publisher presence\",\n )}\n icon={<Store size={20} />}\n onClick={() => handleNavigate(\"/publisher/profile\")}\n />\n )}\n <QuickActionCard\n title={tr(\"devportal.generateApiKey\", \"Generate API Key\")}\n description={tr(\n \"devportal.createCredentialsForYourApps\",\n \"Create credentials for your apps\",\n )}\n icon={<Key size={20} />}\n onClick={() => handleNavigate(\"/api-keys\")}\n />\n <QuickActionCard\n title={tr(\"devportal.configureWebhooks\", \"Configure Webhooks\")}\n description={tr(\n \"devportal.setUpEventNotifications\",\n \"Set up event notifications\",\n )}\n icon={<Webhook size={20} />}\n onClick={() => handleNavigate(\"/webhooks\")}\n />\n </div>\n </PageSection>\n </div>\n\n {/* Organizations */}\n {isLoading ? (\n <PageSection\n data-tour=\"devportal-dashboard-organizations\"\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n >\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n </PageSection>\n ) : organizations.length > 0 ? (\n <PageSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n actions={\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n >\n {tr(\"devportal.viewAll\", \"View all\")} <ArrowRight size={14} />\n </button>\n }\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={() => handleNavigate(`/organizations/${org.id}`)}\n />\n ))}\n </div>\n </PageSection>\n ) : null}\n </PageLayout>\n </PermissionGuard>\n );\n};\n\n// Quick action card component\nconst QuickActionCard: FC<{\n title: string;\n description: string;\n icon: React.ReactNode;\n onClick: () => void;\n primary?: boolean;\n}> = ({ title, description, icon, onClick, primary }) => (\n <button\n onClick={onClick}\n className={[\n \"flex w-full items-center gap-4 rounded-card border p-4 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n primary\n ? \"border-primary bg-action-primary-bg/5 hover:bg-action-primary-bg/10\"\n : \"border-border-seam bg-bg-surface shadow-[var(--shadow-pop)] hover:bg-accent\",\n ].join(\" \")}\n >\n <div\n className={[\n \"rounded-lg p-2\",\n primary ? \"bg-action-primary-bg/20 text-primary\" : \"bg-action-primary-bg/10 text-primary\",\n ].join(\" \")}\n >\n {icon}\n </div>\n <div>\n <p className=\"font-medium\">{title}</p>\n <p className=\"text-sm text-text-secondary\">{description}</p>\n </div>\n </button>\n);\n\n// Organization card component\nconst OrgCard: FC<{\n org: DeveloperOrganization;\n onClick: () => void;\n}> = ({ org, 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 return (\n <button\n onClick={onClick}\n className=\"w-full rounded-card border border-border-seam bg-bg-surface p-4 text-left shadow-[var(--shadow-pop)] transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n >\n <div className=\"flex items-center gap-3\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Building2 size={20} />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium truncate\">{org.name}</p>\n <p className=\"text-sm text-text-secondary\">\n {org.publishedApps}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n </div>\n <div className=\"mt-3 flex gap-4 text-sm text-text-secondary\">\n <span>\n {(org.totalInstalls || 0).toLocaleString()}{\" \"}\n {tr(\"devportal.installs\", \"installs\")}\n </span>\n <span>\n ${(org.totalRevenue || 0).toLocaleString()}{\" \"}\n {tr(\"devportal.revenue\", \"revenue\")}\n </span>\n </div>\n </button>\n );\n};\n"],"mappings":"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"}
|
|
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 { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n AppWindow,\n Building2,\n Key,\n Webhook,\n TrendingUp,\n Users,\n DollarSign,\n Activity,\n Plus,\n ArrowRight,\n Loader2,\n Store,\n} from \"lucide-react\";\nimport { EmphasisPanel, GradientText } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { AppListItem } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport type { Application, DeveloperOrganization } from \"../types\";\nimport {\n useGetDevportalDashboardBootstrapQuery,\n type OrganizationFieldsFragment,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\n/**\n * Developer Portal Dashboard Page\n *\n * Shows an overview of the developer's applications, organizations,\n * and key metrics using real data from the GraphQL API.\n */\nexport const DashboardPage: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Fetch dashboard bootstrap in a single global query\n const { data, loading } = useGetDevportalDashboardBootstrapQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const organizations: OrganizationFieldsFragment[] =\n data?.myDeveloperOrganizations ?? [];\n\n // First-time users: redirect to setup wizard when no org exists and they\n // haven't already dismissed the wizard in this browser.\n useEffect(() => {\n if (loading || !data) return;\n if (organizations.length > 0) return;\n try {\n if (localStorage.getItem(\"devportal_setup_dismissed\")) return;\n } catch {\n // localStorage unavailable — don't redirect\n return;\n }\n navigate?.(`${basePath}/setup`);\n }, [loading, data, organizations.length, navigate, basePath]);\n const applications: ApplicationFieldsFragment[] =\n data?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n // Calculate aggregate stats\n const stats = {\n totalApps: data?.applications?.totalCount ?? 0,\n totalOrgs: organizations.length,\n totalApiKeys: 0,\n totalWebhooks: 0,\n totalApiCalls: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + parseInt(org.totalApiCalls || \"0\"),\n 0,\n ),\n activeInstalls: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + (org.activeInstalls || 0),\n 0,\n ),\n monthlyRevenue: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + (org.totalRevenue || 0),\n 0,\n ),\n errorRate: 0,\n };\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const isLoading = loading;\n\n const hasOrgs = !isLoading && organizations.length > 0;\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n heroBackdrop\n title={\n <>\n {tr(\"devportal.developer\", \"Developer\")}{\" \"}\n <GradientText>{tr(\"devportal.portal\", \"Portal\")}</GradientText>\n </>\n }\n description={tr(\n \"devportal.buildManageMonitor\",\n \"Build, manage, and monitor your applications\",\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 {/* Stats Overview — primary emphasis zone */}\n <EmphasisPanel data-tour=\"devportal-dashboard-stats-overview\">\n <StatCardGrid columns={4}>\n <StatCard\n featured\n title={tr(\"devportal.totalApps\", \"Total Apps\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n stats.totalApps\n )\n }\n icon={<AppWindow size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.organizations\", \"Organizations\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n stats.totalOrgs\n )\n }\n icon={<Building2 size={20} />}\n />\n <StatCard\n title={tr(\"devportal.apiKeys\", \"API Keys\")}\n value={stats.totalApiKeys}\n icon={<Key size={20} />}\n description={tr(\"devportal.activeKeys\", \"Active keys\")}\n />\n <StatCard\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n value={stats.totalWebhooks}\n icon={<Webhook size={20} />}\n description={tr(\"devportal.configured\", \"Configured\")}\n />\n </StatCardGrid>\n </EmphasisPanel>\n\n {/* Usage Stats */}\n <PageSection\n title={tr(\"devportal.usageOverview\", \"Usage Overview\")}\n data-tour=\"devportal-dashboard-usage-overview\"\n >\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.apiCalls\", \"API Calls\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : stats.totalApiCalls > 1000000 ? (\n `${(stats.totalApiCalls / 1000000).toFixed(1)}M`\n ) : stats.totalApiCalls > 1000 ? (\n `${(stats.totalApiCalls / 1000).toFixed(1)}K`\n ) : (\n stats.totalApiCalls.toString()\n )\n }\n icon={<Activity size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.activeInstalls\", \"Active Installs\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : stats.activeInstalls > 1000 ? (\n `${(stats.activeInstalls / 1000).toFixed(1)}K`\n ) : (\n stats.activeInstalls.toString()\n )\n }\n icon={<Users size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.monthlyRevenue\", \"Monthly Revenue\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n `$${stats.monthlyRevenue.toLocaleString()}`\n )\n }\n icon={<DollarSign size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.errorRate\", \"Error Rate\")}\n value={`${stats.errorRate}%`}\n icon={<TrendingUp size={20} />}\n trend={undefined}\n />\n </StatCardGrid>\n </PageSection>\n\n {/* Quick Actions & Recent Items */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Recent Apps */}\n <PageSection\n data-tour=\"devportal-dashboard-recent-apps\"\n title={tr(\"devportal.recentApplications\", \"Recent Applications\")}\n actions={\n <button\n onClick={() => handleNavigate(\"/apps\")}\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n >\n {tr(\"devportal.viewAll\", \"View all\")} <ArrowRight size={14} />\n </button>\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n ) : applications.length > 0 ? (\n <div className=\"space-y-2\">\n {applications\n .slice(0, 3)\n .map((app: ApplicationFieldsFragment) => (\n <AppListItem\n key={app.id}\n app={app as Application}\n onClick={() => handleNavigate(`/apps/${app.id}`)}\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 </PageSection>\n\n {/* Quick Actions */}\n <PageSection\n title={tr(\"devportal.quickActions\", \"Quick Actions\")}\n data-tour=\"devportal-dashboard-quick-actions\"\n >\n <div className=\"grid gap-3\">\n {!hasOrgs ? (\n <QuickActionCard\n title={tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n description={tr(\n \"devportal.setUpATeamToCollaborate\",\n \"Set up a team to collaborate\",\n )}\n icon={<Building2 size={20} />}\n onClick={() => handleNavigate(\"/organizations/new\")}\n primary\n />\n ) : (\n <QuickActionCard\n title={tr(\"devportal.createNewApp\", \"Create New App\")}\n description={tr(\n \"devportal.startBuildingANewApplication\",\n \"Start building a new application\",\n )}\n icon={<AppWindow size={20} />}\n onClick={() => handleNavigate(\"/apps/new\")}\n />\n )}\n {hasOrgs && (\n <QuickActionCard\n title={tr(\"devportal.publisherProfile\", \"Publisher Profile\")}\n description={tr(\n \"devportal.manageYourStorePresence\",\n \"Manage your store publisher presence\",\n )}\n icon={<Store size={20} />}\n onClick={() => handleNavigate(\"/publisher/profile\")}\n />\n )}\n <QuickActionCard\n title={tr(\"devportal.generateApiKey\", \"Generate API Key\")}\n description={tr(\n \"devportal.createCredentialsForYourApps\",\n \"Create credentials for your apps\",\n )}\n icon={<Key size={20} />}\n onClick={() => handleNavigate(\"/api-keys\")}\n />\n <QuickActionCard\n title={tr(\"devportal.configureWebhooks\", \"Configure Webhooks\")}\n description={tr(\n \"devportal.setUpEventNotifications\",\n \"Set up event notifications\",\n )}\n icon={<Webhook size={20} />}\n onClick={() => handleNavigate(\"/webhooks\")}\n />\n </div>\n </PageSection>\n </div>\n\n {/* Organizations */}\n {isLoading ? (\n <PageSection\n data-tour=\"devportal-dashboard-organizations\"\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n >\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-text-secondary\" />\n </div>\n </PageSection>\n ) : organizations.length > 0 ? (\n <PageSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n actions={\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n >\n {tr(\"devportal.viewAll\", \"View all\")} <ArrowRight size={14} />\n </button>\n }\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={() => handleNavigate(`/organizations/${org.id}`)}\n />\n ))}\n </div>\n </PageSection>\n ) : null}\n </PageLayout>\n </PermissionGuard>\n );\n};\n\n// Quick action card component\nconst QuickActionCard: FC<{\n title: string;\n description: string;\n icon: React.ReactNode;\n onClick: () => void;\n primary?: boolean;\n}> = ({ title, description, icon, onClick, primary }) => (\n <button\n onClick={onClick}\n className={[\n \"flex w-full items-center gap-4 rounded-card border p-4 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n primary\n ? \"border-primary bg-action-primary-bg/5 hover:bg-action-primary-bg/10\"\n : \"border-border-seam bg-bg-surface shadow-[var(--shadow-pop)] hover:bg-accent\",\n ].join(\" \")}\n >\n <div\n className={[\n \"rounded-lg p-2\",\n primary ? \"bg-action-primary-bg/20 text-primary\" : \"bg-action-primary-bg/10 text-primary\",\n ].join(\" \")}\n >\n {icon}\n </div>\n <div>\n <p className=\"font-medium\">{title}</p>\n <p className=\"text-sm text-text-secondary\">{description}</p>\n </div>\n </button>\n);\n\n// Organization card component\nconst OrgCard: FC<{\n org: DeveloperOrganization;\n onClick: () => void;\n}> = ({ org, 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 return (\n <button\n onClick={onClick}\n className=\"w-full rounded-card border border-border-seam bg-bg-surface p-4 text-left shadow-[var(--shadow-pop)] transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n >\n <div className=\"flex items-center gap-3\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Building2 size={20} />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium truncate\">{org.name}</p>\n <p className=\"text-sm text-text-secondary\">\n {org.publishedApps}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n </div>\n <div className=\"mt-3 flex gap-4 text-sm text-text-secondary\">\n <span>\n {(org.totalInstalls || 0).toLocaleString()}{\" \"}\n {tr(\"devportal.installs\", \"installs\")}\n </span>\n <span>\n ${(org.totalRevenue || 0).toLocaleString()}{\" \"}\n {tr(\"devportal.revenue\", \"revenue\")}\n </span>\n </div>\n </button>\n );\n};\n"],"mappings":"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"}
|
|
@@ -533,7 +533,7 @@ var j = ({ type: t, className: n }) => {
|
|
|
533
533
|
"aria-labelledby": "delegations-tab-acting-as-me",
|
|
534
534
|
tabIndex: 0,
|
|
535
535
|
children: V.length === 0 && H.length === 0 ? /* @__PURE__ */ T(n, {
|
|
536
|
-
|
|
536
|
+
illustration: "empty-people",
|
|
537
537
|
title: _("devportal.noActiveDelegations", "No Active Delegations"),
|
|
538
538
|
description: _("devportal.noAppsAuthorizedOnYourBehalf", "No apps are currently authorized to act on your behalf.")
|
|
539
539
|
}) : /* @__PURE__ */ E(ne, { children: [V.length === 0 ? /* @__PURE__ */ T(n, {
|
|
@@ -559,7 +559,7 @@ var j = ({ type: t, className: n }) => {
|
|
|
559
559
|
"aria-labelledby": "delegations-tab-granted-by-me",
|
|
560
560
|
tabIndex: 0,
|
|
561
561
|
children: B.length === 0 ? /* @__PURE__ */ T(n, {
|
|
562
|
-
|
|
562
|
+
illustration: "empty-people",
|
|
563
563
|
title: _("devportal.noDelegationsGranted", "No Delegations Granted"),
|
|
564
564
|
description: _("devportal.noGrantedDelegationsDescription", "You haven't granted any app delegations to other users.")
|
|
565
565
|
}) : B.map((e) => /* @__PURE__ */ T(se, {
|