@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.
Files changed (28) hide show
  1. package/dist/devportal/components/EmptyState.js +14 -8
  2. package/dist/devportal/components/EmptyState.js.map +1 -1
  3. package/dist/devportal/components/LoadingState.js.map +1 -1
  4. package/dist/devportal/components/PageLayout.js +39 -34
  5. package/dist/devportal/components/PageLayout.js.map +1 -1
  6. package/dist/devportal/components/StatCard.js +4 -3
  7. package/dist/devportal/components/StatCard.js.map +1 -1
  8. package/dist/devportal/components/StatusBadge.js +1 -1
  9. package/dist/devportal/components/StatusBadge.js.map +1 -1
  10. package/dist/devportal/pages/ApiKeysPage.js +1 -1
  11. package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
  12. package/dist/devportal/pages/AppLogsPage.js +85 -85
  13. package/dist/devportal/pages/AppLogsPage.js.map +1 -1
  14. package/dist/devportal/pages/ApplicationVersionsPage.js +7 -7
  15. package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
  16. package/dist/devportal/pages/DashboardPage.js +157 -151
  17. package/dist/devportal/pages/DashboardPage.js.map +1 -1
  18. package/dist/devportal/pages/DelegationsPage.js +2 -2
  19. package/dist/devportal/pages/DelegationsPage.js.map +1 -1
  20. package/dist/devportal/pages/OAuthClientsPage.js +1 -1
  21. package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
  22. package/dist/devportal/pages/OrganizationMembersPage.js +225 -225
  23. package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
  24. package/dist/devportal/pages/OrganizationsPage.js +25 -25
  25. package/dist/devportal/pages/OrganizationsPage.js.map +1 -1
  26. package/dist/devportal/pages/WebhooksPage.js +1 -1
  27. package/dist/devportal/pages/WebhooksPage.js.map +1 -1
  28. 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 { jsx as w, jsxs as T } from "react/jsx-runtime";
15
- import { EmphasisPanel as E } from "@burdenoff/fe-libs/ui";
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 D = () => {
18
- let { navigate: D, basePath: A } = e(), { t: j } = d(), M = (e, t) => {
19
- let n = j(e);
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: N, loading: P } = n({ fetchPolicy: "cache-and-network" }), F = N?.myDeveloperOrganizations ?? [];
21
+ }, { data: F, loading: I } = n({ fetchPolicy: "cache-and-network" }), L = F?.myDeveloperOrganizations ?? [];
22
22
  u(() => {
23
- if (!(P || !N) && !(F.length > 0)) {
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
- D?.(`${A}/setup`);
29
+ k?.(`${M}/setup`);
30
30
  }
31
31
  }, [
32
- P,
33
- N,
34
- F.length,
35
- D,
36
- A
32
+ I,
33
+ F,
34
+ L.length,
35
+ k,
36
+ M
37
37
  ]);
