@burdenoff/microfe-devportal 2026.609.1 → 2026.609.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/DevportalRoutes.js +10 -7
- package/dist/devportal/DevportalRoutes.js.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +152 -134
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/SetupPage.js +501 -0
- package/dist/devportal/pages/SetupPage.js.map +1 -0
- package/package.json +1 -1
|
@@ -26,8 +26,8 @@ function g(e, t) {
|
|
|
26
26
|
})
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
|
-
var _ = s(() => import("./pages/OrganizationsPage.js").then((e) => ({ default: e.OrganizationsPage }))), v = s(() => import("./pages/OrganizationDetailPage.js").then((e) => ({ default: e.OrganizationDetailPage }))), y = s(() => import("./pages/CreateOrganizationPage.js").then((e) => ({ default: e.CreateOrganizationPage }))), b = s(() => import("./pages/OrganizationMembersPage.js").then((e) => ({ default: e.OrganizationMembersPage }))), x = s(() => import("./pages/RevenueRedirectPage.js").then((e) => ({ default: e.RevenueRedirectPage }))), S = s(() => import("./pages/ApplicationsPage.js").then((e) => ({ default: e.ApplicationsPage }))), C = s(() => import("./pages/ApplicationDetailPage.js").then((e) => ({ default: e.ApplicationDetailPage }))), w = s(() => import("./pages/CreateApplicationPage.js").then((e) => ({ default: e.CreateApplicationPage }))), T = s(() => import("./pages/EditApplicationPage.js").then((e) => ({ default: e.EditApplicationPage }))), E = s(() => import("./pages/ApplicationVersionsPage.js").then((e) => ({ default: e.ApplicationVersionsPage }))), D = s(() => import("./pages/OAuthClientsPage.js").then((e) => ({ default: e.OAuthClientsPage }))), O = s(() => import("./pages/CreateOAuthClientPage.js").then((e) => ({ default: e.CreateOAuthClientPage }))), k = s(() => import("./pages/OAuthClientDetailPage.js").then((e) => ({ default: e.OAuthClientDetailPage }))), A = s(() => import("./pages/ApiKeysPage.js").then((e) => ({ default: e.ApiKeysPage }))), j = s(() => import("./pages/CreateApiKeyPage.js").then((e) => ({ default: e.CreateApiKeyPage }))), M = s(() => import("./pages/WebhooksPage.js").then((e) => ({ default: e.WebhooksPage }))), N = s(() => import("./pages/CreateWebhookPage.js").then((e) => ({ default: e.CreateWebhookPage }))), P = s(() => import("./pages/WebhookDetailPage.js").then((e) => ({ default: e.WebhookDetailPage }))), F = s(() => import("./pages/AnalyticsDashboardPage.js").then((e) => ({ default: e.AnalyticsDashboardPage }))), I = s(() => import("./pages/DocumentationPage.js").then((e) => ({ default: e.DocumentationPage }))), L = s(() => import("./pages/ResourcesPage.js").then((e) => ({ default: e.ResourcesPage }))), R = s(() => import("./pages/SettingsPage.js").then((e) => ({ default: e.SettingsPage }))), z = s(() => import("./pages/PublishPage.js").then((e) => ({ default: e.PublishPage }))), B = s(() => import("./pages/AppLogsPage.js").then((e) => ({ default: e.AppLogsPage }))), V = s(() => import("./pages/DelegationsPage.js").then((e) => ({ default: e.DelegationsPage }))), H = s(() => import("./pages/ListingPreviewPage.js").then((e) => ({ default: e.ListingPreviewPage }))), U = s(() => import("./pages/StoreSubmissionPage.js").then((e) => ({ default: e.StoreSubmissionPage }))), W = s(() => import("./pages/PublisherProfilePage.js").then((e) => ({ default: e.PublisherProfilePage })));
|
|
30
|
-
function
|
|
29
|
+
var _ = s(() => import("./pages/OrganizationsPage.js").then((e) => ({ default: e.OrganizationsPage }))), v = s(() => import("./pages/OrganizationDetailPage.js").then((e) => ({ default: e.OrganizationDetailPage }))), y = s(() => import("./pages/CreateOrganizationPage.js").then((e) => ({ default: e.CreateOrganizationPage }))), b = s(() => import("./pages/OrganizationMembersPage.js").then((e) => ({ default: e.OrganizationMembersPage }))), x = s(() => import("./pages/RevenueRedirectPage.js").then((e) => ({ default: e.RevenueRedirectPage }))), S = s(() => import("./pages/ApplicationsPage.js").then((e) => ({ default: e.ApplicationsPage }))), C = s(() => import("./pages/ApplicationDetailPage.js").then((e) => ({ default: e.ApplicationDetailPage }))), w = s(() => import("./pages/CreateApplicationPage.js").then((e) => ({ default: e.CreateApplicationPage }))), T = s(() => import("./pages/EditApplicationPage.js").then((e) => ({ default: e.EditApplicationPage }))), E = s(() => import("./pages/ApplicationVersionsPage.js").then((e) => ({ default: e.ApplicationVersionsPage }))), D = s(() => import("./pages/OAuthClientsPage.js").then((e) => ({ default: e.OAuthClientsPage }))), O = s(() => import("./pages/CreateOAuthClientPage.js").then((e) => ({ default: e.CreateOAuthClientPage }))), k = s(() => import("./pages/OAuthClientDetailPage.js").then((e) => ({ default: e.OAuthClientDetailPage }))), A = s(() => import("./pages/ApiKeysPage.js").then((e) => ({ default: e.ApiKeysPage }))), j = s(() => import("./pages/CreateApiKeyPage.js").then((e) => ({ default: e.CreateApiKeyPage }))), M = s(() => import("./pages/WebhooksPage.js").then((e) => ({ default: e.WebhooksPage }))), N = s(() => import("./pages/CreateWebhookPage.js").then((e) => ({ default: e.CreateWebhookPage }))), P = s(() => import("./pages/WebhookDetailPage.js").then((e) => ({ default: e.WebhookDetailPage }))), F = s(() => import("./pages/AnalyticsDashboardPage.js").then((e) => ({ default: e.AnalyticsDashboardPage }))), I = s(() => import("./pages/DocumentationPage.js").then((e) => ({ default: e.DocumentationPage }))), L = s(() => import("./pages/ResourcesPage.js").then((e) => ({ default: e.ResourcesPage }))), R = s(() => import("./pages/SettingsPage.js").then((e) => ({ default: e.SettingsPage }))), z = s(() => import("./pages/PublishPage.js").then((e) => ({ default: e.PublishPage }))), B = s(() => import("./pages/AppLogsPage.js").then((e) => ({ default: e.AppLogsPage }))), V = s(() => import("./pages/DelegationsPage.js").then((e) => ({ default: e.DelegationsPage }))), H = s(() => import("./pages/ListingPreviewPage.js").then((e) => ({ default: e.ListingPreviewPage }))), U = s(() => import("./pages/StoreSubmissionPage.js").then((e) => ({ default: e.StoreSubmissionPage }))), W = s(() => import("./pages/PublisherProfilePage.js").then((e) => ({ default: e.PublisherProfilePage }))), G = s(() => import("./pages/SetupPage.js").then((e) => ({ default: e.SetupPage })));
|
|
30
|
+
function K() {
|
|
31
31
|
return /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(i, {}), /* @__PURE__ */ m(d, { children: [
|
|
32
32
|
/* @__PURE__ */ p(u, {
|
|
33
33
|
index: !0,
|
|
@@ -167,16 +167,19 @@ function G() {
|
|
|
167
167
|
})
|
|
168
168
|
] })] });
|
|
169
169
|
}
|
|
170
|
-
function
|
|
170
|
+
function q() {
|
|
171
171
|
return /* @__PURE__ */ p(o, {
|
|
172
172
|
fallback: /* @__PURE__ */ p(t, { message: "Loading page..." }),
|
|
173
|
-
children: /* @__PURE__ */
|
|
173
|
+
children: /* @__PURE__ */ m(d, { children: [/* @__PURE__ */ p(u, {
|
|
174
|
+
path: "setup",
|
|
175
|
+
element: g("setup", /* @__PURE__ */ p(G, {}))
|
|
176
|
+
}), /* @__PURE__ */ p(u, {
|
|
174
177
|
path: "/*",
|
|
175
|
-
element: /* @__PURE__ */ p(
|
|
176
|
-
}) })
|
|
178
|
+
element: /* @__PURE__ */ p(K, {})
|
|
179
|
+
})] })
|
|
177
180
|
});
|
|
178
181
|
}
|
|
179
182
|
//#endregion
|
|
180
|
-
export {
|
|
183
|
+
export { q as DevportalRoutes };
|
|
181
184
|
|
|
182
185
|
//# sourceMappingURL=DevportalRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DevportalRoutes.js","names":[],"sources":["../../src/devportal/DevportalRoutes.tsx"],"sourcesContent":["import { Suspense, lazy, useEffect, type ReactNode } from \"react\";\nimport { Routes, Route, Navigate } from \"react-router-dom\";\nimport { LoadingState } from \"./components/LoadingState\";\nimport { FeatureFlaggedRoute } from \"./components/FeatureFlaggedRoute\";\nimport { TelemetryErrorBoundary } from \"./components/TelemetryErrorBoundary\";\nimport { SectionNav } from \"./components/SectionNav\";\nimport { useDevportal } from \"./providers/DevportalProvider\";\n\n/**\n * Catch-all redirect target for the unmatched `*` route. Uses the\n * DevportalProvider's `basePath` and `navigate` so the redirect works\n * regardless of the integration mode (shell or standalone). Falls back to a\n * relative Navigate when neither is wired, which is the standalone/test path.\n */\nfunction CatchAllRedirect() {\n const { basePath, navigate } = useDevportal();\n useEffect(() => {\n if (basePath && navigate) {\n const target = basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n navigate(target || \"/\");\n }\n }, [basePath, navigate]);\n if (basePath && navigate) return null;\n return <Navigate to=\"..\" replace />;\n}\n\n/**\n * Wrap a page element with the per-route feature flag gate and a\n * telemetry-emitting error boundary. The flag name follows\n * `devportal.<page-slug>` convention; when disabled the user sees a\n * graceful fallback instead of a route-level crash.\n *\n * Per `dev/platform/context/feature-flags` and spec\n * `microfrontends/47-FRONTEND-EVENT-BUS.md` (error.boundary emission).\n */\nfunction gated(slug: string, element: ReactNode): ReactNode {\n return (\n <TelemetryErrorBoundary scope={slug}>\n <FeatureFlaggedRoute flag={`devportal.${slug}`}>\n {element}\n </FeatureFlaggedRoute>\n </TelemetryErrorBoundary>\n );\n}\n\n// Eager load the main dashboard page\nimport { DashboardPage } from \"./pages/DashboardPage\";\n\n// Lazy load all other pages\nconst OrganizationsPage = lazy(() =>\n import(\"./pages/OrganizationsPage\").then((m) => ({\n default: m.OrganizationsPage,\n })),\n);\nconst OrganizationDetailPage = lazy(() =>\n import(\"./pages/OrganizationDetailPage\").then((m) => ({\n default: m.OrganizationDetailPage,\n })),\n);\nconst CreateOrganizationPage = lazy(() =>\n import(\"./pages/CreateOrganizationPage\").then((m) => ({\n default: m.CreateOrganizationPage,\n })),\n);\nconst OrganizationMembersPage = lazy(() =>\n import(\"./pages/OrganizationMembersPage\").then((m) => ({\n default: m.OrganizationMembersPage,\n })),\n);\nconst RevenueRedirectPage = lazy(() =>\n import(\"./pages/RevenueRedirectPage\").then((m) => ({\n default: m.RevenueRedirectPage,\n })),\n);\nconst ApplicationsPage = lazy(() =>\n import(\"./pages/ApplicationsPage\").then((m) => ({\n default: m.ApplicationsPage,\n })),\n);\nconst ApplicationDetailPage = lazy(() =>\n import(\"./pages/ApplicationDetailPage\").then((m) => ({\n default: m.ApplicationDetailPage,\n })),\n);\nconst CreateApplicationPage = lazy(() =>\n import(\"./pages/CreateApplicationPage\").then((m) => ({\n default: m.CreateApplicationPage,\n })),\n);\nconst EditApplicationPage = lazy(() =>\n import(\"./pages/EditApplicationPage\").then((m) => ({\n default: m.EditApplicationPage,\n })),\n);\nconst ApplicationVersionsPage = lazy(() =>\n import(\"./pages/ApplicationVersionsPage\").then((m) => ({\n default: m.ApplicationVersionsPage,\n })),\n);\nconst OAuthClientsPage = lazy(() =>\n import(\"./pages/OAuthClientsPage\").then((m) => ({\n default: m.OAuthClientsPage,\n })),\n);\nconst CreateOAuthClientPage = lazy(() =>\n import(\"./pages/CreateOAuthClientPage\").then((m) => ({\n default: m.CreateOAuthClientPage,\n })),\n);\nconst OAuthClientDetailPage = lazy(() =>\n import(\"./pages/OAuthClientDetailPage\").then((m) => ({\n default: m.OAuthClientDetailPage,\n })),\n);\nconst ApiKeysPage = lazy(() =>\n import(\"./pages/ApiKeysPage\").then((m) => ({ default: m.ApiKeysPage })),\n);\nconst CreateApiKeyPage = lazy(() =>\n import(\"./pages/CreateApiKeyPage\").then((m) => ({\n default: m.CreateApiKeyPage,\n })),\n);\nconst WebhooksPage = lazy(() =>\n import(\"./pages/WebhooksPage\").then((m) => ({ default: m.WebhooksPage })),\n);\nconst CreateWebhookPage = lazy(() =>\n import(\"./pages/CreateWebhookPage\").then((m) => ({\n default: m.CreateWebhookPage,\n })),\n);\nconst WebhookDetailPage = lazy(() =>\n import(\"./pages/WebhookDetailPage\").then((m) => ({\n default: m.WebhookDetailPage,\n })),\n);\nconst AnalyticsDashboardPage = lazy(() =>\n import(\"./pages/AnalyticsDashboardPage\").then((m) => ({\n default: m.AnalyticsDashboardPage,\n })),\n);\nconst DocumentationPage = lazy(() =>\n import(\"./pages/DocumentationPage\").then((m) => ({\n default: m.DocumentationPage,\n })),\n);\nconst ResourcesPage = lazy(() =>\n import(\"./pages/ResourcesPage\").then((m) => ({ default: m.ResourcesPage })),\n);\nconst SettingsPage = lazy(() =>\n import(\"./pages/SettingsPage\").then((m) => ({ default: m.SettingsPage })),\n);\nconst PublishPage = lazy(() =>\n import(\"./pages/PublishPage\").then((m) => ({ default: m.PublishPage })),\n);\nconst AppLogsPage = lazy(() =>\n import(\"./pages/AppLogsPage\").then((m) => ({ default: m.AppLogsPage })),\n);\nconst DelegationsPage = lazy(() =>\n import(\"./pages/DelegationsPage\").then((m) => ({\n default: m.DelegationsPage,\n })),\n);\nconst ListingPreviewPage = lazy(() =>\n import(\"./pages/ListingPreviewPage\").then((m) => ({\n default: m.ListingPreviewPage,\n })),\n);\nconst StoreSubmissionPage = lazy(() =>\n import(\"./pages/StoreSubmissionPage\").then((m) => ({\n default: m.StoreSubmissionPage,\n })),\n);\nconst PublisherProfilePage = lazy(() =>\n import(\"./pages/PublisherProfilePage\").then((m) => ({\n default: m.PublisherProfilePage,\n })),\n);\n\n/**\n * Inner routes component - renders actual page routes\n */\nfunction InnerRoutes() {\n return (\n <>\n <SectionNav />\n <Routes>\n {/* Dashboard */}\n <Route index element={gated(\"dashboard\", <DashboardPage />)} />\n\n {/* Organizations */}\n <Route\n path=\"organizations\"\n element={gated(\"organizations\", <OrganizationsPage />)}\n />\n <Route\n path=\"organizations/new\"\n element={gated(\"organizations.create\", <CreateOrganizationPage />)}\n />\n <Route\n path=\"organizations/:orgId\"\n element={gated(\"organizations.detail\", <OrganizationDetailPage />)}\n />\n <Route\n path=\"organizations/:orgId/members\"\n element={gated(\"organizations.members\", <OrganizationMembersPage />)}\n />\n <Route\n path=\"organizations/:orgId/revenue\"\n element={gated(\"organizations.revenue\", <RevenueRedirectPage />)}\n />\n\n {/* Applications */}\n <Route path=\"apps\" element={gated(\"apps\", <ApplicationsPage />)} />\n <Route\n path=\"apps/new\"\n element={gated(\"apps.create\", <CreateApplicationPage />)}\n />\n <Route\n path=\"apps/:appId\"\n element={gated(\"apps.detail\", <ApplicationDetailPage />)}\n />\n <Route\n path=\"apps/:appId/edit\"\n element={gated(\"apps.edit\", <EditApplicationPage />)}\n />\n <Route\n path=\"apps/:appId/versions\"\n element={gated(\"apps.versions\", <ApplicationVersionsPage />)}\n />\n <Route\n path=\"apps/:appId/publish\"\n element={gated(\"apps.publish\", <PublishPage />)}\n />\n <Route\n path=\"apps/:appId/store-submission\"\n element={gated(\"apps.storeSubmission\", <StoreSubmissionPage />)}\n />\n <Route\n path=\"apps/:appId/listing\"\n element={gated(\"apps.listing\", <ListingPreviewPage />)}\n />\n <Route\n path=\"apps/:appId/logs\"\n element={gated(\"apps.logs\", <AppLogsPage />)}\n />\n\n {/* OAuth Clients — user-scoped via auth-svc (no app nesting) */}\n <Route path=\"oauth\" element={gated(\"oauth\", <OAuthClientsPage />)} />\n <Route\n path=\"oauth/new\"\n element={gated(\"oauth.create\", <CreateOAuthClientPage />)}\n />\n <Route\n path=\"oauth/:clientId\"\n element={gated(\"oauth.detail\", <OAuthClientDetailPage />)}\n />\n\n {/* API Keys — user-scoped via auth-svc (no app nesting) */}\n <Route path=\"api-keys\" element={gated(\"apiKeys\", <ApiKeysPage />)} />\n <Route\n path=\"api-keys/new\"\n element={gated(\"apiKeys.create\", <CreateApiKeyPage />)}\n />\n\n {/* Webhooks (App-specific) */}\n <Route\n path=\"apps/:appId/webhooks\"\n element={gated(\"apps.webhooks\", <WebhooksPage />)}\n />\n <Route\n path=\"apps/:appId/webhooks/new\"\n element={gated(\"apps.webhooks.create\", <CreateWebhookPage />)}\n />\n <Route\n path=\"apps/:appId/webhooks/:webhookId\"\n element={gated(\"apps.webhooks.detail\", <WebhookDetailPage />)}\n />\n\n {/* App Analytics */}\n <Route\n path=\"apps/:appId/analytics\"\n element={gated(\"apps.analytics\", <AnalyticsDashboardPage />)}\n />\n\n {/* Delegations */}\n <Route\n path=\"delegations\"\n element={gated(\"delegations\", <DelegationsPage />)}\n />\n\n {/* Global Webhooks views */}\n <Route\n path=\"webhooks/new\"\n element={gated(\"webhooks.create\", <CreateWebhookPage />)}\n />\n <Route path=\"webhooks\" element={gated(\"webhooks\", <WebhooksPage />)} />\n\n {/* Analytics & Revenue */}\n <Route\n path=\"analytics\"\n element={gated(\"analytics\", <AnalyticsDashboardPage />)}\n />\n <Route\n path=\"revenue\"\n element={gated(\"revenue\", <RevenueRedirectPage />)}\n />\n\n {/* Documentation & Resources */}\n <Route path=\"docs\" element={gated(\"docs\", <DocumentationPage />)} />\n <Route\n path=\"resources\"\n element={gated(\"resources\", <ResourcesPage />)}\n />\n\n {/* Admin surfaces (e.g. /admin/reviews) live in admin-app via\n AdminDevportalRoot — intentionally not mounted in user shells. */}\n\n {/* Publisher */}\n <Route\n path=\"publisher/profile\"\n element={gated(\"publisher.profile\", <PublisherProfilePage />)}\n />\n\n {/* Settings */}\n <Route path=\"settings\" element={gated(\"settings\", <SettingsPage />)} />\n\n {/* Catch-all redirect to dashboard */}\n <Route path=\"*\" element={<CatchAllRedirect />} />\n </Routes>\n </>\n );\n}\n\n/**\n * Developer Portal Routes\n *\n * Defines all the routes for the developer portal microfrontend.\n * Uses lazy loading for all pages except the dashboard for optimal performance.\n *\n * When integrated into a shell (basePath provided), the shell already handles\n * the basePath route (e.g., /devportal/*), so this component should render\n * routes relative to that path - NOT nest them under basePath again.\n *\n * Example:\n * - Shell route: /devportal/*\n * - User navigates to: /devportal/organizations\n * - Inside DevportalRoutes, the path is: /organizations (relative to shell route)\n * - InnerRoutes matches: organizations -> OrganizationsPage\n */\nexport function DevportalRoutes() {\n // When basePath is provided, we're in shell integration mode.\n // The shell already matched the basePath route, so we render routes\n // relative to that - using wildcard to catch all sub-paths.\n // When no basePath (standalone mode), just render InnerRoutes at root.\n return (\n <Suspense fallback={<LoadingState message=\"Loading page...\" />}>\n <Routes>\n {/* Wildcard route to let InnerRoutes handle all paths */}\n <Route path=\"/*\" element={<InnerRoutes />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DevportalRoutes.js","names":[],"sources":["../../src/devportal/DevportalRoutes.tsx"],"sourcesContent":["import { Suspense, lazy, useEffect, type ReactNode } from \"react\";\nimport { Routes, Route, Navigate } from \"react-router-dom\";\nimport { LoadingState } from \"./components/LoadingState\";\nimport { FeatureFlaggedRoute } from \"./components/FeatureFlaggedRoute\";\nimport { TelemetryErrorBoundary } from \"./components/TelemetryErrorBoundary\";\nimport { SectionNav } from \"./components/SectionNav\";\nimport { useDevportal } from \"./providers/DevportalProvider\";\n\n/**\n * Catch-all redirect target for the unmatched `*` route. Uses the\n * DevportalProvider's `basePath` and `navigate` so the redirect works\n * regardless of the integration mode (shell or standalone). Falls back to a\n * relative Navigate when neither is wired, which is the standalone/test path.\n */\nfunction CatchAllRedirect() {\n const { basePath, navigate } = useDevportal();\n useEffect(() => {\n if (basePath && navigate) {\n const target = basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n navigate(target || \"/\");\n }\n }, [basePath, navigate]);\n if (basePath && navigate) return null;\n return <Navigate to=\"..\" replace />;\n}\n\n/**\n * Wrap a page element with the per-route feature flag gate and a\n * telemetry-emitting error boundary. The flag name follows\n * `devportal.<page-slug>` convention; when disabled the user sees a\n * graceful fallback instead of a route-level crash.\n *\n * Per `dev/platform/context/feature-flags` and spec\n * `microfrontends/47-FRONTEND-EVENT-BUS.md` (error.boundary emission).\n */\nfunction gated(slug: string, element: ReactNode): ReactNode {\n return (\n <TelemetryErrorBoundary scope={slug}>\n <FeatureFlaggedRoute flag={`devportal.${slug}`}>\n {element}\n </FeatureFlaggedRoute>\n </TelemetryErrorBoundary>\n );\n}\n\n// Eager load the main dashboard page\nimport { DashboardPage } from \"./pages/DashboardPage\";\n\n// Lazy load all other pages\nconst OrganizationsPage = lazy(() =>\n import(\"./pages/OrganizationsPage\").then((m) => ({\n default: m.OrganizationsPage,\n })),\n);\nconst OrganizationDetailPage = lazy(() =>\n import(\"./pages/OrganizationDetailPage\").then((m) => ({\n default: m.OrganizationDetailPage,\n })),\n);\nconst CreateOrganizationPage = lazy(() =>\n import(\"./pages/CreateOrganizationPage\").then((m) => ({\n default: m.CreateOrganizationPage,\n })),\n);\nconst OrganizationMembersPage = lazy(() =>\n import(\"./pages/OrganizationMembersPage\").then((m) => ({\n default: m.OrganizationMembersPage,\n })),\n);\nconst RevenueRedirectPage = lazy(() =>\n import(\"./pages/RevenueRedirectPage\").then((m) => ({\n default: m.RevenueRedirectPage,\n })),\n);\nconst ApplicationsPage = lazy(() =>\n import(\"./pages/ApplicationsPage\").then((m) => ({\n default: m.ApplicationsPage,\n })),\n);\nconst ApplicationDetailPage = lazy(() =>\n import(\"./pages/ApplicationDetailPage\").then((m) => ({\n default: m.ApplicationDetailPage,\n })),\n);\nconst CreateApplicationPage = lazy(() =>\n import(\"./pages/CreateApplicationPage\").then((m) => ({\n default: m.CreateApplicationPage,\n })),\n);\nconst EditApplicationPage = lazy(() =>\n import(\"./pages/EditApplicationPage\").then((m) => ({\n default: m.EditApplicationPage,\n })),\n);\nconst ApplicationVersionsPage = lazy(() =>\n import(\"./pages/ApplicationVersionsPage\").then((m) => ({\n default: m.ApplicationVersionsPage,\n })),\n);\nconst OAuthClientsPage = lazy(() =>\n import(\"./pages/OAuthClientsPage\").then((m) => ({\n default: m.OAuthClientsPage,\n })),\n);\nconst CreateOAuthClientPage = lazy(() =>\n import(\"./pages/CreateOAuthClientPage\").then((m) => ({\n default: m.CreateOAuthClientPage,\n })),\n);\nconst OAuthClientDetailPage = lazy(() =>\n import(\"./pages/OAuthClientDetailPage\").then((m) => ({\n default: m.OAuthClientDetailPage,\n })),\n);\nconst ApiKeysPage = lazy(() =>\n import(\"./pages/ApiKeysPage\").then((m) => ({ default: m.ApiKeysPage })),\n);\nconst CreateApiKeyPage = lazy(() =>\n import(\"./pages/CreateApiKeyPage\").then((m) => ({\n default: m.CreateApiKeyPage,\n })),\n);\nconst WebhooksPage = lazy(() =>\n import(\"./pages/WebhooksPage\").then((m) => ({ default: m.WebhooksPage })),\n);\nconst CreateWebhookPage = lazy(() =>\n import(\"./pages/CreateWebhookPage\").then((m) => ({\n default: m.CreateWebhookPage,\n })),\n);\nconst WebhookDetailPage = lazy(() =>\n import(\"./pages/WebhookDetailPage\").then((m) => ({\n default: m.WebhookDetailPage,\n })),\n);\nconst AnalyticsDashboardPage = lazy(() =>\n import(\"./pages/AnalyticsDashboardPage\").then((m) => ({\n default: m.AnalyticsDashboardPage,\n })),\n);\nconst DocumentationPage = lazy(() =>\n import(\"./pages/DocumentationPage\").then((m) => ({\n default: m.DocumentationPage,\n })),\n);\nconst ResourcesPage = lazy(() =>\n import(\"./pages/ResourcesPage\").then((m) => ({ default: m.ResourcesPage })),\n);\nconst SettingsPage = lazy(() =>\n import(\"./pages/SettingsPage\").then((m) => ({ default: m.SettingsPage })),\n);\nconst PublishPage = lazy(() =>\n import(\"./pages/PublishPage\").then((m) => ({ default: m.PublishPage })),\n);\nconst AppLogsPage = lazy(() =>\n import(\"./pages/AppLogsPage\").then((m) => ({ default: m.AppLogsPage })),\n);\nconst DelegationsPage = lazy(() =>\n import(\"./pages/DelegationsPage\").then((m) => ({\n default: m.DelegationsPage,\n })),\n);\nconst ListingPreviewPage = lazy(() =>\n import(\"./pages/ListingPreviewPage\").then((m) => ({\n default: m.ListingPreviewPage,\n })),\n);\nconst StoreSubmissionPage = lazy(() =>\n import(\"./pages/StoreSubmissionPage\").then((m) => ({\n default: m.StoreSubmissionPage,\n })),\n);\nconst PublisherProfilePage = lazy(() =>\n import(\"./pages/PublisherProfilePage\").then((m) => ({\n default: m.PublisherProfilePage,\n })),\n);\nconst SetupPage = lazy(() =>\n import(\"./pages/SetupPage\").then((m) => ({ default: m.SetupPage })),\n);\n\n/**\n * Inner routes component - renders actual page routes\n */\nfunction InnerRoutes() {\n return (\n <>\n <SectionNav />\n <Routes>\n {/* Dashboard */}\n <Route index element={gated(\"dashboard\", <DashboardPage />)} />\n\n {/* Organizations */}\n <Route\n path=\"organizations\"\n element={gated(\"organizations\", <OrganizationsPage />)}\n />\n <Route\n path=\"organizations/new\"\n element={gated(\"organizations.create\", <CreateOrganizationPage />)}\n />\n <Route\n path=\"organizations/:orgId\"\n element={gated(\"organizations.detail\", <OrganizationDetailPage />)}\n />\n <Route\n path=\"organizations/:orgId/members\"\n element={gated(\"organizations.members\", <OrganizationMembersPage />)}\n />\n <Route\n path=\"organizations/:orgId/revenue\"\n element={gated(\"organizations.revenue\", <RevenueRedirectPage />)}\n />\n\n {/* Applications */}\n <Route path=\"apps\" element={gated(\"apps\", <ApplicationsPage />)} />\n <Route\n path=\"apps/new\"\n element={gated(\"apps.create\", <CreateApplicationPage />)}\n />\n <Route\n path=\"apps/:appId\"\n element={gated(\"apps.detail\", <ApplicationDetailPage />)}\n />\n <Route\n path=\"apps/:appId/edit\"\n element={gated(\"apps.edit\", <EditApplicationPage />)}\n />\n <Route\n path=\"apps/:appId/versions\"\n element={gated(\"apps.versions\", <ApplicationVersionsPage />)}\n />\n <Route\n path=\"apps/:appId/publish\"\n element={gated(\"apps.publish\", <PublishPage />)}\n />\n <Route\n path=\"apps/:appId/store-submission\"\n element={gated(\"apps.storeSubmission\", <StoreSubmissionPage />)}\n />\n <Route\n path=\"apps/:appId/listing\"\n element={gated(\"apps.listing\", <ListingPreviewPage />)}\n />\n <Route\n path=\"apps/:appId/logs\"\n element={gated(\"apps.logs\", <AppLogsPage />)}\n />\n\n {/* OAuth Clients — user-scoped via auth-svc (no app nesting) */}\n <Route path=\"oauth\" element={gated(\"oauth\", <OAuthClientsPage />)} />\n <Route\n path=\"oauth/new\"\n element={gated(\"oauth.create\", <CreateOAuthClientPage />)}\n />\n <Route\n path=\"oauth/:clientId\"\n element={gated(\"oauth.detail\", <OAuthClientDetailPage />)}\n />\n\n {/* API Keys — user-scoped via auth-svc (no app nesting) */}\n <Route path=\"api-keys\" element={gated(\"apiKeys\", <ApiKeysPage />)} />\n <Route\n path=\"api-keys/new\"\n element={gated(\"apiKeys.create\", <CreateApiKeyPage />)}\n />\n\n {/* Webhooks (App-specific) */}\n <Route\n path=\"apps/:appId/webhooks\"\n element={gated(\"apps.webhooks\", <WebhooksPage />)}\n />\n <Route\n path=\"apps/:appId/webhooks/new\"\n element={gated(\"apps.webhooks.create\", <CreateWebhookPage />)}\n />\n <Route\n path=\"apps/:appId/webhooks/:webhookId\"\n element={gated(\"apps.webhooks.detail\", <WebhookDetailPage />)}\n />\n\n {/* App Analytics */}\n <Route\n path=\"apps/:appId/analytics\"\n element={gated(\"apps.analytics\", <AnalyticsDashboardPage />)}\n />\n\n {/* Delegations */}\n <Route\n path=\"delegations\"\n element={gated(\"delegations\", <DelegationsPage />)}\n />\n\n {/* Global Webhooks views */}\n <Route\n path=\"webhooks/new\"\n element={gated(\"webhooks.create\", <CreateWebhookPage />)}\n />\n <Route path=\"webhooks\" element={gated(\"webhooks\", <WebhooksPage />)} />\n\n {/* Analytics & Revenue */}\n <Route\n path=\"analytics\"\n element={gated(\"analytics\", <AnalyticsDashboardPage />)}\n />\n <Route\n path=\"revenue\"\n element={gated(\"revenue\", <RevenueRedirectPage />)}\n />\n\n {/* Documentation & Resources */}\n <Route path=\"docs\" element={gated(\"docs\", <DocumentationPage />)} />\n <Route\n path=\"resources\"\n element={gated(\"resources\", <ResourcesPage />)}\n />\n\n {/* Admin surfaces (e.g. /admin/reviews) live in admin-app via\n AdminDevportalRoot — intentionally not mounted in user shells. */}\n\n {/* Publisher */}\n <Route\n path=\"publisher/profile\"\n element={gated(\"publisher.profile\", <PublisherProfilePage />)}\n />\n\n {/* Settings */}\n <Route path=\"settings\" element={gated(\"settings\", <SettingsPage />)} />\n\n {/* Catch-all redirect to dashboard */}\n <Route path=\"*\" element={<CatchAllRedirect />} />\n </Routes>\n </>\n );\n}\n\n/**\n * Developer Portal Routes\n *\n * Defines all the routes for the developer portal microfrontend.\n * Uses lazy loading for all pages except the dashboard for optimal performance.\n *\n * When integrated into a shell (basePath provided), the shell already handles\n * the basePath route (e.g., /devportal/*), so this component should render\n * routes relative to that path - NOT nest them under basePath again.\n *\n * Example:\n * - Shell route: /devportal/*\n * - User navigates to: /devportal/organizations\n * - Inside DevportalRoutes, the path is: /organizations (relative to shell route)\n * - InnerRoutes matches: organizations -> OrganizationsPage\n */\nexport function DevportalRoutes() {\n // When basePath is provided, we're in shell integration mode.\n // The shell already matched the basePath route, so we render routes\n // relative to that - using wildcard to catch all sub-paths.\n // When no basePath (standalone mode), just render InnerRoutes at root.\n return (\n <Suspense fallback={<LoadingState message=\"Loading page...\" />}>\n <Routes>\n {/* Setup wizard renders without SectionNav */}\n <Route path=\"setup\" element={gated(\"setup\", <SetupPage />)} />\n {/* All other routes get SectionNav via InnerRoutes */}\n <Route path=\"/*\" element={<InnerRoutes />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
@@ -8,234 +8,252 @@ import { PermissionGuard as c } from "../components/PermissionGuard.js";
|
|
|
8
8
|
import "../components/index.js";
|
|
9
9
|
import { DEVPORTAL_PERMISSIONS as l } from "../constants/permissions.js";
|
|
10
10
|
import "../hooks/index.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { useEffect as u } from "react";
|
|
12
|
+
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
13
|
+
import { Activity as f, AppWindow as p, ArrowRight as m, Building2 as h, DollarSign as g, Key as _, Loader2 as v, Plus as y, TrendingUp as b, Users as x, Webhook as S } from "lucide-react";
|
|
14
|
+
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
14
15
|
//#region src/devportal/pages/DashboardPage.tsx
|
|
15
|
-
var
|
|
16
|
-
let { navigate:
|
|
17
|
-
let n =
|
|
16
|
+
var T = () => {
|
|
17
|
+
let { navigate: T, basePath: O } = e(), { t: k } = d(), A = (e, t) => {
|
|
18
|
+
let n = k(e);
|
|
18
19
|
return n === e ? t : n;
|
|
19
|
-
}, { data:
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
}, { data: j, loading: M } = s({ fetchPolicy: "cache-and-network" }), N = j?.myDeveloperOrganizations ?? [];
|
|
21
|
+
u(() => {
|
|
22
|
+
if (!(M || !j) && !(N.length > 0)) {
|
|
23
|
+
try {
|
|
24
|
+
if (localStorage.getItem("devportal_setup_dismissed")) return;
|
|
25
|
+
} catch {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
T?.(`${O}/setup`);
|
|
29
|
+
}
|
|
30
|
+
}, [
|
|
31
|
+
M,
|
|
32
|
+
j,
|
|
33
|
+
N.length,
|
|
34
|
+
T,
|
|
35
|
+
O
|
|
36
|
+
]);
|
|
37
|
+
let P = j?.applications?.edges?.map((e) => e.node) ?? [], F = {
|
|
38
|
+
totalApps: j?.applications?.totalCount ?? 0,
|
|
39
|
+
totalOrgs: N.length,
|
|
22
40
|
totalApiKeys: 0,
|
|
23
41
|
totalWebhooks: 0,
|
|
24
|
-
totalApiCalls:
|
|
25
|
-
activeInstalls:
|
|
26
|
-
monthlyRevenue:
|
|
42
|
+
totalApiCalls: N.reduce((e, t) => e + parseInt(t.totalApiCalls || "0"), 0),
|
|
43
|
+
activeInstalls: N.reduce((e, t) => e + (t.activeInstalls || 0), 0),
|
|
44
|
+
monthlyRevenue: N.reduce((e, t) => e + (t.totalRevenue || 0), 0),
|
|
27
45
|
errorRate: 0
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
},
|
|
31
|
-
return /* @__PURE__ */
|
|
46
|
+
}, I = (e) => {
|
|
47
|
+
T?.(`${O}${e}`);
|
|
48
|
+
}, L = M;
|
|
49
|
+
return /* @__PURE__ */ C(c, {
|
|
32
50
|
permission: l.APP_READ,
|
|
33
|
-
children: /* @__PURE__ */
|
|
34
|
-
title:
|
|
35
|
-
description:
|
|
36
|
-
actions: /* @__PURE__ */
|
|
51
|
+
children: /* @__PURE__ */ w(n, {
|
|
52
|
+
title: A("devportal.developerPortal", "Developer Portal"),
|
|
53
|
+
description: A("devportal.buildManageMonitor", "Build, manage, and monitor your applications"),
|
|
54
|
+
actions: /* @__PURE__ */ w("button", {
|
|
37
55
|
"data-tour": "devportal-dashboard-create-app-button",
|
|
38
|
-
onClick: () =>
|
|
56
|
+
onClick: () => I("/apps/new"),
|
|
39
57
|
className: "inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
40
|
-
children: [/* @__PURE__ */
|
|
58
|
+
children: [/* @__PURE__ */ C(y, { size: 16 }), A("devportal.createApp", "Create App")]
|
|
41
59
|
}),
|
|
42
60
|
children: [
|
|
43
|
-
/* @__PURE__ */
|
|
61
|
+
/* @__PURE__ */ C(r, {
|
|
44
62
|
"data-tour": "devportal-dashboard-stats-overview",
|
|
45
|
-
children: /* @__PURE__ */
|
|
63
|
+
children: /* @__PURE__ */ w(a, {
|
|
46
64
|
columns: 4,
|
|
47
65
|
children: [
|
|
48
|
-
/* @__PURE__ */
|
|
49
|
-
title:
|
|
50
|
-
value:
|
|
51
|
-
icon: /* @__PURE__ */
|
|
66
|
+
/* @__PURE__ */ C(i, {
|
|
67
|
+
title: A("devportal.totalApps", "Total Apps"),
|
|
68
|
+
value: L ? /* @__PURE__ */ C(v, { className: "h-5 w-5 animate-spin" }) : F.totalApps,
|
|
69
|
+
icon: /* @__PURE__ */ C(p, { size: 20 }),
|
|
52
70
|
trend: void 0
|
|
53
71
|
}),
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
title:
|
|
56
|
-
value:
|
|
57
|
-
icon: /* @__PURE__ */
|
|
72
|
+
/* @__PURE__ */ C(i, {
|
|
73
|
+
title: A("devportal.organizations", "Organizations"),
|
|
74
|
+
value: L ? /* @__PURE__ */ C(v, { className: "h-5 w-5 animate-spin" }) : F.totalOrgs,
|
|
75
|
+
icon: /* @__PURE__ */ C(h, { size: 20 })
|
|
58
76
|
}),
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
-
title:
|
|
61
|
-
value:
|
|
62
|
-
icon: /* @__PURE__ */
|
|
63
|
-
description:
|
|
77
|
+
/* @__PURE__ */ C(i, {
|
|
78
|
+
title: A("devportal.apiKeys", "API Keys"),
|
|
79
|
+
value: F.totalApiKeys,
|
|
80
|
+
icon: /* @__PURE__ */ C(_, { size: 20 }),
|
|
81
|
+
description: A("devportal.activeKeys", "Active keys")
|
|
64
82
|
}),
|
|
65
|
-
/* @__PURE__ */
|
|
66
|
-
title:
|
|
67
|
-
value:
|
|
68
|
-
icon: /* @__PURE__ */ S
|
|
69
|
-
description:
|
|
83
|
+
/* @__PURE__ */ C(i, {
|
|
84
|
+
title: A("devportal.webhooks", "Webhooks"),
|
|
85
|
+
value: F.totalWebhooks,
|
|
86
|
+
icon: /* @__PURE__ */ C(S, { size: 20 }),
|
|
87
|
+
description: A("devportal.configured", "Configured")
|
|
70
88
|
})
|
|
71
89
|
]
|
|
72
90
|
})
|
|
73
91
|
}),
|
|
74
|
-
/* @__PURE__ */
|
|
75
|
-
title:
|
|
92
|
+
/* @__PURE__ */ C(r, {
|
|
93
|
+
title: A("devportal.usageOverview", "Usage Overview"),
|
|
76
94
|
"data-tour": "devportal-dashboard-usage-overview",
|
|
77
|
-
children: /* @__PURE__ */
|
|
95
|
+
children: /* @__PURE__ */ w(a, {
|
|
78
96
|
columns: 4,
|
|
79
97
|
children: [
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
-
title:
|
|
82
|
-
value:
|
|
83
|
-
icon: /* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ C(i, {
|
|
99
|
+
title: A("devportal.apiCalls", "API Calls"),
|
|
100
|
+
value: L ? /* @__PURE__ */ C(v, { className: "h-5 w-5 animate-spin" }) : F.totalApiCalls > 1e6 ? `${(F.totalApiCalls / 1e6).toFixed(1)}M` : F.totalApiCalls > 1e3 ? `${(F.totalApiCalls / 1e3).toFixed(1)}K` : F.totalApiCalls.toString(),
|
|
101
|
+
icon: /* @__PURE__ */ C(f, { size: 20 }),
|
|
84
102
|
trend: void 0
|
|
85
103
|
}),
|
|
86
|
-
/* @__PURE__ */
|
|
87
|
-
title:
|
|
88
|
-
value:
|
|
89
|
-
icon: /* @__PURE__ */
|
|
104
|
+
/* @__PURE__ */ C(i, {
|
|
105
|
+
title: A("devportal.activeInstalls", "Active Installs"),
|
|
106
|
+
value: L ? /* @__PURE__ */ C(v, { className: "h-5 w-5 animate-spin" }) : F.activeInstalls > 1e3 ? `${(F.activeInstalls / 1e3).toFixed(1)}K` : F.activeInstalls.toString(),
|
|
107
|
+
icon: /* @__PURE__ */ C(x, { size: 20 }),
|
|
90
108
|
trend: void 0
|
|
91
109
|
}),
|
|
92
|
-
/* @__PURE__ */
|
|
93
|
-
title:
|
|
94
|
-
value:
|
|
95
|
-
icon: /* @__PURE__ */
|
|
110
|
+
/* @__PURE__ */ C(i, {
|
|
111
|
+
title: A("devportal.monthlyRevenue", "Monthly Revenue"),
|
|
112
|
+
value: L ? /* @__PURE__ */ C(v, { className: "h-5 w-5 animate-spin" }) : `$${F.monthlyRevenue.toLocaleString()}`,
|
|
113
|
+
icon: /* @__PURE__ */ C(g, { size: 20 }),
|
|
96
114
|
trend: void 0
|
|
97
115
|
}),
|
|
98
|
-
/* @__PURE__ */
|
|
99
|
-
title:
|
|
100
|
-
value: `${
|
|
101
|
-
icon: /* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ C(i, {
|
|
117
|
+
title: A("devportal.errorRate", "Error Rate"),
|
|
118
|
+
value: `${F.errorRate}%`,
|
|
119
|
+
icon: /* @__PURE__ */ C(b, { size: 20 }),
|
|
102
120
|
trend: void 0
|
|
103
121
|
})
|
|
104
122
|
]
|
|
105
123
|
})
|
|
106
124
|
}),
|
|
107
|
-
/* @__PURE__ */
|
|
125
|
+
/* @__PURE__ */ w("div", {
|
|
108
126
|
className: "grid gap-6 lg:grid-cols-2",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
127
|
+
children: [/* @__PURE__ */ C(r, {
|
|
110
128
|
"data-tour": "devportal-dashboard-recent-apps",
|
|
111
|
-
title:
|
|
112
|
-
actions: /* @__PURE__ */
|
|
113
|
-
onClick: () =>
|
|
129
|
+
title: A("devportal.recentApplications", "Recent Applications"),
|
|
130
|
+
actions: /* @__PURE__ */ w("button", {
|
|
131
|
+
onClick: () => I("/apps"),
|
|
114
132
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
115
133
|
children: [
|
|
116
|
-
|
|
134
|
+
A("devportal.viewAll", "View all"),
|
|
117
135
|
" ",
|
|
118
|
-
/* @__PURE__ */
|
|
136
|
+
/* @__PURE__ */ C(m, { size: 14 })
|
|
119
137
|
]
|
|
120
138
|
}),
|
|
121
|
-
children:
|
|
139
|
+
children: L ? /* @__PURE__ */ C("div", {
|
|
122
140
|
className: "flex items-center justify-center py-8",
|
|
123
|
-
children: /* @__PURE__ */
|
|
124
|
-
}) :
|
|
141
|
+
children: /* @__PURE__ */ C(v, { className: "h-6 w-6 animate-spin text-muted-foreground" })
|
|
142
|
+
}) : P.length > 0 ? /* @__PURE__ */ C("div", {
|
|
125
143
|
className: "space-y-2",
|
|
126
|
-
children:
|
|
144
|
+
children: P.slice(0, 3).map((e) => /* @__PURE__ */ C(o, {
|
|
127
145
|
app: e,
|
|
128
|
-
onClick: () =>
|
|
146
|
+
onClick: () => I(`/apps/${e.id}`)
|
|
129
147
|
}, e.id))
|
|
130
|
-
}) : /* @__PURE__ */
|
|
131
|
-
icon: /* @__PURE__ */
|
|
132
|
-
title:
|
|
133
|
-
description:
|
|
134
|
-
action: /* @__PURE__ */
|
|
135
|
-
onClick: () =>
|
|
148
|
+
}) : /* @__PURE__ */ C(t, {
|
|
149
|
+
icon: /* @__PURE__ */ C(p, { size: 24 }),
|
|
150
|
+
title: A("devportal.noApplicationsYet", "No applications yet"),
|
|
151
|
+
description: A("devportal.createFirstApplicationToGetStarted", "Create your first application to get started"),
|
|
152
|
+
action: /* @__PURE__ */ w("button", {
|
|
153
|
+
onClick: () => I("/apps/new"),
|
|
136
154
|
className: "inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90",
|
|
137
|
-
children: [/* @__PURE__ */
|
|
155
|
+
children: [/* @__PURE__ */ C(y, { size: 16 }), A("devportal.createApp", "Create App")]
|
|
138
156
|
})
|
|
139
157
|
})
|
|
140
|
-
}), /* @__PURE__ */
|
|
141
|
-
title:
|
|
158
|
+
}), /* @__PURE__ */ C(r, {
|
|
159
|
+
title: A("devportal.quickActions", "Quick Actions"),
|
|
142
160
|
"data-tour": "devportal-dashboard-quick-actions",
|
|
143
|
-
children: /* @__PURE__ */
|
|
161
|
+
children: /* @__PURE__ */ w("div", {
|
|
144
162
|
className: "grid gap-3",
|
|
145
163
|
children: [
|
|
146
|
-
/* @__PURE__ */
|
|
147
|
-
title:
|
|
148
|
-
description:
|
|
149
|
-
icon: /* @__PURE__ */
|
|
150
|
-
onClick: () =>
|
|
164
|
+
/* @__PURE__ */ C(E, {
|
|
165
|
+
title: A("devportal.createNewApp", "Create New App"),
|
|
166
|
+
description: A("devportal.startBuildingANewApplication", "Start building a new application"),
|
|
167
|
+
icon: /* @__PURE__ */ C(p, { size: 20 }),
|
|
168
|
+
onClick: () => I("/apps/new")
|
|
151
169
|
}),
|
|
152
|
-
/* @__PURE__ */
|
|
153
|
-
title:
|
|
154
|
-
description:
|
|
155
|
-
icon: /* @__PURE__ */
|
|
156
|
-
onClick: () =>
|
|
170
|
+
/* @__PURE__ */ C(E, {
|
|
171
|
+
title: A("devportal.createOrganization", "Create Organization"),
|
|
172
|
+
description: A("devportal.setUpATeamToCollaborate", "Set up a team to collaborate"),
|
|
173
|
+
icon: /* @__PURE__ */ C(h, { size: 20 }),
|
|
174
|
+
onClick: () => I("/organizations/new")
|
|
157
175
|
}),
|
|
158
|
-
/* @__PURE__ */
|
|
159
|
-
title:
|
|
160
|
-
description:
|
|
161
|
-
icon: /* @__PURE__ */
|
|
162
|
-
onClick: () =>
|
|
176
|
+
/* @__PURE__ */ C(E, {
|
|
177
|
+
title: A("devportal.generateApiKey", "Generate API Key"),
|
|
178
|
+
description: A("devportal.createCredentialsForYourApps", "Create credentials for your apps"),
|
|
179
|
+
icon: /* @__PURE__ */ C(_, { size: 20 }),
|
|
180
|
+
onClick: () => I("/api-keys")
|
|
163
181
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
-
title:
|
|
166
|
-
description:
|
|
167
|
-
icon: /* @__PURE__ */ S
|
|
168
|
-
onClick: () =>
|
|
182
|
+
/* @__PURE__ */ C(E, {
|
|
183
|
+
title: A("devportal.configureWebhooks", "Configure Webhooks"),
|
|
184
|
+
description: A("devportal.setUpEventNotifications", "Set up event notifications"),
|
|
185
|
+
icon: /* @__PURE__ */ C(S, { size: 20 }),
|
|
186
|
+
onClick: () => I("/webhooks")
|
|
169
187
|
})
|
|
170
188
|
]
|
|
171
189
|
})
|
|
172
190
|
})]
|
|
173
191
|
}),
|
|
174
|
-
|
|
192
|
+
L ? /* @__PURE__ */ C(r, {
|
|
175
193
|
"data-tour": "devportal-dashboard-organizations",
|
|
176
|
-
title:
|
|
177
|
-
children: /* @__PURE__ */
|
|
194
|
+
title: A("devportal.yourOrganizations", "Your Organizations"),
|
|
195
|
+
children: /* @__PURE__ */ C("div", {
|
|
178
196
|
className: "flex items-center justify-center py-8",
|
|
179
|
-
children: /* @__PURE__ */
|
|
197
|
+
children: /* @__PURE__ */ C(v, { className: "h-6 w-6 animate-spin text-muted-foreground" })
|
|
180
198
|
})
|
|
181
|
-
}) :
|
|
182
|
-
title:
|
|
183
|
-
actions: /* @__PURE__ */
|
|
184
|
-
onClick: () =>
|
|
199
|
+
}) : N.length > 0 ? /* @__PURE__ */ C(r, {
|
|
200
|
+
title: A("devportal.yourOrganizations", "Your Organizations"),
|
|
201
|
+
actions: /* @__PURE__ */ w("button", {
|
|
202
|
+
onClick: () => I("/organizations"),
|
|
185
203
|
className: "text-sm text-primary hover:underline flex items-center gap-1",
|
|
186
204
|
children: [
|
|
187
|
-
|
|
205
|
+
A("devportal.viewAll", "View all"),
|
|
188
206
|
" ",
|
|
189
|
-
/* @__PURE__ */
|
|
207
|
+
/* @__PURE__ */ C(m, { size: 14 })
|
|
190
208
|
]
|
|
191
209
|
}),
|
|
192
|
-
children: /* @__PURE__ */
|
|
210
|
+
children: /* @__PURE__ */ C("div", {
|
|
193
211
|
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
194
|
-
children:
|
|
212
|
+
children: N.slice(0, 3).map((e) => /* @__PURE__ */ C(D, {
|
|
195
213
|
org: e,
|
|
196
|
-
onClick: () =>
|
|
214
|
+
onClick: () => I(`/organizations/${e.id}`)
|
|
197
215
|
}, e.id))
|
|
198
216
|
})
|
|
199
217
|
}) : null
|
|
200
218
|
]
|
|
201
219
|
})
|
|
202
220
|
});
|
|
203
|
-
},
|
|
221
|
+
}, E = ({ title: e, description: t, icon: n, onClick: r }) => /* @__PURE__ */ w("button", {
|
|
204
222
|
onClick: r,
|
|
205
223
|
className: "flex items-center gap-4 rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors",
|
|
206
|
-
children: [/* @__PURE__ */
|
|
224
|
+
children: [/* @__PURE__ */ C("div", {
|
|
207
225
|
className: "rounded-lg bg-primary/10 p-2 text-primary",
|
|
208
226
|
children: n
|
|
209
|
-
}), /* @__PURE__ */
|
|
227
|
+
}), /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("p", {
|
|
210
228
|
className: "font-medium",
|
|
211
229
|
children: e
|
|
212
|
-
}), /* @__PURE__ */
|
|
230
|
+
}), /* @__PURE__ */ C("p", {
|
|
213
231
|
className: "text-sm text-muted-foreground",
|
|
214
232
|
children: t
|
|
215
233
|
})] })]
|
|
216
|
-
}),
|
|
217
|
-
let { t: n } =
|
|
234
|
+
}), D = ({ org: e, onClick: t }) => {
|
|
235
|
+
let { t: n } = d(), r = (e, t) => {
|
|
218
236
|
let r = n(e);
|
|
219
237
|
return r === e ? t : r;
|
|
220
238
|
};
|
|
221
|
-
return /* @__PURE__ */
|
|
239
|
+
return /* @__PURE__ */ w("button", {
|
|
222
240
|
onClick: t,
|
|
223
241
|
className: "rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors",
|
|
224
|
-
children: [/* @__PURE__ */
|
|
242
|
+
children: [/* @__PURE__ */ w("div", {
|
|
225
243
|
className: "flex items-center gap-3",
|
|
226
|
-
children: [e.logoUrl ? /* @__PURE__ */
|
|
244
|
+
children: [e.logoUrl ? /* @__PURE__ */ C("img", {
|
|
227
245
|
src: e.logoUrl,
|
|
228
246
|
alt: e.name,
|
|
229
247
|
className: "h-10 w-10 rounded-lg object-cover"
|
|
230
|
-
}) : /* @__PURE__ */
|
|
248
|
+
}) : /* @__PURE__ */ C("div", {
|
|
231
249
|
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary",
|
|
232
|
-
children: /* @__PURE__ */
|
|
233
|
-
}), /* @__PURE__ */
|
|
250
|
+
children: /* @__PURE__ */ C(h, { size: 20 })
|
|
251
|
+
}), /* @__PURE__ */ w("div", {
|
|
234
252
|
className: "flex-1 min-w-0",
|
|
235
|
-
children: [/* @__PURE__ */
|
|
253
|
+
children: [/* @__PURE__ */ C("p", {
|
|
236
254
|
className: "font-medium truncate",
|
|
237
255
|
children: e.name
|
|
238
|
-
}), /* @__PURE__ */
|
|
256
|
+
}), /* @__PURE__ */ w("p", {
|
|
239
257
|
className: "text-sm text-muted-foreground",
|
|
240
258
|
children: [
|
|
241
259
|
e.publishedApps,
|
|
@@ -244,13 +262,13 @@ var w = () => {
|
|
|
244
262
|
]
|
|
245
263
|
})]
|
|
246
264
|
})]
|
|
247
|
-
}), /* @__PURE__ */
|
|
265
|
+
}), /* @__PURE__ */ w("div", {
|
|
248
266
|
className: "mt-3 flex gap-4 text-sm text-muted-foreground",
|
|
249
|
-
children: [/* @__PURE__ */
|
|
267
|
+
children: [/* @__PURE__ */ w("span", { children: [
|
|
250
268
|
(e.totalInstalls || 0).toLocaleString(),
|
|
251
269
|
" ",
|
|
252
270
|
r("devportal.installs", "installs")
|
|
253
|
-
] }), /* @__PURE__ */
|
|
271
|
+
] }), /* @__PURE__ */ w("span", { children: [
|
|
254
272
|
"$",
|
|
255
273
|
(e.totalRevenue || 0).toLocaleString(),
|
|
256
274
|
" ",
|
|
@@ -260,6 +278,6 @@ var w = () => {
|
|
|
260
278
|
});
|
|
261
279
|
};
|
|
262
280
|
//#endregion
|
|
263
|
-
export {
|
|
281
|
+
export { T as DashboardPage };
|
|
264
282
|
|
|
265
283
|
//# 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 { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n AppWindow,\n Building2,\n Key,\n Webhook,\n TrendingUp,\n Users,\n DollarSign,\n Activity,\n Plus,\n ArrowRight,\n Loader2,\n} from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { AppListItem } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport type { Application, DeveloperOrganization } from \"../types\";\nimport {\n useGetDevportalDashboardBootstrapQuery,\n type OrganizationFieldsFragment,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\n/**\n * Developer Portal Dashboard Page\n *\n * Shows an overview of the developer's applications, organizations,\n * and key metrics using real data from the GraphQL API.\n */\nexport const DashboardPage: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Fetch dashboard bootstrap in a single global query\n const { data, loading } = useGetDevportalDashboardBootstrapQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const organizations: OrganizationFieldsFragment[] =\n data?.myDeveloperOrganizations ?? [];\n const applications: ApplicationFieldsFragment[] =\n data?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n // Calculate aggregate stats\n const stats = {\n totalApps: data?.applications?.totalCount ?? 0,\n totalOrgs: organizations.length,\n totalApiKeys: 0,\n totalWebhooks: 0,\n totalApiCalls: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + parseInt(org.totalApiCalls || \"0\"),\n 0,\n ),\n activeInstalls: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + (org.activeInstalls || 0),\n 0,\n ),\n monthlyRevenue: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + (org.totalRevenue || 0),\n 0,\n ),\n errorRate: 0,\n };\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const isLoading = loading;\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n title={tr(\"devportal.developerPortal\", \"Developer Portal\")}\n description={tr(\n \"devportal.buildManageMonitor\",\n \"Build, manage, and monitor your applications\",\n )}\n actions={\n <button\n data-tour=\"devportal-dashboard-create-app-button\"\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n }\n >\n {/* Stats Overview */}\n <PageSection data-tour=\"devportal-dashboard-stats-overview\">\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.totalApps\", \"Total Apps\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n stats.totalApps\n )\n }\n icon={<AppWindow size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.organizations\", \"Organizations\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n stats.totalOrgs\n )\n }\n icon={<Building2 size={20} />}\n />\n <StatCard\n title={tr(\"devportal.apiKeys\", \"API Keys\")}\n value={stats.totalApiKeys}\n icon={<Key size={20} />}\n description={tr(\"devportal.activeKeys\", \"Active keys\")}\n />\n <StatCard\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n value={stats.totalWebhooks}\n icon={<Webhook size={20} />}\n description={tr(\"devportal.configured\", \"Configured\")}\n />\n </StatCardGrid>\n </PageSection>\n\n {/* Usage Stats */}\n <PageSection\n title={tr(\"devportal.usageOverview\", \"Usage Overview\")}\n data-tour=\"devportal-dashboard-usage-overview\"\n >\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.apiCalls\", \"API Calls\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : stats.totalApiCalls > 1000000 ? (\n `${(stats.totalApiCalls / 1000000).toFixed(1)}M`\n ) : stats.totalApiCalls > 1000 ? (\n `${(stats.totalApiCalls / 1000).toFixed(1)}K`\n ) : (\n stats.totalApiCalls.toString()\n )\n }\n icon={<Activity size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.activeInstalls\", \"Active Installs\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : stats.activeInstalls > 1000 ? (\n `${(stats.activeInstalls / 1000).toFixed(1)}K`\n ) : (\n stats.activeInstalls.toString()\n )\n }\n icon={<Users size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.monthlyRevenue\", \"Monthly Revenue\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n `$${stats.monthlyRevenue.toLocaleString()}`\n )\n }\n icon={<DollarSign size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.errorRate\", \"Error Rate\")}\n value={`${stats.errorRate}%`}\n icon={<TrendingUp size={20} />}\n trend={undefined}\n />\n </StatCardGrid>\n </PageSection>\n\n {/* Quick Actions & Recent Items */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Recent Apps */}\n <PageSection\n data-tour=\"devportal-dashboard-recent-apps\"\n title={tr(\"devportal.recentApplications\", \"Recent Applications\")}\n actions={\n <button\n onClick={() => handleNavigate(\"/apps\")}\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n >\n {tr(\"devportal.viewAll\", \"View all\")} <ArrowRight size={14} />\n </button>\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n </div>\n ) : applications.length > 0 ? (\n <div className=\"space-y-2\">\n {applications\n .slice(0, 3)\n .map((app: ApplicationFieldsFragment) => (\n <AppListItem\n key={app.id}\n app={app as Application}\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<AppWindow size={24} />}\n title={tr(\"devportal.noApplicationsYet\", \"No applications yet\")}\n description={tr(\n \"devportal.createFirstApplicationToGetStarted\",\n \"Create your first application to get started\",\n )}\n action={\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n }\n />\n )}\n </PageSection>\n\n {/* Quick Actions */}\n <PageSection\n title={tr(\"devportal.quickActions\", \"Quick Actions\")}\n data-tour=\"devportal-dashboard-quick-actions\"\n >\n <div className=\"grid gap-3\">\n <QuickActionCard\n title={tr(\"devportal.createNewApp\", \"Create New App\")}\n description={tr(\n \"devportal.startBuildingANewApplication\",\n \"Start building a new application\",\n )}\n icon={<AppWindow size={20} />}\n onClick={() => handleNavigate(\"/apps/new\")}\n />\n <QuickActionCard\n title={tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n description={tr(\n \"devportal.setUpATeamToCollaborate\",\n \"Set up a team to collaborate\",\n )}\n icon={<Building2 size={20} />}\n onClick={() => handleNavigate(\"/organizations/new\")}\n />\n <QuickActionCard\n title={tr(\"devportal.generateApiKey\", \"Generate API Key\")}\n description={tr(\n \"devportal.createCredentialsForYourApps\",\n \"Create credentials for your apps\",\n )}\n icon={<Key size={20} />}\n onClick={() => handleNavigate(\"/api-keys\")}\n />\n <QuickActionCard\n title={tr(\"devportal.configureWebhooks\", \"Configure Webhooks\")}\n description={tr(\n \"devportal.setUpEventNotifications\",\n \"Set up event notifications\",\n )}\n icon={<Webhook size={20} />}\n onClick={() => handleNavigate(\"/webhooks\")}\n />\n </div>\n </PageSection>\n </div>\n\n {/* Organizations */}\n {isLoading ? (\n <PageSection\n data-tour=\"devportal-dashboard-organizations\"\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n >\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n </div>\n </PageSection>\n ) : organizations.length > 0 ? (\n <PageSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n actions={\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n >\n {tr(\"devportal.viewAll\", \"View all\")} <ArrowRight size={14} />\n </button>\n }\n >\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {organizations\n .slice(0, 3)\n .map((org: OrganizationFieldsFragment) => (\n <OrgCard\n key={org.id}\n org={org as DeveloperOrganization}\n onClick={() => handleNavigate(`/organizations/${org.id}`)}\n />\n ))}\n </div>\n </PageSection>\n ) : null}\n </PageLayout>\n </PermissionGuard>\n );\n};\n\n// Quick action card component\nconst QuickActionCard: FC<{\n title: string;\n description: string;\n icon: React.ReactNode;\n onClick: () => void;\n}> = ({ title, description, icon, onClick }) => (\n <button\n onClick={onClick}\n className=\"flex items-center gap-4 rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors\"\n >\n <div className=\"rounded-lg bg-primary/10 p-2 text-primary\">{icon}</div>\n <div>\n <p className=\"font-medium\">{title}</p>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </div>\n </button>\n);\n\n// Organization card component\nconst OrgCard: FC<{\n org: DeveloperOrganization;\n onClick: () => void;\n}> = ({ org, onClick }) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n return (\n <button\n onClick={onClick}\n className=\"rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary\">\n <Building2 size={20} />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium truncate\">{org.name}</p>\n <p className=\"text-sm text-muted-foreground\">\n {org.publishedApps}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n </div>\n <div className=\"mt-3 flex gap-4 text-sm text-muted-foreground\">\n <span>\n {(org.totalInstalls || 0).toLocaleString()}{\" \"}\n {tr(\"devportal.installs\", \"installs\")}\n </span>\n <span>\n ${(org.totalRevenue || 0).toLocaleString()}{\" \"}\n {tr(\"devportal.revenue\", \"revenue\")}\n </span>\n </div>\n </button>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/devportal/pages/DashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n AppWindow,\n Building2,\n Key,\n Webhook,\n TrendingUp,\n Users,\n DollarSign,\n Activity,\n Plus,\n ArrowRight,\n Loader2,\n} from \"lucide-react\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { AppListItem } from \"../components/AppCard\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport type { Application, DeveloperOrganization } from \"../types\";\nimport {\n useGetDevportalDashboardBootstrapQuery,\n type OrganizationFieldsFragment,\n type ApplicationFieldsFragment,\n} from \"../hooks\";\n\n/**\n * Developer Portal Dashboard Page\n *\n * Shows an overview of the developer's applications, organizations,\n * and key metrics using real data from the GraphQL API.\n */\nexport const DashboardPage: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Fetch dashboard bootstrap in a single global query\n const { data, loading } = useGetDevportalDashboardBootstrapQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const organizations: OrganizationFieldsFragment[] =\n data?.myDeveloperOrganizations ?? [];\n\n // First-time users: redirect to setup wizard when no org exists and they\n // haven't already dismissed the wizard in this browser.\n useEffect(() => {\n if (loading || !data) return;\n if (organizations.length > 0) return;\n try {\n if (localStorage.getItem(\"devportal_setup_dismissed\")) return;\n } catch {\n // localStorage unavailable — don't redirect\n return;\n }\n navigate?.(`${basePath}/setup`);\n }, [loading, data, organizations.length, navigate, basePath]);\n const applications: ApplicationFieldsFragment[] =\n data?.applications?.edges?.map(\n (e: { node: ApplicationFieldsFragment }) => e.node,\n ) ?? [];\n\n // Calculate aggregate stats\n const stats = {\n totalApps: data?.applications?.totalCount ?? 0,\n totalOrgs: organizations.length,\n totalApiKeys: 0,\n totalWebhooks: 0,\n totalApiCalls: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + parseInt(org.totalApiCalls || \"0\"),\n 0,\n ),\n activeInstalls: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + (org.activeInstalls || 0),\n 0,\n ),\n monthlyRevenue: organizations.reduce(\n (acc: number, org: OrganizationFieldsFragment) =>\n acc + (org.totalRevenue || 0),\n 0,\n ),\n errorRate: 0,\n };\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const isLoading = loading;\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n title={tr(\"devportal.developerPortal\", \"Developer Portal\")}\n description={tr(\n \"devportal.buildManageMonitor\",\n \"Build, manage, and monitor your applications\",\n )}\n actions={\n <button\n data-tour=\"devportal-dashboard-create-app-button\"\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n }\n >\n {/* Stats Overview */}\n <PageSection data-tour=\"devportal-dashboard-stats-overview\">\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.totalApps\", \"Total Apps\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n stats.totalApps\n )\n }\n icon={<AppWindow size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.organizations\", \"Organizations\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n stats.totalOrgs\n )\n }\n icon={<Building2 size={20} />}\n />\n <StatCard\n title={tr(\"devportal.apiKeys\", \"API Keys\")}\n value={stats.totalApiKeys}\n icon={<Key size={20} />}\n description={tr(\"devportal.activeKeys\", \"Active keys\")}\n />\n <StatCard\n title={tr(\"devportal.webhooks\", \"Webhooks\")}\n value={stats.totalWebhooks}\n icon={<Webhook size={20} />}\n description={tr(\"devportal.configured\", \"Configured\")}\n />\n </StatCardGrid>\n </PageSection>\n\n {/* Usage Stats */}\n <PageSection\n title={tr(\"devportal.usageOverview\", \"Usage Overview\")}\n data-tour=\"devportal-dashboard-usage-overview\"\n >\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.apiCalls\", \"API Calls\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : stats.totalApiCalls > 1000000 ? (\n `${(stats.totalApiCalls / 1000000).toFixed(1)}M`\n ) : stats.totalApiCalls > 1000 ? (\n `${(stats.totalApiCalls / 1000).toFixed(1)}K`\n ) : (\n stats.totalApiCalls.toString()\n )\n }\n icon={<Activity size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.activeInstalls\", \"Active Installs\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : stats.activeInstalls > 1000 ? (\n `${(stats.activeInstalls / 1000).toFixed(1)}K`\n ) : (\n stats.activeInstalls.toString()\n )\n }\n icon={<Users size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.monthlyRevenue\", \"Monthly Revenue\")}\n value={\n isLoading ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n `$${stats.monthlyRevenue.toLocaleString()}`\n )\n }\n icon={<DollarSign size={20} />}\n trend={undefined}\n />\n <StatCard\n title={tr(\"devportal.errorRate\", \"Error Rate\")}\n value={`${stats.errorRate}%`}\n icon={<TrendingUp size={20} />}\n trend={undefined}\n />\n </StatCardGrid>\n </PageSection>\n\n {/* Quick Actions & Recent Items */}\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Recent Apps */}\n <PageSection\n data-tour=\"devportal-dashboard-recent-apps\"\n title={tr(\"devportal.recentApplications\", \"Recent Applications\")}\n actions={\n <button\n onClick={() => handleNavigate(\"/apps\")}\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n >\n {tr(\"devportal.viewAll\", \"View all\")} <ArrowRight size={14} />\n </button>\n }\n >\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n </div>\n ) : applications.length > 0 ? (\n <div className=\"space-y-2\">\n {applications\n .slice(0, 3)\n .map((app: ApplicationFieldsFragment) => (\n <AppListItem\n key={app.id}\n app={app as Application}\n onClick={() => handleNavigate(`/apps/${app.id}`)}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<AppWindow size={24} />}\n title={tr(\"devportal.noApplicationsYet\", \"No applications yet\")}\n description={tr(\n \"devportal.createFirstApplicationToGetStarted\",\n \"Create your first application to get started\",\n )}\n action={\n <button\n onClick={() => handleNavigate(\"/apps/new\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createApp\", \"Create App\")}\n </button>\n }\n />\n )}\n </PageSection>\n\n {/* Quick Actions */}\n <PageSection\n title={tr(\"devportal.quickActions\", \"Quick Actions\")}\n data-tour=\"devportal-dashboard-quick-actions\"\n >\n <div className=\"grid gap-3\">\n <QuickActionCard\n title={tr(\"devportal.createNewApp\", \"Create New App\")}\n description={tr(\n \"devportal.startBuildingANewApplication\",\n \"Start building a new application\",\n )}\n icon={<AppWindow size={20} />}\n onClick={() => handleNavigate(\"/apps/new\")}\n />\n <QuickActionCard\n title={tr(\n \"devportal.createOrganization\",\n \"Create Organization\",\n )}\n description={tr(\n \"devportal.setUpATeamToCollaborate\",\n \"Set up a team to collaborate\",\n )}\n icon={<Building2 size={20} />}\n onClick={() => handleNavigate(\"/organizations/new\")}\n />\n <QuickActionCard\n title={tr(\"devportal.generateApiKey\", \"Generate API Key\")}\n description={tr(\n \"devportal.createCredentialsForYourApps\",\n \"Create credentials for your apps\",\n )}\n icon={<Key size={20} />}\n onClick={() => handleNavigate(\"/api-keys\")}\n />\n <QuickActionCard\n title={tr(\"devportal.configureWebhooks\", \"Configure Webhooks\")}\n description={tr(\n \"devportal.setUpEventNotifications\",\n \"Set up event notifications\",\n )}\n icon={<Webhook size={20} />}\n onClick={() => handleNavigate(\"/webhooks\")}\n />\n </div>\n </PageSection>\n </div>\n\n {/* Organizations */}\n {isLoading ? (\n <PageSection\n data-tour=\"devportal-dashboard-organizations\"\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n >\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n </div>\n </PageSection>\n ) : organizations.length > 0 ? (\n <PageSection\n title={tr(\"devportal.yourOrganizations\", \"Your Organizations\")}\n actions={\n <button\n onClick={() => handleNavigate(\"/organizations\")}\n className=\"text-sm text-primary hover:underline flex items-center gap-1\"\n >\n {tr(\"devportal.viewAll\", \"View all\")} <ArrowRight size={14} />\n </button>\n }\n >\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {organizations\n .slice(0, 3)\n .map((org: OrganizationFieldsFragment) => (\n <OrgCard\n key={org.id}\n org={org as DeveloperOrganization}\n onClick={() => handleNavigate(`/organizations/${org.id}`)}\n />\n ))}\n </div>\n </PageSection>\n ) : null}\n </PageLayout>\n </PermissionGuard>\n );\n};\n\n// Quick action card component\nconst QuickActionCard: FC<{\n title: string;\n description: string;\n icon: React.ReactNode;\n onClick: () => void;\n}> = ({ title, description, icon, onClick }) => (\n <button\n onClick={onClick}\n className=\"flex items-center gap-4 rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors\"\n >\n <div className=\"rounded-lg bg-primary/10 p-2 text-primary\">{icon}</div>\n <div>\n <p className=\"font-medium\">{title}</p>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </div>\n </button>\n);\n\n// Organization card component\nconst OrgCard: FC<{\n org: DeveloperOrganization;\n onClick: () => void;\n}> = ({ org, onClick }) => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n return (\n <button\n onClick={onClick}\n className=\"rounded-lg border bg-card p-4 text-left hover:bg-accent transition-colors\"\n >\n <div className=\"flex items-center gap-3\">\n {org.logoUrl ? (\n <img\n src={org.logoUrl}\n alt={org.name}\n className=\"h-10 w-10 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary\">\n <Building2 size={20} />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium truncate\">{org.name}</p>\n <p className=\"text-sm text-muted-foreground\">\n {org.publishedApps}{\" \"}\n {tr(\"devportal.appsPublished\", \"apps published\")}\n </p>\n </div>\n </div>\n <div className=\"mt-3 flex gap-4 text-sm text-muted-foreground\">\n <span>\n {(org.totalInstalls || 0).toLocaleString()}{\" \"}\n {tr(\"devportal.installs\", \"installs\")}\n </span>\n <span>\n ${(org.totalRevenue || 0).toLocaleString()}{\" \"}\n {tr(\"devportal.revenue\", \"revenue\")}\n </span>\n </div>\n </button>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
|
+
import { useDevportalEventBus as t } from "../events/index.js";
|
|
3
|
+
import { APP_TYPE_LABELS as n } from "../utils/constants.js";
|
|
4
|
+
import { InputField as r, SelectField as i, TextareaField as a } from "../components/FormField.js";
|
|
5
|
+
import { useCreateApplicationMutation as ee, useCreateOrganizationMutation as te } from "../../generated/global-operations.js";
|
|
6
|
+
import { useState as o } from "react";
|
|
7
|
+
import { useI18n as s } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
|
+
import { AppWindow as c, ArrowRight as ne, BookOpen as re, Building2 as l, Check as u, ChevronRight as d, Key as f, Rocket as p } from "lucide-react";
|
|
9
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
10
|
+
//#region src/devportal/pages/SetupPage.tsx
|
|
11
|
+
var g = [
|
|
12
|
+
{
|
|
13
|
+
value: "FREE",
|
|
14
|
+
label: "Free",
|
|
15
|
+
price: "Free",
|
|
16
|
+
apps: "1 app",
|
|
17
|
+
calls: "100K calls/mo"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: "DEVELOPER",
|
|
21
|
+
label: "Developer",
|
|
22
|
+
price: "$29/mo",
|
|
23
|
+
apps: "3 apps",
|
|
24
|
+
calls: "1M calls/mo"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
value: "PROFESSIONAL",
|
|
28
|
+
label: "Professional",
|
|
29
|
+
price: "$99/mo",
|
|
30
|
+
apps: "10 apps",
|
|
31
|
+
calls: "10M calls/mo"
|
|
32
|
+
}
|
|
33
|
+
], _ = [{
|
|
34
|
+
value: "",
|
|
35
|
+
label: "Select type…"
|
|
36
|
+
}, ...Object.entries(n).map(([e, t]) => ({
|
|
37
|
+
value: e,
|
|
38
|
+
label: t
|
|
39
|
+
}))], v = [
|
|
40
|
+
{
|
|
41
|
+
value: "",
|
|
42
|
+
label: "Select category…"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
value: "productivity",
|
|
46
|
+
label: "Productivity"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
value: "communication",
|
|
50
|
+
label: "Communication"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
value: "analytics",
|
|
54
|
+
label: "Analytics"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
value: "automation",
|
|
58
|
+
label: "Automation"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
value: "developer-tools",
|
|
62
|
+
label: "Developer Tools"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
value: "finance",
|
|
66
|
+
label: "Finance"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
value: "hr",
|
|
70
|
+
label: "HR & People"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
value: "marketing",
|
|
74
|
+
label: "Marketing"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
value: "project-management",
|
|
78
|
+
label: "Project Management"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
value: "security",
|
|
82
|
+
label: "Security"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
value: "support",
|
|
86
|
+
label: "Support"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
value: "other",
|
|
90
|
+
label: "Other"
|
|
91
|
+
}
|
|
92
|
+
];
|
|
93
|
+
function y(e) {
|
|
94
|
+
return e.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
95
|
+
}
|
|
96
|
+
function b() {
|
|
97
|
+
try {
|
|
98
|
+
localStorage.setItem("devportal_setup_dismissed", "1");
|
|
99
|
+
} catch {}
|
|
100
|
+
}
|
|
101
|
+
var x = [
|
|
102
|
+
{
|
|
103
|
+
key: "org",
|
|
104
|
+
label: "Organization"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
key: "app",
|
|
108
|
+
label: "First app"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
key: "done",
|
|
112
|
+
label: "Done"
|
|
113
|
+
}
|
|
114
|
+
], ie = ({ current: e }) => {
|
|
115
|
+
let t = x.findIndex((t) => t.key === e);
|
|
116
|
+
return /* @__PURE__ */ m("div", {
|
|
117
|
+
className: "flex items-center gap-0",
|
|
118
|
+
children: x.map((e, n) => {
|
|
119
|
+
let r = n < t, i = n === t;
|
|
120
|
+
return /* @__PURE__ */ h("div", {
|
|
121
|
+
className: "flex items-center",
|
|
122
|
+
children: [/* @__PURE__ */ h("div", {
|
|
123
|
+
className: "flex flex-col items-center gap-1.5",
|
|
124
|
+
children: [/* @__PURE__ */ m("div", {
|
|
125
|
+
className: ["flex h-8 w-8 items-center justify-center rounded-full text-sm font-semibold transition-colors", r ? "bg-primary text-primary-foreground" : i ? "bg-primary text-primary-foreground ring-4 ring-primary/20" : "bg-muted text-muted-foreground"].join(" "),
|
|
126
|
+
children: r ? /* @__PURE__ */ m(u, { size: 14 }) : n + 1
|
|
127
|
+
}), /* @__PURE__ */ m("span", {
|
|
128
|
+
className: ["text-xs font-medium whitespace-nowrap", i ? "text-foreground" : "text-muted-foreground"].join(" "),
|
|
129
|
+
children: e.label
|
|
130
|
+
})]
|
|
131
|
+
}), n < x.length - 1 && /* @__PURE__ */ m("div", { className: ["mx-3 mb-5 h-px w-16 flex-shrink-0 transition-colors", n < t ? "bg-primary" : "bg-border"].join(" ") })]
|
|
132
|
+
}, e.key);
|
|
133
|
+
})
|
|
134
|
+
});
|
|
135
|
+
}, S = () => {
|
|
136
|
+
let { navigate: n, basePath: u } = e(), { t: x } = s(), S = t(), w = (e, t) => {
|
|
137
|
+
let n = x(e);
|
|
138
|
+
return n === e ? t : n;
|
|
139
|
+
}, T = (e) => n?.(`${u}${e}`), [E, D] = o("org"), [O, k] = o(null), [A, ae] = o(null), [j, M] = o(""), [N, P] = o(""), [F, oe] = o(""), [I, L] = o("FREE"), [R, z] = o({}), [B, { loading: V }] = te(), H = () => {
|
|
140
|
+
let e = {};
|
|
141
|
+
return j.trim() ? j.length < 2 && (e.name = w("devportal.orgNameTooShort", "Name must be at least 2 characters")) : e.name = w("devportal.orgNameRequired", "Organization name is required"), N.trim() ? /^[a-z0-9-]+$/.test(N) || (e.slug = w("devportal.slugInvalid", "Lowercase letters, numbers, and hyphens only")) : e.slug = w("devportal.slugRequired", "Slug is required"), z(e), Object.keys(e).length === 0;
|
|
142
|
+
}, U = async () => {
|
|
143
|
+
if (H()) try {
|
|
144
|
+
let e = (await B({ variables: { input: {
|
|
145
|
+
name: j,
|
|
146
|
+
slug: N || void 0,
|
|
147
|
+
description: F || void 0,
|
|
148
|
+
metadata: I === "FREE" ? void 0 : { tier: I }
|
|
149
|
+
} } })).data?.createDeveloperOrganization?.id;
|
|
150
|
+
e && (S.emit("devportal.org.created", {
|
|
151
|
+
organizationId: e,
|
|
152
|
+
name: j
|
|
153
|
+
}), k(e), D("app"));
|
|
154
|
+
} catch {}
|
|
155
|
+
}, [W, G] = o(""), [K, se] = o(""), [q, ce] = o(""), [J, le] = o(""), [Y, ue] = o(""), [X, Z] = o({}), [de, { loading: Q }] = ee(), fe = () => {
|
|
156
|
+
let e = {};
|
|
157
|
+
return W.trim() ? W.length < 3 && (e.name = w("devportal.nameTooShort", "Name must be at least 3 characters")) : e.name = w("devportal.appNameRequired", "Application name is required"), q || (e.appType = w("devportal.appTypeRequired", "Application type is required")), J || (e.category = w("devportal.categoryRequired", "Category is required")), Y.trim() || (e.shortDescription = w("devportal.shortDescriptionRequired", "Short description is required")), Z(e), Object.keys(e).length === 0;
|
|
158
|
+
}, pe = async () => {
|
|
159
|
+
if (fe()) try {
|
|
160
|
+
let e = (await de({ variables: { input: {
|
|
161
|
+
name: W,
|
|
162
|
+
slug: K || void 0,
|
|
163
|
+
appType: q,
|
|
164
|
+
category: J || void 0,
|
|
165
|
+
shortDescription: Y || void 0,
|
|
166
|
+
visibility: "PRIVATE",
|
|
167
|
+
developerOrganizationId: O || void 0
|
|
168
|
+
} } })).data?.createApplication?.id;
|
|
169
|
+
e && (S.emit("devportal.app.created", {
|
|
170
|
+
appId: e,
|
|
171
|
+
name: W,
|
|
172
|
+
appType: q,
|
|
173
|
+
organizationId: O
|
|
174
|
+
}), ae(e), b(), D("done"));
|
|
175
|
+
} catch {}
|
|
176
|
+
}, $ = () => {
|
|
177
|
+
b(), T("/");
|
|
178
|
+
};
|
|
179
|
+
return /* @__PURE__ */ h("main", {
|
|
180
|
+
className: "min-h-screen bg-surface-default",
|
|
181
|
+
children: [/* @__PURE__ */ m("div", {
|
|
182
|
+
className: "border-b border-border-default bg-surface-raised",
|
|
183
|
+
children: /* @__PURE__ */ m("div", {
|
|
184
|
+
className: "mx-auto max-w-2xl px-6 py-5",
|
|
185
|
+
children: /* @__PURE__ */ h("div", {
|
|
186
|
+
className: "flex items-center justify-between",
|
|
187
|
+
children: [/* @__PURE__ */ h("div", {
|
|
188
|
+
className: "flex items-center gap-3",
|
|
189
|
+
children: [/* @__PURE__ */ m("div", {
|
|
190
|
+
className: "flex h-9 w-9 items-center justify-center rounded-lg bg-primary/10 text-primary",
|
|
191
|
+
children: /* @__PURE__ */ m(p, { size: 20 })
|
|
192
|
+
}), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
|
|
193
|
+
className: "text-xs font-medium text-muted-foreground",
|
|
194
|
+
children: w("devportal.developerPortal", "Developer Portal")
|
|
195
|
+
}), /* @__PURE__ */ m("h1", {
|
|
196
|
+
className: "text-base font-semibold text-foreground",
|
|
197
|
+
children: w("devportal.setup.title", "Get set up")
|
|
198
|
+
})] })]
|
|
199
|
+
}), E !== "done" && /* @__PURE__ */ m("button", {
|
|
200
|
+
type: "button",
|
|
201
|
+
onClick: $,
|
|
202
|
+
className: "text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
203
|
+
children: w("devportal.setup.skipForNow", "Skip for now")
|
|
204
|
+
})]
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
}), /* @__PURE__ */ h("div", {
|
|
208
|
+
className: "mx-auto max-w-2xl px-6 py-10",
|
|
209
|
+
children: [
|
|
210
|
+
/* @__PURE__ */ m("div", {
|
|
211
|
+
className: "mb-10 flex justify-center",
|
|
212
|
+
children: /* @__PURE__ */ m(ie, { current: E })
|
|
213
|
+
}),
|
|
214
|
+
E === "org" && /* @__PURE__ */ h("div", {
|
|
215
|
+
className: "rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm",
|
|
216
|
+
children: [
|
|
217
|
+
/* @__PURE__ */ h("div", {
|
|
218
|
+
className: "mb-6 flex items-center gap-3",
|
|
219
|
+
children: [/* @__PURE__ */ m("div", {
|
|
220
|
+
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary",
|
|
221
|
+
children: /* @__PURE__ */ m(l, { size: 20 })
|
|
222
|
+
}), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h2", {
|
|
223
|
+
className: "text-lg font-semibold text-foreground",
|
|
224
|
+
children: w("devportal.setup.orgTitle", "Set up your organization")
|
|
225
|
+
}), /* @__PURE__ */ m("p", {
|
|
226
|
+
className: "text-sm text-muted-foreground",
|
|
227
|
+
children: w("devportal.setup.orgSubtitle", "This groups your apps and teams under one publisher identity.")
|
|
228
|
+
})] })]
|
|
229
|
+
}),
|
|
230
|
+
/* @__PURE__ */ h("div", {
|
|
231
|
+
className: "space-y-5",
|
|
232
|
+
children: [
|
|
233
|
+
/* @__PURE__ */ m(r, {
|
|
234
|
+
label: w("devportal.organizationName", "Organization Name"),
|
|
235
|
+
value: j,
|
|
236
|
+
onChange: (e) => {
|
|
237
|
+
M(e.target.value), (!N || N === y(j)) && P(y(e.target.value)), z((e) => ({
|
|
238
|
+
...e,
|
|
239
|
+
name: void 0
|
|
240
|
+
}));
|
|
241
|
+
},
|
|
242
|
+
placeholder: w("devportal.organizationNamePlaceholder", "Acme Development"),
|
|
243
|
+
required: !0,
|
|
244
|
+
error: R.name
|
|
245
|
+
}),
|
|
246
|
+
/* @__PURE__ */ m(r, {
|
|
247
|
+
label: w("devportal.slug", "Slug"),
|
|
248
|
+
value: N,
|
|
249
|
+
onChange: (e) => {
|
|
250
|
+
P(e.target.value.toLowerCase()), z((e) => ({
|
|
251
|
+
...e,
|
|
252
|
+
slug: void 0
|
|
253
|
+
}));
|
|
254
|
+
},
|
|
255
|
+
placeholder: "acme-dev",
|
|
256
|
+
required: !0,
|
|
257
|
+
error: R.slug,
|
|
258
|
+
hint: w("devportal.slugHint", "URL-friendly identifier — auto-generated from name")
|
|
259
|
+
}),
|
|
260
|
+
/* @__PURE__ */ m(a, {
|
|
261
|
+
label: w("devportal.description", "Description"),
|
|
262
|
+
value: F,
|
|
263
|
+
onChange: (e) => oe(e.target.value),
|
|
264
|
+
placeholder: w("devportal.orgDescriptionPlaceholder", "Tell us about your organization…"),
|
|
265
|
+
rows: 2
|
|
266
|
+
}),
|
|
267
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
|
|
268
|
+
className: "mb-2 text-sm font-medium text-foreground",
|
|
269
|
+
children: w("devportal.selectPlan", "Select Plan")
|
|
270
|
+
}), /* @__PURE__ */ m("div", {
|
|
271
|
+
className: "grid grid-cols-3 gap-3",
|
|
272
|
+
children: g.map((e) => /* @__PURE__ */ h("button", {
|
|
273
|
+
type: "button",
|
|
274
|
+
onClick: () => L(e.value),
|
|
275
|
+
className: ["rounded-lg border p-3 text-left text-sm transition-colors", I === e.value ? "border-primary bg-primary/5 ring-2 ring-primary" : "border-border-default hover:border-muted-foreground/50"].join(" "),
|
|
276
|
+
children: [
|
|
277
|
+
/* @__PURE__ */ m("p", {
|
|
278
|
+
className: "font-semibold text-foreground",
|
|
279
|
+
children: e.label
|
|
280
|
+
}),
|
|
281
|
+
/* @__PURE__ */ m("p", {
|
|
282
|
+
className: "mt-0.5 text-xs text-primary",
|
|
283
|
+
children: e.price
|
|
284
|
+
}),
|
|
285
|
+
/* @__PURE__ */ m("p", {
|
|
286
|
+
className: "mt-1.5 text-xs text-muted-foreground",
|
|
287
|
+
children: e.apps
|
|
288
|
+
}),
|
|
289
|
+
/* @__PURE__ */ m("p", {
|
|
290
|
+
className: "text-xs text-muted-foreground",
|
|
291
|
+
children: e.calls
|
|
292
|
+
})
|
|
293
|
+
]
|
|
294
|
+
}, e.value))
|
|
295
|
+
})] })
|
|
296
|
+
]
|
|
297
|
+
}),
|
|
298
|
+
/* @__PURE__ */ h("div", {
|
|
299
|
+
className: "mt-8 flex items-center justify-between border-t border-border-default pt-6",
|
|
300
|
+
children: [/* @__PURE__ */ m("button", {
|
|
301
|
+
type: "button",
|
|
302
|
+
onClick: $,
|
|
303
|
+
className: "text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
304
|
+
children: w("devportal.setup.skipForNow", "Skip for now")
|
|
305
|
+
}), /* @__PURE__ */ h("button", {
|
|
306
|
+
type: "button",
|
|
307
|
+
onClick: () => void U(),
|
|
308
|
+
disabled: V,
|
|
309
|
+
className: "inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:opacity-60 transition-colors",
|
|
310
|
+
children: [
|
|
311
|
+
V ? /* @__PURE__ */ m("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }) : null,
|
|
312
|
+
w("devportal.setup.createAndContinue", "Create & Continue"),
|
|
313
|
+
!V && /* @__PURE__ */ m(d, { size: 16 })
|
|
314
|
+
]
|
|
315
|
+
})]
|
|
316
|
+
})
|
|
317
|
+
]
|
|
318
|
+
}),
|
|
319
|
+
E === "app" && /* @__PURE__ */ h("div", {
|
|
320
|
+
className: "rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm",
|
|
321
|
+
children: [
|
|
322
|
+
/* @__PURE__ */ h("div", {
|
|
323
|
+
className: "mb-6 flex items-center gap-3",
|
|
324
|
+
children: [/* @__PURE__ */ m("div", {
|
|
325
|
+
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary",
|
|
326
|
+
children: /* @__PURE__ */ m(c, { size: 20 })
|
|
327
|
+
}), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h2", {
|
|
328
|
+
className: "text-lg font-semibold text-foreground",
|
|
329
|
+
children: w("devportal.setup.appTitle", "Create your first app")
|
|
330
|
+
}), /* @__PURE__ */ m("p", {
|
|
331
|
+
className: "text-sm text-muted-foreground",
|
|
332
|
+
children: w("devportal.setup.appSubtitle", "Register the app you want to publish or integrate.")
|
|
333
|
+
})] })]
|
|
334
|
+
}),
|
|
335
|
+
/* @__PURE__ */ h("div", {
|
|
336
|
+
className: "space-y-5",
|
|
337
|
+
children: [
|
|
338
|
+
/* @__PURE__ */ m(r, {
|
|
339
|
+
label: w("devportal.applicationName", "Application Name"),
|
|
340
|
+
value: W,
|
|
341
|
+
onChange: (e) => {
|
|
342
|
+
G(e.target.value), (!K || K === y(W)) && se(y(e.target.value)), Z((e) => ({
|
|
343
|
+
...e,
|
|
344
|
+
name: void 0
|
|
345
|
+
}));
|
|
346
|
+
},
|
|
347
|
+
placeholder: w("devportal.applicationNamePlaceholder", "My Awesome App"),
|
|
348
|
+
required: !0,
|
|
349
|
+
error: X.name
|
|
350
|
+
}),
|
|
351
|
+
/* @__PURE__ */ h("div", {
|
|
352
|
+
className: "grid grid-cols-2 gap-4",
|
|
353
|
+
children: [/* @__PURE__ */ m(i, {
|
|
354
|
+
label: w("devportal.applicationType", "Application Type"),
|
|
355
|
+
value: q,
|
|
356
|
+
onChange: (e) => {
|
|
357
|
+
ce(e.target.value), Z((e) => ({
|
|
358
|
+
...e,
|
|
359
|
+
appType: void 0
|
|
360
|
+
}));
|
|
361
|
+
},
|
|
362
|
+
options: _,
|
|
363
|
+
required: !0,
|
|
364
|
+
error: X.appType
|
|
365
|
+
}), /* @__PURE__ */ m(i, {
|
|
366
|
+
label: w("devportal.category", "Category"),
|
|
367
|
+
value: J,
|
|
368
|
+
onChange: (e) => {
|
|
369
|
+
le(e.target.value), Z((e) => ({
|
|
370
|
+
...e,
|
|
371
|
+
category: void 0
|
|
372
|
+
}));
|
|
373
|
+
},
|
|
374
|
+
options: v,
|
|
375
|
+
required: !0,
|
|
376
|
+
error: X.category
|
|
377
|
+
})]
|
|
378
|
+
}),
|
|
379
|
+
/* @__PURE__ */ m(a, {
|
|
380
|
+
label: w("devportal.shortDescription", "Short Description"),
|
|
381
|
+
value: Y,
|
|
382
|
+
onChange: (e) => {
|
|
383
|
+
ue(e.target.value), Z((e) => ({
|
|
384
|
+
...e,
|
|
385
|
+
shortDescription: void 0
|
|
386
|
+
}));
|
|
387
|
+
},
|
|
388
|
+
placeholder: w("devportal.shortDescriptionPlaceholder", "A brief description of what your app does"),
|
|
389
|
+
required: !0,
|
|
390
|
+
error: X.shortDescription,
|
|
391
|
+
rows: 2,
|
|
392
|
+
hint: `${Y.length}/200`
|
|
393
|
+
})
|
|
394
|
+
]
|
|
395
|
+
}),
|
|
396
|
+
/* @__PURE__ */ h("div", {
|
|
397
|
+
className: "mt-8 flex items-center justify-between border-t border-border-default pt-6",
|
|
398
|
+
children: [/* @__PURE__ */ m("button", {
|
|
399
|
+
type: "button",
|
|
400
|
+
onClick: $,
|
|
401
|
+
className: "text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
402
|
+
children: w("devportal.setup.skipForNow", "Skip for now")
|
|
403
|
+
}), /* @__PURE__ */ h("button", {
|
|
404
|
+
type: "button",
|
|
405
|
+
onClick: () => void pe(),
|
|
406
|
+
disabled: Q,
|
|
407
|
+
className: "inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:opacity-60 transition-colors",
|
|
408
|
+
children: [
|
|
409
|
+
Q ? /* @__PURE__ */ m("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }) : null,
|
|
410
|
+
w("devportal.setup.createApp", "Create App"),
|
|
411
|
+
!Q && /* @__PURE__ */ m(d, { size: 16 })
|
|
412
|
+
]
|
|
413
|
+
})]
|
|
414
|
+
})
|
|
415
|
+
]
|
|
416
|
+
}),
|
|
417
|
+
E === "done" && /* @__PURE__ */ h("div", {
|
|
418
|
+
className: "text-center",
|
|
419
|
+
children: [
|
|
420
|
+
/* @__PURE__ */ m("div", {
|
|
421
|
+
className: "mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 text-primary",
|
|
422
|
+
children: /* @__PURE__ */ m(p, { size: 32 })
|
|
423
|
+
}),
|
|
424
|
+
/* @__PURE__ */ m("h2", {
|
|
425
|
+
className: "text-2xl font-bold text-foreground",
|
|
426
|
+
children: w("devportal.setup.doneTitle", "You're all set!")
|
|
427
|
+
}),
|
|
428
|
+
/* @__PURE__ */ m("p", {
|
|
429
|
+
className: "mt-2 text-muted-foreground",
|
|
430
|
+
children: w("devportal.setup.doneSubtitle", "Your organization and first app are ready. Here's what to do next.")
|
|
431
|
+
}),
|
|
432
|
+
/* @__PURE__ */ h("div", {
|
|
433
|
+
className: "mt-8 grid gap-3 sm:grid-cols-2",
|
|
434
|
+
children: [
|
|
435
|
+
A && /* @__PURE__ */ m(C, {
|
|
436
|
+
icon: /* @__PURE__ */ m(c, { size: 20 }),
|
|
437
|
+
title: w("devportal.setup.viewApp", "View your app"),
|
|
438
|
+
description: w("devportal.setup.viewAppDesc", "Configure OAuth, webhooks, and versioning"),
|
|
439
|
+
onClick: () => T(`/apps/${A}`),
|
|
440
|
+
primary: !0
|
|
441
|
+
}),
|
|
442
|
+
/* @__PURE__ */ m(C, {
|
|
443
|
+
icon: /* @__PURE__ */ m(f, { size: 20 }),
|
|
444
|
+
title: w("devportal.setup.generateApiKey", "Generate API key"),
|
|
445
|
+
description: w("devportal.setup.generateApiKeyDesc", "Create credentials to start calling APIs"),
|
|
446
|
+
onClick: () => T("/api-keys/new")
|
|
447
|
+
}),
|
|
448
|
+
/* @__PURE__ */ m(C, {
|
|
449
|
+
icon: /* @__PURE__ */ m(re, { size: 20 }),
|
|
450
|
+
title: w("devportal.setup.browseDocs", "Browse docs"),
|
|
451
|
+
description: w("devportal.setup.browseDocsDesc", "Learn about publishing, SDKs, and webhooks"),
|
|
452
|
+
onClick: () => T("/docs")
|
|
453
|
+
}),
|
|
454
|
+
O && /* @__PURE__ */ m(C, {
|
|
455
|
+
icon: /* @__PURE__ */ m(l, { size: 20 }),
|
|
456
|
+
title: w("devportal.setup.inviteTeam", "Invite your team"),
|
|
457
|
+
description: w("devportal.setup.inviteTeamDesc", "Add members to collaborate on your apps"),
|
|
458
|
+
onClick: () => T(`/organizations/${O}/members`)
|
|
459
|
+
})
|
|
460
|
+
]
|
|
461
|
+
}),
|
|
462
|
+
/* @__PURE__ */ h("button", {
|
|
463
|
+
type: "button",
|
|
464
|
+
onClick: () => T("/"),
|
|
465
|
+
className: "mt-8 inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
466
|
+
children: [w("devportal.setup.goToDashboard", "Go to dashboard"), /* @__PURE__ */ m(ne, { size: 14 })]
|
|
467
|
+
})
|
|
468
|
+
]
|
|
469
|
+
})
|
|
470
|
+
]
|
|
471
|
+
})]
|
|
472
|
+
});
|
|
473
|
+
}, C = ({ icon: e, title: t, description: n, onClick: r, primary: i }) => /* @__PURE__ */ h("button", {
|
|
474
|
+
type: "button",
|
|
475
|
+
onClick: r,
|
|
476
|
+
className: ["flex items-start gap-4 rounded-xl border p-5 text-left transition-colors", i ? "border-primary bg-primary/5 hover:bg-primary/10" : "border-border-default bg-surface-raised hover:bg-surface-hover"].join(" "),
|
|
477
|
+
children: [
|
|
478
|
+
/* @__PURE__ */ m("div", {
|
|
479
|
+
className: ["flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg", i ? "bg-primary/20 text-primary" : "bg-muted text-muted-foreground"].join(" "),
|
|
480
|
+
children: e
|
|
481
|
+
}),
|
|
482
|
+
/* @__PURE__ */ h("div", {
|
|
483
|
+
className: "min-w-0 flex-1",
|
|
484
|
+
children: [/* @__PURE__ */ m("p", {
|
|
485
|
+
className: "font-semibold text-foreground",
|
|
486
|
+
children: t
|
|
487
|
+
}), /* @__PURE__ */ m("p", {
|
|
488
|
+
className: "mt-0.5 text-sm text-muted-foreground",
|
|
489
|
+
children: n
|
|
490
|
+
})]
|
|
491
|
+
}),
|
|
492
|
+
/* @__PURE__ */ m(d, {
|
|
493
|
+
size: 16,
|
|
494
|
+
className: "mt-0.5 flex-shrink-0 text-muted-foreground"
|
|
495
|
+
})
|
|
496
|
+
]
|
|
497
|
+
});
|
|
498
|
+
//#endregion
|
|
499
|
+
export { S as SetupPage };
|
|
500
|
+
|
|
501
|
+
//# sourceMappingURL=SetupPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SetupPage.js","names":[],"sources":["../../../src/devportal/pages/SetupPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport {\n ArrowRight,\n Building2,\n AppWindow,\n Rocket,\n Check,\n Key,\n BookOpen,\n ChevronRight,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { useDevportalEventBus } from \"../events\";\nimport type { AppType, DeveloperOrgTier } from \"../types\";\nimport { APP_TYPE_LABELS } from \"../utils/constants\";\nimport {\n useCreateOrganizationMutation,\n useCreateApplicationMutation,\n} from \"../../generated/global-operations\";\n\ntype SetupStep = \"org\" | \"app\" | \"done\";\n\nconst TIER_OPTIONS: {\n value: DeveloperOrgTier;\n label: string;\n price: string;\n apps: string;\n calls: string;\n}[] = [\n {\n value: \"FREE\",\n label: \"Free\",\n price: \"Free\",\n apps: \"1 app\",\n calls: \"100K calls/mo\",\n },\n {\n value: \"DEVELOPER\",\n label: \"Developer\",\n price: \"$29/mo\",\n apps: \"3 apps\",\n calls: \"1M calls/mo\",\n },\n {\n value: \"PROFESSIONAL\",\n label: \"Professional\",\n price: \"$99/mo\",\n apps: \"10 apps\",\n calls: \"10M calls/mo\",\n },\n];\n\nconst APP_TYPE_OPTIONS = [\n { value: \"\", label: \"Select type…\" },\n ...Object.entries(APP_TYPE_LABELS).map(([value, label]) => ({\n value,\n label,\n })),\n];\n\nconst CATEGORY_OPTIONS = [\n { value: \"\", label: \"Select category…\" },\n { value: \"productivity\", label: \"Productivity\" },\n { value: \"communication\", label: \"Communication\" },\n { value: \"analytics\", label: \"Analytics\" },\n { value: \"automation\", label: \"Automation\" },\n { value: \"developer-tools\", label: \"Developer Tools\" },\n { value: \"finance\", label: \"Finance\" },\n { value: \"hr\", label: \"HR & People\" },\n { value: \"marketing\", label: \"Marketing\" },\n { value: \"project-management\", label: \"Project Management\" },\n { value: \"security\", label: \"Security\" },\n { value: \"support\", label: \"Support\" },\n { value: \"other\", label: \"Other\" },\n];\n\nfunction generateSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** Mark that the user has interacted with setup so the dashboard gate won't redirect again. */\nfunction markSetupSeen() {\n try {\n localStorage.setItem(\"devportal_setup_dismissed\", \"1\");\n } catch {\n // localStorage unavailable — not critical\n }\n}\n\n// ─── Step indicator ────────────────────────────────────────────────────────\n\nconst STEPS: { key: SetupStep; label: string }[] = [\n { key: \"org\", label: \"Organization\" },\n { key: \"app\", label: \"First app\" },\n { key: \"done\", label: \"Done\" },\n];\n\nconst StepIndicator: FC<{ current: SetupStep }> = ({ current }) => {\n const currentIdx = STEPS.findIndex((s) => s.key === current);\n return (\n <div className=\"flex items-center gap-0\">\n {STEPS.map((step, idx) => {\n const isCompleted = idx < currentIdx;\n const isActive = idx === currentIdx;\n return (\n <div key={step.key} className=\"flex items-center\">\n <div className=\"flex flex-col items-center gap-1.5\">\n <div\n className={[\n \"flex h-8 w-8 items-center justify-center rounded-full text-sm font-semibold transition-colors\",\n isCompleted\n ? \"bg-primary text-primary-foreground\"\n : isActive\n ? \"bg-primary text-primary-foreground ring-4 ring-primary/20\"\n : \"bg-muted text-muted-foreground\",\n ].join(\" \")}\n >\n {isCompleted ? <Check size={14} /> : idx + 1}\n </div>\n <span\n className={[\n \"text-xs font-medium whitespace-nowrap\",\n isActive ? \"text-foreground\" : \"text-muted-foreground\",\n ].join(\" \")}\n >\n {step.label}\n </span>\n </div>\n {idx < STEPS.length - 1 && (\n <div\n className={[\n \"mx-3 mb-5 h-px w-16 flex-shrink-0 transition-colors\",\n idx < currentIdx ? \"bg-primary\" : \"bg-border\",\n ].join(\" \")}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n};\n\n// ─── Main component ────────────────────────────────────────────────────────\n\nexport const SetupPage: FC = () => {\n const { navigate, basePath } = useDevportal();\n const { t } = useI18n();\n const bus = useDevportalEventBus();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const handleNavigate = (path: string) => navigate?.(`${basePath}${path}`);\n\n const [step, setStep] = useState<SetupStep>(\"org\");\n\n // IDs created during wizard — carried forward for success CTAs\n const [createdOrgId, setCreatedOrgId] = useState<string | null>(null);\n const [createdAppId, setCreatedAppId] = useState<string | null>(null);\n\n // ── Step 1: org ──────────────────────────────────────────────\n const [orgName, setOrgName] = useState(\"\");\n const [orgSlug, setOrgSlug] = useState(\"\");\n const [orgDescription, setOrgDescription] = useState(\"\");\n const [orgTier, setOrgTier] = useState<DeveloperOrgTier>(\"FREE\");\n const [orgErrors, setOrgErrors] = useState<{ name?: string; slug?: string }>(\n {},\n );\n\n const [createOrg, { loading: creatingOrg }] = useCreateOrganizationMutation();\n\n const validateOrg = () => {\n const errs: { name?: string; slug?: string } = {};\n if (!orgName.trim())\n errs.name = tr(\n \"devportal.orgNameRequired\",\n \"Organization name is required\",\n );\n else if (orgName.length < 2)\n errs.name = tr(\n \"devportal.orgNameTooShort\",\n \"Name must be at least 2 characters\",\n );\n if (!orgSlug.trim())\n errs.slug = tr(\"devportal.slugRequired\", \"Slug is required\");\n else if (!/^[a-z0-9-]+$/.test(orgSlug))\n errs.slug = tr(\n \"devportal.slugInvalid\",\n \"Lowercase letters, numbers, and hyphens only\",\n );\n setOrgErrors(errs);\n return Object.keys(errs).length === 0;\n };\n\n const handleOrgSubmit = async () => {\n if (!validateOrg()) return;\n try {\n const result = await createOrg({\n variables: {\n input: {\n name: orgName,\n slug: orgSlug || undefined,\n description: orgDescription || undefined,\n metadata: orgTier !== \"FREE\" ? { tier: orgTier } : undefined,\n },\n },\n });\n const id = result.data?.createDeveloperOrganization?.id;\n if (id) {\n bus.emit(\"devportal.org.created\", {\n organizationId: id,\n name: orgName,\n });\n setCreatedOrgId(id);\n setStep(\"app\");\n }\n } catch {\n // error surfaced via Apollo cache; leave the form open\n }\n };\n\n // ── Step 2: app ──────────────────────────────────────────────\n const [appName, setAppName] = useState(\"\");\n const [appSlug, setAppSlug] = useState(\"\");\n const [appType, setAppType] = useState<AppType | \"\">(\"\");\n const [appCategory, setAppCategory] = useState(\"\");\n const [appShortDesc, setAppShortDesc] = useState(\"\");\n const [appErrors, setAppErrors] = useState<{\n name?: string;\n appType?: string;\n category?: string;\n shortDescription?: string;\n }>({});\n\n const [createApp, { loading: creatingApp }] = useCreateApplicationMutation();\n\n const validateApp = () => {\n const errs: typeof appErrors = {};\n if (!appName.trim())\n errs.name = tr(\n \"devportal.appNameRequired\",\n \"Application name is required\",\n );\n else if (appName.length < 3)\n errs.name = tr(\n \"devportal.nameTooShort\",\n \"Name must be at least 3 characters\",\n );\n if (!appType)\n errs.appType = tr(\n \"devportal.appTypeRequired\",\n \"Application type is required\",\n );\n if (!appCategory)\n errs.category = tr(\"devportal.categoryRequired\", \"Category is required\");\n if (!appShortDesc.trim())\n errs.shortDescription = tr(\n \"devportal.shortDescriptionRequired\",\n \"Short description is required\",\n );\n setAppErrors(errs);\n return Object.keys(errs).length === 0;\n };\n\n const handleAppSubmit = async () => {\n if (!validateApp()) return;\n try {\n const result = await createApp({\n variables: {\n input: {\n name: appName,\n slug: appSlug || undefined,\n appType: appType as AppType,\n category: appCategory || undefined,\n shortDescription: appShortDesc || undefined,\n visibility: \"PRIVATE\",\n developerOrganizationId: createdOrgId || undefined,\n },\n },\n });\n const id = result.data?.createApplication?.id;\n if (id) {\n bus.emit(\"devportal.app.created\", {\n appId: id,\n name: appName,\n appType,\n organizationId: createdOrgId,\n });\n setCreatedAppId(id);\n markSetupSeen();\n setStep(\"done\");\n }\n } catch {\n // error surfaced via Apollo cache\n }\n };\n\n const handleSkip = () => {\n markSetupSeen();\n handleNavigate(\"/\");\n };\n\n // ─── Render ─────────────────────────────────────────────────\n\n return (\n <main className=\"min-h-screen bg-surface-default\">\n {/* Header */}\n <div className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-2xl px-6 py-5\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-primary/10 text-primary\">\n <Rocket size={20} />\n </div>\n <div>\n <p className=\"text-xs font-medium text-muted-foreground\">\n {tr(\"devportal.developerPortal\", \"Developer Portal\")}\n </p>\n <h1 className=\"text-base font-semibold text-foreground\">\n {tr(\"devportal.setup.title\", \"Get set up\")}\n </h1>\n </div>\n </div>\n {step !== \"done\" && (\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"mx-auto max-w-2xl px-6 py-10\">\n {/* Step indicator */}\n <div className=\"mb-10 flex justify-center\">\n <StepIndicator current={step} />\n </div>\n\n {/* ── Step 1: Organization ─────────────────────── */}\n {step === \"org\" && (\n <div className=\"rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary\">\n <Building2 size={20} />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr(\"devportal.setup.orgTitle\", \"Set up your organization\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n \"devportal.setup.orgSubtitle\",\n \"This groups your apps and teams under one publisher identity.\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-5\">\n <InputField\n label={tr(\"devportal.organizationName\", \"Organization Name\")}\n value={orgName}\n onChange={(e) => {\n setOrgName(e.target.value);\n if (!orgSlug || orgSlug === generateSlug(orgName)) {\n setOrgSlug(generateSlug(e.target.value));\n }\n setOrgErrors((p) => ({ ...p, name: undefined }));\n }}\n placeholder={tr(\n \"devportal.organizationNamePlaceholder\",\n \"Acme Development\",\n )}\n required\n error={orgErrors.name}\n />\n\n <InputField\n label={tr(\"devportal.slug\", \"Slug\")}\n value={orgSlug}\n onChange={(e) => {\n setOrgSlug(e.target.value.toLowerCase());\n setOrgErrors((p) => ({ ...p, slug: undefined }));\n }}\n placeholder=\"acme-dev\"\n required\n error={orgErrors.slug}\n hint={tr(\n \"devportal.slugHint\",\n \"URL-friendly identifier — auto-generated from name\",\n )}\n />\n\n <TextareaField\n label={tr(\"devportal.description\", \"Description\")}\n value={orgDescription}\n onChange={(e) => setOrgDescription(e.target.value)}\n placeholder={tr(\n \"devportal.orgDescriptionPlaceholder\",\n \"Tell us about your organization…\",\n )}\n rows={2}\n />\n\n {/* Tier cards */}\n <div>\n <p className=\"mb-2 text-sm font-medium text-foreground\">\n {tr(\"devportal.selectPlan\", \"Select Plan\")}\n </p>\n <div className=\"grid grid-cols-3 gap-3\">\n {TIER_OPTIONS.map((tier) => {\n const active = orgTier === tier.value;\n return (\n <button\n key={tier.value}\n type=\"button\"\n onClick={() => setOrgTier(tier.value)}\n className={[\n \"rounded-lg border p-3 text-left text-sm transition-colors\",\n active\n ? \"border-primary bg-primary/5 ring-2 ring-primary\"\n : \"border-border-default hover:border-muted-foreground/50\",\n ].join(\" \")}\n >\n <p className=\"font-semibold text-foreground\">\n {tier.label}\n </p>\n <p className=\"mt-0.5 text-xs text-primary\">\n {tier.price}\n </p>\n <p className=\"mt-1.5 text-xs text-muted-foreground\">\n {tier.apps}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {tier.calls}\n </p>\n </button>\n );\n })}\n </div>\n </div>\n </div>\n\n <div className=\"mt-8 flex items-center justify-between border-t border-border-default pt-6\">\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleOrgSubmit()}\n disabled={creatingOrg}\n className=\"inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:opacity-60 transition-colors\"\n >\n {creatingOrg ? (\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n ) : null}\n {tr(\"devportal.setup.createAndContinue\", \"Create & Continue\")}\n {!creatingOrg && <ChevronRight size={16} />}\n </button>\n </div>\n </div>\n )}\n\n {/* ── Step 2: First app ─────────────────────── */}\n {step === \"app\" && (\n <div className=\"rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary\">\n <AppWindow size={20} />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr(\"devportal.setup.appTitle\", \"Create your first app\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n \"devportal.setup.appSubtitle\",\n \"Register the app you want to publish or integrate.\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-5\">\n <InputField\n label={tr(\"devportal.applicationName\", \"Application Name\")}\n value={appName}\n onChange={(e) => {\n setAppName(e.target.value);\n if (!appSlug || appSlug === generateSlug(appName)) {\n setAppSlug(generateSlug(e.target.value));\n }\n setAppErrors((p) => ({ ...p, name: undefined }));\n }}\n placeholder={tr(\n \"devportal.applicationNamePlaceholder\",\n \"My Awesome App\",\n )}\n required\n error={appErrors.name}\n />\n\n <div className=\"grid grid-cols-2 gap-4\">\n <SelectField\n label={tr(\"devportal.applicationType\", \"Application Type\")}\n value={appType}\n onChange={(e) => {\n setAppType(e.target.value as AppType);\n setAppErrors((p) => ({ ...p, appType: undefined }));\n }}\n options={APP_TYPE_OPTIONS}\n required\n error={appErrors.appType}\n />\n <SelectField\n label={tr(\"devportal.category\", \"Category\")}\n value={appCategory}\n onChange={(e) => {\n setAppCategory(e.target.value);\n setAppErrors((p) => ({ ...p, category: undefined }));\n }}\n options={CATEGORY_OPTIONS}\n required\n error={appErrors.category}\n />\n </div>\n\n <TextareaField\n label={tr(\"devportal.shortDescription\", \"Short Description\")}\n value={appShortDesc}\n onChange={(e) => {\n setAppShortDesc(e.target.value);\n setAppErrors((p) => ({ ...p, shortDescription: undefined }));\n }}\n placeholder={tr(\n \"devportal.shortDescriptionPlaceholder\",\n \"A brief description of what your app does\",\n )}\n required\n error={appErrors.shortDescription}\n rows={2}\n hint={`${appShortDesc.length}/200`}\n />\n </div>\n\n <div className=\"mt-8 flex items-center justify-between border-t border-border-default pt-6\">\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleAppSubmit()}\n disabled={creatingApp}\n className=\"inline-flex items-center gap-2 rounded-lg bg-primary px-5 py-2.5 text-sm font-semibold text-primary-foreground hover:bg-primary/90 disabled:opacity-60 transition-colors\"\n >\n {creatingApp ? (\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n ) : null}\n {tr(\"devportal.setup.createApp\", \"Create App\")}\n {!creatingApp && <ChevronRight size={16} />}\n </button>\n </div>\n </div>\n )}\n\n {/* ── Step 3: Done ─────────────────────────── */}\n {step === \"done\" && (\n <div className=\"text-center\">\n <div className=\"mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 text-primary\">\n <Rocket size={32} />\n </div>\n <h2 className=\"text-2xl font-bold text-foreground\">\n {tr(\"devportal.setup.doneTitle\", \"You're all set!\")}\n </h2>\n <p className=\"mt-2 text-muted-foreground\">\n {tr(\n \"devportal.setup.doneSubtitle\",\n \"Your organization and first app are ready. Here's what to do next.\",\n )}\n </p>\n\n <div className=\"mt-8 grid gap-3 sm:grid-cols-2\">\n {createdAppId && (\n <NextStepCard\n icon={<AppWindow size={20} />}\n title={tr(\"devportal.setup.viewApp\", \"View your app\")}\n description={tr(\n \"devportal.setup.viewAppDesc\",\n \"Configure OAuth, webhooks, and versioning\",\n )}\n onClick={() => handleNavigate(`/apps/${createdAppId}`)}\n primary\n />\n )}\n <NextStepCard\n icon={<Key size={20} />}\n title={tr(\"devportal.setup.generateApiKey\", \"Generate API key\")}\n description={tr(\n \"devportal.setup.generateApiKeyDesc\",\n \"Create credentials to start calling APIs\",\n )}\n onClick={() => handleNavigate(\"/api-keys/new\")}\n />\n <NextStepCard\n icon={<BookOpen size={20} />}\n title={tr(\"devportal.setup.browseDocs\", \"Browse docs\")}\n description={tr(\n \"devportal.setup.browseDocsDesc\",\n \"Learn about publishing, SDKs, and webhooks\",\n )}\n onClick={() => handleNavigate(\"/docs\")}\n />\n {createdOrgId && (\n <NextStepCard\n icon={<Building2 size={20} />}\n title={tr(\"devportal.setup.inviteTeam\", \"Invite your team\")}\n description={tr(\n \"devportal.setup.inviteTeamDesc\",\n \"Add members to collaborate on your apps\",\n )}\n onClick={() =>\n handleNavigate(`/organizations/${createdOrgId}/members`)\n }\n />\n )}\n </div>\n\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/\")}\n className=\"mt-8 inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n {tr(\"devportal.setup.goToDashboard\", \"Go to dashboard\")}\n <ArrowRight size={14} />\n </button>\n </div>\n )}\n </div>\n </main>\n );\n};\n\n// ─── Next-step card ────────────────────────────────────────────────────────\n\nconst NextStepCard: FC<{\n icon: React.ReactNode;\n title: string;\n description: string;\n onClick: () => void;\n primary?: boolean;\n}> = ({ icon, title, description, onClick, primary }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={[\n \"flex items-start gap-4 rounded-xl border p-5 text-left transition-colors\",\n primary\n ? \"border-primary bg-primary/5 hover:bg-primary/10\"\n : \"border-border-default bg-surface-raised hover:bg-surface-hover\",\n ].join(\" \")}\n >\n <div\n className={[\n \"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg\",\n primary\n ? \"bg-primary/20 text-primary\"\n : \"bg-muted text-muted-foreground\",\n ].join(\" \")}\n >\n {icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold text-foreground\">{title}</p>\n <p className=\"mt-0.5 text-sm text-muted-foreground\">{description}</p>\n </div>\n <ChevronRight\n size={16}\n className=\"mt-0.5 flex-shrink-0 text-muted-foreground\"\n />\n </button>\n);\n"],"mappings":"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"}
|