'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import {
getInsightInterruptionTotal,
getInsightOverviewLoadMode,
isAllInsightAgents,
shouldShowInsightTimeseriesLoading,
} from './overview-utils'
import {
buildQueryRange,
getInsightErrorMessage,
type InsightOverviewSummary,
} from './overview-shared'
import { useInsightFilters, type InsightOverviewFiltersController } from './use-insight-filters'
import { useInsightHealth, type InsightOverviewHealthController } from './use-insight-health'
import {
useInsightInterruptions,
type InsightOverviewInterruptionsController,
} from './use-insight-interruptions'
import { useInsightSessions, type InsightOverviewSessionsController } from './use-insight-sessions'
import {
useInsightTimeseries,
type InsightOverviewTimeseriesController,
} from './use-insight-timeseries'
export type {
InsightOverviewSummary,
InsightQueryRange,
InsightRangePreset,
} from './overview-shared'
export type { InsightOverviewFiltersController } from './use-insight-filters'
export type { InsightOverviewHealthController } from './use-insight-health'
export type { InsightOverviewInterruptionsController } from './use-insight-interruptions'
export type { InsightOverviewSessionsController } from './use-insight-sessions'
export type { InsightOverviewTimeseriesController } from './use-insight-timeseries'
export interface InsightOverviewStatusController {
loading: boolean
refreshing: boolean
error: string | null
lastUpdated: Date | null
}
export interface InsightOverviewSummaryController {
loading: boolean
interruptionCountLoaded: boolean
summary: InsightOverviewSummary
}
export interface InsightOverviewActionsController {
refreshOverview: () => Promise<void>
}
export interface InsightOverviewController {
filters: InsightOverviewFiltersController
status: InsightOverviewStatusController
summary: InsightOverviewSummaryController
sessions: InsightOverviewSessionsController
timeseries: InsightOverviewTimeseriesController
health: InsightOverviewHealthController
interruptions: InsightOverviewInterruptionsController
actions: InsightOverviewActionsController
}
export function useInsightOverview(options?: { enabled?: boolean }) {
const enabled = options?.enabled ?? true
const [loading, setLoading] = useState(true)
const [refreshing, setRefreshing] = useState(false)
const [error, setError] = useState<string | null>(null)
const [lastUpdated, setLastUpdated] = useState<Date | null>(null)
const requestIdRef = useRef(0)
const overviewStateRef = useRef({
lastUpdated: null as Date | null,
sessionCount: 0,
tokenSeriesCount: 0,
modelSeriesCount: 0,
})
const filtersState = useInsightFilters()
const sessionsState = useInsightSessions()
const timeseriesState = useInsightTimeseries()
const interruptionsState = useInsightInterruptions()
const healthState = useInsightHealth({
enabled,
selectedWittyAgentId: filtersState.selectedWittyAgentId,
})
const {
controller: filtersController,
queryRange,
selectedPreset,
selectedWittyAgentId,
refreshWittyAgents,
setQueryRange,
} = filtersState
const { controller: sessionsController, sessions, refreshSessions } = sessionsState
const {
controller: timeseriesController,
tokenSeries,
modelSeries,
refreshTimeseries,
} = timeseriesState
const {
controller: interruptionsController,
interruptionCount,
interruptionCountLoaded,
sessionInterruptionCounts,
sessionInterruptionCountsLoaded,
refreshInterruptionAggregates,
} = interruptionsState
const { controller: healthController, refreshHealth } = healthState
useEffect(() => {
overviewStateRef.current = {
lastUpdated,
sessionCount: sessions.length,
tokenSeriesCount: tokenSeries.length,
modelSeriesCount: modelSeries.length,
}
}, [lastUpdated, modelSeries.length, sessions.length, tokenSeries.length])
const refreshOverview = useCallback(async () => {
if (!enabled) {
return
}
const requestId = ++requestIdRef.current
const nextQueryRange = buildQueryRange(selectedPreset)
const nextLoadMode = getInsightOverviewLoadMode({
sessionCount: overviewStateRef.current.sessionCount,
lastUpdated: overviewStateRef.current.lastUpdated,
})
if (nextLoadMode === 'loading') {
setLoading(true)
} else {
setRefreshing(true)
}
setError(null)
setLastUpdated(new Date())
setQueryRange(nextQueryRange)
const selectedAgentId = isAllInsightAgents(selectedWittyAgentId)
? undefined
: selectedWittyAgentId
const showTimeseriesLoading = shouldShowInsightTimeseriesLoading({
tokenSeriesCount: overviewStateRef.current.tokenSeriesCount,
modelSeriesCount: overviewStateRef.current.modelSeriesCount,
})
const results = await Promise.allSettled([
refreshWittyAgents(),
refreshSessions({
queryRange: nextQueryRange,
wittyAgentId: selectedAgentId,
}),
refreshTimeseries({
queryRange: nextQueryRange,
wittyAgentId: selectedAgentId,
showLoading: showTimeseriesLoading,
}),
refreshInterruptionAggregates({
queryRange: nextQueryRange,
wittyAgentId: selectedAgentId,
}),
refreshHealth(),
])
if (requestIdRef.current !== requestId) {
return
}
const sessionsResult = results[1]
if (sessionsResult.status === 'rejected') {
setError(getInsightErrorMessage(sessionsResult.reason))
setLoading(false)
setRefreshing(false)
return
}
setLoading(false)
setRefreshing(false)
}, [
enabled,
refreshHealth,
refreshInterruptionAggregates,
refreshSessions,
refreshTimeseries,
refreshWittyAgents,
selectedPreset,
selectedWittyAgentId,
setQueryRange,
])
useEffect(() => {
if (!enabled) {
return
}
let cancelled = false
queueMicrotask(() => {
if (!cancelled) {
void refreshOverview()
}
})
return () => {
cancelled = true
}
}, [enabled, refreshOverview])
const sessionCount = sessions.length
const conversationCount = sessions.reduce(
(count, session) => count + session.conversation_count,
0
)
const totalInputTokens = sessions.reduce(
(count, session) => count + session.total_input_tokens,
0
)
const totalOutputTokens = sessions.reduce(
(count, session) => count + session.total_output_tokens,
0
)
const totalTokens = totalInputTokens + totalOutputTokens
const interruptionTotal = getInsightInterruptionTotal({
selectedWittyAgentId,
interruptionCountLoaded,
sessionInterruptionCountsLoaded,
interruptionCount,
sessions,
sessionInterruptionCounts,
})
const controller: InsightOverviewController = {
filters: filtersController,
status: {
loading,
refreshing,
error,
lastUpdated,
},
summary: {
loading,
interruptionCountLoaded,
summary: {
sessionCount,
conversationCount,
totalInputTokens,
totalOutputTokens,
totalTokens,
interruptionTotal,
},
},
sessions: sessionsController,
timeseries: {
...timeseriesController,
startNs: queryRange.startNs,
endNs: queryRange.endNs,
},
health: healthController,
interruptions: interruptionsController,
actions: {
refreshOverview,
},
}
return controller
}