fix(web): speed up homepage live evidence loading
This commit is contained in:
@@ -33,8 +33,10 @@ import { FlywheelKPICard } from '@/components/dashboard/flywheel-kpi-card'
|
||||
import { AutomationEvidenceCard } from '@/components/dashboard/automation-evidence-card'
|
||||
|
||||
const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? ''
|
||||
const STATUS_CHAIN_PREFETCH_LIMIT = 25
|
||||
const HOMEPAGE_INCIDENT_LIMIT = STATUS_CHAIN_PREFETCH_LIMIT
|
||||
const HOMEPAGE_STATUS_CHAIN_PREFETCH_LIMIT = 8
|
||||
const HOMEPAGE_STATUS_CHAIN_CONCURRENCY = 2
|
||||
const HOMEPAGE_STATUS_CHAIN_TIMEOUT_MS = 6000
|
||||
const HOMEPAGE_INCIDENT_LIMIT = HOMEPAGE_STATUS_CHAIN_PREFETCH_LIMIT
|
||||
const HOMEPAGE_BLUEPRINT_STAGE_KEYS = ['signal', 'intake', 'ai', 'mcp', 'playbook', 'ansible', 'approval', 'verify'] as const
|
||||
type HomepageBlueprintStageKey = typeof HOMEPAGE_BLUEPRINT_STAGE_KEYS[number]
|
||||
|
||||
@@ -712,7 +714,9 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
isLoading: isStatusChainsLoading,
|
||||
} = useIncidentStatusChains({
|
||||
incidentIds: incidents?.map(incident => incident.incident_id) ?? [],
|
||||
limit: STATUS_CHAIN_PREFETCH_LIMIT,
|
||||
limit: HOMEPAGE_STATUS_CHAIN_PREFETCH_LIMIT,
|
||||
concurrency: HOMEPAGE_STATUS_CHAIN_CONCURRENCY,
|
||||
timeoutMs: HOMEPAGE_STATUS_CHAIN_TIMEOUT_MS,
|
||||
refreshKey: incidentsLastUpdated?.toISOString() ?? null,
|
||||
})
|
||||
|
||||
@@ -864,8 +868,19 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
const unavailableValue = tDashboard('automationDelivery.unavailableValue')
|
||||
const loadingStatus = tDashboard('automationDelivery.status.loading')
|
||||
const unavailableStatus = tDashboard('automationDelivery.status.unavailable')
|
||||
const formatLiveEvidenceTime = (value: string | null | undefined) => {
|
||||
if (!value) return automationBriefLoaded ? unavailableValue : loadingStatus
|
||||
const hasAutomationBriefPart = (key: keyof HomepageAutomationBriefSnapshot) => (
|
||||
Object.prototype.hasOwnProperty.call(automationBrief, key)
|
||||
)
|
||||
const callbackRepliesLoaded = hasAutomationBriefPart('callbackReplies')
|
||||
const aiRouteStatusLoaded = hasAutomationBriefPart('aiRouteStatus')
|
||||
const kmStaleCandidatesLoaded = hasAutomationBriefPart('kmStaleCandidates')
|
||||
const dossierCoverageLoaded = hasAutomationBriefPart('dossierCoverage')
|
||||
const eventRecurrenceLoaded = hasAutomationBriefPart('eventRecurrence')
|
||||
const runsListLoaded = hasAutomationBriefPart('runsList')
|
||||
const cicdEventsLoaded = hasAutomationBriefPart('cicdEvents')
|
||||
const kmOwnerReviewBurndownLoaded = hasAutomationBriefPart('kmOwnerReviewBurndown')
|
||||
const formatLiveEvidenceTime = (value: string | null | undefined, loaded = automationBriefLoaded) => {
|
||||
if (!value) return loaded ? unavailableValue : loadingStatus
|
||||
const date = new Date(value)
|
||||
if (Number.isNaN(date.getTime())) return unavailableValue
|
||||
return date.toLocaleString(locale === 'en' ? 'en-US' : 'zh-TW', {
|
||||
@@ -917,7 +932,7 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
const callbackTraceSummary = automationBrief.callbackReplies?.summary ?? null
|
||||
const missingTraceRecent24h = callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_24h_total ?? 0
|
||||
const traceRecoveryStatus = callbackTraceSummary?.outbound_reply_markup_trace_ref_gap_recovery_status
|
||||
?? (automationBriefLoaded ? unavailableValue : loadingStatus)
|
||||
?? (callbackRepliesLoaded ? unavailableValue : loadingStatus)
|
||||
const traceRecoveryCount = callbackTraceSummary?.outbound_reply_markup_trace_ref_after_gap_total
|
||||
const aiRouteSelectedProvider = automationBrief.aiRouteStatus?.selected_provider ?? '--'
|
||||
const aiRouteLaneMode = automationBrief.aiRouteStatus?.lane_mode ?? '--'
|
||||
@@ -986,7 +1001,7 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
const kmBurndownSnapshot = kmOwnerReviewBurndown?.current_snapshot ?? null
|
||||
const staleRatioLabel = typeof kmBurndownSnapshot?.stale_ratio === 'number'
|
||||
? `${(kmBurndownSnapshot.stale_ratio * 100).toFixed(1)}%`
|
||||
: automationBriefLoaded ? unavailableValue : loadingStatus
|
||||
: kmOwnerReviewBurndownLoaded ? unavailableValue : loadingStatus
|
||||
const automationWorkToneStyle: Record<HomepageWorkTone, { bg: string; border: string; color: string }> = {
|
||||
live: { bg: '#f0faf2', border: '#9bc7a4', color: '#17602a' },
|
||||
progress: { bg: '#fff7e8', border: '#d9b36f', color: '#8a5a08' },
|
||||
@@ -1019,11 +1034,11 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
title: tDashboard('automationDelivery.delivered.callbackEvidence.title'),
|
||||
status: hasCallbackEvidence
|
||||
? tDashboard('automationDelivery.status.live')
|
||||
: automationBriefLoaded
|
||||
: callbackRepliesLoaded
|
||||
? unavailableStatus
|
||||
: loadingStatus,
|
||||
detail: tDashboard('automationDelivery.delivered.callbackEvidence.detail', {
|
||||
total: formatAutomationNumber(automationBrief.callbackReplies?.total, automationBriefLoaded),
|
||||
total: formatAutomationNumber(automationBrief.callbackReplies?.total, callbackRepliesLoaded),
|
||||
}),
|
||||
href: `/${locale}/awooop/runs?project_id=awoooi#tg-callback-evidence`,
|
||||
tone: hasCallbackEvidence ? 'live' : 'watching',
|
||||
@@ -1033,15 +1048,15 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
title: tDashboard('automationDelivery.delivered.callbackTrace.title'),
|
||||
status: callbackTraceSummary && traceRecoveryStatus === 'recovered_after_gap'
|
||||
? tDashboard('automationDelivery.status.progress')
|
||||
: !automationBriefLoaded
|
||||
: !callbackRepliesLoaded
|
||||
? loadingStatus
|
||||
: callbackTraceSummary
|
||||
? tDashboard('automationDelivery.status.watching')
|
||||
: unavailableStatus,
|
||||
detail: tDashboard('automationDelivery.delivered.callbackTrace.detail', {
|
||||
status: traceRecoveryStatus,
|
||||
recovered: formatAutomationNumber(traceRecoveryCount, automationBriefLoaded),
|
||||
recent24h: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_24h_total, automationBriefLoaded),
|
||||
recovered: formatAutomationNumber(traceRecoveryCount, callbackRepliesLoaded),
|
||||
recent24h: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_24h_total, callbackRepliesLoaded),
|
||||
}),
|
||||
href: `/${locale}/awooop/work-items?project_id=awoooi`,
|
||||
tone: traceRecoveryStatus === 'recovered_after_gap' ? 'progress' : 'watching',
|
||||
@@ -1118,7 +1133,7 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
{
|
||||
key: 'kmGovernance',
|
||||
title: tDashboard('automationDelivery.remaining.kmGovernance.title'),
|
||||
status: !automationBriefLoaded
|
||||
status: !kmStaleCandidatesLoaded
|
||||
? loadingStatus
|
||||
: typeof kmStaleTotal === 'number' && kmStaleTotal > 0
|
||||
? tDashboard('automationDelivery.status.progress')
|
||||
@@ -1126,8 +1141,8 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
? tDashboard('automationDelivery.status.live')
|
||||
: unavailableStatus,
|
||||
detail: tDashboard('automationDelivery.remaining.kmGovernance.detail', {
|
||||
stale: formatAutomationNumber(kmStaleTotal, automationBriefLoaded),
|
||||
days: formatAutomationNumber(kmStaleThreshold, automationBriefLoaded),
|
||||
stale: formatAutomationNumber(kmStaleTotal, kmStaleCandidatesLoaded),
|
||||
days: formatAutomationNumber(kmStaleThreshold, kmStaleCandidatesLoaded),
|
||||
}),
|
||||
href: `/${locale}/awooop/work-items?project_id=awoooi`,
|
||||
tone: typeof kmStaleTotal === 'number' && kmStaleTotal > 0 ? 'progress' : hasKmStaleCandidates ? 'live' : 'watching',
|
||||
@@ -1135,7 +1150,7 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
{
|
||||
key: 'callbackBacklogDecay',
|
||||
title: tDashboard('automationDelivery.remaining.callbackBacklogDecay.title'),
|
||||
status: !automationBriefLoaded
|
||||
status: !callbackRepliesLoaded
|
||||
? loadingStatus
|
||||
: callbackTraceSummary && missingTraceRecent24h > 0
|
||||
? tDashboard('automationDelivery.status.progress')
|
||||
@@ -1143,9 +1158,9 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
? tDashboard('automationDelivery.status.live')
|
||||
: unavailableStatus,
|
||||
detail: tDashboard('automationDelivery.remaining.callbackBacklogDecay.detail', {
|
||||
recent1h: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_1h_total, automationBriefLoaded),
|
||||
recent24h: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_24h_total, automationBriefLoaded),
|
||||
missing: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_total, automationBriefLoaded),
|
||||
recent1h: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_1h_total, callbackRepliesLoaded),
|
||||
recent24h: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_24h_total, callbackRepliesLoaded),
|
||||
missing: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_total, callbackRepliesLoaded),
|
||||
}),
|
||||
href: `/${locale}/awooop/runs?project_id=awoooi#tg-callback-evidence`,
|
||||
tone: callbackTraceSummary && missingTraceRecent24h > 0 ? 'progress' : callbackTraceSummary ? 'live' : 'watching',
|
||||
@@ -1210,10 +1225,9 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
key: 'signal',
|
||||
title: tDashboard('automationDiagrams.workspace.flow.stages.signal'),
|
||||
status: tDashboard('automationDelivery.status.live'),
|
||||
detail: tDashboard('automationDiagrams.workspace.values.callback', {
|
||||
missing: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_total, automationBriefLoaded),
|
||||
recent1h: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_1h_total, automationBriefLoaded),
|
||||
recent24h: formatAutomationNumber(callbackTraceSummary?.outbound_reply_markup_missing_trace_ref_recent_24h_total, automationBriefLoaded),
|
||||
detail: tDashboard('automationDiagrams.workspace.liveEvidence.signal.metric', {
|
||||
sources: formatAutomationNumber(dossierCoverageSummary?.source_count, dossierCoverageLoaded),
|
||||
refs: formatAutomationNumber(dossierCoverageSummary?.source_ref_total, dossierCoverageLoaded),
|
||||
}),
|
||||
href: `/${locale}/awooop/runs?project_id=awoooi#tg-callback-evidence`,
|
||||
tone: 'live',
|
||||
@@ -1222,18 +1236,18 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
nextAction: tDashboard('automationDiagrams.workspace.inspector.stages.signal.nextAction'),
|
||||
liveEvidence: {
|
||||
metric: tDashboard('automationDiagrams.workspace.liveEvidence.signal.metric', {
|
||||
sources: formatAutomationNumber(dossierCoverageSummary?.source_count, automationBriefLoaded),
|
||||
refs: formatAutomationNumber(dossierCoverageSummary?.source_ref_total, automationBriefLoaded),
|
||||
sources: formatAutomationNumber(dossierCoverageSummary?.source_count, dossierCoverageLoaded),
|
||||
refs: formatAutomationNumber(dossierCoverageSummary?.source_ref_total, dossierCoverageLoaded),
|
||||
}),
|
||||
detail: tDashboard('automationDiagrams.workspace.liveEvidence.signal.detail', {
|
||||
missing: formatAutomationNumber(dossierCoverageSummary?.missing_source_refs_total, automationBriefLoaded),
|
||||
duplicates: formatAutomationNumber(dossierCoverageSummary?.duplicate_total, automationBriefLoaded),
|
||||
alert: formatAutomationNumber(dossierCoverageSummary?.alert_ref_total, automationBriefLoaded),
|
||||
sentry: formatAutomationNumber(dossierCoverageSummary?.sentry_ref_total, automationBriefLoaded),
|
||||
signoz: formatAutomationNumber(dossierCoverageSummary?.signoz_ref_total, automationBriefLoaded),
|
||||
missing: formatAutomationNumber(dossierCoverageSummary?.missing_source_refs_total, dossierCoverageLoaded),
|
||||
duplicates: formatAutomationNumber(dossierCoverageSummary?.duplicate_total, dossierCoverageLoaded),
|
||||
alert: formatAutomationNumber(dossierCoverageSummary?.alert_ref_total, dossierCoverageLoaded),
|
||||
sentry: formatAutomationNumber(dossierCoverageSummary?.sentry_ref_total, dossierCoverageLoaded),
|
||||
signoz: formatAutomationNumber(dossierCoverageSummary?.signoz_ref_total, dossierCoverageLoaded),
|
||||
}),
|
||||
source: tDashboard('automationDiagrams.workspace.liveEvidence.sources.dossierCoverage'),
|
||||
updated: formatLiveEvidenceTime(dossierCoverageSummary?.latest_received_at),
|
||||
updated: formatLiveEvidenceTime(dossierCoverageSummary?.latest_received_at, dossierCoverageLoaded),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1248,17 +1262,17 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
nextAction: tDashboard('automationDiagrams.workspace.inspector.stages.intake.nextAction'),
|
||||
liveEvidence: {
|
||||
metric: tDashboard('automationDiagrams.workspace.liveEvidence.intake.metric', {
|
||||
runs: formatAutomationNumber(automationBrief.runsList?.total, automationBriefLoaded),
|
||||
linked: formatAutomationNumber(recurrenceSummary?.linked_run_total, automationBriefLoaded),
|
||||
runs: formatAutomationNumber(automationBrief.runsList?.total, runsListLoaded),
|
||||
linked: formatAutomationNumber(recurrenceSummary?.linked_run_total, eventRecurrenceLoaded),
|
||||
}),
|
||||
detail: tDashboard('automationDiagrams.workspace.liveEvidence.intake.detail', {
|
||||
stage: latestCicdEvent?.stage ?? (automationBriefLoaded ? unavailableValue : loadingStatus),
|
||||
status: latestCicdEvent?.status ?? (automationBriefLoaded ? unavailableValue : loadingStatus),
|
||||
commit: latestCicdEvent?.commit_sha?.slice(0, 7) ?? (automationBriefLoaded ? unavailableValue : loadingStatus),
|
||||
attention: formatAutomationNumber(cicdNeedsAttention, automationBriefLoaded),
|
||||
stage: latestCicdEvent?.stage ?? (cicdEventsLoaded ? unavailableValue : loadingStatus),
|
||||
status: latestCicdEvent?.status ?? (cicdEventsLoaded ? unavailableValue : loadingStatus),
|
||||
commit: latestCicdEvent?.commit_sha?.slice(0, 7) ?? (cicdEventsLoaded ? unavailableValue : loadingStatus),
|
||||
attention: formatAutomationNumber(cicdNeedsAttention, cicdEventsLoaded),
|
||||
}),
|
||||
source: tDashboard('automationDiagrams.workspace.liveEvidence.sources.runsAndCicd'),
|
||||
updated: formatLiveEvidenceTime(latestCicdEvent?.created_at ?? latestRun?.created_at),
|
||||
updated: formatLiveEvidenceTime(latestCicdEvent?.created_at ?? latestRun?.created_at, cicdEventsLoaded || runsListLoaded),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1280,12 +1294,12 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
provider: aiRouteSelectedProvider,
|
||||
}),
|
||||
detail: tDashboard('automationDiagrams.workspace.liveEvidence.ai.detail', {
|
||||
skipped: formatAutomationNumber(automationBrief.aiRouteStatus?.skipped_lanes?.length, automationBriefLoaded),
|
||||
action: automationBrief.aiRouteStatus?.operator_action?.action ?? (automationBriefLoaded ? unavailableValue : loadingStatus),
|
||||
reason: automationBrief.aiRouteStatus?.operator_action?.reason ?? (automationBriefLoaded ? unavailableValue : loadingStatus),
|
||||
skipped: formatAutomationNumber(automationBrief.aiRouteStatus?.skipped_lanes?.length, aiRouteStatusLoaded),
|
||||
action: automationBrief.aiRouteStatus?.operator_action?.action ?? (aiRouteStatusLoaded ? unavailableValue : loadingStatus),
|
||||
reason: automationBrief.aiRouteStatus?.operator_action?.reason ?? (aiRouteStatusLoaded ? unavailableValue : loadingStatus),
|
||||
}),
|
||||
source: tDashboard('automationDiagrams.workspace.liveEvidence.sources.aiRouteStatus'),
|
||||
updated: automationBriefLoaded ? tDashboard('automationDiagrams.workspace.liveEvidence.realtime') : loadingStatus,
|
||||
updated: aiRouteStatusLoaded ? tDashboard('automationDiagrams.workspace.liveEvidence.realtime') : loadingStatus,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1295,7 +1309,7 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
? tDashboard('automationDelivery.status.live')
|
||||
: tDashboard('automationDelivery.status.watching'),
|
||||
detail: tDashboard('automationDiagrams.workspace.liveEvidence.mcp.metric', {
|
||||
observations: formatAutomationNumber(runEvidenceSummary.mcpTotal, automationBriefLoaded),
|
||||
observations: formatAutomationNumber(runEvidenceSummary.mcpTotal, runsListLoaded),
|
||||
gateway: formatAutomationNumber(statusChainEvidenceSummary.mcpGateway, true),
|
||||
}),
|
||||
href: `/${locale}/awooop/runs?project_id=awoooi`,
|
||||
@@ -1305,17 +1319,17 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
nextAction: tDashboard('automationDiagrams.workspace.inspector.stages.mcp.nextAction'),
|
||||
liveEvidence: {
|
||||
metric: tDashboard('automationDiagrams.workspace.liveEvidence.mcp.metric', {
|
||||
observations: formatAutomationNumber(runEvidenceSummary.mcpTotal, automationBriefLoaded),
|
||||
observations: formatAutomationNumber(runEvidenceSummary.mcpTotal, runsListLoaded),
|
||||
gateway: formatAutomationNumber(statusChainEvidenceSummary.mcpGateway, true),
|
||||
}),
|
||||
detail: tDashboard('automationDiagrams.workspace.liveEvidence.mcp.detail', {
|
||||
success: formatAutomationNumber(runEvidenceSummary.mcpSuccess + statusChainEvidenceSummary.mcpGatewaySuccess, automationBriefLoaded),
|
||||
failed: formatAutomationNumber(runEvidenceSummary.mcpFailed + statusChainEvidenceSummary.mcpGatewayFailed, automationBriefLoaded),
|
||||
server: runEvidenceSummary.latestMcpServer ?? (automationBriefLoaded ? unavailableValue : loadingStatus),
|
||||
route: runEvidenceSummary.latestRoute ?? (automationBriefLoaded ? unavailableValue : loadingStatus),
|
||||
success: formatAutomationNumber(runEvidenceSummary.mcpSuccess + statusChainEvidenceSummary.mcpGatewaySuccess, runsListLoaded),
|
||||
failed: formatAutomationNumber(runEvidenceSummary.mcpFailed + statusChainEvidenceSummary.mcpGatewayFailed, runsListLoaded),
|
||||
server: runEvidenceSummary.latestMcpServer ?? (runsListLoaded ? unavailableValue : loadingStatus),
|
||||
route: runEvidenceSummary.latestRoute ?? (runsListLoaded ? unavailableValue : loadingStatus),
|
||||
}),
|
||||
source: tDashboard('automationDiagrams.workspace.liveEvidence.sources.runsAndStatusChain'),
|
||||
updated: formatLiveEvidenceTime(latestRun?.created_at),
|
||||
updated: formatLiveEvidenceTime(latestRun?.created_at, runsListLoaded),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -1406,8 +1420,8 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
title: tDashboard('automationDiagrams.workspace.flow.stages.verify'),
|
||||
status: typeof kmStaleTotal === 'number' && kmStaleTotal > 0 ? tDashboard('automationDelivery.status.progress') : tDashboard('automationDelivery.status.live'),
|
||||
detail: tDashboard('automationDiagrams.workspace.values.km', {
|
||||
stale: formatAutomationNumber(kmStaleTotal, automationBriefLoaded),
|
||||
days: formatAutomationNumber(kmStaleThreshold, automationBriefLoaded),
|
||||
stale: formatAutomationNumber(kmStaleTotal, kmStaleCandidatesLoaded),
|
||||
days: formatAutomationNumber(kmStaleThreshold, kmStaleCandidatesLoaded),
|
||||
}),
|
||||
href: `/${locale}/knowledge-base`,
|
||||
tone: typeof kmStaleTotal === 'number' && kmStaleTotal > 0 ? 'progress' : hasKmStaleCandidates ? 'live' : 'watching',
|
||||
@@ -1416,16 +1430,16 @@ export default function Home({ params }: { params: { locale: string } }) {
|
||||
nextAction: tDashboard('automationDiagrams.workspace.inspector.stages.verify.nextAction'),
|
||||
liveEvidence: {
|
||||
metric: tDashboard('automationDiagrams.workspace.liveEvidence.verify.metric', {
|
||||
stale: formatAutomationNumber(kmBurndownSnapshot?.stale_count ?? kmStaleTotal, automationBriefLoaded),
|
||||
stale: formatAutomationNumber(kmBurndownSnapshot?.stale_count ?? kmStaleTotal, kmOwnerReviewBurndownLoaded || kmStaleCandidatesLoaded),
|
||||
ratio: staleRatioLabel,
|
||||
}),
|
||||
detail: tDashboard('automationDiagrams.workspace.liveEvidence.verify.detail', {
|
||||
pending: formatAutomationNumber(kmOwnerReviewBurndown?.pending_owner_reviews, automationBriefLoaded),
|
||||
completed: formatAutomationNumber(kmOwnerReviewBurndown?.completed_owner_reviews, automationBriefLoaded),
|
||||
remaining: formatAutomationNumber(kmOwnerReviewBurndown?.entries_to_threshold, automationBriefLoaded),
|
||||
pending: formatAutomationNumber(kmOwnerReviewBurndown?.pending_owner_reviews, kmOwnerReviewBurndownLoaded),
|
||||
completed: formatAutomationNumber(kmOwnerReviewBurndown?.completed_owner_reviews, kmOwnerReviewBurndownLoaded),
|
||||
remaining: formatAutomationNumber(kmOwnerReviewBurndown?.entries_to_threshold, kmOwnerReviewBurndownLoaded),
|
||||
}),
|
||||
source: tDashboard('automationDiagrams.workspace.liveEvidence.sources.kmBurndown'),
|
||||
updated: formatLiveEvidenceTime(kmOwnerReviewBurndown?.generated_at),
|
||||
updated: formatLiveEvidenceTime(kmOwnerReviewBurndown?.generated_at, kmOwnerReviewBurndownLoaded),
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@@ -8,6 +8,7 @@ import { API_V1_URL } from '@/lib/config'
|
||||
interface UseIncidentStatusChainsOptions {
|
||||
incidentIds: string[]
|
||||
limit?: number
|
||||
concurrency?: number
|
||||
projectId?: string
|
||||
refreshKey?: string | number | Date | null
|
||||
timeoutMs?: number
|
||||
@@ -22,6 +23,7 @@ interface UseIncidentStatusChainsResult {
|
||||
export function useIncidentStatusChains({
|
||||
incidentIds,
|
||||
limit = 25,
|
||||
concurrency = 4,
|
||||
projectId = 'awoooi',
|
||||
refreshKey = null,
|
||||
timeoutMs = 12000,
|
||||
@@ -44,40 +46,63 @@ export function useIncidentStatusChains({
|
||||
let active = true
|
||||
const controller = new AbortController()
|
||||
const timeout = window.setTimeout(() => controller.abort(), timeoutMs)
|
||||
const requestConcurrency = Math.max(1, Math.min(concurrency, requestedIncidentIds.length))
|
||||
let nextIndex = 0
|
||||
let activeRequests = 0
|
||||
let completedRequests = 0
|
||||
setIsLoading(true)
|
||||
setStatusChains(Object.fromEntries(requestedIncidentIds.map(incidentId => [incidentId, null])))
|
||||
|
||||
Promise.all(
|
||||
requestedIncidentIds.map(async (incidentId): Promise<[string, AwoooPStatusChain | null]> => {
|
||||
const params = new URLSearchParams({ project_id: projectId, incident_id: incidentId })
|
||||
try {
|
||||
const response = await fetch(`${API_V1_URL}/platform/status-chain?${params.toString()}`, {
|
||||
cache: 'no-store',
|
||||
signal: controller.signal,
|
||||
})
|
||||
if (!response.ok) return [incidentId, null]
|
||||
return [incidentId, await response.json() as AwoooPStatusChain]
|
||||
} catch {
|
||||
return [incidentId, null]
|
||||
}
|
||||
})
|
||||
)
|
||||
.then(entries => {
|
||||
if (active) setStatusChains(Object.fromEntries(entries))
|
||||
})
|
||||
.catch(() => {
|
||||
if (active) setStatusChains({})
|
||||
})
|
||||
.finally(() => {
|
||||
const fetchStatusChain = async (incidentId: string): Promise<AwoooPStatusChain | null> => {
|
||||
const params = new URLSearchParams({ project_id: projectId, incident_id: incidentId })
|
||||
try {
|
||||
const response = await fetch(`${API_V1_URL}/platform/status-chain?${params.toString()}`, {
|
||||
cache: 'no-store',
|
||||
signal: controller.signal,
|
||||
})
|
||||
if (!response.ok) return null
|
||||
return await response.json() as AwoooPStatusChain
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const finish = () => {
|
||||
completedRequests += 1
|
||||
if (completedRequests >= requestedIncidentIds.length) {
|
||||
window.clearTimeout(timeout)
|
||||
if (active) setIsLoading(false)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const pump = () => {
|
||||
if (!active) return
|
||||
while (activeRequests < requestConcurrency && nextIndex < requestedIncidentIds.length) {
|
||||
const incidentId = requestedIncidentIds[nextIndex]
|
||||
nextIndex += 1
|
||||
activeRequests += 1
|
||||
fetchStatusChain(incidentId)
|
||||
.then(statusChain => {
|
||||
if (active) {
|
||||
setStatusChains(previous => ({ ...previous, [incidentId]: statusChain }))
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
activeRequests -= 1
|
||||
finish()
|
||||
pump()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pump()
|
||||
|
||||
return () => {
|
||||
active = false
|
||||
window.clearTimeout(timeout)
|
||||
controller.abort()
|
||||
}
|
||||
}, [incidentKey, projectId, requestedIncidentIds, refreshKey, timeoutMs])
|
||||
}, [concurrency, incidentKey, projectId, requestedIncidentIds, refreshKey, timeoutMs])
|
||||
|
||||
return { statusChains, requestedIncidentIds, isLoading }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user