feat(mobile-fixes-phase4): Complete US-405 - Fix batchProgressStore - Restaurare Joburi Failed

Implemented by Ralph autonomous loop.
Iteration: 4

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 19:31:08 +00:00
parent c3c129a04c
commit 043797edaa
3 changed files with 82 additions and 19 deletions

View File

@@ -104,8 +104,8 @@
"unifiedItems computed include joburile failed pentru afișare",
"npm run build passes"
],
"passes": false,
"notes": ""
"passes": true,
"notes": "Completed in iteration 4"
},
{
"id": "US-406",

View File

@@ -21,3 +21,9 @@ Stories: 8 (US-401 to US-408)
[2026-01-12 19:23:21] Working on story: US-403
[2026-01-12 19:23:21] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-403.log)
[2026-01-12 19:27:05] SUCCESS: Story US-403 passed!
[2026-01-12 19:27:05] Changes committed
[2026-01-12 19:27:05] Progress: 4/8 stories completed
[2026-01-12 19:27:07] === Iteration 4/50 ===
[2026-01-12 19:27:07] Working on story: US-405
[2026-01-12 19:27:07] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_4_US-405.log)
[2026-01-12 19:31:08] SUCCESS: Story US-405 passed!

View File

@@ -287,9 +287,13 @@ export const useBatchProgressStore = defineStore('batchProgress', () => {
console.log('[BatchProgress] All jobs finished, stopping polling')
isPolling.value = false
// US-009: Remove completed batch from localStorage
if (batchId.value) {
// US-405: Only remove batch from localStorage if there are NO failed jobs
// Failed jobs need to persist so they're visible after refresh
if (batchId.value && data.failed_count === 0) {
console.log('[BatchProgress] No failed jobs, removing batch from storage')
removeActiveBatch(batchId.value)
} else if (data.failed_count > 0) {
console.log(`[BatchProgress] Batch has ${data.failed_count} failed jobs, keeping in storage for retry`)
}
break
@@ -339,12 +343,46 @@ export const useBatchProgressStore = defineStore('batchProgress', () => {
* Clear a specific batch ID from localStorage.
* Called when a batch is determined to be complete (all items processed).
*
* US-405: Can also be called when all failed jobs have been resolved
* (e.g., after successful retry or deletion of failed receipts).
*
* @param {string} batchIdToRemove - Batch ID to remove from storage
*/
function clearStoredBatch(batchIdToRemove) {
removeActiveBatch(batchIdToRemove)
}
/**
* US-405: Check if a batch should be cleared from storage.
* Returns true if the batch has no remaining failed jobs that need attention.
*
* Call this after retry or delete operations to clean up batches
* that no longer have failed jobs.
*/
function shouldClearBatch() {
// If no jobs remain, batch can be cleared
if (jobs.value.size === 0) return true
// If any jobs are still failed, keep the batch
for (const job of jobs.value.values()) {
if (job.status === 'failed') return false
}
// No failed jobs remain
return true
}
/**
* US-405: Clear batch from storage if no failed jobs remain.
* Call this after retry/delete operations to clean up localStorage.
*/
function clearBatchIfNoFailedJobs() {
if (shouldClearBatch() && batchId.value) {
console.log('[BatchProgress] No failed jobs remain, clearing batch from storage')
removeActiveBatch(batchId.value)
}
}
/**
* Clear all stored batch IDs from localStorage.
* Used during cleanup or when all batches are confirmed complete.
@@ -365,12 +403,13 @@ export const useBatchProgressStore = defineStore('batchProgress', () => {
/**
* Restore jobs from a stored batch by fetching current status from API.
* Only pending/processing jobs are added to the store (completed/failed are already receipts).
* Restores pending, processing, AND failed jobs to the store.
*
* US-023: Called on page refresh/return to restore visibility of active jobs.
* US-405: Now includes failed jobs so users can see OCR errors after refresh.
*
* @param {string} storedBatchId - The batch ID to restore from
* @returns {Promise<{hasActiveJobs: boolean, jobCount: number}>} Result of restoration
* @returns {Promise<{hasActiveJobs: boolean, jobCount: number, hasFailedJobs: boolean}>} Result of restoration
*/
async function restoreJobsFromBatch(storedBatchId) {
try {
@@ -387,26 +426,35 @@ export const useBatchProgressStore = defineStore('batchProgress', () => {
if (!data.jobs || data.jobs.length === 0) {
console.log(`[BatchProgress] Batch ${storedBatchId} has no jobs, removing from storage`)
removeActiveBatch(storedBatchId)
return { hasActiveJobs: false, jobCount: 0 }
return { hasActiveJobs: false, jobCount: 0, hasFailedJobs: false }
}
// Count and filter active jobs (pending/processing only)
// US-405: Include failed jobs in restoration (not just pending/processing)
// Failed jobs need to remain visible so users can see and resolve errors
const jobsToRestore = data.jobs.filter(
job => job.status === 'pending' || job.status === 'processing' || job.status === 'failed'
)
// Count active jobs (pending/processing) for polling decision
const activeJobs = data.jobs.filter(
job => job.status === 'pending' || job.status === 'processing'
)
if (activeJobs.length === 0) {
// All jobs are completed or failed - no need to restore to UI
console.log(`[BatchProgress] Batch ${storedBatchId} has no active jobs (all completed/failed), removing from storage`)
// Count failed jobs for return value
const failedJobs = data.jobs.filter(job => job.status === 'failed')
if (jobsToRestore.length === 0) {
// All jobs are completed - safe to remove batch from storage
console.log(`[BatchProgress] Batch ${storedBatchId} has no jobs to restore (all completed), removing from storage`)
removeActiveBatch(storedBatchId)
return { hasActiveJobs: false, jobCount: 0 }
return { hasActiveJobs: false, jobCount: 0, hasFailedJobs: false }
}
// Set batch ID and add active jobs to store
// Set batch ID and add jobs to store
batchId.value = storedBatchId
// Add jobs to the Map (merge with existing if any)
for (const job of activeJobs) {
for (const job of jobsToRestore) {
jobs.value.set(job.job_id, {
job_id: job.job_id,
filename: job.filename,
@@ -417,17 +465,22 @@ export const useBatchProgressStore = defineStore('batchProgress', () => {
})
}
console.log(`[BatchProgress] Restored ${activeJobs.length} active jobs from batch ${storedBatchId}`)
console.log(`[BatchProgress] Restored ${jobsToRestore.length} jobs from batch ${storedBatchId} (${activeJobs.length} active, ${failedJobs.length} failed)`)
// Start polling for updates
if (!isPolling.value) {
// Only start polling if there are active jobs (pending/processing)
// Failed-only batches don't need polling - they're waiting for user action
if (activeJobs.length > 0 && !isPolling.value) {
isPolling.value = true
abortController = new AbortController()
// Start polling loop in background
pollLoop()
}
return { hasActiveJobs: true, jobCount: activeJobs.length }
return {
hasActiveJobs: activeJobs.length > 0,
jobCount: jobsToRestore.length,
hasFailedJobs: failedJobs.length > 0
}
} catch (err) {
console.error(`[BatchProgress] Error restoring batch ${storedBatchId}:`, err)
@@ -437,7 +490,7 @@ export const useBatchProgressStore = defineStore('batchProgress', () => {
removeActiveBatch(storedBatchId)
}
return { hasActiveJobs: false, jobCount: 0 }
return { hasActiveJobs: false, jobCount: 0, hasFailedJobs: false }
}
}
@@ -578,6 +631,10 @@ export const useBatchProgressStore = defineStore('batchProgress', () => {
clearStoredBatch,
clearAllStoredBatches,
// US-405: Failed Jobs Cleanup
shouldClearBatch,
clearBatchIfNoFailedJobs,
// US-023: Restore Jobs from Batch
restoreJobsFromBatch,