"use client"; export const dynamic = "force-dynamic"; import { type KeyboardEvent, type MouseEvent, useEffect, useMemo, useState, } from "react"; import { useRouter } from "next/navigation"; import { useQuery } from "@tanstack/react-query"; import { SignedIn, SignedOut, useAuth } from "@/auth/clerk"; import { Activity, Bot, LayoutGrid, Timer } from "lucide-react"; import { DashboardSidebar } from "@/components/organisms/DashboardSidebar"; import { DashboardShell } from "@/components/templates/DashboardShell"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; import { ForgejoIssueMetricCards } from "@/components/git/ForgejoIssueMetricCards"; import { CreateForgejoIssueDialog } from "@/components/git/CreateForgejoIssueDialog"; import { ForgejoHeatmap } from "@/components/git/ForgejoHeatmap"; import { DashboardMetricCard, DashboardInfoBlock, PendingApprovalsSection, SessionsSection, RecentActivitySection, } from "@/components/dashboard"; import { ApiError } from "@/api/mutator"; import { type dashboardMetricsApiV1MetricsDashboardGetResponse, useDashboardMetricsApiV1MetricsDashboardGet, } from "@/api/generated/metrics/metrics"; import { gatewaysStatusApiV1GatewaysStatusGet, getGatewayProviderUsageApiV1GatewaysGatewayIdProviderUsageGet, getGatewayRuntimeUsageApiV1GatewaysGatewayIdRuntimeUsageGet, } from "@/api/generated/gateways/gateways"; import type { GatewaysStatusResponse } from "@/api/generated/model/gatewaysStatusResponse"; import type { CronStatusResponse, ProviderUsageResponse, RuntimeUsageResponse, SystemHealthResponse, } from "@/api/generated/model"; import { getProviderUsageLiveApiV1ProviderCredentialsCredentialIdUsageGet, listProviderCredentialsApiV1ProviderCredentialsGet, } from "@/api/generated/provider-credentials/provider-credentials"; import { getGatewayCronApiV1GatewaysGatewayIdCronGet, getGatewayHealthApiV1GatewaysGatewayIdHealthGet, } from "@/api/generated/gateways/gateways"; import { type ProviderNativeUsageWindow, RuntimeUsageSection, aggregateRuntimeUsage, buildPerGatewayUsage, type AggregatedRuntimeUsage, type PerGatewayUsage, } from "@/components/dashboard/RuntimeUsageSection"; import { GatewayHealthPanel } from "@/components/dashboard/GatewayHealthPanel"; import { GatewayCronPanel } from "@/components/dashboard/GatewayCronPanel"; import { BotStatusSection } from "@/components/dashboard/BotStatusSection"; import { type listAgentsApiV1AgentsGetResponse, useListAgentsApiV1AgentsGet, } from "@/api/generated/agents/agents"; import { type listBoardsApiV1BoardsGetResponse, useListBoardsApiV1BoardsGet, } from "@/api/generated/boards/boards"; import { type listActivityApiV1ActivityGetResponse, useListActivityApiV1ActivityGet, } from "@/api/generated/activity/activity"; import type { ActivityEventRead } from "@/api/generated/model"; import { getForgejoHeatmap, getForgejoLastPush, getForgejoMetrics, getForgejoRepositories, syncRepository, type ForgejoHeatmapDay, type ForgejoIssueMetrics, type ForgejoRepository, } from "@/lib/api-forgejo"; import { formatRelativeTimestamp, formatTimestamp, parseTimestamp, } from "@/lib/formatters"; import { selectPreferredProviderCredential } from "@/lib/provider-credential-selection"; type SessionSummary = { key: string; title: string; subtitle: string; usage: string; lastSeenAt: string | null; isMain: boolean; }; type SummaryRow = { label: string; value: string; tone?: "default" | "success" | "warning" | "danger"; }; type GatewayTarget = { gatewayId: string; boardId: string; boardName: string; }; type GatewaySnapshot = GatewayTarget & { connected: boolean; gatewayUrl: string | null; sessionsCount: number; sessions: unknown[]; mainSession: unknown | null; mainSessionError: string | null; error: string | null; requestError: string | null; }; const DASH = "—"; const FORGEJO_DASHBOARD_REFETCH_INTERVAL_MS = 5 * 60 * 1000; const ALL_FORGEJO_REPOSITORIES = "all"; const DASHBOARD_RANGE = "7d"; const DASHBOARD_RANGE_DAYS = 7; const DASHBOARD_RANGE_LABEL = "7 days"; const numberFormatter = new Intl.NumberFormat("en-US"); const SESSION_ID_KEYS = ["key", "id", "session_key", "sessionKey", "sessionId"]; const toRecord = (value: unknown): Record | null => { if (!value || Array.isArray(value) || typeof value !== "object") return null; return value as Record; }; const readString = ( record: Record | null, keys: string[], ): string | null => { if (!record) return null; for (const key of keys) { const value = record[key]; if (typeof value === "string" && value.trim()) return value.trim(); } return null; }; const readNumber = ( record: Record | null, keys: string[], ): number | null => { if (!record) return null; for (const key of keys) { const value = record[key]; if (typeof value === "number" && Number.isFinite(value)) return value; if (typeof value === "string") { const cleaned = value.replace(/[^0-9.-]/g, ""); const parsed = Number.parseFloat(cleaned); if (Number.isFinite(parsed)) return parsed; } } return null; }; const readStringFromRecords = ( records: Array | null>, keys: string[], ): string | null => { for (const record of records) { const value = readString(record, keys); if (value) return value; } return null; }; const readNumberFromRecords = ( records: Array | null>, keys: string[], ): number | null => { for (const record of records) { const value = readNumber(record, keys); if (value !== null) return value; } return null; }; const normalizeEpochMs = (value: number): number => { if (value >= 1_000_000_000_000) return value; if (value >= 1_000_000_000) return value * 1000; return value; }; const readTimestamp = ( record: Record | null, keys: string[], ): string | null => { if (!record) return null; for (const key of keys) { const value = record[key]; if (typeof value === "number" && Number.isFinite(value)) { const date = new Date(normalizeEpochMs(value)); if (!Number.isNaN(date.getTime())) return date.toISOString(); } if (typeof value === "string") { const trimmed = value.trim(); if (!trimmed) continue; const numeric = Number.parseFloat(trimmed); if (Number.isFinite(numeric)) { const date = new Date(normalizeEpochMs(numeric)); if (!Number.isNaN(date.getTime())) return date.toISOString(); } const parsed = parseTimestamp(trimmed); if (parsed) return parsed.toISOString(); } } return null; }; const readTimestampFromRecords = ( records: Array | null>, keys: string[], ): string | null => { for (const record of records) { const value = readTimestamp(record, keys); if (value) return value; } return null; }; const sessionIdentifiers = ( record: Record | null, ): string[] => { if (!record) return []; const ids = SESSION_ID_KEYS.map((key) => readString(record, [key])).filter( Boolean, ) as string[]; return [...new Set(ids)]; }; const sharesSessionIdentity = (left: string[], right: string[]): boolean => left.some((value) => right.includes(value)); const compactNumber = (value: number): string => { if (!Number.isFinite(value)) return DASH; if (Math.abs(value) >= 1_000_000) { return `${(value / 1_000_000).toFixed(1)}m`; } if (Math.abs(value) >= 1_000) { return `${(value / 1_000).toFixed(1)}k`; } return numberFormatter.format(value); }; const formatCount = (value: number): string => Number.isFinite(value) ? numberFormatter.format(Math.max(0, Math.round(value))) : "0"; const formatPercent = (value: number): string => Number.isFinite(value) ? `${value.toFixed(1)}%` : DASH; const formatPerDay = (total: number, days: number): string => { if (!Number.isFinite(total) || !Number.isFinite(days) || days <= 0) return DASH; return `${(total / days).toFixed(1)}/day`; }; const formatForgejoDashboardError = (error: Error | null): string | null => { if (!error) return null; if (error.message === "Failed to fetch") { return "Pipeline could not load Git Project data. Check that the frontend can reach the backend API."; } return error.message; }; const toSessionSummaries = ( sessions: unknown[] | null | undefined, mainSession: unknown, ): SessionSummary[] => { const sessionRecords = (sessions ?? []) .map(toRecord) .filter(Boolean) as Array>; const mainRecord = toRecord(mainSession); const mainIdentifiers = sessionIdentifiers(mainRecord); if (mainRecord && mainIdentifiers.length > 0) { const exists = sessionRecords.some((entry) => sharesSessionIdentity(sessionIdentifiers(entry), mainIdentifiers), ); if (!exists) sessionRecords.unshift(mainRecord); } const uniqueRecords: Record[] = []; const seenIdentifiers = new Set(); for (const entry of sessionRecords) { const identifiers = sessionIdentifiers(entry); if ( identifiers.length > 0 && identifiers.some((value) => seenIdentifiers.has(value)) ) { continue; } uniqueRecords.push(entry); identifiers.forEach((value) => seenIdentifiers.add(value)); } return uniqueRecords.map((entry, index) => { const usageRecord = toRecord(entry.usage); const statsRecord = toRecord(entry.stats); const metricsRecord = toRecord(entry.metrics); const originRecord = toRecord(entry.origin); const candidateRecords = [entry, usageRecord, statsRecord, metricsRecord]; const identifiers = sessionIdentifiers(entry); const key = readString(entry, [ "key", "session_key", "sessionKey", "id", "sessionId", ]) ?? `session-${index}`; const label = readString(entry, ["label", "name", "title"]) ?? key; const channel = readStringFromRecords( [entry, originRecord], ["channel", "source", "kind", "chatType"], ); const model = readString(entry, [ "model", "model_name", "provider", "engine", ]); const modelProvider = readString(entry, [ "modelProvider", "model_provider", "provider", ]); const lastSeenAt = readTimestampFromRecords(candidateRecords, [ "updated_at", "updatedAt", "last_updated_at", "lastUpdatedAt", "last_seen_at", "lastSeen", "last_seen", "last_active_at", "lastActiveAt", "lastActivityAt", "activityAt", "created_at", "createdAt", ]); const usedTokens = readNumberFromRecords(candidateRecords, [ "used", "used_tokens", "tokens", "current", "token_count", "tokenCount", "totalTokens", "total_tokens", "inputTokens", "input_tokens", ]); const maxTokens = readNumberFromRecords(candidateRecords, [ "max", "limit", "token_limit", "capacity", "max_tokens", "maxTokens", "context_window", "contextWindow", "contextTokens", "context_tokens", "maxContextTokens", "max_context_tokens", ]); const pctFromPayload = readNumberFromRecords(candidateRecords, [ "pct", "percent", "ratio_pct", "ratioPct", "token_pct", "usage_pct", "percentUsed", "contextPercent", ]); const usagePct = Number.isFinite(pctFromPayload ?? NaN) ? Math.max(0, Math.min(100, Math.round(pctFromPayload ?? 0))) : usedTokens !== null && maxTokens !== null && maxTokens > 0 ? Math.max(0, Math.min(100, Math.round((usedTokens / maxTokens) * 100))) : 0; const usage = usedTokens !== null && maxTokens !== null ? `${compactNumber(usedTokens)}/${compactNumber(maxTokens)} (${usagePct}%)` : usedTokens !== null ? `${compactNumber(usedTokens)} tokens` : DASH; const subtitleBits = [channel, model].filter(Boolean) as string[]; const subtitle = subtitleBits.length > 0 ? subtitleBits.join(" · ") : "Session"; const modelWithProvider = modelProvider && model && modelProvider !== model ? `${model} · ${modelProvider}` : model; const subtitleWithProvider = [channel, modelWithProvider] .filter(Boolean) .join(" · "); return { key, title: label, subtitle: subtitleWithProvider || subtitle, usage, lastSeenAt, isMain: mainIdentifiers.length > 0 && sharesSessionIdentity(identifiers, mainIdentifiers), }; }); }; export default function DashboardPage() { const router = useRouter(); const { isSignedIn } = useAuth(); const [selectedForgejoRepositoryId, setSelectedForgejoRepositoryId] = useState(ALL_FORGEJO_REPOSITORIES); const [isRefreshingForgejoSync, setIsRefreshingForgejoSync] = useState(false); const [createForgejoIssueOpen, setCreateForgejoIssueOpen] = useState(false); const boardsQuery = useListBoardsApiV1BoardsGet< listBoardsApiV1BoardsGetResponse, ApiError >( { limit: 200 }, { query: { enabled: Boolean(isSignedIn), refetchInterval: 30_000, refetchOnMount: "always", }, }, ); const agentsQuery = useListAgentsApiV1AgentsGet< listAgentsApiV1AgentsGetResponse, ApiError >( { limit: 200 }, { query: { enabled: Boolean(isSignedIn), refetchInterval: 15_000, refetchOnMount: "always", }, }, ); const metricsQuery = useDashboardMetricsApiV1MetricsDashboardGet< dashboardMetricsApiV1MetricsDashboardGetResponse, ApiError >( { range_key: DASHBOARD_RANGE, }, { query: { enabled: Boolean(isSignedIn), refetchInterval: 15_000, refetchOnMount: "always", retry: 3, retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 5000), }, }, ); const activityQuery = useListActivityApiV1ActivityGet< listActivityApiV1ActivityGetResponse, ApiError >( { limit: 200 }, { query: { enabled: Boolean(isSignedIn), refetchInterval: 15_000, refetchOnMount: "always", }, }, ); const forgejoRepositoriesQuery = useQuery({ queryKey: ["dashboard", "forgejo", "repositories"], enabled: Boolean(isSignedIn), refetchInterval: FORGEJO_DASHBOARD_REFETCH_INTERVAL_MS, refetchOnMount: "always", queryFn: () => getForgejoRepositories(), }); const forgejoRepositories = useMemo( () => forgejoRepositoriesQuery.data ?? [], [forgejoRepositoriesQuery.data], ); const selectedForgejoRepository = useMemo( () => selectedForgejoRepositoryId === ALL_FORGEJO_REPOSITORIES ? null : (forgejoRepositories.find( (repository) => repository.id === selectedForgejoRepositoryId, ) ?? null), [forgejoRepositories, selectedForgejoRepositoryId], ); const scopedForgejoRepositories = useMemo( () => selectedForgejoRepositoryId === ALL_FORGEJO_REPOSITORIES ? forgejoRepositories : selectedForgejoRepository ? [selectedForgejoRepository] : [], [ forgejoRepositories, selectedForgejoRepository, selectedForgejoRepositoryId, ], ); const forgejoOrganizationId = useMemo( () => forgejoRepositories.find((repository) => repository.organization_id) ?.organization_id ?? null, [forgejoRepositories], ); useEffect(() => { if ( selectedForgejoRepositoryId !== ALL_FORGEJO_REPOSITORIES && forgejoRepositories.length > 0 && !forgejoRepositories.some( (repository) => repository.id === selectedForgejoRepositoryId, ) ) { setSelectedForgejoRepositoryId(ALL_FORGEJO_REPOSITORIES); } }, [forgejoRepositories, selectedForgejoRepositoryId]); const forgejoMetricsQuery = useQuery({ queryKey: [ "dashboard", "forgejo", "metrics", forgejoOrganizationId, selectedForgejoRepositoryId, ], enabled: Boolean( isSignedIn && !forgejoRepositoriesQuery.isLoading && !forgejoRepositoriesQuery.error && (selectedForgejoRepositoryId === ALL_FORGEJO_REPOSITORIES ? forgejoOrganizationId : selectedForgejoRepository), ), refetchInterval: FORGEJO_DASHBOARD_REFETCH_INTERVAL_MS, refetchOnMount: "always", queryFn: () => { if (selectedForgejoRepositoryId !== ALL_FORGEJO_REPOSITORIES) { return getForgejoMetrics({ repository_id: selectedForgejoRepositoryId, }); } if (!forgejoOrganizationId) return Promise.resolve(null); return getForgejoMetrics({ organization_id: forgejoOrganizationId }); }, }); const forgejoHeatmapQuery = useQuery< { days: ForgejoHeatmapDay[]; max_count: number; total_additions: number; total_deletions: number; has_line_stats: boolean; } | null, Error >({ queryKey: ["dashboard", "forgejo", "heatmap", forgejoOrganizationId], enabled: Boolean( isSignedIn && forgejoOrganizationId && !forgejoRepositoriesQuery.isLoading && !forgejoRepositoriesQuery.error, ), refetchInterval: (query) => query.state.data?.has_line_stats === false ? 3_000 : FORGEJO_DASHBOARD_REFETCH_INTERVAL_MS, refetchOnMount: "always", queryFn: () => { if (!forgejoOrganizationId) return Promise.resolve(null); return getForgejoHeatmap({ organization_id: forgejoOrganizationId }); }, }); const forgejoLastPushQuery = useQuery({ queryKey: ["dashboard", "forgejo", "last-push", forgejoOrganizationId], enabled: Boolean(isSignedIn && forgejoOrganizationId), refetchInterval: FORGEJO_DASHBOARD_REFETCH_INTERVAL_MS, refetchOnMount: "always", queryFn: () => { if (!forgejoOrganizationId) return Promise.resolve(null); return getForgejoLastPush({ organization_id: forgejoOrganizationId }); }, }); const handleRefreshForgejoLastSync = async () => { if (isRefreshingForgejoSync) return; const repositoriesToSync = selectedForgejoRepositoryId === ALL_FORGEJO_REPOSITORIES ? forgejoRepositories.filter((repository) => repository.active) : selectedForgejoRepository ? [selectedForgejoRepository] : []; setIsRefreshingForgejoSync(true); try { for (const repository of repositoriesToSync) { await syncRepository(repository.id); } await Promise.all([ forgejoRepositoriesQuery.refetch(), forgejoMetricsQuery.refetch(), forgejoHeatmapQuery.refetch(), forgejoLastPushQuery.refetch(), ]); } catch (error) { console.error("Failed to refresh Forgejo sync status:", error); } finally { setIsRefreshingForgejoSync(false); } }; const boards = useMemo( () => boardsQuery.data?.status === 200 ? [...(boardsQuery.data.data.items ?? [])].sort((a, b) => a.name.localeCompare(b.name), ) : [], [boardsQuery.data], ); const agents = useMemo( () => agentsQuery.data?.status === 200 ? [...(agentsQuery.data.data.items ?? [])].sort((a, b) => a.name.localeCompare(b.name), ) : [], [agentsQuery.data], ); const metrics = metricsQuery.data?.status === 200 ? metricsQuery.data.data : null; const onlineAgents = useMemo( () => agents.filter((agent) => (agent.status ?? "").toLowerCase() === "online") .length, [agents], ); const gatewayTargets = useMemo(() => { const byGateway = new Map(); for (const board of boards) { const gatewayId = board.gateway_id; if (!gatewayId) continue; if (byGateway.has(gatewayId)) continue; byGateway.set(gatewayId, { gatewayId, boardId: board.id, boardName: board.name, }); } return [...byGateway.values()].sort((a, b) => a.boardName.localeCompare(b.boardName), ); }, [boards]); const hasConfiguredGateways = gatewayTargets.length > 0; const gatewayStatusesQuery = useQuery({ queryKey: [ "dashboard", "gateway-statuses", gatewayTargets.map((target) => `${target.gatewayId}:${target.boardId}`), ], enabled: Boolean(isSignedIn && hasConfiguredGateways), refetchInterval: 15_000, refetchOnMount: "always", queryFn: async ({ signal }) => { return Promise.all( gatewayTargets.map(async (target): Promise => { try { const response = await gatewaysStatusApiV1GatewaysStatusGet( { board_id: target.boardId }, { signal }, ); if (response.status !== 200) { return { ...target, connected: false, gatewayUrl: null, sessionsCount: 0, sessions: [], mainSession: null, mainSessionError: null, error: null, requestError: `Gateway status request failed (${response.status})`, }; } const payload: GatewaysStatusResponse = response.data; return { ...target, connected: Boolean(payload.connected), gatewayUrl: payload.gateway_url ?? null, sessionsCount: Number(payload.sessions_count ?? 0), sessions: Array.isArray(payload.sessions) ? payload.sessions : [], mainSession: payload.main_session ?? null, mainSessionError: payload.main_session_error ?? null, error: payload.error ?? null, requestError: null, }; } catch (error) { if (signal.aborted) throw error; return { ...target, connected: false, gatewayUrl: null, sessionsCount: 0, sessions: [], mainSession: null, mainSessionError: null, error: null, requestError: error instanceof Error ? error.message : "Gateway status request failed.", }; } }), ); }, }); const gatewaySnapshots = useMemo( () => gatewayStatusesQuery.data ?? [], [gatewayStatusesQuery.data], ); // Runtime usage — query all gateways in parallel, aggregate + per-gateway const runtimeUsageQuery = useQuery< { aggregate: AggregatedRuntimeUsage; perGateway: PerGatewayUsage[] }, ApiError >({ queryKey: [ "dashboard", "runtime-usage", gatewayTargets.map((t) => t.gatewayId), ], enabled: Boolean(isSignedIn && hasConfiguredGateways), refetchInterval: 30_000, refetchOnMount: "always", queryFn: async () => { const results = await Promise.allSettled( gatewayTargets.map((target) => getGatewayRuntimeUsageApiV1GatewaysGatewayIdRuntimeUsageGet( target.gatewayId, ).then((res) => { if (res.status === 200) return res.data as RuntimeUsageResponse; return null; }), ), ); const valid = results .filter( (r): r is PromiseFulfilledResult => r.status === "fulfilled" && r.value !== null, ) .map((r) => r.value); const labels: Record = {}; for (const t of gatewayTargets) { labels[t.gatewayId] = t.boardName; } return { aggregate: aggregateRuntimeUsage(valid), perGateway: buildPerGatewayUsage(valid, labels), }; }, }); const runtimeUsage = runtimeUsageQuery.data?.aggregate ?? null; const perGatewayUsage = runtimeUsageQuery.data?.perGateway ?? []; const providerUsageQuery = useQuery({ queryKey: [ "dashboard", "provider-usage", gatewayTargets.map((t) => t.gatewayId), ], enabled: Boolean(isSignedIn && hasConfiguredGateways), refetchInterval: 30_000, refetchOnMount: "always", queryFn: async () => { const settled = await Promise.allSettled( gatewayTargets.map((target) => getGatewayProviderUsageApiV1GatewaysGatewayIdProviderUsageGet( target.gatewayId, ).then((res) => ({ target, res })), ), ); const windows: ProviderNativeUsageWindow[] = []; for (const item of settled) { if (item.status !== "fulfilled") continue; const { target, res } = item.value; if (res.status !== 200) continue; const payload = res.data as ProviderUsageResponse; if (!payload.scraper_enabled) continue; for (const scrape of payload.results ?? []) { const source = scrape.source ?? "provider_native"; const confidence = scrape.confidence ?? "medium"; if (Array.isArray(scrape.windows) && scrape.windows.length > 0) { for (const window of scrape.windows) { windows.push({ key: window.key, label: window.label, pctUsed: window.pct_used ?? null, remainingMs: window.remaining_ms ?? null, remainingLabel: window.remaining_label ?? null, source: window.source ?? source, confidence: window.confidence ?? confidence, provider: scrape.provider, gatewayLabel: target.boardName, }); } continue; } if (scrape.current_pct !== null || scrape.remaining_ms !== null) { windows.push({ key: "current_session", label: "Current session", pctUsed: scrape.current_pct ?? null, remainingMs: scrape.remaining_ms ?? null, remainingLabel: scrape.remaining_label ?? null, source, confidence, provider: scrape.provider, gatewayLabel: target.boardName, }); } } } return windows; }, }); const credentialUsageQuery = useQuery({ queryKey: ["dashboard", "provider-credential-usage"], enabled: Boolean(isSignedIn), refetchInterval: 60_000, refetchOnMount: "always", queryFn: async () => { const credentialsRes = await listProviderCredentialsApiV1ProviderCredentialsGet(); if (credentialsRes.status !== 200) return []; const credentials = credentialsRes.data ?? []; const providerIds = Array.from( new Set( credentials .filter((cred) => cred.active && cred.has_session_key) .map((cred) => cred.provider), ), ); const selectedCredentials = providerIds .map((providerId) => selectPreferredProviderCredential( credentials.filter((cred) => cred.has_session_key), providerId, ), ) .filter((cred): cred is (typeof credentials)[number] => Boolean(cred?.active && cred.has_session_key), ); const settled = await Promise.allSettled( selectedCredentials.map((cred) => getProviderUsageLiveApiV1ProviderCredentialsCredentialIdUsageGet( cred.id, ).then((res) => ({ cred, res })), ), ); const windows: ProviderNativeUsageWindow[] = []; for (const item of settled) { if (item.status !== "fulfilled") continue; const { cred, res } = item.value; if (res.status !== 200) continue; const usage = res.data; const accountLabel = cred.display_name || cred.account_key || cred.provider; const subscriptionWindows = usage.subscription_windows ?? []; for (const window of subscriptionWindows) { windows.push({ key: window.key, label: window.label, pctUsed: window.pct_used, remainingMs: window.reset_in_ms ?? null, remainingLabel: null, source: window.source ?? "provider_native", confidence: window.confidence ?? "high", provider: usage.provider, gatewayLabel: accountLabel, }); } if (subscriptionWindows.length === 0) { windows.push({ key: `${cred.id}:subscription_unavailable`, label: "Subscription usage", pctUsed: null, remainingMs: null, remainingLabel: null, source: "provider_native", confidence: "low", provider: usage.provider, gatewayLabel: accountLabel, note: usage.error ?? "No subscription usage returned for this session key.", }); } } return windows; }, }); const statuslineUsageWindows = providerUsageQuery.data ?? []; const statuslineProviders = new Set( statuslineUsageWindows .filter( (window) => window.pctUsed !== null || window.remainingMs !== null, ) .map((window) => window.provider), ); const providerUsageWindows = [ ...statuslineUsageWindows, ...(credentialUsageQuery.data ?? []).filter( (window) => !( window.key.includes("subscription_unavailable") && statuslineProviders.has(window.provider) ), ), ]; // Gateway health — query the first gateway only for the compact dashboard panel const primaryGatewayId = gatewayTargets[0]?.gatewayId ?? null; const gatewayHealthQuery = useQuery({ queryKey: ["dashboard", "gateway-health", primaryGatewayId], enabled: Boolean(isSignedIn && primaryGatewayId), refetchInterval: 60_000, refetchOnMount: "always", queryFn: () => { if (!primaryGatewayId) return Promise.resolve(null); return getGatewayHealthApiV1GatewaysGatewayIdHealthGet( primaryGatewayId, ).then((r) => r.status === 200 ? (r.data as SystemHealthResponse) : null, ); }, }); const gatewayCronQuery = useQuery({ queryKey: ["dashboard", "gateway-cron", primaryGatewayId], enabled: Boolean(isSignedIn && primaryGatewayId), refetchInterval: 60_000, refetchOnMount: "always", queryFn: () => { if (!primaryGatewayId) return Promise.resolve(null); return getGatewayCronApiV1GatewaysGatewayIdCronGet(primaryGatewayId).then( (r) => (r.status === 200 ? (r.data as CronStatusResponse) : null), ); }, }); // Build a session-id → TopSession lookup for enriching session summaries const topSessionById = useMemo(() => { const map = new Map< string, { costUsd: number; totalTokens: number; model: string | null } >(); for (const s of runtimeUsage?.topSessions ?? []) { if (s.session_id) { map.set(s.session_id, { costUsd: s.cost_usd, totalTokens: s.total_tokens, model: s.model ?? null, }); } } return map; }, [runtimeUsage]); const sessionSummaries = useMemo( () => gatewaySnapshots.flatMap((snapshot) => { if (snapshot.requestError) return []; const sourceLabel = snapshot.gatewayUrl || snapshot.boardName; return toSessionSummaries(snapshot.sessions, snapshot.mainSession).map( (session) => { const enrichment = topSessionById.get(session.key) ?? topSessionById.get(`${snapshot.gatewayId}:${session.key}`); return { ...session, key: `${snapshot.gatewayId}:${session.key}`, subtitle: `${sourceLabel} · ${session.subtitle}`, costUsd: enrichment?.costUsd ?? null, totalTokens: enrichment?.totalTokens ?? null, model: enrichment?.model ?? null, }; }, ); }), [gatewaySnapshots, topSessionById], ); const activityEvents = useMemo( () => activityQuery.data?.status === 200 ? [...(activityQuery.data.data.items ?? [])] : [], [activityQuery.data], ); const orderedActivityEvents = useMemo( () => [...activityEvents].sort((a, b) => { const left = parseTimestamp(a.created_at)?.getTime() ?? 0; const right = parseTimestamp(b.created_at)?.getTime() ?? 0; return right - left; }), [activityEvents], ); const recentLogs = orderedActivityEvents.slice(0, 8); const latestThroughputPoint = metrics?.throughput.primary.points?.[ metrics.throughput.primary.points.length - 1 ] ?? null; const throughputTotal = (metrics?.throughput.primary.points ?? []).reduce( (sum, point) => sum + Number(point.value ?? 0), 0, ); const completionDaysCount = (metrics?.throughput.primary.points ?? []).reduce( (sum, point) => sum + (Number(point.value ?? 0) > 0 ? 1 : 0), 0, ); const inboxTasksMetric = metrics?.kpis.inbox_tasks ?? 0; const inProgressTasksMetric = metrics?.kpis.in_progress_tasks ?? 0; const reviewTasksMetric = metrics?.kpis.review_tasks ?? 0; const doneTasksMetric = metrics?.kpis.done_tasks ?? 0; const activeAgentsMetric = onlineAgents; const tasksTotal = inboxTasksMetric + inProgressTasksMetric + reviewTasksMetric + doneTasksMetric; const tasksInProgressMetric = metrics?.kpis.tasks_in_progress ?? inProgressTasksMetric; const errorRateMetric = Number(metrics?.kpis.error_rate_pct ?? 0); const reviewBacklogRatio = inProgressTasksMetric > 0 ? reviewTasksMetric / inProgressTasksMetric : null; const gatewayConnectedCount = gatewaySnapshots.filter( (snapshot) => !snapshot.requestError && snapshot.connected, ).length; const gatewayDisconnectedCount = gatewaySnapshots.filter( (snapshot) => !snapshot.requestError && !snapshot.connected, ).length; const gatewayUnavailableCount = gatewaySnapshots.filter((snapshot) => Boolean(snapshot.requestError), ).length; const gatewayHealthErrorCount = gatewaySnapshots.filter((snapshot) => Boolean(snapshot.error || snapshot.mainSessionError), ).length; const countedSessions = gatewaySnapshots.reduce( (sum, snapshot) => sum + Math.max(0, snapshot.sessionsCount), 0, ); const activeSessions = Math.max(countedSessions, sessionSummaries.length); const gatewayStatusLabel = !hasConfiguredGateways ? "Not configured" : gatewayStatusesQuery.isLoading ? "Checking" : gatewayConnectedCount === gatewayTargets.length ? "All connected" : gatewayConnectedCount > 0 ? "Partially connected" : gatewayUnavailableCount === gatewayTargets.length ? "Unavailable" : "Disconnected"; const gatewayBadgeTone: "online" | "offline" | "neutral" = gatewayStatusLabel === "All connected" ? "online" : gatewayStatusLabel === "Partially connected" || gatewayStatusLabel === "Disconnected" || gatewayStatusLabel === "Unavailable" ? "offline" : "neutral"; const gatewayStatusTone: SummaryRow["tone"] = gatewayStatusLabel === "All connected" ? "success" : gatewayStatusLabel === "Checking" || gatewayStatusLabel === "Not configured" ? "default" : gatewayStatusLabel === "Partially connected" || gatewayStatusLabel === "Disconnected" ? "warning" : "danger"; const workloadRows: SummaryRow[] = [ { label: "Total work items", value: formatCount(tasksTotal), }, { label: "Inbox", value: formatCount(inboxTasksMetric), }, { label: "In progress", value: formatCount(inProgressTasksMetric), tone: inProgressTasksMetric > 0 ? "warning" : "default", }, { label: "In review", value: formatCount(reviewTasksMetric), }, { label: "Completed", value: formatCount(doneTasksMetric), tone: doneTasksMetric > 0 ? "success" : "default", }, ]; const throughputRows: SummaryRow[] = [ { label: "Completed tasks", value: formatCount(throughputTotal), }, { label: "Average throughput", value: formatPerDay(throughputTotal, DASHBOARD_RANGE_DAYS), }, { label: "Error rate", value: formatPercent(errorRateMetric), tone: errorRateMetric > 0 ? "warning" : "success", }, { label: "Completion consistency", value: `${formatCount(completionDaysCount)} active days`, tone: completionDaysCount >= Math.ceil(DASHBOARD_RANGE_DAYS * 0.75) ? "success" : "default", }, { label: "Review backlog ratio", value: reviewBacklogRatio !== null ? `${reviewBacklogRatio.toFixed(2)}x` : reviewTasksMetric > 0 ? "∞" : "0.00x", tone: reviewBacklogRatio !== null ? reviewBacklogRatio > 1 ? "warning" : "success" : reviewTasksMetric > 0 ? "warning" : "success", }, ]; const gatewayRows: SummaryRow[] = [ { label: "Gateway status", value: gatewayStatusLabel, tone: gatewayStatusTone, }, { label: "Configured gateways", value: formatCount(gatewayTargets.length) }, { label: "Connected gateways", value: formatCount(gatewayConnectedCount), tone: gatewayConnectedCount > 0 ? "success" : "default", }, { label: "Unavailable gateways", value: formatCount(gatewayUnavailableCount), tone: gatewayUnavailableCount > 0 ? "danger" : "default", }, { label: "Gateways with issues", value: formatCount(gatewayHealthErrorCount + gatewayDisconnectedCount), tone: gatewayHealthErrorCount + gatewayDisconnectedCount > 0 ? "warning" : "success", }, ]; const pendingApprovalItems = metrics?.pending_approvals.items ?? []; const pendingApprovalsTotal = metrics?.pending_approvals.total ?? 0; const activityFeedHref = "/activity"; const forgejoIssueMetrics = forgejoMetricsQuery.data ?? null; const forgejoIssueMetricsError = formatForgejoDashboardError(forgejoRepositoriesQuery.error) ?? formatForgejoDashboardError(forgejoMetricsQuery.error) ?? null; const forgejoIssueMetricsLoading = forgejoRepositoriesQuery.isLoading || forgejoMetricsQuery.isLoading; const shouldIgnoreRowNavigation = (target: EventTarget | null): boolean => { if (!(target instanceof HTMLElement)) return false; return Boolean(target.closest("a")); }; const buildActivityEventHref = (event: ActivityEventRead): string => { const routeName = event.route_name ?? null; const routeParams = event.route_params ?? {}; if (routeName === "board.approvals") { const boardId = routeParams.boardId; if (boardId) { return `/boards/${encodeURIComponent(boardId)}/approvals`; } } if (routeName === "board") { const boardId = routeParams.boardId; if (boardId) { const params = new URLSearchParams(); Object.entries(routeParams).forEach(([key, value]) => { if (key !== "boardId") params.set(key, value); }); const query = params.toString(); return query ? `/boards/${encodeURIComponent(boardId)}?${query}` : `/boards/${encodeURIComponent(boardId)}`; } } const params = new URLSearchParams( Object.keys(routeParams).length > 0 ? routeParams : { eventId: event.id, eventType: event.event_type, createdAt: event.created_at, }, ); if (event.task_id && !params.has("taskId")) { params.set("taskId", event.task_id); } return `${activityFeedHref}?${params.toString()}`; }; const navigateToActivityFeed = (href: string) => { router.push(href); }; const handleLogRowClick = ( event: MouseEvent, href: string, ) => { if (shouldIgnoreRowNavigation(event.target)) return; navigateToActivityFeed(href); }; const handleLogRowKeyDown = ( event: KeyboardEvent, href: string, ) => { if (event.key !== "Enter" && event.key !== " ") return; if (shouldIgnoreRowNavigation(event.target)) return; event.preventDefault(); navigateToActivityFeed(href); }; return (
); }