@burdenoff/microfe-devportal 2026.1113.1 → 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.
- package/dist/devportal/DevportalRoot.js +65 -119
- package/dist/devportal/DevportalRoot.js.map +1 -1
- package/dist/devportal/components/AppCard.js +4 -4
- package/dist/devportal/components/DeveloperSettingsHub.d.ts +6 -0
- package/dist/devportal/components/DeveloperSettingsHub.js +59 -0
- package/dist/devportal/components/DeveloperSettingsHub.js.map +1 -0
- package/dist/devportal/components/SectionNav.d.ts +1 -1
- package/dist/devportal/components/SectionNav.js +60 -36
- package/dist/devportal/components/SectionNav.js.map +1 -1
- package/dist/devportal/pages/AnalyticsDashboardPage.js +186 -179
- package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +113 -111
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/AppLogsPage.js +80 -84
- package/dist/devportal/pages/AppLogsPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationDetailPage.js +529 -504
- package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
- package/dist/devportal/pages/ApplicationsPage.js +68 -67
- package/dist/devportal/pages/ApplicationsPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +163 -166
- package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
- package/dist/devportal/pages/CreateOAuthClientPage.js +134 -130
- package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
- package/dist/devportal/pages/DelegationsPage.js +11 -11
- package/dist/devportal/pages/DocumentationPage.js +100 -94
- package/dist/devportal/pages/DocumentationPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +379 -377
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/InvitationDecisionPage.js +83 -79
- package/dist/devportal/pages/InvitationDecisionPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientDetailPage.js +76 -76
- package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js +126 -124
- package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
- package/dist/devportal/pages/OrganizationDetailPage.js +19 -19
- package/dist/devportal/pages/OrganizationMembersPage.js +223 -204
- package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
- package/dist/devportal/pages/ResourcesPage.d.ts +6 -0
- package/dist/devportal/pages/ResourcesPage.js +171 -155
- package/dist/devportal/pages/ResourcesPage.js.map +1 -1
- package/dist/devportal/pages/SellerDashboardPage.js +13 -13
- package/dist/devportal/pages/SellerItemsPage.js +4 -4
- package/dist/devportal/pages/SettingsPage.js +330 -315
- package/dist/devportal/pages/SettingsPage.js.map +1 -1
- package/dist/devportal/pages/SetupPage.js +5 -5
- package/dist/devportal/pages/WebhookDetailPage.js +6 -6
- package/dist/devportal/pages/WebhooksPage.js +39 -39
- package/dist/devportal/pages/WebhooksPage.js.map +1 -1
- package/dist/devportal/utils/appDetailTabs.d.ts +73 -0
- package/dist/devportal/utils/appDetailTabs.js +62 -0
- package/dist/devportal/utils/appDetailTabs.js.map +1 -0
- package/dist/devportal/utils/developerNav.d.ts +39 -0
- package/dist/devportal/utils/developerNav.js +304 -0
- package/dist/devportal/utils/developerNav.js.map +1 -0
- 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
|
|
6
|
-
import { EmptyState as
|
|
7
|
-
import { ConfirmDialog as
|
|
8
|
-
import { NewSecretAlert as
|
|
9
|
-
import { SelectionCheckbox as
|
|
10
|
-
import { useDevportal as
|
|
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
|
|
13
|
-
import { cn as
|
|
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
|
|
16
|
-
import { AlertCircle as
|
|
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
|
|
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
|
|
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
|
-
},
|
|
39
|
-
let { navigate:
|
|
40
|
-
let n =
|
|
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
|
-
}, [
|
|
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(), [
|
|
46
|
-
|
|
47
|
-
},
|
|
48
|
-
if (
|
|
49
|
-
await B({ variables: { keyId:
|
|
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
|
-
},
|
|
52
|
-
if (
|
|
53
|
-
let e = (await
|
|
54
|
-
e &&
|
|
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 = !
|
|
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 =
|
|
60
|
-
|
|
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
|
-
|
|
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 = () =>
|
|
72
|
-
if (
|
|
73
|
-
title:
|
|
74
|
-
description:
|
|
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(
|
|
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:
|
|
79
|
+
children: b("devportal.loadingApiKeys", "Loading API keys...")
|
|
80
80
|
})]
|
|
81
81
|
})
|
|
82
82
|
});
|
|
83
83
|
if (I) return /* @__PURE__ */ p(e, {
|
|
84
|
-
title:
|
|
85
|
-
description:
|
|
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(
|
|
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:
|
|
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:
|
|
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:
|
|
101
|
+
children: b("devportal.tryAgain", "Try Again")
|
|
102
102
|
})
|
|
103
103
|
]
|
|
104
104
|
})
|
|
105
105
|
});
|
|
106
|
-
let
|
|
106
|
+
let Oe = [
|
|
107
107
|
{
|
|
108
108
|
key: "select",
|
|
109
|
-
header: /* @__PURE__ */ p(
|
|
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(
|
|
119
|
-
checked:
|
|
120
|
-
disabled:
|
|
121
|
-
onChange: (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:
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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: !
|
|
199
|
+
children: [/* @__PURE__ */ p(n, { active: !_(e.expiresAt) }), /* @__PURE__ */ p("span", {
|
|
200
200
|
className: "text-sm",
|
|
201
|
-
children:
|
|
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) =>
|
|
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(),
|
|
219
|
+
t.stopPropagation(), E(e);
|
|
220
220
|
},
|
|
221
|
-
className:
|
|
222
|
-
children: [/* @__PURE__ */ p(
|
|
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(),
|
|
231
|
+
t.stopPropagation(), w(e);
|
|
232
232
|
},
|
|
233
|
-
className:
|
|
234
|
-
children: [/* @__PURE__ */ p(
|
|
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) => !
|
|
244
|
-
expiringSoon: G.filter((e) =>
|
|
245
|
-
expired: G.filter((e) =>
|
|
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(
|
|
247
|
+
return /* @__PURE__ */ p(ce, {
|
|
248
248
|
authOnly: !0,
|
|
249
249
|
children: /* @__PURE__ */ m(e, {
|
|
250
|
-
title:
|
|
251
|
-
description:
|
|
252
|
-
|
|
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 }),
|
|
258
|
+
children: [/* @__PURE__ */ p(f, { size: 16 }), b("devportal.generateApiKey", "Generate API Key")]
|
|
257
259
|
}),
|
|
258
260
|
children: [
|
|
259
|
-
|
|
261
|
+
D && /* @__PURE__ */ p(ae, {
|
|
260
262
|
title: "API Key Rotated",
|
|
261
|
-
secret:
|
|
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: () =>
|
|
265
|
+
onDismiss: () => O(null)
|
|
264
266
|
}),
|
|
265
|
-
|
|
266
|
-
className:
|
|
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:
|
|
270
|
-
onClick: () =>
|
|
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(
|
|
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:
|
|
289
|
-
onChange: (e) =>
|
|
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:
|
|
294
|
-
onChange: (e) =>
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
}),
|
|
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: [
|
|
412
|
+
children: [k.size, " selected"]
|
|
411
413
|
}), /* @__PURE__ */ m("button", {
|
|
412
|
-
onClick: () =>
|
|
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(
|
|
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:
|
|
428
|
+
columns: Oe,
|
|
427
429
|
keyField: "id"
|
|
428
|
-
}) : /* @__PURE__ */ p(
|
|
430
|
+
}) : /* @__PURE__ */ p(ie, {
|
|
429
431
|
illustration: "empty-data",
|
|
430
|
-
title:
|
|
431
|
-
description:
|
|
432
|
-
action: !
|
|
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 }),
|
|
437
|
+
children: [/* @__PURE__ */ p(f, { size: 16 }), b("devportal.generateApiKey", "Generate API Key")]
|
|
436
438
|
}) : void 0
|
|
437
439
|
}),
|
|
438
|
-
/* @__PURE__ */ p(
|
|
439
|
-
open:
|
|
440
|
+
/* @__PURE__ */ p(i, {
|
|
441
|
+
open: C !== null,
|
|
440
442
|
onOpenChange: (e) => {
|
|
441
|
-
e ||
|
|
443
|
+
e || w(null);
|
|
442
444
|
},
|
|
443
|
-
onConfirm:
|
|
445
|
+
onConfirm: Te,
|
|
444
446
|
title: "Revoke API Key",
|
|
445
|
-
description: `Are you sure you want to revoke "${
|
|
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(
|
|
452
|
-
open:
|
|
453
|
+
/* @__PURE__ */ p(i, {
|
|
454
|
+
open: T !== null,
|
|
453
455
|
onOpenChange: (e) => {
|
|
454
|
-
e ||
|
|
456
|
+
e || E(null);
|
|
455
457
|
},
|
|
456
|
-
onConfirm:
|
|
458
|
+
onConfirm: Ee,
|
|
457
459
|
title: "Rotate API Key Secret",
|
|
458
|
-
description: `This will generate a new secret for "${
|
|
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(
|
|
465
|
-
open:
|
|
466
|
-
onOpenChange:
|
|
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
|
-
|
|
472
|
+
M({
|
|
471
473
|
type: n === 0 ? "success" : "error",
|
|
472
474
|
message: `Revoked ${t} API key${t === 1 ? "" : "s"}${n > 0 ? ` • ${n} failed` : ""}`
|
|
473
|
-
}),
|
|
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 {
|
|
488
|
+
export { v as ApiKeysPage };
|
|
487
489
|
|
|
488
490
|
//# sourceMappingURL=ApiKeysPage.js.map
|