diff --git a/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue b/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue index e1f3fd9e..be976b7e 100644 --- a/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue +++ b/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue @@ -15,7 +15,7 @@
- + {{ t(`wiki.jobStage.${stage.key}`) }}
@@ -96,7 +96,12 @@ const currentStage = computed(() => { return stageMapping[raw] ?? raw }) -// True when backend status indicates a terminal failure (dots should not pulse) +// True when the job has reached any terminal state (dots should not pulse) +const isTerminal = computed(() => + props.status === 'completed' || props.status === 'failed' || props.status === 'partial' || props.status === 'cancelled' +) + +// True specifically for failure terminals (dots show red instead of done) const isTerminalFailure = computed(() => props.status === 'failed' || props.status === 'partial' || props.status === 'cancelled' ) @@ -117,11 +122,16 @@ function dotClass(key: string) { const target = stageIndex(key) if (cur < 0) return 'pending' // unknown stage → all pending if (isTerminalFailure.value) { - // Terminal failure: all dots before the failure point are done, the failure point is failed + // Terminal failure: dots before failure point are done, failure point is red if (target < cur) return 'done' if (target === cur) return 'failed' return 'pending' } + if (isTerminal.value) { + // Successful terminal (completed): all dots up to and including current are done (no pulse) + if (target <= cur) return 'done' + return 'pending' + } if (target < cur) return 'done' if (target === cur) return 'active' return 'pending' @@ -202,6 +212,7 @@ const elapsed = computed(() => { flex: 1; } .stage-label.active { color: var(--mc-primary); font-weight: 600; } +.stage-label.done { color: var(--mc-success, #5a8a5a); font-weight: 600; } .stage-info { display: flex; diff --git a/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue b/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue index c89fd288..e7ed2d1c 100644 --- a/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue +++ b/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue @@ -91,9 +91,9 @@ - + { const rawJobs = reactive>({}) let jobPoller: ReturnType | null = null +const TERMINAL_STATUSES = new Set(['completed', 'failed', 'partial', 'cancelled']) + async function pollJobs() { if (!store.currentKB) return + const kbId = store.currentKB.id const processingRaws = store.rawMaterials.filter( r => r.processingStatus === 'processing' || r.processingStatus === 'pending' ) + let anyTerminal = false for (const raw of processingRaws) { try { - const res: any = await wikiApi.getWikiJobs(store.currentKB.id, raw.id) + const res: any = await wikiApi.getWikiJobs(kbId, raw.id) const list = res.data || res || [] - if (list.length > 0) rawJobs[raw.id] = list[0] + if (list.length > 0) { + const job = list[0] + rawJobs[raw.id] = job + if (TERMINAL_STATUSES.has(job.status)) { + anyTerminal = true + } + } } catch { /* ignore */ } } - if (processingRaws.length > 0) { + // When any job reaches terminal, refresh raw materials to sync status badges + if (anyTerminal) { + await store.fetchRawMaterials(kbId) + } + // Continue polling while there are still processing/pending raws + const stillActive = store.rawMaterials.some( + r => r.processingStatus === 'processing' || r.processingStatus === 'pending' + ) + if (stillActive) { jobPoller = setTimeout(pollJobs, 3000) } }