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)
}
}