38
- let I = N?.applications?.edges?.map((e) => e.node) ?? [], L = {
39
- totalApps: N?.applications?.totalCount ?? 0,
40
- totalOrgs: F.length,
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: F.reduce((e, t) => e + parseInt(t.totalApiCalls || "0"), 0),
44
- activeInstalls: F.reduce((e, t) => e + (t.activeInstalls || 0), 0),
45
- monthlyRevenue: F.reduce((e, t) => e + (t.totalRevenue || 0), 0),
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
- }, R = (e) => {
48
- D?.(`${A}${e}`);
49
- }, z = P, B = !z && F.length > 0;
50
- return /* @__PURE__ */ w(c, {
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__ */ T(r, {
53
- title: M("devportal.developerPortal", "Developer Portal"),
54
- description: M("devportal.buildManageMonitor", "Build, manage, and monitor your applications"),
55
- actions: B ? /* @__PURE__ */ T("button", {
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: () => R("/apps/new"),
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__ */ w(y, { size: 16 }), M("devportal.createApp", "Create App")]
60
- }) : z ? null : /* @__PURE__ */ T("button", {
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: () => R("/organizations/new"),
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__ */ w(y, { size: 16 }), M("devportal.createOrganization", "Create Organization")]
69
+ children: [/* @__PURE__ */ T(y, { size: 16 }), P("devportal.createOrganization", "Create Organization")]
65
70
  }),
66
71
  children: [
67
- /* @__PURE__ */ w(E, {
72
+ /* @__PURE__ */ T(D, {
68
73
  "data-tour": "devportal-dashboard-stats-overview",
69
- children: /* @__PURE__ */ T(o, {
74
+ children: /* @__PURE__ */ E(o, {
70
75
  columns: 4,
71
76
  children: [
72
- /* @__PURE__ */ w(a, {
73
- title: M("devportal.totalApps", "Total Apps"),
74
- value: z ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : L.totalApps,
75
- icon: /* @__PURE__ */ w(p, { size: 20 }),
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__ */ w(a, {
79
- title: M("devportal.organizations", "Organizations"),
80
- value: z ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : L.totalOrgs,
81
- icon: /* @__PURE__ */ w(h, { size: 20 })
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__ */ w(a, {
84
- title: M("devportal.apiKeys", "API Keys"),
85
- value: L.totalApiKeys,
86
- icon: /* @__PURE__ */ w(_, { size: 20 }),
87
- description: M("devportal.activeKeys", "Active keys")
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__ */ w(a, {
90
- title: M("devportal.webhooks", "Webhooks"),
91
- value: L.totalWebhooks,
92
- icon: /* @__PURE__ */ w(C, { size: 20 }),
93
- description: M("devportal.configured", "Configured")
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__ */ w(i, {
99
- title: M("devportal.usageOverview", "Usage Overview"),
104
+ /* @__PURE__ */ T(i, {
105
+ title: P("devportal.usageOverview", "Usage Overview"),
100
106
  "data-tour": "devportal-dashboard-usage-overview",
101
- children: /* @__PURE__ */ T(o, {
107
+ children: /* @__PURE__ */ E(o, {
102
108
  columns: 4,
103
109
  children: [
104
- /* @__PURE__ */ w(a, {
105
- title: M("devportal.apiCalls", "API Calls"),
106
- value: z ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : L.totalApiCalls > 1e6 ? `${(L.totalApiCalls / 1e6).toFixed(1)}M` : L.totalApiCalls > 1e3 ? `${(L.totalApiCalls / 1e3).toFixed(1)}K` : L.totalApiCalls.toString(),
107
- icon: /* @__PURE__ */ w(f, { size: 20 }),
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__ */ w(a, {
111
- title: M("devportal.activeInstalls", "Active Installs"),
112
- value: z ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : L.activeInstalls > 1e3 ? `${(L.activeInstalls / 1e3).toFixed(1)}K` : L.activeInstalls.toString(),
113
- icon: /* @__PURE__ */ w(S, { size: 20 }),
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__ */ w(a, {
117
- title: M("devportal.monthlyRevenue", "Monthly Revenue"),
118
- value: z ? /* @__PURE__ */ w(v, { className: "h-5 w-5 animate-spin" }) : `$${L.monthlyRevenue.toLocaleString()}`,
119
- icon: /* @__PURE__ */ w(g, { size: 20 }),
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__ */ w(a, {
123
- title: M("devportal.errorRate", "Error Rate"),
124
- value: `${L.errorRate}%`,
125
- icon: /* @__PURE__ */ w(x, { size: 20 }),
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__ */ T("div", {
137
+ /* @__PURE__ */ E("div", {
132
138
  className: "grid gap-6 lg:grid-cols-2",
133
- children: [/* @__PURE__ */ w(i, {
139
+ children: [/* @__PURE__ */ T(i, {
134
140
  "data-tour": "devportal-dashboard-recent-apps",
135
- title: M("devportal.recentApplications", "Recent Applications"),
136
- actions: /* @__PURE__ */ T("button", {
137
- onClick: () => R("/apps"),
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
- M("devportal.viewAll", "View all"),
146
+ P("devportal.viewAll", "View all"),
141
147
  " ",
142
- /* @__PURE__ */ w(m, { size: 14 })
148
+ /* @__PURE__ */ T(m, { size: 14 })
143
149
  ]
144
150
  }),
145
- children: z ? /* @__PURE__ */ w("div", {
151
+ children: V ? /* @__PURE__ */ T("div", {
146
152
  className: "flex items-center justify-center py-8",
147
- children: /* @__PURE__ */ w(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
148
- }) : I.length > 0 ? /* @__PURE__ */ w("div", {
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: I.slice(0, 3).map((e) => /* @__PURE__ */ w(s, {
156
+ children: R.slice(0, 3).map((e) => /* @__PURE__ */ T(s, {
151
157
  app: e,
152
- onClick: () => R(`/apps/${e.id}`)
158
+ onClick: () => B(`/apps/${e.id}`)
153
159
  }, e.id))
154
- }) : /* @__PURE__ */ w(t, {
155
- icon: /* @__PURE__ */ w(p, { size: 24 }),
156
- title: M("devportal.noApplicationsYet", "No applications yet"),
157
- description: B ? M("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : M("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
158
- action: B ? /* @__PURE__ */ T("button", {
159
- onClick: () => R("/apps/new"),
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__ */ w(y, { size: 16 }), M("devportal.createApp", "Create App")]
162
- }) : /* @__PURE__ */ T("button", {
163
- onClick: () => R("/organizations/new"),
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__ */ w(y, { size: 16 }), M("devportal.createOrganization", "Create Organization")]
171
+ children: [/* @__PURE__ */ T(y, { size: 16 }), P("devportal.createOrganization", "Create Organization")]
166
172
  })
167
173
  })
168
- }), /* @__PURE__ */ w(i, {
169
- title: M("devportal.quickActions", "Quick Actions"),
174
+ }), /* @__PURE__ */ T(i, {
175
+ title: P("devportal.quickActions", "Quick Actions"),
170
176
  "data-tour": "devportal-dashboard-quick-actions",
171
- children: /* @__PURE__ */ T("div", {
177
+ children: /* @__PURE__ */ E("div", {
172
178
  className: "grid gap-3",
173
179
  children: [
174
- B ? /* @__PURE__ */ w(O, {
175
- title: M("devportal.createNewApp", "Create New App"),
176
- description: M("devportal.startBuildingANewApplication", "Start building a new application"),
177
- icon: /* @__PURE__ */ w(p, { size: 20 }),
178
- onClick: () => R("/apps/new")
179
- }) : /* @__PURE__ */ w(O, {
180
- title: M("devportal.createOrganization", "Create Organization"),
181
- description: M("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
182
- icon: /* @__PURE__ */ w(h, { size: 20 }),
183
- onClick: () => R("/organizations/new"),
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
- B && /* @__PURE__ */ w(O, {
187
- title: M("devportal.publisherProfile", "Publisher Profile"),
188
- description: M("devportal.manageYourStorePresence", "Manage your store publisher presence"),
189
- icon: /* @__PURE__ */ w(b, { size: 20 }),
190
- onClick: () => R("/publisher/profile")
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__ */ w(O, {
193
- title: M("devportal.generateApiKey", "Generate API Key"),
194
- description: M("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
195
- icon: /* @__PURE__ */ w(_, { size: 20 }),
196
- onClick: () => R("/api-keys")
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__ */ w(O, {
199
- title: M("devportal.configureWebhooks", "Configure Webhooks"),
200
- description: M("devportal.setUpEventNotifications", "Set up event notifications"),
201
- icon: /* @__PURE__ */ w(C, { size: 20 }),
202
- onClick: () => R("/webhooks")
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
- z ? /* @__PURE__ */ w(i, {
214
+ V ? /* @__PURE__ */ T(i, {
209
215
  "data-tour": "devportal-dashboard-organizations",
210
- title: M("devportal.yourOrganizations", "Your Organizations"),
211
- children: /* @__PURE__ */ w("div", {
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__ */ w(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
219
+ children: /* @__PURE__ */ T(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
214
220
  })
215
- }) : F.length > 0 ? /* @__PURE__ */ w(i, {
216
- title: M("devportal.yourOrganizations", "Your Organizations"),
217
- actions: /* @__PURE__ */ T("button", {
218
- onClick: () => R("/organizations"),
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
- M("devportal.viewAll", "View all"),
227
+ P("devportal.viewAll", "View all"),
222
228
  " ",
223
- /* @__PURE__ */ w(m, { size: 14 })
229
+ /* @__PURE__ */ T(m, { size: 14 })
224
230
  ]
225
231
  }),
226
- children: /* @__PURE__ */ w("div", {
232
+ children: /* @__PURE__ */ T("div", {
227
233
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
228
- children: F.slice(0, 3).map((e) => /* @__PURE__ */ w(k, {
234
+ children: L.slice(0, 3).map((e) => /* @__PURE__ */ T(j, {
229
235
  org: e,
230
- onClick: () => R(`/organizations/${e.id}`)
236
+ onClick: () => B(`/organizations/${e.id}`)
231
237
  }, e.id))
232
238
  })
233
239
  }) : null
234
240
  ]
235
241
  })
236
242
  });
237
- }, O = ({ title: e, description: t, icon: n, onClick: r, primary: i }) => /* @__PURE__ */ T("button", {
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__ */ w("div", {
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__ */ T("div", { children: [/* @__PURE__ */ w("p", {
249
+ }), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("p", {
244
250
  className: "font-medium",
245
251
  children: e
246
- }), /* @__PURE__ */ w("p", {
252
+ }), /* @__PURE__ */ T("p", {
247
253
  className: "text-sm text-text-secondary",
248
254
  children: t
249
255
  })] })]
250
- }), k = ({ org: e, onClick: t }) => {
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__ */ T("button", {
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__ */ T("div", {
264
+ children: [/* @__PURE__ */ E("div", {
259
265
  className: "flex items-center gap-3",
260
- children: [e.logoUrl ? /* @__PURE__ */ w("img", {
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__ */ w("div", {
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__ */ w(h, { size: 20 })
267
- }), /* @__PURE__ */ T("div", {
272
+ children: /* @__PURE__ */ T(h, { size: 20 })
273
+ }), /* @__PURE__ */ E("div", {
268
274
  className: "flex-1 min-w-0",
269
- children: [/* @__PURE__ */ w("p", {
275
+ children: [/* @__PURE__ */ T("p", {
270
276
  className: "font-medium truncate",
271
277
  children: e.name
272
- }), /* @__PURE__ */ T("p", {
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__ */ T("div", {
287
+ }), /* @__PURE__ */ E("div", {
282
288
  className: "mt-3 flex gap-4 text-sm text-text-secondary",
283
- children: [/* @__PURE__ */ T("span", { children: [
289
+ children: [/* @__PURE__ */ E("span", { children: [
284
290
  (e.totalInstalls || 0).toLocaleString(),
285
291
  " ",
286
292
  r("devportal.installs", "installs")
287
- ] }), /* @__PURE__ */ T("span", { children: [
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 { D as DashboardPage };
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
- icon: /* @__PURE__ */ T(x, { size: 32 }),
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
- icon: /* @__PURE__ */ T(S, { size: 32 }),
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, {