refactor: move ocr domain components to lib/ocr/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
93
frontend/src/lib/ocr/OcrProgress.svelte
Normal file
93
frontend/src/lib/ocr/OcrProgress.svelte
Normal file
@@ -0,0 +1,93 @@
|
||||
<script lang="ts">
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
|
||||
interface Props {
|
||||
jobId: string;
|
||||
onDone: () => void;
|
||||
}
|
||||
|
||||
let { jobId, onDone }: Props = $props();
|
||||
|
||||
let status: 'running' | 'done' | 'error' = $state('running');
|
||||
let processed: number = $state(0);
|
||||
let total: number = $state(0);
|
||||
let currentPage: number = $state(0);
|
||||
let totalPages: number = $state(0);
|
||||
let retryCount: number = $state(0);
|
||||
|
||||
let progressPercent = $derived(total > 0 ? Math.round((processed / total) * 100) : 0);
|
||||
|
||||
$effect(() => {
|
||||
void retryCount; // track dependency to re-create EventSource on retry
|
||||
const source = new EventSource(`/api/ocr/jobs/${jobId}/progress`);
|
||||
|
||||
source.addEventListener('document', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
processed = data.processed;
|
||||
total = data.total;
|
||||
});
|
||||
|
||||
source.addEventListener('page', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
currentPage = data.page;
|
||||
totalPages = data.totalPages;
|
||||
});
|
||||
|
||||
source.addEventListener('done', () => {
|
||||
status = 'done';
|
||||
source.close();
|
||||
onDone();
|
||||
});
|
||||
|
||||
source.addEventListener('error', () => {
|
||||
status = 'error';
|
||||
source.close();
|
||||
});
|
||||
|
||||
source.onerror = () => {
|
||||
status = 'error';
|
||||
source.close();
|
||||
};
|
||||
|
||||
return () => {
|
||||
source.close();
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if status === 'running'}
|
||||
<div class="border-brand-sand rounded-sm border bg-white p-4">
|
||||
<h3 class="mb-3 text-xs font-bold tracking-widest text-gray-400 uppercase">
|
||||
{m.ocr_progress_heading()}
|
||||
</h3>
|
||||
<div class="bg-brand-sand h-2 w-full overflow-hidden rounded-full">
|
||||
<div
|
||||
class="h-full bg-brand-mint transition-all duration-300"
|
||||
style="width: {progressPercent}%"
|
||||
role="progressbar"
|
||||
aria-label={m.ocr_progress_heading()}
|
||||
aria-valuenow={progressPercent}
|
||||
aria-valuemin={0}
|
||||
aria-valuemax={100}
|
||||
></div>
|
||||
</div>
|
||||
<p class="mt-2 text-right text-sm text-gray-500">
|
||||
<span aria-live="polite" aria-atomic="true">
|
||||
{m.ocr_progress_page({ current: String(currentPage), total: String(totalPages) })}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
{:else if status === 'error'}
|
||||
<div class="border-brand-sand rounded-sm border border-l-4 border-l-red-500 bg-white p-4">
|
||||
<h3 class="mb-2 text-sm font-semibold text-red-700">
|
||||
{m.ocr_error_heading()}
|
||||
</h3>
|
||||
<button
|
||||
type="button"
|
||||
onclick={() => { retryCount++; status = 'running'; }}
|
||||
class="text-sm font-medium text-brand-navy transition-colors hover:text-brand-navy/80"
|
||||
>
|
||||
{m.ocr_error_retry()}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user