@burdenoff/microfe-devportal 2026.1112.2 → 2026.1113.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/devportal/DevportalRoot.js +65 -119
  2. package/dist/devportal/DevportalRoot.js.map +1 -1
  3. package/dist/devportal/components/AppCard.js +72 -69
  4. package/dist/devportal/components/AppCard.js.map +1 -1
  5. package/dist/devportal/components/DeveloperSettingsHub.d.ts +6 -0
  6. package/dist/devportal/components/DeveloperSettingsHub.js +59 -0
  7. package/dist/devportal/components/DeveloperSettingsHub.js.map +1 -0
  8. package/dist/devportal/components/ImageUploadField.js +82 -80
  9. package/dist/devportal/components/ImageUploadField.js.map +1 -1
  10. package/dist/devportal/components/SectionNav.d.ts +1 -1
  11. package/dist/devportal/components/SectionNav.js +60 -36
  12. package/dist/devportal/components/SectionNav.js.map +1 -1
  13. package/dist/devportal/components/StoreImage.d.ts +15 -0
  14. package/dist/devportal/components/StoreImage.js +30 -0
  15. package/dist/devportal/components/StoreImage.js.map +1 -0
  16. package/dist/devportal/components/index.d.ts +1 -0
  17. package/dist/devportal/components/index.js +17 -16
  18. package/dist/devportal/hooks/usePackageUpload.d.ts +46 -4
  19. package/dist/devportal/hooks/usePackageUpload.js +90 -36
  20. package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
  21. package/dist/devportal/pages/AdminReviewPage.js +205 -202
  22. package/dist/devportal/pages/AdminReviewPage.js.map +1 -1
  23. package/dist/devportal/pages/AnalyticsDashboardPage.js +186 -179
  24. package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
  25. package/dist/devportal/pages/ApiKeysPage.js +113 -111
  26. package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
  27. package/dist/devportal/pages/AppLogsPage.js +80 -84
  28. package/dist/devportal/pages/AppLogsPage.js.map +1 -1
  29. package/dist/devportal/pages/ApplicationDetailPage.js +529 -504
  30. package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
  31. package/dist/devportal/pages/ApplicationsPage.js +186 -183
  32. package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
  33. package/dist/devportal/pages/CreateApiKeyPage.js +163 -166
  34. package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
  35. package/dist/devportal/pages/CreateOAuthClientPage.js +134 -130
  36. package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
  37. package/dist/devportal/pages/CreatePhysicalItemPage.js +2 -2
  38. package/dist/devportal/pages/CreatePhysicalItemPage.js.map +1 -1
  39. package/dist/devportal/pages/DelegationsPage.js +11 -11
  40. package/dist/devportal/pages/DocumentationPage.js +100 -94
  41. package/dist/devportal/pages/DocumentationPage.js.map +1 -1
  42. package/dist/devportal/pages/EditApplicationPage.js +379 -377
  43. package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
  44. package/dist/devportal/pages/EditPhysicalItemPage.js +312 -307
  45. package/dist/devportal/pages/EditPhysicalItemPage.js.map +1 -1
  46. package/dist/devportal/pages/InvitationDecisionPage.js +83 -79
  47. package/dist/devportal/pages/InvitationDecisionPage.js.map +1 -1
  48. package/dist/devportal/pages/ListingPreviewPage.js +175 -171
  49. package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
  50. package/dist/devportal/pages/OAuthClientDetailPage.js +76 -76
  51. package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
  52. package/dist/devportal/pages/OAuthClientsPage.js +126 -124
  53. package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
  54. package/dist/devportal/pages/OrganizationDetailPage.js +19 -19
  55. package/dist/devportal/pages/OrganizationMembersPage.js +223 -204
  56. package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
  57. package/dist/devportal/pages/ResourcesPage.d.ts +6 -0
  58. package/dist/devportal/pages/ResourcesPage.js +171 -155
  59. package/dist/devportal/pages/ResourcesPage.js.map +1 -1
  60. package/dist/devportal/pages/SellerDashboardPage.js +117 -115
  61. package/dist/devportal/pages/SellerDashboardPage.js.map +1 -1
  62. package/dist/devportal/pages/SellerItemsPage.js +202 -199
  63. package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
  64. package/dist/devportal/pages/SettingsPage.js +330 -315
  65. package/dist/devportal/pages/SettingsPage.js.map +1 -1
  66. package/dist/devportal/pages/SetupPage.js +5 -5
  67. package/dist/devportal/pages/StoreSubmissionPage.js +133 -131
  68. package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
  69. package/dist/devportal/pages/WebhookDetailPage.js +6 -6
  70. package/dist/devportal/pages/WebhooksPage.js +39 -39
  71. package/dist/devportal/pages/WebhooksPage.js.map +1 -1
  72. package/dist/devportal/utils/appDetailTabs.d.ts +73 -0
  73. package/dist/devportal/utils/appDetailTabs.js +62 -0
  74. package/dist/devportal/utils/appDetailTabs.js.map +1 -0
  75. package/dist/devportal/utils/developerNav.d.ts +39 -0
  76. package/dist/devportal/utils/developerNav.js +304 -0
  77. package/dist/devportal/utils/developerNav.js.map +1 -0
  78. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsDashboardPage.js","names":["EmptyState","_Fragment","EmptyState$1","daysByRange","chartColors","AnalyticsDashboardPage","useParams","useDevportal","tr","useTr","timeRange","setTimeRange","useState","selectedApplicationId","setSelectedApplicationId","appId","now","useMemo","startDate","nextDate","optionsData","optionsLoading","optionsError","useGetAnalyticsDashboardOptionsQuery","useGetApplicationAnalyticsDashboardQuery","allApplications","edge","applications","activeOrgId","a","selectedApplication","data","name","id","reg","analytics","left","right","latest","previous","chartData","entry","errorRate","previousErrorRate","responseTrend","handleNavigate","basePath","path","PageLayout","Loader2","AlertCircle","getErrorMessage","AppListItem","application","BarChart3","error","event","loading","Fragment","EmphasisPanel","StatCardGrid","StatCard","Activity","Users","AlertTriangle","Clock","PageSection","GlassCard","ResponsiveContainer","LineChart","CartesianGrid","XAxis","YAxis","Tooltip","Line","BarChart","Bar","DataTable"],"sources":["../../../src/devportal/pages/AnalyticsDashboardPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useParams } from \"react-router\";\nimport {\n Activity,\n AlertCircle,\n AlertTriangle,\n BarChart3,\n Clock,\n Loader2,\n Users,\n} from \"lucide-react\";\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport { EmphasisPanel, GlassCard } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { AppListItem } from \"../components/AppCard\";\nimport type { Application } from \"../types\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport {\n type GetAnalyticsDashboardOptionsQuery,\n getErrorMessage,\n useGetAnalyticsDashboardOptionsQuery,\n useGetApplicationAnalyticsDashboardQuery,\n} from \"../hooks\";\nimport { useTr } from \"../utils/i18n\";\n\ntype TimeRange = \"7d\" | \"30d\" | \"90d\";\n\nconst daysByRange: Record<TimeRange, number> = {\n \"7d\": 7,\n \"30d\": 30,\n \"90d\": 90,\n};\n\nconst chartColors = {\n primary: \"var(--color-chart-1)\",\n secondary: \"var(--color-chart-2)\",\n tertiary: \"var(--color-chart-4)\",\n negative: \"var(--color-chart-negative)\",\n};\n\nexport const AnalyticsDashboardPage: FC = () => {\n const { appId } = useParams<{ appId: string }>();\n const { navigate, basePath, activeOrgId } = useDevportal();\n const tr = useTr();\n const [timeRange, setTimeRange] = useState<TimeRange>(\"30d\");\n const [selectedApplicationId, setSelectedApplicationId] = useState(\n appId || \"\",\n );\n\n const now = useMemo(() => new Date(), []);\n const startDate = useMemo(() => {\n const nextDate = new Date(now);\n nextDate.setDate(nextDate.getDate() - daysByRange[timeRange]);\n return nextDate.toISOString();\n }, [now, timeRange]);\n\n const {\n data: optionsData,\n loading: optionsLoading,\n error: optionsError,\n } = useGetAnalyticsDashboardOptionsQuery({\n skip: !!appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const { data, loading, error } = useGetApplicationAnalyticsDashboardQuery({\n variables: {\n applicationId: selectedApplicationId || appId || \"\",\n period: \"daily\",\n startDate,\n endDate: now.toISOString(),\n },\n skip: !(selectedApplicationId || appId),\n fetchPolicy: \"cache-and-network\",\n });\n\n const allApplications: Application[] =\n optionsData?.applications?.edges?.map(\n (\n edge: GetAnalyticsDashboardOptionsQuery[\"applications\"][\"edges\"][number],\n ) => edge.node as Application,\n ) ?? [];\n // Scope the app picker to the active org when one is selected\n const applications = activeOrgId\n ? allApplications.filter((a) => a.developerOrganizationId === activeOrgId)\n : allApplications;\n const selectedApplication = data?.application;\n\n useEffect(() => {\n const name = selectedApplication?.name;\n const id = appId || selectedApplicationId;\n if (!id || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(id, name);\n return () => reg?.unregister(id);\n }, [appId, selectedApplicationId, selectedApplication?.name]);\n\n const analytics = [...(data?.applicationAnalytics ?? [])].sort(\n (left, right) =>\n new Date(left.date).getTime() - new Date(right.date).getTime(),\n );\n\n const latest = analytics[analytics.length - 1];\n const previous = analytics[analytics.length - 2];\n const chartData = analytics.map((entry) => ({\n date: new Date(entry.date).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n }),\n apiCalls: entry.apiCalls,\n errors: entry.errors,\n activeUsers: entry.activeUsers,\n newUsers: entry.newUsers,\n sessions: entry.sessions,\n avgResponseTime: entry.avgResponseTime ?? 0,\n }));\n\n const errorRate =\n latest && latest.apiCalls > 0 ? (latest.errors / latest.apiCalls) * 100 : 0;\n const previousErrorRate =\n previous && previous.apiCalls > 0\n ? (previous.errors / previous.apiCalls) * 100\n : 0;\n const responseTrend =\n latest?.avgResponseTime && previous?.avgResponseTime\n ? previous.avgResponseTime - latest.avgResponseTime\n : undefined;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n if (!appId && optionsLoading && !optionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n >\n <div className=\"flex items-center justify-center py-16 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n </PageLayout>\n );\n }\n\n if (!appId && optionsError) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.analytics.errorLoadApps\",\n \"Unable to load applications\",\n )}\n description={getErrorMessage(optionsError)}\n />\n </PageLayout>\n );\n }\n\n if (!appId && !selectedApplicationId) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.selectAppDescription\",\n \"Select an application to review live usage metrics from the public gateway\",\n )}\n >\n {applications.length > 0 ? (\n <div className=\"space-y-2\">\n {applications.map((application) => (\n <AppListItem\n key={application.id}\n app={application}\n onClick={() => setSelectedApplicationId(application.id)}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BarChart3 size={32} />}\n title={tr(\n \"devportal.analytics.noAppsTitle\",\n \"No applications available\",\n )}\n description={tr(\n \"devportal.analytics.noAppsDescription\",\n \"Create an application before viewing analytics.\",\n )}\n />\n )}\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n backLink={\n selectedApplicationId ? `/apps/${selectedApplicationId}` : undefined\n }\n backLabel={tr(\n \"devportal.analytics.backToApplication\",\n \"Back to Application\",\n )}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.analytics.errorLoadAnalytics\",\n \"Unable to load analytics\",\n )}\n description={getErrorMessage(error)}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n appId\n ? tr(\"devportal.analytics.appTitle\", \"Application Analytics\")\n : tr(\"devportal.analytics.title\", \"Analytics\")\n }\n description={\n selectedApplication\n ? `${tr(\"devportal.analytics.liveUsageFor\", \"Live usage and performance for\")} ${selectedApplication.name}`\n : tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )\n }\n purpose={tr(\n \"devportal.analytics.purpose\",\n \"Analytics shows how your apps are actually used — API call volume, active installs, latency, and error rates over time — so you can spot regressions, plan capacity, and prove value to your users. Pick an app and a time range to drill in.\",\n )}\n backLink={\n appId && selectedApplication\n ? `/apps/${selectedApplication.id}`\n : undefined\n }\n backLabel={tr(\n \"devportal.analytics.backToApplication\",\n \"Back to Application\",\n )}\n actions={\n <div className=\"flex flex-wrap gap-2\">\n {!appId && applications.length > 0 && (\n <select\n value={selectedApplicationId}\n onChange={(event) => setSelectedApplicationId(event.target.value)}\n aria-label={tr(\n \"devportal.analytics.selectAppAria\",\n \"Select application\",\n )}\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n {applications.map((application) => (\n <option key={application.id} value={application.id}>\n {application.name}\n </option>\n ))}\n </select>\n )}\n <select\n value={timeRange}\n onChange={(event) => setTimeRange(event.target.value as TimeRange)}\n aria-label={tr(\n \"devportal.analytics.selectRangeAria\",\n \"Select time range\",\n )}\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"7d\">\n {tr(\"devportal.analytics.range.7d\", \"Last 7 days\")}\n </option>\n <option value=\"30d\">\n {tr(\"devportal.analytics.range.30d\", \"Last 30 days\")}\n </option>\n <option value=\"90d\">\n {tr(\"devportal.analytics.range.90d\", \"Last 90 days\")}\n </option>\n </select>\n </div>\n }\n >\n {loading && !analytics.length ? (\n <div className=\"flex items-center justify-center py-16 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n ) : analytics.length > 0 ? (\n <>\n <EmphasisPanel className=\"mb-6\">\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.analytics.apiCalls\", \"API Calls\")}\n value={(latest?.apiCalls ?? 0).toLocaleString()}\n icon={<Activity size={20} />}\n />\n <StatCard\n title={tr(\"devportal.analytics.activeUsers\", \"Active Users\")}\n value={(latest?.activeUsers ?? 0).toLocaleString()}\n icon={<Users size={20} />}\n />\n <StatCard\n title={tr(\"devportal.analytics.errorRate\", \"Error Rate\")}\n value={`${errorRate.toFixed(2)}%`}\n icon={<AlertTriangle size={20} />}\n trend={\n previous\n ? {\n value: previousErrorRate - errorRate,\n label: tr(\n \"devportal.analytics.vsPrevious\",\n \"vs previous period\",\n ),\n }\n : undefined\n }\n />\n <StatCard\n title={tr(\n \"devportal.analytics.avgResponseTime\",\n \"Avg Response Time\",\n )}\n value={\n latest?.avgResponseTime\n ? `${latest.avgResponseTime}ms`\n : tr(\"devportal.common.na\", \"N/A\")\n }\n icon={<Clock size={20} />}\n trend={\n typeof responseTrend === \"number\"\n ? {\n value: responseTrend,\n label: tr(\n \"devportal.analytics.fasterThanPrevious\",\n \"faster than previous\",\n ),\n }\n : undefined\n }\n />\n </StatCardGrid>\n </EmphasisPanel>\n\n <div className=\"grid gap-6 xl:grid-cols-2\">\n <PageSection\n title={tr(\"devportal.analytics.trafficTrend\", \"Traffic trend\")}\n >\n <GlassCard treatment=\"insight\" className=\"p-4\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <LineChart data={chartData}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-chart-grid)\"\n />\n <XAxis\n dataKey=\"date\"\n stroke=\"var(--color-chart-axis)\"\n fontSize={12}\n />\n <YAxis stroke=\"var(--color-chart-axis)\" fontSize={12} />\n <Tooltip\n contentStyle={{\n backgroundColor: \"var(--color-card)\",\n border: \"1px solid var(--color-border-default)\",\n borderRadius: 12,\n }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"apiCalls\"\n stroke={chartColors.primary}\n strokeWidth={2}\n dot={false}\n />\n <Line\n type=\"monotone\"\n dataKey=\"errors\"\n stroke={chartColors.negative}\n strokeWidth={2}\n dot={false}\n />\n </LineChart>\n </ResponsiveContainer>\n </GlassCard>\n </PageSection>\n\n <PageSection\n title={tr(\"devportal.analytics.userActivity\", \"User activity\")}\n >\n <GlassCard treatment=\"insight\" className=\"p-4\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <BarChart data={chartData}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-chart-grid)\"\n />\n <XAxis\n dataKey=\"date\"\n stroke=\"var(--color-chart-axis)\"\n fontSize={12}\n />\n <YAxis stroke=\"var(--color-chart-axis)\" fontSize={12} />\n <Tooltip\n contentStyle={{\n backgroundColor: \"var(--color-card)\",\n border: \"1px solid var(--color-border-default)\",\n borderRadius: 12,\n }}\n />\n <Bar\n dataKey=\"activeUsers\"\n fill={chartColors.primary}\n radius={[6, 6, 0, 0]}\n />\n <Bar\n dataKey=\"newUsers\"\n fill={chartColors.secondary}\n radius={[6, 6, 0, 0]}\n />\n </BarChart>\n </ResponsiveContainer>\n </GlassCard>\n </PageSection>\n </div>\n\n <PageSection\n title={tr(\"devportal.analytics.dailyBreakdown\", \"Daily breakdown\")}\n >\n <DataTable\n data={analytics}\n keyField=\"id\"\n columns={[\n {\n key: \"date\",\n header: tr(\"devportal.analytics.date\", \"Date\"),\n render: (entry) =>\n new Date(entry.date).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n }),\n },\n {\n key: \"apiCalls\",\n header: tr(\"devportal.analytics.apiCalls\", \"API Calls\"),\n render: (entry) => entry.apiCalls.toLocaleString(),\n },\n {\n key: \"errors\",\n header: tr(\"devportal.analytics.errors\", \"Errors\"),\n render: (entry) => entry.errors.toLocaleString(),\n },\n {\n key: \"activeUsers\",\n header: tr(\"devportal.analytics.activeUsers\", \"Active Users\"),\n render: (entry) => entry.activeUsers.toLocaleString(),\n },\n {\n key: \"newUsers\",\n header: tr(\"devportal.analytics.newUsers\", \"New Users\"),\n render: (entry) => entry.newUsers.toLocaleString(),\n },\n {\n key: \"avgResponseTime\",\n header: tr(\"devportal.analytics.avgResponse\", \"Avg Response\"),\n render: (entry) =>\n entry.avgResponseTime\n ? `${entry.avgResponseTime}ms`\n : tr(\"devportal.common.na\", \"N/A\"),\n },\n {\n key: \"sessions\",\n header: tr(\"devportal.analytics.sessions\", \"Sessions\"),\n render: (entry) => entry.sessions.toLocaleString(),\n },\n ]}\n />\n </PageSection>\n </>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BarChart3 size={32} />}\n title={tr(\n \"devportal.analytics.noAnalyticsTitle\",\n \"No analytics available yet\",\n )}\n description={tr(\n \"devportal.analytics.noAnalyticsDescription\",\n \"Analytics will appear after this application starts receiving traffic through the public gateway.\",\n )}\n action={\n !appId ? (\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/apps\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n {tr(\n \"devportal.analytics.viewApplications\",\n \"View applications\",\n )}\n </button>\n ) : undefined\n }\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsDashboardPage.js","names":["_Fragment","EmptyState","EmptyState$1","daysByRange","chartColors","AnalyticsDetails","tr","useTr","fold","OptionalFields","Fragment","AnalyticsDashboardPage","isFluidgrids","useIsFluidgridsVariant","useParams","useDevportal","timeRange","setTimeRange","useState","selectedApplicationId","setSelectedApplicationId","appId","now","useMemo","startDate","nextDate","optionsData","optionsLoading","optionsError","useGetAnalyticsDashboardOptionsQuery","useGetApplicationAnalyticsDashboardQuery","allApplications","edge","applications","activeOrgId","a","selectedApplication","data","name","id","reg","analytics","left","right","latest","previous","chartData","entry","errorRate","previousErrorRate","responseTrend","handleNavigate","basePath","path","PageLayout","Loader2","AlertCircle","getErrorMessage","AppListItem","application","BarChart3","error","event","loading","EmphasisPanel","StatCardGrid","StatCard","Activity","Users","AlertTriangle","Clock","PageSection","GlassCard","ResponsiveContainer","LineChart","CartesianGrid","XAxis","YAxis","Tooltip","Line","BarChart","Bar","DataTable"],"sources":["../../../src/devportal/pages/AnalyticsDashboardPage.tsx"],"sourcesContent":["import type { FC, ReactNode } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useParams } from \"react-router\";\nimport {\n Activity,\n AlertCircle,\n AlertTriangle,\n BarChart3,\n Clock,\n Loader2,\n Users,\n} from \"lucide-react\";\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Line,\n LineChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport {\n EmphasisPanel,\n GlassCard,\n OptionalFields,\n} from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { StatCard, StatCardGrid } from \"../components/StatCard\";\nimport { AppListItem } from \"../components/AppCard\";\nimport type { Application } from \"../types\";\nimport {\n useDevportal,\n useIsFluidgridsVariant,\n} from \"../providers/DevportalProvider\";\nimport {\n type GetAnalyticsDashboardOptionsQuery,\n getErrorMessage,\n useGetAnalyticsDashboardOptionsQuery,\n useGetApplicationAnalyticsDashboardQuery,\n} from \"../hooks\";\nimport { useTr } from \"../utils/i18n\";\n\ntype TimeRange = \"7d\" | \"30d\" | \"90d\";\n\nconst daysByRange: Record<TimeRange, number> = {\n \"7d\": 7,\n \"30d\": 30,\n \"90d\": 90,\n};\n\nconst chartColors = {\n primary: \"var(--color-chart-1)\",\n secondary: \"var(--color-chart-2)\",\n tertiary: \"var(--color-chart-4)\",\n negative: \"var(--color-chart-negative)\",\n};\n\n/**\n * FluidGrids only: the KPI tiles stay on the page and the trend charts plus the\n * daily table fold one click deeper (\"More options\", remembered). With the\n * variant off the content renders inline, exactly as before.\n */\nconst AnalyticsDetails: FC<{ fold: boolean; children: ReactNode }> = ({\n fold,\n children,\n}) => {\n const tr = useTr();\n if (!fold) return <>{children}</>;\n return (\n <OptionalFields\n storageKey=\"devportal.analytics.details\"\n title={tr(\n \"devportal.analytics.detailsTitle\",\n \"Trends and daily breakdown\",\n )}\n description={tr(\n \"devportal.analytics.detailsDescription\",\n \"Traffic and user charts, plus the day-by-day table.\",\n )}\n >\n {children}\n </OptionalFields>\n );\n};\n\nexport const AnalyticsDashboardPage: FC = () => {\n const isFluidgrids = useIsFluidgridsVariant();\n const { appId } = useParams<{ appId: string }>();\n const { navigate, basePath, activeOrgId } = useDevportal();\n const tr = useTr();\n const [timeRange, setTimeRange] = useState<TimeRange>(\"30d\");\n const [selectedApplicationId, setSelectedApplicationId] = useState(\n appId || \"\",\n );\n\n const now = useMemo(() => new Date(), []);\n const startDate = useMemo(() => {\n const nextDate = new Date(now);\n nextDate.setDate(nextDate.getDate() - daysByRange[timeRange]);\n return nextDate.toISOString();\n }, [now, timeRange]);\n\n const {\n data: optionsData,\n loading: optionsLoading,\n error: optionsError,\n } = useGetAnalyticsDashboardOptionsQuery({\n skip: !!appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const { data, loading, error } = useGetApplicationAnalyticsDashboardQuery({\n variables: {\n applicationId: selectedApplicationId || appId || \"\",\n period: \"daily\",\n startDate,\n endDate: now.toISOString(),\n },\n skip: !(selectedApplicationId || appId),\n fetchPolicy: \"cache-and-network\",\n });\n\n const allApplications: Application[] =\n optionsData?.applications?.edges?.map(\n (\n edge: GetAnalyticsDashboardOptionsQuery[\"applications\"][\"edges\"][number],\n ) => edge.node as Application,\n ) ?? [];\n // Scope the app picker to the active org when one is selected\n const applications = activeOrgId\n ? allApplications.filter((a) => a.developerOrganizationId === activeOrgId)\n : allApplications;\n const selectedApplication = data?.application;\n\n useEffect(() => {\n const name = selectedApplication?.name;\n const id = appId || selectedApplicationId;\n if (!id || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(id, name);\n return () => reg?.unregister(id);\n }, [appId, selectedApplicationId, selectedApplication?.name]);\n\n const analytics = [...(data?.applicationAnalytics ?? [])].sort(\n (left, right) =>\n new Date(left.date).getTime() - new Date(right.date).getTime(),\n );\n\n const latest = analytics[analytics.length - 1];\n const previous = analytics[analytics.length - 2];\n const chartData = analytics.map((entry) => ({\n date: new Date(entry.date).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n }),\n apiCalls: entry.apiCalls,\n errors: entry.errors,\n activeUsers: entry.activeUsers,\n newUsers: entry.newUsers,\n sessions: entry.sessions,\n avgResponseTime: entry.avgResponseTime ?? 0,\n }));\n\n const errorRate =\n latest && latest.apiCalls > 0 ? (latest.errors / latest.apiCalls) * 100 : 0;\n const previousErrorRate =\n previous && previous.apiCalls > 0\n ? (previous.errors / previous.apiCalls) * 100\n : 0;\n const responseTrend =\n latest?.avgResponseTime && previous?.avgResponseTime\n ? previous.avgResponseTime - latest.avgResponseTime\n : undefined;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n if (!appId && optionsLoading && !optionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n >\n <div className=\"flex items-center justify-center py-16 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n </PageLayout>\n );\n }\n\n if (!appId && optionsError) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.analytics.errorLoadApps\",\n \"Unable to load applications\",\n )}\n description={getErrorMessage(optionsError)}\n />\n </PageLayout>\n );\n }\n\n if (!appId && !selectedApplicationId) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.selectAppDescription\",\n \"Select an application to review live usage metrics from the public gateway\",\n )}\n >\n {applications.length > 0 ? (\n <div className=\"space-y-2\">\n {applications.map((application) => (\n <AppListItem\n key={application.id}\n app={application}\n onClick={() => setSelectedApplicationId(application.id)}\n />\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BarChart3 size={32} />}\n title={tr(\n \"devportal.analytics.noAppsTitle\",\n \"No applications available\",\n )}\n description={tr(\n \"devportal.analytics.noAppsDescription\",\n \"Create an application before viewing analytics.\",\n )}\n />\n )}\n </PageLayout>\n );\n }\n\n if (error) {\n return (\n <PageLayout\n title={tr(\"devportal.analytics.title\", \"Analytics\")}\n description={tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )}\n backLink={\n selectedApplicationId ? `/apps/${selectedApplicationId}` : undefined\n }\n backLabel={tr(\n \"devportal.analytics.backToApplication\",\n \"Back to Application\",\n )}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.analytics.errorLoadAnalytics\",\n \"Unable to load analytics\",\n )}\n description={getErrorMessage(error)}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={\n appId\n ? tr(\"devportal.analytics.appTitle\", \"Application Analytics\")\n : tr(\"devportal.analytics.title\", \"Analytics\")\n }\n description={\n selectedApplication\n ? `${tr(\"devportal.analytics.liveUsageFor\", \"Live usage and performance for\")} ${selectedApplication.name}`\n : tr(\n \"devportal.analytics.description\",\n \"Explore live application usage and performance metrics\",\n )\n }\n purpose={tr(\n \"devportal.analytics.purpose\",\n \"Analytics shows how your apps are actually used — API call volume, active installs, latency, and error rates over time — so you can spot regressions, plan capacity, and prove value to your users. Pick an app and a time range to drill in.\",\n )}\n backLink={\n appId && selectedApplication\n ? `/apps/${selectedApplication.id}`\n : undefined\n }\n backLabel={tr(\n \"devportal.analytics.backToApplication\",\n \"Back to Application\",\n )}\n actions={\n <div className=\"flex flex-wrap gap-2\">\n {!appId && applications.length > 0 && (\n <select\n value={selectedApplicationId}\n onChange={(event) => setSelectedApplicationId(event.target.value)}\n aria-label={tr(\n \"devportal.analytics.selectAppAria\",\n \"Select application\",\n )}\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n {applications.map((application) => (\n <option key={application.id} value={application.id}>\n {application.name}\n </option>\n ))}\n </select>\n )}\n <select\n value={timeRange}\n onChange={(event) => setTimeRange(event.target.value as TimeRange)}\n aria-label={tr(\n \"devportal.analytics.selectRangeAria\",\n \"Select time range\",\n )}\n className=\"rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"7d\">\n {tr(\"devportal.analytics.range.7d\", \"Last 7 days\")}\n </option>\n <option value=\"30d\">\n {tr(\"devportal.analytics.range.30d\", \"Last 30 days\")}\n </option>\n <option value=\"90d\">\n {tr(\"devportal.analytics.range.90d\", \"Last 90 days\")}\n </option>\n </select>\n </div>\n }\n >\n {loading && !analytics.length ? (\n <div className=\"flex items-center justify-center py-16 text-text-secondary\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n </div>\n ) : analytics.length > 0 ? (\n <>\n <EmphasisPanel className=\"mb-6\">\n <StatCardGrid columns={4}>\n <StatCard\n title={tr(\"devportal.analytics.apiCalls\", \"API Calls\")}\n value={(latest?.apiCalls ?? 0).toLocaleString()}\n icon={<Activity size={20} />}\n />\n <StatCard\n title={tr(\"devportal.analytics.activeUsers\", \"Active Users\")}\n value={(latest?.activeUsers ?? 0).toLocaleString()}\n icon={<Users size={20} />}\n />\n <StatCard\n title={tr(\"devportal.analytics.errorRate\", \"Error Rate\")}\n value={`${errorRate.toFixed(2)}%`}\n icon={<AlertTriangle size={20} />}\n trend={\n previous\n ? {\n value: previousErrorRate - errorRate,\n label: tr(\n \"devportal.analytics.vsPrevious\",\n \"vs previous period\",\n ),\n }\n : undefined\n }\n />\n <StatCard\n title={tr(\n \"devportal.analytics.avgResponseTime\",\n \"Avg Response Time\",\n )}\n value={\n latest?.avgResponseTime\n ? `${latest.avgResponseTime}ms`\n : tr(\"devportal.common.na\", \"N/A\")\n }\n icon={<Clock size={20} />}\n trend={\n typeof responseTrend === \"number\"\n ? {\n value: responseTrend,\n label: tr(\n \"devportal.analytics.fasterThanPrevious\",\n \"faster than previous\",\n ),\n }\n : undefined\n }\n />\n </StatCardGrid>\n </EmphasisPanel>\n\n <AnalyticsDetails fold={isFluidgrids}>\n <div className=\"grid gap-6 xl:grid-cols-2\">\n <PageSection\n title={tr(\"devportal.analytics.trafficTrend\", \"Traffic trend\")}\n >\n <GlassCard treatment=\"insight\" className=\"p-4\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <LineChart data={chartData}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-chart-grid)\"\n />\n <XAxis\n dataKey=\"date\"\n stroke=\"var(--color-chart-axis)\"\n fontSize={12}\n />\n <YAxis stroke=\"var(--color-chart-axis)\" fontSize={12} />\n <Tooltip\n contentStyle={{\n backgroundColor: \"var(--color-card)\",\n border: \"1px solid var(--color-border-default)\",\n borderRadius: 12,\n }}\n />\n <Line\n type=\"monotone\"\n dataKey=\"apiCalls\"\n stroke={chartColors.primary}\n strokeWidth={2}\n dot={false}\n />\n <Line\n type=\"monotone\"\n dataKey=\"errors\"\n stroke={chartColors.negative}\n strokeWidth={2}\n dot={false}\n />\n </LineChart>\n </ResponsiveContainer>\n </GlassCard>\n </PageSection>\n\n <PageSection\n title={tr(\"devportal.analytics.userActivity\", \"User activity\")}\n >\n <GlassCard treatment=\"insight\" className=\"p-4\">\n <ResponsiveContainer width=\"100%\" height={300}>\n <BarChart data={chartData}>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-chart-grid)\"\n />\n <XAxis\n dataKey=\"date\"\n stroke=\"var(--color-chart-axis)\"\n fontSize={12}\n />\n <YAxis stroke=\"var(--color-chart-axis)\" fontSize={12} />\n <Tooltip\n contentStyle={{\n backgroundColor: \"var(--color-card)\",\n border: \"1px solid var(--color-border-default)\",\n borderRadius: 12,\n }}\n />\n <Bar\n dataKey=\"activeUsers\"\n fill={chartColors.primary}\n radius={[6, 6, 0, 0]}\n />\n <Bar\n dataKey=\"newUsers\"\n fill={chartColors.secondary}\n radius={[6, 6, 0, 0]}\n />\n </BarChart>\n </ResponsiveContainer>\n </GlassCard>\n </PageSection>\n </div>\n\n <PageSection\n title={tr(\n \"devportal.analytics.dailyBreakdown\",\n \"Daily breakdown\",\n )}\n >\n <DataTable\n data={analytics}\n keyField=\"id\"\n columns={[\n {\n key: \"date\",\n header: tr(\"devportal.analytics.date\", \"Date\"),\n render: (entry) =>\n new Date(entry.date).toLocaleDateString(undefined, {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n }),\n },\n {\n key: \"apiCalls\",\n header: tr(\"devportal.analytics.apiCalls\", \"API Calls\"),\n render: (entry) => entry.apiCalls.toLocaleString(),\n },\n {\n key: \"errors\",\n header: tr(\"devportal.analytics.errors\", \"Errors\"),\n render: (entry) => entry.errors.toLocaleString(),\n },\n {\n key: \"activeUsers\",\n header: tr(\n \"devportal.analytics.activeUsers\",\n \"Active Users\",\n ),\n render: (entry) => entry.activeUsers.toLocaleString(),\n },\n {\n key: \"newUsers\",\n header: tr(\"devportal.analytics.newUsers\", \"New Users\"),\n render: (entry) => entry.newUsers.toLocaleString(),\n },\n {\n key: \"avgResponseTime\",\n header: tr(\n \"devportal.analytics.avgResponse\",\n \"Avg Response\",\n ),\n render: (entry) =>\n entry.avgResponseTime\n ? `${entry.avgResponseTime}ms`\n : tr(\"devportal.common.na\", \"N/A\"),\n },\n {\n key: \"sessions\",\n header: tr(\"devportal.analytics.sessions\", \"Sessions\"),\n render: (entry) => entry.sessions.toLocaleString(),\n },\n ]}\n />\n </PageSection>\n </AnalyticsDetails>\n </>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n icon={<BarChart3 size={32} />}\n title={tr(\n \"devportal.analytics.noAnalyticsTitle\",\n \"No analytics available yet\",\n )}\n description={tr(\n \"devportal.analytics.noAnalyticsDescription\",\n \"Analytics will appear after this application starts receiving traffic through the public gateway.\",\n )}\n action={\n !appId ? (\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/apps\")}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\"\n >\n {tr(\n \"devportal.analytics.viewApplications\",\n \"View applications\",\n )}\n </button>\n ) : undefined\n }\n />\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
@@ -2,23 +2,23 @@ import { PageLayout as e } from "../components/PageLayout.js";
2
2
  import { DataTable as t } from "../components/DataTable.js";
3
3
  import { ActiveIndicator as n } from "../components/StatusBadge.js";
4
4
  import { useCurrentUserAppsQuery as r, useMyApiKeysQuery as ee, useRevokeApiKeyAuthMutation as te, useRotateApiKeyAuthMutation as ne } from "../../generated/global-operations.js";
5
- import { getErrorMessage as i } from "../hooks/useGraphQL.js";
6
- import { EmptyState as re } from "../components/EmptyState.js";
7
- import { ConfirmDialog as a } from "../components/ConfirmDialog.js";
8
- import { NewSecretAlert as ie } from "../components/SecretDisplay.js";
9
- import { SelectionCheckbox as o } from "../components/SelectionCheckbox.js";
10
- import { useDevportal as ae } from "../providers/DevportalProvider.js";
5
+ import { getErrorMessage as re } from "../hooks/useGraphQL.js";
6
+ import { EmptyState as ie } from "../components/EmptyState.js";
7
+ import { ConfirmDialog as i } from "../components/ConfirmDialog.js";
8
+ import { NewSecretAlert as ae } from "../components/SecretDisplay.js";
9
+ import { SelectionCheckbox as a } from "../components/SelectionCheckbox.js";
10
+ import { useDevportal as oe, useIsFluidgridsVariant as se } from "../providers/DevportalProvider.js";
11
11
  import "../components/index.js";
12
- import { PermissionGuard as oe } from "../components/PermissionGuard.js";
13
- import { cn as s } from "../utils/cn.js";
12
+ import { PermissionGuard as ce } from "../components/PermissionGuard.js";
13
+ import { cn as o } from "../utils/cn.js";
14
14
  import "../hooks/index.js";
15
- import { useMemo as c, useState as l } from "react";
16
- import { AlertCircle as se, AlertTriangle as ce, Ban as u, Clock as le, Key as d, Loader2 as ue, Plus as f, RotateCw as de, Search as fe, Shield as pe } from "lucide-react";
15
+ import { useMemo as s, useState as c } from "react";
16
+ import { AlertCircle as le, AlertTriangle as ue, Ban as l, Clock as u, Key as d, Loader2 as de, Plus as f, RotateCw as fe, Search as pe, Shield as me } from "lucide-react";
17
17
  import { jsx as p, jsxs as m } from "react/jsx-runtime";
18
- import { useI18n as me } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
19
18
  import { EmphasisPanel as he, GlassCard as h } from "@burdenoff/fe-libs/ui";
19
+ import { useI18n as ge } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
20
20
  // src/devportal/pages/ApiKeysPage.tsx
21
- function g(e) {
21
+ function _e(e) {
22
22
  return {
23
23
  id: e.id,
24
24
  name: e.name,
@@ -31,82 +31,82 @@ function g(e) {
31
31
  lastUsedAt: e.lastUsedAt ?? void 0
32
32
  };
33
33
  }
34
- var _ = (e) => {
34
+ var g = (e) => {
35
35
  if (!e) return !1;
36
36
  let t = Math.ceil((new Date(e).getTime() - Date.now()) / 864e5);
37
37
  return t <= 30 && t > 0;
38
- }, v = (e) => e ? new Date(e).getTime() < Date.now() : !1, y = () => {
39
- let { navigate: y, basePath: b, activeOrgId: ge } = ae(), { t: _e } = me(), x = (e, t) => {
40
- let n = _e(e);
38
+ }, _ = (e) => e ? new Date(e).getTime() < Date.now() : !1, v = () => {
39
+ let { navigate: v, basePath: ve, activeOrgId: ye } = oe(), be = se(), { t: y } = ge(), b = (e, t) => {
40
+ let n = y(e);
41
41
  return n === e ? t : n;
42
- }, [S, ve] = l(""), [C, ye] = l("all"), [w, T] = l(null), [E, D] = l(null), [O, k] = l(null), [A, j] = l(/* @__PURE__ */ new Set()), [M, N] = l(null), [be, P] = l(!1), { data: F, loading: xe, error: I, refetch: L } = ee({ fetchPolicy: "cache-and-network" }), { data: R } = r({ fetchPolicy: "cache-first" }), z = c(() => {
42
+ }, [x, xe] = c(""), [S, Se] = c("all"), [C, w] = c(null), [T, E] = c(null), [D, O] = c(null), [k, A] = c(/* @__PURE__ */ new Set()), [j, M] = c(null), [Ce, N] = c(!1), { data: P, loading: F, error: I, refetch: L } = ee({ fetchPolicy: "cache-and-network" }), { data: R } = r({ fetchPolicy: "cache-first" }), z = s(() => {
43
43
  let e = R?.currentUserApps?.apps;
44
44
  return e?.length ? new Set(e.map((e) => e.id)) : null;
45
- }, [R?.currentUserApps?.apps]), [B, { loading: V }] = te(), [Se, { loading: H }] = ne(), U = c(() => F?.myApiKeys?.apiKeys.map(g) ?? [], [F?.myApiKeys?.apiKeys]), W = (e) => {
46
- y?.(`${b}${e}`);
47
- }, Ce = async () => {
48
- if (w) try {
49
- await B({ variables: { keyId: w.id } }), T(null), await L();
45
+ }, [R?.currentUserApps?.apps]), [B, { loading: V }] = te(), [we, { loading: H }] = ne(), U = s(() => P?.myApiKeys?.apiKeys.map(_e) ?? [], [P?.myApiKeys?.apiKeys]), W = (e) => {
46
+ v?.(`${ve}${e}`);
47
+ }, Te = async () => {
48
+ if (C) try {
49
+ await B({ variables: { keyId: C.id } }), w(null), await L();
50
50
  } catch {}
51
- }, we = async () => {
52
- if (E) try {
53
- let e = (await Se({ variables: { keyId: E.id } })).data?.rotateAPIKey.apiSecret;
54
- e && k(e), D(null), await L();
51
+ }, Ee = async () => {
52
+ if (T) try {
53
+ let e = (await we({ variables: { keyId: T.id } })).data?.rotateAPIKey.apiSecret;
54
+ e && O(e), E(null), await L();
55
55
  } catch {}
56
56
  }, G = U.filter((e) => {
57
- let t = !z || z.has(e.appId), n = !S || e.name.toLowerCase().includes(S.toLowerCase()) || e.prefix.toLowerCase().includes(S.toLowerCase()) || (e.appName?.toLowerCase().includes(S.toLowerCase()) ?? !1), r = C === "all" || C === "active" && !v(e.expiresAt) || C === "expired" && v(e.expiresAt);
57
+ let t = !z || z.has(e.appId), n = !x || e.name.toLowerCase().includes(x.toLowerCase()) || e.prefix.toLowerCase().includes(x.toLowerCase()) || (e.appName?.toLowerCase().includes(x.toLowerCase()) ?? !1), r = S === "all" || S === "active" && !_(e.expiresAt) || S === "expired" && _(e.expiresAt);
58
58
  return t && n && r;
59
- }), K = c(() => U.filter((e) => A.has(e.id)), [U, A]), q = c(() => K.filter((e) => !v(e.expiresAt)), [K]), J = G.filter((e) => !v(e.expiresAt)), Y = J.length > 0 && J.every((e) => A.has(e.id)), X = J.some((e) => A.has(e.id)) && !Y, Te = (e, t) => {
60
- j((n) => {
59
+ }), K = s(() => U.filter((e) => k.has(e.id)), [U, k]), q = s(() => K.filter((e) => !_(e.expiresAt)), [K]), J = G.filter((e) => !_(e.expiresAt)), Y = J.length > 0 && J.every((e) => k.has(e.id)), X = J.some((e) => k.has(e.id)) && !Y, De = (e, t) => {
60
+ A((n) => {
61
61
  let r = new Set(n);
62
62
  return t ? r.add(e) : r.delete(e), r;
63
63
  });
64
64
  }, Z = (e) => {
65
- j((t) => {
65
+ A((t) => {
66
66
  let n = new Set(t);
67
67
  return J.forEach((t) => {
68
68
  e ? n.add(t.id) : n.delete(t.id);
69
69
  }), n;
70
70
  });
71
- }, Q = () => j(/* @__PURE__ */ new Set());
72
- if (xe && !F) return /* @__PURE__ */ p(e, {
73
- title: x("devportal.apiKeys", "API Keys"),
74
- description: x("devportal.manageApiKeys", "Manage your API keys for programmatic access"),
71
+ }, Q = () => A(/* @__PURE__ */ new Set());
72
+ if (F && !P) return /* @__PURE__ */ p(e, {
73
+ title: b("devportal.apiKeys", "API Keys"),
74
+ description: b("devportal.manageApiKeys", "Manage your API keys for programmatic access"),
75
75
  children: /* @__PURE__ */ m("div", {
76
76
  className: "flex items-center justify-center py-12",
77
- children: [/* @__PURE__ */ p(ue, { className: "h-8 w-8 animate-spin text-text-secondary" }), /* @__PURE__ */ p("span", {
77
+ children: [/* @__PURE__ */ p(de, { className: "h-8 w-8 animate-spin text-text-secondary" }), /* @__PURE__ */ p("span", {
78
78
  className: "ml-2 text-text-secondary",
79
- children: x("devportal.loadingApiKeys", "Loading API keys...")
79
+ children: b("devportal.loadingApiKeys", "Loading API keys...")
80
80
  })]
81
81
  })
82
82
  });
83
83
  if (I) return /* @__PURE__ */ p(e, {
84
- title: x("devportal.apiKeys", "API Keys"),
85
- description: x("devportal.manageApiKeys", "Manage your API keys for programmatic access"),
84
+ title: b("devportal.apiKeys", "API Keys"),
85
+ description: b("devportal.manageApiKeys", "Manage your API keys for programmatic access"),
86
86
  children: /* @__PURE__ */ m("div", {
87
87
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center",
88
88
  children: [
89
- /* @__PURE__ */ p(se, { className: "mx-auto h-8 w-8 text-status-error-text" }),
89
+ /* @__PURE__ */ p(le, { className: "mx-auto h-8 w-8 text-status-error-text" }),
90
90
  /* @__PURE__ */ p("h3", {
91
91
  className: "mt-2 font-medium text-status-error-text",
92
- children: x("devportal.failedToLoadApiKeys", "Failed to load API keys")
92
+ children: b("devportal.failedToLoadApiKeys", "Failed to load API keys")
93
93
  }),
94
94
  /* @__PURE__ */ p("p", {
95
95
  className: "mt-1 text-sm text-text-secondary",
96
- children: i(I)
96
+ children: re(I)
97
97
  }),
98
98
  /* @__PURE__ */ p("button", {
99
99
  onClick: () => L(),
100
100
  className: "mt-4 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
101
- children: x("devportal.tryAgain", "Try Again")
101
+ children: b("devportal.tryAgain", "Try Again")
102
102
  })
103
103
  ]
104
104
  })
105
105
  });
106
- let Ee = [
106
+ let Oe = [
107
107
  {
108
108
  key: "select",
109
- header: /* @__PURE__ */ p(o, {
109
+ header: /* @__PURE__ */ p(a, {
110
110
  checked: Y,
111
111
  indeterminate: X,
112
112
  onChange: Z,
@@ -115,10 +115,10 @@ var _ = (e) => {
115
115
  className: "w-12",
116
116
  render: (e) => /* @__PURE__ */ p("div", {
117
117
  onClick: (e) => e.stopPropagation(),
118
- children: /* @__PURE__ */ p(o, {
119
- checked: A.has(e.id),
120
- disabled: v(e.expiresAt),
121
- onChange: (t) => Te(e.id, t),
118
+ children: /* @__PURE__ */ p(a, {
119
+ checked: k.has(e.id),
120
+ disabled: _(e.expiresAt),
121
+ onChange: (t) => De(e.id, t),
122
122
  ariaLabel: `Select API key ${e.name}`
123
123
  })
124
124
  })
@@ -129,7 +129,7 @@ var _ = (e) => {
129
129
  render: (e) => /* @__PURE__ */ m("div", {
130
130
  className: "flex items-center gap-3",
131
131
  children: [/* @__PURE__ */ p("div", {
132
- className: s("flex h-10 w-10 items-center justify-center rounded-lg", v(e.expiresAt) ? "bg-status-error-bg-subtle text-status-error-text" : "bg-action-primary-bg/10 text-primary"),
132
+ className: o("flex h-10 w-10 items-center justify-center rounded-lg", _(e.expiresAt) ? "bg-status-error-bg-subtle text-status-error-text" : "bg-action-primary-bg/10 text-primary"),
133
133
  children: /* @__PURE__ */ p(d, { size: 20 })
134
134
  }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ m("div", {
135
135
  className: "flex items-center gap-2",
@@ -138,11 +138,11 @@ var _ = (e) => {
138
138
  className: "font-medium",
139
139
  children: e.name
140
140
  }),
141
- v(e.expiresAt) && /* @__PURE__ */ p("span", {
141
+ _(e.expiresAt) && /* @__PURE__ */ p("span", {
142
142
  className: "rounded bg-status-error-bg-subtle px-1.5 py-0.5 text-xs text-status-error-text",
143
143
  children: "Expired"
144
144
  }),
145
- _(e.expiresAt) && !v(e.expiresAt) && /* @__PURE__ */ p("span", {
145
+ g(e.expiresAt) && !_(e.expiresAt) && /* @__PURE__ */ p("span", {
146
146
  className: "rounded bg-status-warning-bg-subtle px-1.5 py-0.5 text-xs text-status-warning-text",
147
147
  children: "Expiring Soon"
148
148
  })
@@ -187,7 +187,7 @@ var _ = (e) => {
187
187
  key: "expires",
188
188
  header: "Expires",
189
189
  render: (e) => /* @__PURE__ */ p("span", {
190
- className: s("text-sm", v(e.expiresAt) ? "text-status-error-text" : _(e.expiresAt) ? "text-status-warning-text" : "text-text-secondary"),
190
+ className: o("text-sm", _(e.expiresAt) ? "text-status-error-text" : g(e.expiresAt) ? "text-status-warning-text" : "text-text-secondary"),
191
191
  children: e.expiresAt ? new Date(e.expiresAt).toLocaleDateString() : "Never"
192
192
  })
193
193
  },
@@ -196,9 +196,9 @@ var _ = (e) => {
196
196
  header: "Status",
197
197
  render: (e) => /* @__PURE__ */ m("div", {
198
198
  className: "flex items-center gap-2",
199
- children: [/* @__PURE__ */ p(n, { active: !v(e.expiresAt) }), /* @__PURE__ */ p("span", {
199
+ children: [/* @__PURE__ */ p(n, { active: !_(e.expiresAt) }), /* @__PURE__ */ p("span", {
200
200
  className: "text-sm",
201
- children: v(e.expiresAt) ? "Expired" : "Active"
201
+ children: _(e.expiresAt) ? "Expired" : "Active"
202
202
  })]
203
203
  })
204
204
  },
@@ -206,7 +206,7 @@ var _ = (e) => {
206
206
  key: "actions",
207
207
  header: "Actions",
208
208
  className: "w-28",
209
- render: (e) => v(e.expiresAt) ? /* @__PURE__ */ p("span", {
209
+ render: (e) => _(e.expiresAt) ? /* @__PURE__ */ p("span", {
210
210
  className: "text-xs text-text-secondary",
211
211
  children: "No actions"
212
212
  }) : /* @__PURE__ */ m("div", {
@@ -216,10 +216,10 @@ var _ = (e) => {
216
216
  title: "Rotate secret",
217
217
  disabled: H,
218
218
  onClick: (t) => {
219
- t.stopPropagation(), D(e);
219
+ t.stopPropagation(), E(e);
220
220
  },
221
- className: s("inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors", "text-status-info-text hover:bg-status-info-bg-subtle", "disabled:opacity-50 disabled:cursor-not-allowed"),
222
- children: [/* @__PURE__ */ p(de, { size: 14 }), /* @__PURE__ */ p("span", {
221
+ className: o("inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors", "text-status-info-text hover:bg-status-info-bg-subtle", "disabled:opacity-50 disabled:cursor-not-allowed"),
222
+ children: [/* @__PURE__ */ p(fe, { size: 14 }), /* @__PURE__ */ p("span", {
223
223
  className: "sr-only sm:not-sr-only",
224
224
  children: "Rotate"
225
225
  })]
@@ -228,10 +228,10 @@ var _ = (e) => {
228
228
  title: "Revoke key",
229
229
  disabled: V,
230
230
  onClick: (t) => {
231
- t.stopPropagation(), T(e);
231
+ t.stopPropagation(), w(e);
232
232
  },
233
- className: s("inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors", "text-status-error-text hover:bg-status-error-bg-subtle", "disabled:opacity-50 disabled:cursor-not-allowed"),
234
- children: [/* @__PURE__ */ p(u, { size: 14 }), /* @__PURE__ */ p("span", {
233
+ className: o("inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors", "text-status-error-text hover:bg-status-error-bg-subtle", "disabled:opacity-50 disabled:cursor-not-allowed"),
234
+ children: [/* @__PURE__ */ p(l, { size: 14 }), /* @__PURE__ */ p("span", {
235
235
  className: "sr-only sm:not-sr-only",
236
236
  children: "Revoke"
237
237
  })]
@@ -240,34 +240,36 @@ var _ = (e) => {
240
240
  }
241
241
  ], $ = {
242
242
  total: G.length,
243
- active: G.filter((e) => !v(e.expiresAt)).length,
244
- expiringSoon: G.filter((e) => _(e.expiresAt)).length,
245
- expired: G.filter((e) => v(e.expiresAt)).length
243
+ active: G.filter((e) => !_(e.expiresAt)).length,
244
+ expiringSoon: G.filter((e) => g(e.expiresAt)).length,
245
+ expired: G.filter((e) => _(e.expiresAt)).length
246
246
  };
247
- return /* @__PURE__ */ p(oe, {
247
+ return /* @__PURE__ */ p(ce, {
248
248
  authOnly: !0,
249
249
  children: /* @__PURE__ */ m(e, {
250
- title: x("devportal.apiKeys", "API Keys"),
251
- description: x("devportal.manageApiKeys", "Manage your API keys for programmatic access"),
252
- purpose: x("devportal.apiKeysPurpose", "API keys authenticate server-to-server calls — use them in scripts, CI jobs, or backend services that talk to the platform without a logged-in user. Generate a key here, scope it to an app, and rotate it if it is ever exposed."),
250
+ title: b("devportal.apiKeys", "API Keys"),
251
+ description: b("devportal.manageApiKeys", "Manage your API keys for programmatic access"),
252
+ backLink: be ? "/settings" : void 0,
253
+ backLabel: b("devportal.developerSettings.back", "Developer settings"),
254
+ purpose: b("devportal.apiKeysPurpose", "API keys authenticate server-to-server calls — use them in scripts, CI jobs, or backend services that talk to the platform without a logged-in user. Generate a key here, scope it to an app, and rotate it if it is ever exposed."),
253
255
  actions: /* @__PURE__ */ m("button", {
254
256
  onClick: () => W("/api-keys/new"),
255
257
  className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
256
- children: [/* @__PURE__ */ p(f, { size: 16 }), x("devportal.generateApiKey", "Generate API Key")]
258
+ children: [/* @__PURE__ */ p(f, { size: 16 }), b("devportal.generateApiKey", "Generate API Key")]
257
259
  }),
258
260
  children: [
259
- O && /* @__PURE__ */ p(ie, {
261
+ D && /* @__PURE__ */ p(ae, {
260
262
  title: "API Key Rotated",
261
- secret: O,
263
+ secret: D,
262
264
  description: "The previous secret has been invalidated. Copy the new secret below — it will not be shown again.",
263
- onDismiss: () => k(null)
265
+ onDismiss: () => O(null)
264
266
  }),
265
- M && /* @__PURE__ */ p("div", {
266
- className: s("rounded-lg border px-4 py-3 text-sm", M.type === "success" ? "border-border-subtle bg-status-success-bg-subtle text-status-success-text" : "border-status-error-border/40 bg-status-error-bg/10 text-status-error-text"),
267
+ j && /* @__PURE__ */ p("div", {
268
+ className: o("rounded-lg border px-4 py-3 text-sm", j.type === "success" ? "border-border-subtle bg-status-success-bg-subtle text-status-success-text" : "border-status-error-border/40 bg-status-error-bg/10 text-status-error-text"),
267
269
  children: /* @__PURE__ */ m("div", {
268
270
  className: "flex items-center justify-between gap-3",
269
- children: [/* @__PURE__ */ p("span", { children: M.message }), /* @__PURE__ */ p("button", {
270
- onClick: () => N(null),
271
+ children: [/* @__PURE__ */ p("span", { children: j.message }), /* @__PURE__ */ p("button", {
272
+ onClick: () => M(null),
271
273
  className: "text-xs font-medium underline underline-offset-2",
272
274
  children: "Dismiss"
273
275
  })]
@@ -279,19 +281,19 @@ var _ = (e) => {
279
281
  className: "flex flex-1 items-center gap-4",
280
282
  children: [/* @__PURE__ */ m("div", {
281
283
  className: "relative flex-1 max-w-md",
282
- children: [/* @__PURE__ */ p(fe, {
284
+ children: [/* @__PURE__ */ p(pe, {
283
285
  size: 16,
284
286
  className: "absolute left-3 top-1/2 -translate-y-1/2 text-text-secondary"
285
287
  }), /* @__PURE__ */ p("input", {
286
288
  type: "text",
287
289
  placeholder: "Search by name, prefix, or app...",
288
- value: S,
289
- onChange: (e) => ve(e.target.value),
290
+ value: x,
291
+ onChange: (e) => xe(e.target.value),
290
292
  className: "w-full rounded-md border bg-bg-surface pl-9 pr-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
291
293
  })]
292
294
  }), /* @__PURE__ */ m("select", {
293
- value: C,
294
- onChange: (e) => ye(e.target.value),
295
+ value: S,
296
+ onChange: (e) => Se(e.target.value),
295
297
  className: "rounded-md border bg-bg-surface px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary",
296
298
  children: [
297
299
  /* @__PURE__ */ p("option", {
@@ -332,7 +334,7 @@ var _ = (e) => {
332
334
  className: "p-4",
333
335
  children: [/* @__PURE__ */ m("div", {
334
336
  className: "flex items-center gap-2 text-status-success-text",
335
- children: [/* @__PURE__ */ p(pe, { size: 16 }), /* @__PURE__ */ p("span", {
337
+ children: [/* @__PURE__ */ p(me, { size: 16 }), /* @__PURE__ */ p("span", {
336
338
  className: "text-sm",
337
339
  children: "Active"
338
340
  })]
@@ -346,7 +348,7 @@ var _ = (e) => {
346
348
  className: "p-4",
347
349
  children: [/* @__PURE__ */ m("div", {
348
350
  className: "flex items-center gap-2 text-status-warning-text",
349
- children: [/* @__PURE__ */ p(le, { size: 16 }), /* @__PURE__ */ p("span", {
351
+ children: [/* @__PURE__ */ p(u, { size: 16 }), /* @__PURE__ */ p("span", {
350
352
  className: "text-sm",
351
353
  children: "Expiring Soon"
352
354
  })]
@@ -360,7 +362,7 @@ var _ = (e) => {
360
362
  className: "p-4",
361
363
  children: [/* @__PURE__ */ m("div", {
362
364
  className: "flex items-center gap-2 text-status-error-text",
363
- children: [/* @__PURE__ */ p(ce, { size: 16 }), /* @__PURE__ */ p("span", {
365
+ children: [/* @__PURE__ */ p(ue, { size: 16 }), /* @__PURE__ */ p("span", {
364
366
  className: "text-sm",
365
367
  children: "Expired"
366
368
  })]
@@ -389,7 +391,7 @@ var _ = (e) => {
389
391
  className: "flex flex-wrap items-center gap-3",
390
392
  children: [/* @__PURE__ */ m("div", {
391
393
  className: "flex items-center gap-2",
392
- children: [/* @__PURE__ */ p(o, {
394
+ children: [/* @__PURE__ */ p(a, {
393
395
  checked: Y,
394
396
  indeterminate: X,
395
397
  onChange: Z,
@@ -398,7 +400,7 @@ var _ = (e) => {
398
400
  className: "text-sm font-medium",
399
401
  children: "Select all active keys"
400
402
  })]
401
- }), A.size > 0 && /* @__PURE__ */ p("button", {
403
+ }), k.size > 0 && /* @__PURE__ */ p("button", {
402
404
  onClick: Q,
403
405
  className: "text-sm text-text-secondary hover:text-text-primary",
404
406
  children: "Clear selection"
@@ -407,13 +409,13 @@ var _ = (e) => {
407
409
  className: "flex flex-wrap items-center gap-2",
408
410
  children: [/* @__PURE__ */ m("span", {
409
411
  className: "rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary",
410
- children: [A.size, " selected"]
412
+ children: [k.size, " selected"]
411
413
  }), /* @__PURE__ */ m("button", {
412
- onClick: () => P(!0),
414
+ onClick: () => N(!0),
413
415
  disabled: q.length === 0,
414
416
  className: "inline-flex items-center gap-2 rounded-md border border-status-error-bg px-3 py-2 text-sm font-medium text-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:cursor-not-allowed disabled:opacity-50",
415
417
  children: [
416
- /* @__PURE__ */ p(u, { size: 16 }),
418
+ /* @__PURE__ */ p(l, { size: 16 }),
417
419
  "Revoke Selected",
418
420
  q.length > 0 ? ` (${q.length})` : ""
419
421
  ]
@@ -423,54 +425,54 @@ var _ = (e) => {
423
425
  }),
424
426
  G.length > 0 ? /* @__PURE__ */ p(t, {
425
427
  data: G,
426
- columns: Ee,
428
+ columns: Oe,
427
429
  keyField: "id"
428
- }) : /* @__PURE__ */ p(re, {
430
+ }) : /* @__PURE__ */ p(ie, {
429
431
  illustration: "empty-data",
430
- title: x("devportal.noApiKeysFound", "No API keys found"),
431
- description: S || C !== "all" ? x("devportal.tryAdjustingFilters", "Try adjusting your filters") : x("devportal.generateFirstApiKey", "Generate your first API key to start making API calls"),
432
- action: !S && C === "all" ? /* @__PURE__ */ m("button", {
432
+ title: b("devportal.noApiKeysFound", "No API keys found"),
433
+ description: x || S !== "all" ? b("devportal.tryAdjustingFilters", "Try adjusting your filters") : b("devportal.generateFirstApiKey", "Generate your first API key to start making API calls"),
434
+ action: !x && S === "all" ? /* @__PURE__ */ m("button", {
433
435
  onClick: () => W("/api-keys/new"),
434
436
  className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
435
- children: [/* @__PURE__ */ p(f, { size: 16 }), x("devportal.generateApiKey", "Generate API Key")]
437
+ children: [/* @__PURE__ */ p(f, { size: 16 }), b("devportal.generateApiKey", "Generate API Key")]
436
438
  }) : void 0
437
439
  }),
438
- /* @__PURE__ */ p(a, {
439
- open: w !== null,
440
+ /* @__PURE__ */ p(i, {
441
+ open: C !== null,
440
442
  onOpenChange: (e) => {
441
- e || T(null);
443
+ e || w(null);
442
444
  },
443
- onConfirm: Ce,
445
+ onConfirm: Te,
444
446
  title: "Revoke API Key",
445
- description: `Are you sure you want to revoke "${w?.name ?? ""}"? This action cannot be undone. Any application or service using this key will immediately lose access.`,
447
+ description: `Are you sure you want to revoke "${C?.name ?? ""}"? This action cannot be undone. Any application or service using this key will immediately lose access.`,
446
448
  confirmLabel: "Revoke Key",
447
449
  cancelLabel: "Cancel",
448
450
  variant: "danger",
449
451
  loading: V
450
452
  }),
451
- /* @__PURE__ */ p(a, {
452
- open: E !== null,
453
+ /* @__PURE__ */ p(i, {
454
+ open: T !== null,
453
455
  onOpenChange: (e) => {
454
- e || D(null);
456
+ e || E(null);
455
457
  },
456
- onConfirm: we,
458
+ onConfirm: Ee,
457
459
  title: "Rotate API Key Secret",
458
- description: `This will generate a new secret for "${E?.name ?? ""}" and immediately invalidate the current one. Make sure to update all services using this key before the old secret stops working.`,
460
+ description: `This will generate a new secret for "${T?.name ?? ""}" and immediately invalidate the current one. Make sure to update all services using this key before the old secret stops working.`,
459
461
  confirmLabel: "Rotate Secret",
460
462
  cancelLabel: "Cancel",
461
463
  variant: "warning",
462
464
  loading: H
463
465
  }),
464
- /* @__PURE__ */ p(a, {
465
- open: be,
466
- onOpenChange: P,
466
+ /* @__PURE__ */ p(i, {
467
+ open: Ce,
468
+ onOpenChange: N,
467
469
  onConfirm: async () => {
468
470
  if (q.length === 0) return;
469
471
  let e = await Promise.allSettled(q.map((e) => B({ variables: { keyId: e.id } }))), t = e.filter((e) => e.status === "fulfilled").length, n = e.length - t;
470
- N({
472
+ M({
471
473
  type: n === 0 ? "success" : "error",
472
474
  message: `Revoked ${t} API key${t === 1 ? "" : "s"}${n > 0 ? ` • ${n} failed` : ""}`
473
- }), P(!1), Q(), await L();
475
+ }), N(!1), Q(), await L();
474
476
  },
475
477
  title: "Revoke selected API keys",
476
478
  description: `Revoke ${q.length} selected API key${q.length === 1 ? "" : "s"}? Any service using these keys will immediately lose access.`,
@@ -483,6 +485,6 @@ var _ = (e) => {
483
485
  })
484
486
  });
485
487
  };
486
- export { y as ApiKeysPage };
488
+ export { v as ApiKeysPage };
487
489
 
488
490
  //# sourceMappingURL=ApiKeysPage.js.map