@burdenoff/microfe-devportal 2026.611.2 → 2026.614.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/components/SectionNav.js +105 -41
- package/dist/devportal/components/SectionNav.js.map +1 -1
- package/dist/devportal/hooks/useGraphQL.js +1 -1
- package/dist/devportal/pages/AdminReviewPage.js +36 -36
- package/dist/devportal/pages/AnalyticsDashboardPage.js +150 -150
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +184 -172
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +9 -9
- package/dist/devportal/pages/ApplicationDetailPage.js +414 -414
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationVersionsPage.js +44 -44
- package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +118 -118
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +20 -20
- package/dist/devportal/pages/CreateApplicationPage.js +32 -32
- package/dist/devportal/pages/CreateOAuthClientPage.js +20 -20
- package/dist/devportal/pages/CreateOrganizationPage.js +125 -442
- package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
- package/dist/devportal/pages/CreateWebhookPage.js +25 -25
- package/dist/devportal/pages/DashboardPage.js +160 -145
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +22 -22
- package/dist/devportal/pages/EditApplicationPage.js +283 -283
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/ListingPreviewPage.js +9 -9
- package/dist/devportal/pages/OAuthClientDetailPage.js +17 -17
- package/dist/devportal/pages/OAuthClientsPage.js +16 -16
- package/dist/devportal/pages/OrganizationDetailPage.js +78 -78
- package/dist/devportal/pages/OrganizationMembersPage.js +260 -260
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationsPage.js +18 -18
- package/dist/devportal/pages/PublishPage.js +16 -16
- package/dist/devportal/pages/PublisherProfilePage.js +156 -146
- package/dist/devportal/pages/PublisherProfilePage.js.map +1 -1
- package/dist/devportal/pages/RevenueDashboardPage.js +512 -362
- package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -1
- package/dist/devportal/pages/SettingsPage.js +17 -17
- package/dist/devportal/pages/SetupPage.js +99 -99
- package/dist/devportal/pages/SetupPage.js.map +1 -1
- package/dist/devportal/pages/WebhookDetailPage.js +143 -143
- package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
- package/dist/devportal/pages/WebhooksPage.js +140 -140
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/providers/DevportalProvider.js +46 -33
- package/dist/devportal/providers/DevportalProvider.js.map +1 -1
- package/dist/generated/global-operations.js +69 -55
- package/dist/generated/global-operations.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,259 +1,274 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
2
|
import { EmptyState as t } from "../components/EmptyState.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { useGetDevportalDashboardBootstrapQuery as n } from "../../generated/global-operations.js";
|
|
4
|
+
import { PageLayout as r, PageSection as i } from "../components/PageLayout.js";
|
|
5
|
+
import { StatCard as a, StatCardGrid as o } from "../components/StatCard.js";
|
|
6
|
+
import { AppListItem as s } from "../components/AppCard.js";
|
|
7
7
|
import { PermissionGuard as c } from "../components/PermissionGuard.js";
|
|
8
8
|
import "../components/index.js";
|
|
9
9
|
import { DEVPORTAL_PERMISSIONS as l } from "../constants/permissions.js";
|
|
10
10
|
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
|
-
import { Activity as f, AppWindow as p, ArrowRight as m, Building2 as h, DollarSign as g, Key as _, Loader2 as v, Plus as y,
|
|
14
|
-
import { jsx as
|
|
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 { jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
15
15
|
//#region src/devportal/pages/DashboardPage.tsx
|
|
16
|
-
var
|
|
17
|
-
let { navigate:
|
|
18
|
-
let n =
|
|
16
|
+
var E = () => {
|
|
17
|
+
let { navigate: E, basePath: k } = e(), { t: A } = d(), j = (e, t) => {
|
|
18
|
+
let n = A(e);
|
|
19
19
|
return n === e ? t : n;
|
|
20
|
-
}, { data:
|
|
20
|
+
}, { data: M, loading: N } = n({ fetchPolicy: "cache-and-network" }), P = M?.myDeveloperOrganizations ?? [];
|
|
21
21
|
u(() => {
|
|
22
|
-
if (!(
|
|
22
|
+
if (!(N || !M) && !(P.length > 0)) {
|
|
23
23
|
try {
|
|
24
24
|
if (localStorage.getItem("devportal_setup_dismissed")) return;
|
|
25
25
|
} catch {
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
E?.(`${k}/setup`);
|
|
29
29
|
}
|
|
30
30
|
}, [
|
|
31
|
+
N,
|
|
31
32
|
M,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
O
|
|
33
|
+
P.length,
|
|
34
|
+
E,
|
|
35
|
+
k
|
|
36
36
|
]);
|
|
37
|
-
let
|
|
38
|
-
totalApps:
|
|
39
|
-
totalOrgs:
|
|
37
|
+
let F = M?.applications?.edges?.map((e) => e.node) ?? [], I = {
|
|
38
|
+
totalApps: M?.applications?.totalCount ?? 0,
|
|
39
|
+
totalOrgs: P.length,
|
|
40
40
|
totalApiKeys: 0,
|
|
41
41
|
totalWebhooks: 0,
|
|
42
|
-
totalApiCalls:
|
|
43
|
-
activeInstalls:
|
|
44
|
-
monthlyRevenue:
|
|
42
|
+
totalApiCalls: P.reduce((e, t) => e + parseInt(t.totalApiCalls || "0"), 0),
|
|
43
|
+
activeInstalls: P.reduce((e, t) => e + (t.activeInstalls || 0), 0),
|
|
44
|
+
monthlyRevenue: P.reduce((e, t) => e + (t.totalRevenue || 0), 0),
|
|
45
45
|
errorRate: 0
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
},
|
|
49
|
-
return /* @__PURE__ */
|
|
46
|
+
}, L = (e) => {
|
|
47
|
+
E?.(`${k}${e}`);
|
|
48
|
+
}, R = N, z = !R && P.length > 0;
|
|
49
|
+
return /* @__PURE__ */ w(c, {
|
|
50
50
|
permission: l.APP_READ,
|
|
51
|
-
children: /* @__PURE__ */
|
|
52
|
-
title:
|
|
53
|
-
description:
|
|
54
|
-
actions: /* @__PURE__ */
|
|
51
|
+
children: /* @__PURE__ */ T(r, {
|
|
52
|
+
title: j("devportal.developerPortal", "Developer Portal"),
|
|
53
|
+
description: j("devportal.buildManageMonitor", "Build, manage, and monitor your applications"),
|
|
54
|
+
actions: z ? /* @__PURE__ */ T("button", {
|
|
55
55
|
"data-tour": "devportal-dashboard-create-app-button",
|
|
56
|
-
onClick: () =>
|
|
56
|
+
onClick: () => L("/apps/new"),
|
|
57
57
|
className: "inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
58
|
-
children: [/* @__PURE__ */
|
|
58
|
+
children: [/* @__PURE__ */ w(y, { size: 16 }), j("devportal.createApp", "Create App")]
|
|
59
|
+
}) : R ? null : /* @__PURE__ */ T("button", {
|
|
60
|
+
"data-tour": "devportal-dashboard-create-org-button",
|
|
61
|
+
onClick: () => L("/organizations/new"),
|
|
62
|
+
className: "inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
63
|
+
children: [/* @__PURE__ */ w(y, { size: 16 }), j("devportal.createOrganization", "Create Organization")]
|
|
59
64
|
}),
|
|
60
65
|
children: [
|
|
61
|
-
/* @__PURE__ */
|
|
66
|
+
/* @__PURE__ */ w(i, {
|
|
62
67
|
"data-tour": "devportal-dashboard-stats-overview",
|
|
63
|
-
children: /* @__PURE__ */
|
|
68
|
+
children: /* @__PURE__ */ T(o, {
|
|
64
69
|
columns: 4,
|
|
65
70
|
children: [
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
title:
|
|
68
|
-
value:
|
|
69
|
-
icon: /* @__PURE__ */
|
|
71
|
+
/* @__PURE__ */ w(a, {
|
|
72
|
+
title: j("devportal.totalApps", "Total Apps"),
|
|
73
|
+
value: R ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : I.totalApps,
|
|
74
|
+
icon: /* @__PURE__ */ w(p, { size: 20 }),
|
|
70
75
|
trend: void 0
|
|
71
76
|
}),
|
|
72
|
-
/* @__PURE__ */
|
|
73
|
-
title:
|
|
74
|
-
value:
|
|
75
|
-
icon: /* @__PURE__ */
|
|
77
|
+
/* @__PURE__ */ w(a, {
|
|
78
|
+
title: j("devportal.organizations", "Organizations"),
|
|
79
|
+
value: R ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : I.totalOrgs,
|
|
80
|
+
icon: /* @__PURE__ */ w(h, { size: 20 })
|
|
76
81
|
}),
|
|
77
|
-
/* @__PURE__ */
|
|
78
|
-
title:
|
|
79
|
-
value:
|
|
80
|
-
icon: /* @__PURE__ */
|
|
81
|
-
description:
|
|
82
|
+
/* @__PURE__ */ w(a, {
|
|
83
|
+
title: j("devportal.apiKeys", "API Keys"),
|
|
84
|
+
value: I.totalApiKeys,
|
|
85
|
+
icon: /* @__PURE__ */ w(_, { size: 20 }),
|
|
86
|
+
description: j("devportal.activeKeys", "Active keys")
|
|
82
87
|
}),
|
|
83
|
-
/* @__PURE__ */
|
|
84
|
-
title:
|
|
85
|
-
value:
|
|
86
|
-
icon: /* @__PURE__ */ C
|
|
87
|
-
description:
|
|
88
|
+
/* @__PURE__ */ w(a, {
|
|
89
|
+
title: j("devportal.webhooks", "Webhooks"),
|
|
90
|
+
value: I.totalWebhooks,
|
|
91
|
+
icon: /* @__PURE__ */ w(C, { size: 20 }),
|
|
92
|
+
description: j("devportal.configured", "Configured")
|
|
88
93
|
})
|
|
89
94
|
]
|
|
90
95
|
})
|
|
91
96
|
}),
|
|
92
|
-
/* @__PURE__ */
|
|
93
|
-
title:
|
|
97
|
+
/* @__PURE__ */ w(i, {
|
|
98
|
+
title: j("devportal.usageOverview", "Usage Overview"),
|
|
94
99
|
"data-tour": "devportal-dashboard-usage-overview",
|
|
95
|
-
children: /* @__PURE__ */
|
|
100
|
+
children: /* @__PURE__ */ T(o, {
|
|
96
101
|
columns: 4,
|
|
97
102
|
children: [
|
|
98
|
-
/* @__PURE__ */
|
|
99
|
-
title:
|
|
100
|
-
value:
|
|
101
|
-
icon: /* @__PURE__ */
|
|
103
|
+
/* @__PURE__ */ w(a, {
|
|
104
|
+
title: j("devportal.apiCalls", "API Calls"),
|
|
105
|
+
value: R ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : I.totalApiCalls > 1e6 ? `${(I.totalApiCalls / 1e6).toFixed(1)}M` : I.totalApiCalls > 1e3 ? `${(I.totalApiCalls / 1e3).toFixed(1)}K` : I.totalApiCalls.toString(),
|
|
106
|
+
icon: /* @__PURE__ */ w(f, { size: 20 }),
|
|
102
107
|
trend: void 0
|
|
103
108
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
-
title:
|
|
106
|
-
value:
|
|
107
|
-
icon: /* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ w(a, {
|
|
110
|
+
title: j("devportal.activeInstalls", "Active Installs"),
|
|
111
|
+
value: R ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : I.activeInstalls > 1e3 ? `${(I.activeInstalls / 1e3).toFixed(1)}K` : I.activeInstalls.toString(),
|
|
112
|
+
icon: /* @__PURE__ */ w(S, { size: 20 }),
|
|
108
113
|
trend: void 0
|
|
109
114
|
}),
|
|
110
|
-
/* @__PURE__ */
|
|
111
|
-
title:
|
|
112
|
-
value:
|
|
113
|
-
icon: /* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ w(a, {
|
|
116
|
+
title: j("devportal.monthlyRevenue", "Monthly Revenue"),
|
|
117
|
+
value: R ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : `$${I.monthlyRevenue.toLocaleString()}`,
|
|
118
|
+
icon: /* @__PURE__ */ w(g, { size: 20 }),
|
|
114
119
|
trend: void 0
|
|
115
120
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
-
title:
|
|
118
|
-
value: `${
|
|
119
|
-
icon: /* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ w(a, {
|
|
122
|
+
title: j("devportal.errorRate", "Error Rate"),
|
|
123
|
+
value: `${I.errorRate}%`,
|
|
124
|
+
icon: /* @__PURE__ */ w(x, { size: 20 }),
|
|
120
125
|
trend: void 0
|
|
121
126
|
})
|
|
122
127
|
]
|
|
123
128
|
})
|
|
124
129
|
}),
|
|
125
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ T("div", {
|
|
126
131
|
className: "grid gap-6 lg:grid-cols-2",
|
|
127
|
-
children: [/* @__PURE__ */
|
|
132
|
+
children: [/* @__PURE__ */ w(i, {
|
|
128
133
|
"data-tour": "devportal-dashboard-recent-apps",
|
|
129
|
-
title:
|
|
130
|
-
actions: /* @__PURE__ */
|
|
131
|
-
onClick: () =>
|
|
134
|
+
title: j("devportal.recentApplications", "Recent Applications"),
|
|
135
|
+
actions: /* @__PURE__ */ T("button", {
|
|
136
|
+
onClick: () => L("/apps"),
|
|
132
137
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
133
138
|
children: [
|
|
134
|
-
|
|
139
|
+
j("devportal.viewAll", "View all"),
|
|
135
140
|
" ",
|
|
136
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ w(m, { size: 14 })
|
|
137
142
|
]
|
|
138
143
|
}),
|
|
139
|
-
children:
|
|
144
|
+
children: R ? /* @__PURE__ */ w("div", {
|
|
140
145
|
className: "flex items-center justify-center py-8",
|
|
141
|
-
children: /* @__PURE__ */
|
|
142
|
-
}) :
|
|
146
|
+
children: /* @__PURE__ */ w(v, { className: "h-6 w-6 animate-spin text-muted-foreground" })
|
|
147
|
+
}) : F.length > 0 ? /* @__PURE__ */ w("div", {
|
|
143
148
|
className: "space-y-2",
|
|
144
|
-
children:
|
|
149
|
+
children: F.slice(0, 3).map((e) => /* @__PURE__ */ w(s, {
|
|
145
150
|
app: e,
|
|
146
|
-
onClick: () =>
|
|
151
|
+
onClick: () => L(`/apps/${e.id}`)
|
|
147
152
|
}, e.id))
|
|
148
|
-
}) : /* @__PURE__ */
|
|
149
|
-
icon: /* @__PURE__ */
|
|
150
|
-
title:
|
|
151
|
-
description:
|
|
152
|
-
action: /* @__PURE__ */
|
|
153
|
-
onClick: () =>
|
|
153
|
+
}) : /* @__PURE__ */ w(t, {
|
|
154
|
+
icon: /* @__PURE__ */ w(p, { size: 24 }),
|
|
155
|
+
title: j("devportal.noApplicationsYet", "No applications yet"),
|
|
156
|
+
description: z ? j("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : j("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
|
|
157
|
+
action: z ? /* @__PURE__ */ T("button", {
|
|
158
|
+
onClick: () => L("/apps/new"),
|
|
159
|
+
className: "inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90",
|
|
160
|
+
children: [/* @__PURE__ */ w(y, { size: 16 }), j("devportal.createApp", "Create App")]
|
|
161
|
+
}) : /* @__PURE__ */ T("button", {
|
|
162
|
+
onClick: () => L("/organizations/new"),
|
|
154
163
|
className: "inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90",
|
|
155
|
-
children: [/* @__PURE__ */
|
|
164
|
+
children: [/* @__PURE__ */ w(y, { size: 16 }), j("devportal.createOrganization", "Create Organization")]
|
|
156
165
|
})
|
|
157
166
|
})
|
|
158
|
-
}), /* @__PURE__ */
|
|
159
|
-
title:
|
|
167
|
+
}), /* @__PURE__ */ w(i, {
|
|
168
|
+
title: j("devportal.quickActions", "Quick Actions"),
|
|
160
169
|
"data-tour": "devportal-dashboard-quick-actions",
|
|
161
|
-
children: /* @__PURE__ */
|
|
170
|
+
children: /* @__PURE__ */ T("div", {
|
|
162
171
|
className: "grid gap-3",
|
|
163
172
|
children: [
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
-
title:
|
|
166
|
-
description:
|
|
167
|
-
icon: /* @__PURE__ */
|
|
168
|
-
onClick: () =>
|
|
173
|
+
z ? /* @__PURE__ */ w(D, {
|
|
174
|
+
title: j("devportal.createNewApp", "Create New App"),
|
|
175
|
+
description: j("devportal.startBuildingANewApplication", "Start building a new application"),
|
|
176
|
+
icon: /* @__PURE__ */ w(p, { size: 20 }),
|
|
177
|
+
onClick: () => L("/apps/new")
|
|
178
|
+
}) : /* @__PURE__ */ w(D, {
|
|
179
|
+
title: j("devportal.createOrganization", "Create Organization"),
|
|
180
|
+
description: j("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
|
|
181
|
+
icon: /* @__PURE__ */ w(h, { size: 20 }),
|
|
182
|
+
onClick: () => L("/organizations/new"),
|
|
183
|
+
primary: !0
|
|
169
184
|
}),
|
|
170
|
-
/* @__PURE__ */
|
|
171
|
-
title:
|
|
172
|
-
description:
|
|
173
|
-
icon: /* @__PURE__ */
|
|
174
|
-
onClick: () =>
|
|
185
|
+
z && /* @__PURE__ */ w(D, {
|
|
186
|
+
title: j("devportal.publisherProfile", "Publisher Profile"),
|
|
187
|
+
description: j("devportal.manageYourStorePresence", "Manage your store publisher presence"),
|
|
188
|
+
icon: /* @__PURE__ */ w(b, { size: 20 }),
|
|
189
|
+
onClick: () => L("/publisher/profile")
|
|
175
190
|
}),
|
|
176
|
-
/* @__PURE__ */
|
|
177
|
-
title:
|
|
178
|
-
description:
|
|
179
|
-
icon: /* @__PURE__ */
|
|
180
|
-
onClick: () =>
|
|
191
|
+
/* @__PURE__ */ w(D, {
|
|
192
|
+
title: j("devportal.generateApiKey", "Generate API Key"),
|
|
193
|
+
description: j("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
|
|
194
|
+
icon: /* @__PURE__ */ w(_, { size: 20 }),
|
|
195
|
+
onClick: () => L("/api-keys")
|
|
181
196
|
}),
|
|
182
|
-
/* @__PURE__ */
|
|
183
|
-
title:
|
|
184
|
-
description:
|
|
185
|
-
icon: /* @__PURE__ */ C
|
|
186
|
-
onClick: () =>
|
|
197
|
+
/* @__PURE__ */ w(D, {
|
|
198
|
+
title: j("devportal.configureWebhooks", "Configure Webhooks"),
|
|
199
|
+
description: j("devportal.setUpEventNotifications", "Set up event notifications"),
|
|
200
|
+
icon: /* @__PURE__ */ w(C, { size: 20 }),
|
|
201
|
+
onClick: () => L("/webhooks")
|
|
187
202
|
})
|
|
188
203
|
]
|
|
189
204
|
})
|
|
190
205
|
})]
|
|
191
206
|
}),
|
|
192
|
-
|
|
207
|
+
R ? /* @__PURE__ */ w(i, {
|
|
193
208
|
"data-tour": "devportal-dashboard-organizations",
|
|
194
|
-
title:
|
|
195
|
-
children: /* @__PURE__ */
|
|
209
|
+
title: j("devportal.yourOrganizations", "Your Organizations"),
|
|
210
|
+
children: /* @__PURE__ */ w("div", {
|
|
196
211
|
className: "flex items-center justify-center py-8",
|
|
197
|
-
children: /* @__PURE__ */
|
|
212
|
+
children: /* @__PURE__ */ w(v, { className: "h-6 w-6 animate-spin text-muted-foreground" })
|
|
198
213
|
})
|
|
199
|
-
}) :
|
|
200
|
-
title:
|
|
201
|
-
actions: /* @__PURE__ */
|
|
202
|
-
onClick: () =>
|
|
214
|
+
}) : P.length > 0 ? /* @__PURE__ */ w(i, {
|
|
215
|
+
title: j("devportal.yourOrganizations", "Your Organizations"),
|
|
216
|
+
actions: /* @__PURE__ */ T("button", {
|
|
217
|
+
onClick: () => L("/organizations"),
|
|
203
218
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
204
219
|
children: [
|
|
205
|
-
|
|
220
|
+
j("devportal.viewAll", "View all"),
|
|
206
221
|
" ",
|
|
207
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ w(m, { size: 14 })
|
|
208
223
|
]
|
|
209
224
|
}),
|
|
210
|
-
children: /* @__PURE__ */
|
|
225
|
+
children: /* @__PURE__ */ w("div", {
|
|
211
226
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
212
|
-
children:
|
|
227
|
+
children: P.slice(0, 3).map((e) => /* @__PURE__ */ w(O, {
|
|
213
228
|
org: e,
|
|
214
|
-
onClick: () =>
|
|
229
|
+
onClick: () => L(`/organizations/${e.id}`)
|
|
215
230
|
}, e.id))
|
|
216
231
|
})
|
|
217
232
|
}) : null
|
|
218
233
|
]
|
|
219
234
|
})
|
|
220
235
|
});
|
|
221
|
-
},
|
|
236
|
+
}, D = ({ title: e, description: t, icon: n, onClick: r, primary: i }) => /* @__PURE__ */ T("button", {
|
|
222
237
|
onClick: r,
|
|
223
|
-
className: "flex items-center gap-4 rounded-lg border
|
|
224
|
-
children: [/* @__PURE__ */
|
|
225
|
-
className: "rounded-lg bg-primary/
|
|
238
|
+
className: ["flex items-center gap-4 rounded-lg border p-4 text-left transition-colors", i ? "border-primary bg-primary/5 hover:bg-primary/10" : "bg-card hover:bg-accent"].join(" "),
|
|
239
|
+
children: [/* @__PURE__ */ w("div", {
|
|
240
|
+
className: ["rounded-lg p-2", i ? "bg-primary/20 text-primary" : "bg-primary/10 text-primary"].join(" "),
|
|
226
241
|
children: n
|
|
227
|
-
}), /* @__PURE__ */
|
|
242
|
+
}), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("p", {
|
|
228
243
|
className: "font-medium",
|
|
229
244
|
children: e
|
|
230
|
-
}), /* @__PURE__ */
|
|
245
|
+
}), /* @__PURE__ */ w("p", {
|
|
231
246
|
className: "text-sm text-muted-foreground",
|
|
232
247
|
children: t
|
|
233
248
|
})] })]
|
|
234
|
-
}),
|
|
249
|
+
}), O = ({ org: e, onClick: t }) => {
|
|
235
250
|
let { t: n } = d(), r = (e, t) => {
|
|
236
251
|
let r = n(e);
|
|
237
252
|
return r === e ? t : r;
|
|
238
253
|
};
|
|
239
|
-
return /* @__PURE__ */
|
|
254
|
+
return /* @__PURE__ */ T("button", {
|
|
240
255
|
onClick: t,
|
|
241
256
|
className: "rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors",
|
|
242
|
-
children: [/* @__PURE__ */
|
|
257
|
+
children: [/* @__PURE__ */ T("div", {
|
|
243
258
|
className: "flex items-center gap-3",
|
|
244
|
-
children: [e.logoUrl ? /* @__PURE__ */
|
|
259
|
+
children: [e.logoUrl ? /* @__PURE__ */ w("img", {
|
|
245
260
|
src: e.logoUrl,
|
|
246
261
|
alt: e.name,
|
|
247
262
|
className: "h-10 w-10 rounded-lg object-cover"
|
|
248
|
-
}) : /* @__PURE__ */
|
|
263
|
+
}) : /* @__PURE__ */ w("div", {
|
|
249
264
|
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary",
|
|
250
|
-
children: /* @__PURE__ */
|
|
251
|
-
}), /* @__PURE__ */
|
|
265
|
+
children: /* @__PURE__ */ w(h, { size: 20 })
|
|
266
|
+
}), /* @__PURE__ */ T("div", {
|
|
252
267
|
className: "flex-1 min-w-0",
|
|
253
|
-
children: [/* @__PURE__ */
|
|
268
|
+
children: [/* @__PURE__ */ w("p", {
|
|
254
269
|
className: "font-medium truncate",
|
|
255
270
|
children: e.name
|
|
256
|
-
}), /* @__PURE__ */
|
|
271
|
+
}), /* @__PURE__ */ T("p", {
|
|
257
272
|
className: "text-sm text-muted-foreground",
|
|
258
273
|
children: [
|
|
259
274
|
e.publishedApps,
|
|
@@ -262,13 +277,13 @@ var T = () => {
|
|
|
262
277
|
]
|
|
263
278
|
})]
|
|
264
279
|
})]
|
|
265
|
-
}), /* @__PURE__ */
|
|
280
|
+
}), /* @__PURE__ */ T("div", {
|
|
266
281
|
className: "mt-3 flex gap-4 text-sm text-muted-foreground",
|
|
267
|
-
children: [/* @__PURE__ */
|
|
282
|
+
children: [/* @__PURE__ */ T("span", { children: [
|
|
268
283
|
(e.totalInstalls || 0).toLocaleString(),
|
|
269
284
|
" ",
|
|
270
285
|
r("devportal.installs", "installs")
|
|
271
|
-
] }), /* @__PURE__ */
|
|
286
|
+
] }), /* @__PURE__ */ T("span", { children: [
|
|
272
287
|
"$",
|
|
273
288
|
(e.totalRevenue || 0).toLocaleString(),
|
|
274
289
|
" ",
|
|
@@ -278,6 +293,6 @@ var T = () => {
|
|
|
278
293
|
});
|
|
279
294
|
};
|
|
280
295
|
//#endregion
|
|
281
|
-
export {
|
|
296
|
+
export { E as DashboardPage };
|
|
282
297
|
|
|
283
298
|
//# 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} from \"lucide-react\";\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 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 <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-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n }\n >\n {/* Stats Overview */}\n <PageSection 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 </PageSection>\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-muted-foreground\" />\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={tr(\n \"devportal.createFirstApplicationToGetStarted\",\n \"Create your first application to get started\",\n )}\n action={\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\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 <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 <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 />\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-muted-foreground\" />\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}> = ({ title, description, icon, onClick }) => (\n <button\n onClick={onClick}\n className=\"flex items-center gap-4 rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors\"\n >\n <div className=\"rounded-lg bg-primary/10 p-2 text-primary\">{icon}</div>\n <div>\n <p className=\"font-medium\">{title}</p>\n <p className=\"text-sm text-muted-foreground\">{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=\"rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors\"\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-primary/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-muted-foreground\">\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-muted-foreground\">\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 { 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-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/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-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createOrganization\", \"Create Organization\")}\n </button>\n ) : null\n }\n >\n {/* Stats Overview */}\n <PageSection 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 </PageSection>\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-muted-foreground\" />\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-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/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-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/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-muted-foreground\" />\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 items-center gap-4 rounded-lg border p-4 text-left transition-colors\",\n primary\n ? \"border-primary bg-primary/5 hover:bg-primary/10\"\n : \"bg-card hover:bg-accent\",\n ].join(\" \")}\n >\n <div\n className={[\n \"rounded-lg p-2\",\n primary ? \"bg-primary/20 text-primary\" : \"bg-primary/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-muted-foreground\">{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=\"rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors\"\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-primary/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-muted-foreground\">\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-muted-foreground\">\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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"}
|