@burdenoff/microfe-devportal 2026.629.4 → 2026.629.6

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.
@@ -15,15 +15,29 @@ import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
15
15
  import { EmphasisPanel as D, GradientText as O, NextSteps as k } from "@burdenoff/fe-libs/ui";
16
16
  //#region src/devportal/pages/DashboardPage.tsx
17
17
  var A = () => {
18
- let { navigate: A, basePath: N, activeOrgId: P } = e(), { t: F } = d(), I = (e, t) => {
19
- let n = F(e);
18
+ let { navigate: A, basePath: N, activeOrgId: P, setActiveOrgId: F } = e(), { t: I } = d(), L = (e, t) => {
19
+ let n = I(e);
20
20
  return n === e ? t : n;
21
- }, { data: L, loading: R } = n({
22
- variables: { organizationId: P ?? void 0 },
21
+ }, { data: R, loading: z } = n({
22
+ variables: {},
23
23
  fetchPolicy: "cache-and-network"
24
- }), z = L?.myDeveloperOrganizations ?? [];
24
+ }), B = R?.myDeveloperOrganizations ?? [], V = (() => {
25
+ if (B.length) return P && B.some((e) => e.id === P) ? P : B[0]?.id;
26
+ })();
25
27
  u(() => {
26
- if (!(R || !L) && !(z.length > 0)) {
28
+ V && V !== P && F(V);
29
+ }, [
30
+ V,
31
+ P,
32
+ F
33
+ ]);
34
+ let { data: H, loading: U } = n({
35
+ variables: { organizationId: V },
36
+ skip: !V,
37
+ fetchPolicy: "cache-and-network"
38
+ }), W = z || U, G = H ?? R;
39
+ u(() => {
40
+ if (!(z || !R) && !(B.length > 0)) {
27
41
  try {
28
42
  if (localStorage.getItem("devportal_setup_dismissed")) return;
29
43
  } catch {
@@ -32,45 +46,45 @@ var A = () => {
32
46
  A?.(`${N}/setup`);
33
47
  }
34
48
  }, [
49
+ z,
35
50
  R,
36
- L,
37
- z.length,
51
+ B.length,
38
52
  A,
39
53
  N
40
54
  ]);
41
- let B = L?.applications?.edges?.map((e) => e.node) ?? [], V = {
42
- totalApps: L?.applications?.totalCount ?? 0,
43
- totalOrgs: z.length,
55
+ let K = H?.applications?.edges?.map((e) => e.node) ?? [], q = {
56
+ totalApps: G?.applications?.totalCount ?? 0,
57
+ totalOrgs: B.length,
44
58
  totalApiKeys: 0,
45
59
  totalWebhooks: 0,
46
- totalApiCalls: z.reduce((e, t) => e + parseInt(t.totalApiCalls || "0"), 0),
47
- activeInstalls: z.reduce((e, t) => e + (t.activeInstalls || 0), 0),
48
- monthlyRevenue: z.reduce((e, t) => e + (t.totalRevenue || 0), 0),
60
+ totalApiCalls: B.reduce((e, t) => e + parseInt(t.totalApiCalls || "0"), 0),
61
+ activeInstalls: B.reduce((e, t) => e + (t.activeInstalls || 0), 0),
62
+ monthlyRevenue: B.reduce((e, t) => e + (t.totalRevenue || 0), 0),
49
63
  errorRate: 0
50
- }, H = (e) => {
64
+ }, J = (e) => {
51
65
  A?.(`${N}${e}`);
52
- }, U = R, W = !U && z.length > 0;
66
+ }, Y = W, X = !Y && B.length > 0;
53
67
  return /* @__PURE__ */ T(c, {
54
68
  permission: l.APP_READ,
55
69
  children: /* @__PURE__ */ E(r, {
56
70
  heroBackdrop: !0,
57
71
  title: /* @__PURE__ */ E(w, { children: [
58
- I("devportal.developer", "Developer"),
72
+ L("devportal.developer", "Developer"),
59
73
  " ",
60
- /* @__PURE__ */ T(O, { children: I("devportal.portal", "Portal") })
74
+ /* @__PURE__ */ T(O, { children: L("devportal.portal", "Portal") })
61
75
  ] }),
62
- description: I("devportal.buildManageMonitor", "Build, manage, and monitor your applications"),
63
- purpose: I("devportal.dashboardPurpose", "This is your developer home base — a single view of the apps you ship, the organizations behind them, and how they are performing (installs, API usage, revenue). Start an organization, then create and publish apps from here."),
64
- actions: W ? /* @__PURE__ */ E("button", {
76
+ description: L("devportal.buildManageMonitor", "Build, manage, and monitor your applications"),
77
+ purpose: L("devportal.dashboardPurpose", "This is your developer home base — a single view of the apps you ship, the organizations behind them, and how they are performing (installs, API usage, revenue). Start an organization, then create and publish apps from here."),
78
+ actions: X ? /* @__PURE__ */ E("button", {
65
79
  "data-tour": "devportal-dashboard-create-app-button",
66
- onClick: () => H("/apps/new"),
80
+ onClick: () => J("/apps/new"),
67
81
  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",
68
- children: [/* @__PURE__ */ T(y, { size: 16 }), I("devportal.createApp", "Create App")]
69
- }) : U ? null : /* @__PURE__ */ E("button", {
82
+ children: [/* @__PURE__ */ T(y, { size: 16 }), L("devportal.createApp", "Create App")]
83
+ }) : Y ? null : /* @__PURE__ */ E("button", {
70
84
  "data-tour": "devportal-dashboard-create-org-button",
71
- onClick: () => H("/organizations/new"),
85
+ onClick: () => J("/organizations/new"),
72
86
  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",
73
- children: [/* @__PURE__ */ T(y, { size: 16 }), I("devportal.createOrganization", "Create Organization")]
87
+ children: [/* @__PURE__ */ T(y, { size: 16 }), L("devportal.createOrganization", "Create Organization")]
74
88
  }),
75
89
  children: [
76
90
  /* @__PURE__ */ T(D, {
@@ -80,93 +94,93 @@ var A = () => {
80
94
  children: [
81
95
  /* @__PURE__ */ T(a, {
82
96
  featured: !0,
83
- title: I("devportal.totalApps", "Total Apps"),
84
- value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : V.totalApps,
97
+ title: L("devportal.totalApps", "Total Apps"),
98
+ value: Y ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : q.totalApps,
85
99
  icon: /* @__PURE__ */ T(p, { size: 20 }),
86
100
  trend: void 0
87
101
  }),
88
102
  /* @__PURE__ */ T(a, {
89
- title: I("devportal.organizations", "Organizations"),
90
- value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : V.totalOrgs,
103
+ title: L("devportal.organizations", "Organizations"),
104
+ value: Y ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : q.totalOrgs,
91
105
  icon: /* @__PURE__ */ T(h, { size: 20 })
92
106
  }),
93
107
  /* @__PURE__ */ T(a, {
94
- title: I("devportal.apiKeys", "API Keys"),
95
- value: V.totalApiKeys,
108
+ title: L("devportal.apiKeys", "API Keys"),
109
+ value: q.totalApiKeys,
96
110
  icon: /* @__PURE__ */ T(_, { size: 20 }),
97
- description: I("devportal.activeKeys", "Active keys")
111
+ description: L("devportal.activeKeys", "Active keys")
98
112
  }),
99
113
  /* @__PURE__ */ T(a, {
100
- title: I("devportal.webhooks", "Webhooks"),
101
- value: V.totalWebhooks,
114
+ title: L("devportal.webhooks", "Webhooks"),
115
+ value: q.totalWebhooks,
102
116
  icon: /* @__PURE__ */ T(C, { size: 20 }),
103
- description: I("devportal.configured", "Configured")
117
+ description: L("devportal.configured", "Configured")
104
118
  })
105
119
  ]
106
120
  })
107
121
  }),
108
- !U && /* @__PURE__ */ T(k, {
122
+ !Y && /* @__PURE__ */ T(k, {
109
123
  storageKey: "devportal-dashboard",
110
124
  steps: [
111
125
  {
112
126
  id: "create-org",
113
- label: I("devportal.createOrganization", "Create Organization"),
114
- description: I("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
127
+ label: L("devportal.createOrganization", "Create Organization"),
128
+ description: L("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
115
129
  icon: /* @__PURE__ */ T(h, { size: 16 }),
116
- onClick: () => H("/organizations/new"),
117
- done: W
130
+ onClick: () => J("/organizations/new"),
131
+ done: X
118
132
  },
119
133
  {
120
134
  id: "create-app",
121
- label: I("devportal.createApp", "Create App"),
122
- description: I("devportal.startBuildingANewApplication", "Start building a new application"),
135
+ label: L("devportal.createApp", "Create App"),
136
+ description: L("devportal.startBuildingANewApplication", "Start building a new application"),
123
137
  icon: /* @__PURE__ */ T(p, { size: 16 }),
124
- onClick: () => H("/apps/new"),
125
- done: V.totalApps > 0
138
+ onClick: () => J("/apps/new"),
139
+ done: q.totalApps > 0
126
140
  },
127
141
  {
128
142
  id: "api-key",
129
- label: I("devportal.generateApiKey", "Generate API Key"),
130
- description: I("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
143
+ label: L("devportal.generateApiKey", "Generate API Key"),
144
+ description: L("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
131
145
  icon: /* @__PURE__ */ T(_, { size: 16 }),
132
- onClick: () => H("/api-keys")
146
+ onClick: () => J("/api-keys")
133
147
  },
134
148
  {
135
149
  id: "webhooks",
136
- label: I("devportal.configureWebhooks", "Configure Webhooks"),
137
- description: I("devportal.setUpEventNotifications", "Set up event notifications"),
150
+ label: L("devportal.configureWebhooks", "Configure Webhooks"),
151
+ description: L("devportal.setUpEventNotifications", "Set up event notifications"),
138
152
  icon: /* @__PURE__ */ T(C, { size: 16 }),
139
- onClick: () => H("/webhooks")
153
+ onClick: () => J("/webhooks")
140
154
  }
141
155
  ]
142
156
  }),
143
157
  /* @__PURE__ */ T(i, {
144
- title: I("devportal.usageOverview", "Usage Overview"),
158
+ title: L("devportal.usageOverview", "Usage Overview"),
145
159
  "data-tour": "devportal-dashboard-usage-overview",
146
160
  children: /* @__PURE__ */ E(o, {
147
161
  columns: 4,
148
162
  children: [
149
163
  /* @__PURE__ */ T(a, {
150
- title: I("devportal.apiCalls", "API Calls"),
151
- value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : V.totalApiCalls > 1e6 ? `${(V.totalApiCalls / 1e6).toFixed(1)}M` : V.totalApiCalls > 1e3 ? `${(V.totalApiCalls / 1e3).toFixed(1)}K` : V.totalApiCalls.toString(),
164
+ title: L("devportal.apiCalls", "API Calls"),
165
+ value: Y ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : q.totalApiCalls > 1e6 ? `${(q.totalApiCalls / 1e6).toFixed(1)}M` : q.totalApiCalls > 1e3 ? `${(q.totalApiCalls / 1e3).toFixed(1)}K` : q.totalApiCalls.toString(),
152
166
  icon: /* @__PURE__ */ T(f, { size: 20 }),
153
167
  trend: void 0
154
168
  }),
155
169
  /* @__PURE__ */ T(a, {
156
- title: I("devportal.activeInstalls", "Active Installs"),
157
- value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : V.activeInstalls > 1e3 ? `${(V.activeInstalls / 1e3).toFixed(1)}K` : V.activeInstalls.toString(),
170
+ title: L("devportal.activeInstalls", "Active Installs"),
171
+ value: Y ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : q.activeInstalls > 1e3 ? `${(q.activeInstalls / 1e3).toFixed(1)}K` : q.activeInstalls.toString(),
158
172
  icon: /* @__PURE__ */ T(S, { size: 20 }),
159
173
  trend: void 0
160
174
  }),
161
175
  /* @__PURE__ */ T(a, {
162
- title: I("devportal.monthlyRevenue", "Monthly Revenue"),
163
- value: U ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : `$${V.monthlyRevenue.toLocaleString()}`,
176
+ title: L("devportal.monthlyRevenue", "Monthly Revenue"),
177
+ value: Y ? /* @__PURE__ */ T(v, { className: "h-5 w-5 animate-spin" }) : `$${q.monthlyRevenue.toLocaleString()}`,
164
178
  icon: /* @__PURE__ */ T(g, { size: 20 }),
165
179
  trend: void 0
166
180
  }),
167
181
  /* @__PURE__ */ T(a, {
168
- title: I("devportal.errorRate", "Error Rate"),
169
- value: `${V.errorRate}%`,
182
+ title: L("devportal.errorRate", "Error Rate"),
183
+ value: `${q.errorRate}%`,
170
184
  icon: /* @__PURE__ */ T(x, { size: 20 }),
171
185
  trend: void 0
172
186
  })
@@ -177,102 +191,102 @@ var A = () => {
177
191
  className: "grid gap-6 lg:grid-cols-2",
178
192
  children: [/* @__PURE__ */ T(i, {
179
193
  "data-tour": "devportal-dashboard-recent-apps",
180
- title: I("devportal.recentApplications", "Recent Applications"),
194
+ title: L("devportal.recentApplications", "Recent Applications"),
181
195
  actions: /* @__PURE__ */ E("button", {
182
- onClick: () => H("/apps"),
196
+ onClick: () => J("/apps"),
183
197
  className: "text-sm text-primary hover:underline flex items-center gap-1",
184
198
  children: [
185
- I("devportal.viewAll", "View all"),
199
+ L("devportal.viewAll", "View all"),
186
200
  " ",
187
201
  /* @__PURE__ */ T(m, { size: 14 })
188
202
  ]
189
203
  }),
190
- children: U ? /* @__PURE__ */ T("div", {
204
+ children: Y ? /* @__PURE__ */ T("div", {
191
205
  className: "flex items-center justify-center py-8",
192
206
  children: /* @__PURE__ */ T(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
193
- }) : B.length > 0 ? /* @__PURE__ */ T("div", {
207
+ }) : K.length > 0 ? /* @__PURE__ */ T("div", {
194
208
  className: "space-y-2",
195
- children: B.slice(0, 3).map((e) => /* @__PURE__ */ T(s, {
209
+ children: K.slice(0, 3).map((e) => /* @__PURE__ */ T(s, {
196
210
  app: e,
197
- onClick: () => H(`/apps/${e.id}`)
211
+ onClick: () => J(`/apps/${e.id}`)
198
212
  }, e.id))
199
213
  }) : /* @__PURE__ */ T(t, {
200
214
  illustration: "empty-generic",
201
- title: I("devportal.noApplicationsYet", "No applications yet"),
202
- description: W ? I("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : I("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
203
- action: W ? /* @__PURE__ */ E("button", {
204
- onClick: () => H("/apps/new"),
215
+ title: L("devportal.noApplicationsYet", "No applications yet"),
216
+ description: X ? L("devportal.createFirstApplicationToGetStarted", "Create your first application to get started") : L("devportal.createOrgFirstToPublishApps", "Create an organization first to start publishing apps"),
217
+ action: X ? /* @__PURE__ */ E("button", {
218
+ onClick: () => J("/apps/new"),
205
219
  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",
206
- children: [/* @__PURE__ */ T(y, { size: 16 }), I("devportal.createApp", "Create App")]
220
+ children: [/* @__PURE__ */ T(y, { size: 16 }), L("devportal.createApp", "Create App")]
207
221
  }) : /* @__PURE__ */ E("button", {
208
- onClick: () => H("/organizations/new"),
222
+ onClick: () => J("/organizations/new"),
209
223
  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",
210
- children: [/* @__PURE__ */ T(y, { size: 16 }), I("devportal.createOrganization", "Create Organization")]
224
+ children: [/* @__PURE__ */ T(y, { size: 16 }), L("devportal.createOrganization", "Create Organization")]
211
225
  })
212
226
  })
213
227
  }), /* @__PURE__ */ T(i, {
214
- title: I("devportal.quickActions", "Quick Actions"),
228
+ title: L("devportal.quickActions", "Quick Actions"),
215
229
  "data-tour": "devportal-dashboard-quick-actions",
216
230
  children: /* @__PURE__ */ E("div", {
217
231
  className: "grid gap-3",
218
232
  children: [
219
- W ? /* @__PURE__ */ T(j, {
220
- title: I("devportal.createNewApp", "Create New App"),
221
- description: I("devportal.startBuildingANewApplication", "Start building a new application"),
233
+ X ? /* @__PURE__ */ T(j, {
234
+ title: L("devportal.createNewApp", "Create New App"),
235
+ description: L("devportal.startBuildingANewApplication", "Start building a new application"),
222
236
  icon: /* @__PURE__ */ T(p, { size: 20 }),
223
- onClick: () => H("/apps/new")
237
+ onClick: () => J("/apps/new")
224
238
  }) : /* @__PURE__ */ T(j, {
225
- title: I("devportal.createOrganization", "Create Organization"),
226
- description: I("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
239
+ title: L("devportal.createOrganization", "Create Organization"),
240
+ description: L("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
227
241
  icon: /* @__PURE__ */ T(h, { size: 20 }),
228
- onClick: () => H("/organizations/new"),
242
+ onClick: () => J("/organizations/new"),
229
243
  primary: !0
230
244
  }),
231
- W && /* @__PURE__ */ T(j, {
232
- title: I("devportal.publisherProfile", "Publisher Profile"),
233
- description: I("devportal.manageYourStorePresence", "Manage your store publisher presence"),
245
+ X && /* @__PURE__ */ T(j, {
246
+ title: L("devportal.publisherProfile", "Publisher Profile"),
247
+ description: L("devportal.manageYourStorePresence", "Manage your store publisher presence"),
234
248
  icon: /* @__PURE__ */ T(b, { size: 20 }),
235
- onClick: () => H("/publisher/profile")
249
+ onClick: () => J("/publisher/profile")
236
250
  }),
237
251
  /* @__PURE__ */ T(j, {
238
- title: I("devportal.generateApiKey", "Generate API Key"),
239
- description: I("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
252
+ title: L("devportal.generateApiKey", "Generate API Key"),
253
+ description: L("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
240
254
  icon: /* @__PURE__ */ T(_, { size: 20 }),
241
- onClick: () => H("/api-keys")
255
+ onClick: () => J("/api-keys")
242
256
  }),
243
257
  /* @__PURE__ */ T(j, {
244
- title: I("devportal.configureWebhooks", "Configure Webhooks"),
245
- description: I("devportal.setUpEventNotifications", "Set up event notifications"),
258
+ title: L("devportal.configureWebhooks", "Configure Webhooks"),
259
+ description: L("devportal.setUpEventNotifications", "Set up event notifications"),
246
260
  icon: /* @__PURE__ */ T(C, { size: 20 }),
247
- onClick: () => H("/webhooks")
261
+ onClick: () => J("/webhooks")
248
262
  })
249
263
  ]
250
264
  })
251
265
  })]
252
266
  }),
253
- U ? /* @__PURE__ */ T(i, {
267
+ Y ? /* @__PURE__ */ T(i, {
254
268
  "data-tour": "devportal-dashboard-organizations",
255
- title: I("devportal.yourOrganizations", "Your Organizations"),
269
+ title: L("devportal.yourOrganizations", "Your Organizations"),
256
270
  children: /* @__PURE__ */ T("div", {
257
271
  className: "flex items-center justify-center py-8",
258
272
  children: /* @__PURE__ */ T(v, { className: "h-6 w-6 animate-spin text-text-secondary" })
259
273
  })
260
- }) : z.length > 0 ? /* @__PURE__ */ T(i, {
261
- title: I("devportal.yourOrganizations", "Your Organizations"),
274
+ }) : B.length > 0 ? /* @__PURE__ */ T(i, {
275
+ title: L("devportal.yourOrganizations", "Your Organizations"),
262
276
  actions: /* @__PURE__ */ E("button", {
263
- onClick: () => H("/organizations"),
277
+ onClick: () => J("/organizations"),
264
278
  className: "text-sm text-primary hover:underline flex items-center gap-1",
265
279
  children: [
266
- I("devportal.viewAll", "View all"),
280
+ L("devportal.viewAll", "View all"),
267
281
  " ",
268
282
  /* @__PURE__ */ T(m, { size: 14 })
269
283
  ]
270
284
  }),
271
285
  children: /* @__PURE__ */ T("div", {
272
286
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
273
- children: z.slice(0, 3).map((e) => /* @__PURE__ */ T(M, {
287
+ children: B.slice(0, 3).map((e) => /* @__PURE__ */ T(M, {
274
288
  org: e,
275
- onClick: () => H(`/organizations/${e.id}`)
289
+ onClick: () => J(`/organizations/${e.id}`)
276
290
  }, e.id))
277
291
  })
278
292
  }) : null
@@ -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, GradientText, NextSteps } 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, activeOrgId } = 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 — scope apps to the active org\n const { data, loading } = useGetDevportalDashboardBootstrapQuery({\n variables: { organizationId: activeOrgId ?? undefined },\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 purpose={tr(\n \"devportal.dashboardPurpose\",\n \"This is your developer home base — a single view of the apps you ship, the organizations behind them, and how they are performing (installs, API usage, revenue). Start an organization, then create and publish apps from here.\",\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 {/* Guided next steps for new developers (dismissable) */}\n {!isLoading && (\n <NextSteps\n storageKey=\"devportal-dashboard\"\n steps={[\n {\n id: \"create-org\",\n label: 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={16} />,\n onClick: () => handleNavigate(\"/organizations/new\"),\n done: hasOrgs,\n },\n {\n id: \"create-app\",\n label: tr(\"devportal.createApp\", \"Create App\"),\n description: tr(\n \"devportal.startBuildingANewApplication\",\n \"Start building a new application\",\n ),\n icon: <AppWindow size={16} />,\n onClick: () => handleNavigate(\"/apps/new\"),\n done: stats.totalApps > 0,\n },\n {\n id: \"api-key\",\n label: tr(\"devportal.generateApiKey\", \"Generate API Key\"),\n description: tr(\n \"devportal.createCredentialsForYourApps\",\n \"Create credentials for your apps\",\n ),\n icon: <Key size={16} />,\n onClick: () => handleNavigate(\"/api-keys\"),\n },\n {\n id: \"webhooks\",\n label: tr(\"devportal.configureWebhooks\", \"Configure Webhooks\"),\n description: tr(\n \"devportal.setUpEventNotifications\",\n \"Set up event notifications\",\n ),\n icon: <Webhook size={16} />,\n onClick: () => handleNavigate(\"/webhooks\"),\n },\n ]}\n />\n )}\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\n ? \"bg-action-primary-bg/20 text-primary\"\n : \"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, NextSteps } 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, activeOrgId, setActiveOrgId } = 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 // Phase 1: fetch orgs only (no apps yet) so we can resolve the active org\n const { data: orgsData, loading: orgsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: {},\n fetchPolicy: \"cache-and-network\",\n });\n\n const organizations: OrganizationFieldsFragment[] =\n orgsData?.myDeveloperOrganizations ?? [];\n\n // Resolve the org to scope apps to: prefer stored activeOrgId if valid,\n // otherwise fall back to the first org the user belongs to.\n const resolvedOrgId: string | undefined = (() => {\n if (!organizations.length) return undefined;\n if (activeOrgId && organizations.some((o) => o.id === activeOrgId))\n return activeOrgId;\n return organizations[0]?.id;\n })();\n\n // Sync activeOrgId when we auto-selected a fallback org\n useEffect(() => {\n if (resolvedOrgId && resolvedOrgId !== activeOrgId) {\n setActiveOrgId(resolvedOrgId);\n }\n }, [resolvedOrgId, activeOrgId, setActiveOrgId]);\n\n // Phase 2: fetch apps scoped to the resolved org (skip until org is known)\n const { data: appsData, loading: appsLoading } =\n useGetDevportalDashboardBootstrapQuery({\n variables: { organizationId: resolvedOrgId },\n skip: !resolvedOrgId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const loading = orgsLoading || appsLoading;\n const data = appsData ?? orgsData;\n\n // 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 (orgsLoading || !orgsData) 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 }, [orgsLoading, orgsData, organizations.length, navigate, basePath]);\n\n const applications: ApplicationFieldsFragment[] =\n appsData?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n // 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 purpose={tr(\n \"devportal.dashboardPurpose\",\n \"This is your developer home base — a single view of the apps you ship, the organizations behind them, and how they are performing (installs, API usage, revenue). Start an organization, then create and publish apps from here.\",\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 {/* Guided next steps for new developers (dismissable) */}\n {!isLoading && (\n <NextSteps\n storageKey=\"devportal-dashboard\"\n steps={[\n {\n id: \"create-org\",\n label: 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={16} />,\n onClick: () => handleNavigate(\"/organizations/new\"),\n done: hasOrgs,\n },\n {\n id: \"create-app\",\n label: tr(\"devportal.createApp\", \"Create App\"),\n description: tr(\n \"devportal.startBuildingANewApplication\",\n \"Start building a new application\",\n ),\n icon: <AppWindow size={16} />,\n onClick: () => handleNavigate(\"/apps/new\"),\n done: stats.totalApps > 0,\n },\n {\n id: \"api-key\",\n label: tr(\"devportal.generateApiKey\", \"Generate API Key\"),\n description: tr(\n \"devportal.createCredentialsForYourApps\",\n \"Create credentials for your apps\",\n ),\n icon: <Key size={16} />,\n onClick: () => handleNavigate(\"/api-keys\"),\n },\n {\n id: \"webhooks\",\n label: tr(\"devportal.configureWebhooks\", \"Configure Webhooks\"),\n description: tr(\n \"devportal.setUpEventNotifications\",\n \"Set up event notifications\",\n ),\n icon: <Webhook size={16} />,\n onClick: () => handleNavigate(\"/webhooks\"),\n },\n ]}\n />\n )}\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\n ? \"bg-action-primary-bg/20 text-primary\"\n : \"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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-devportal",
3
- "version": "2026.629.4",
3
+ "version": "2026.629.6",
4
4
  "description": "Burdenoff Developer Portal Microfrontend - Complete developer operations interface",
5
5
  "type": "module",
6
6
  "files": [