fix(web): error handling in LibraryView, taskId watch in IngestProgress, type fixes
This commit is contained in:
parent
b4837163d5
commit
e401cb5f48
4 changed files with 50 additions and 12 deletions
|
|
@ -36,6 +36,11 @@ export interface TaskStatus {
|
|||
error?: string
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
role: string
|
||||
content: string
|
||||
}
|
||||
|
||||
export const api = {
|
||||
async getLibrary(): Promise<Document[]> {
|
||||
const r = await fetch(`${BASE}/api/library`)
|
||||
|
|
@ -72,7 +77,7 @@ export const api = {
|
|||
},
|
||||
async chat(
|
||||
message: string,
|
||||
history: { role: string; content: string }[],
|
||||
history: ChatMessage[],
|
||||
docIds?: string[],
|
||||
topK = 5,
|
||||
): Promise<ChatResponse> {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div class="doc-card" :class="`status-${doc.status}`">
|
||||
<div class="doc-status-badge">{{ doc.status }}</div>
|
||||
<div class="doc-title">{{ doc.title }}</div>
|
||||
<div class="doc-meta" v-if="doc.page_count">{{ doc.page_count }} pages</div>
|
||||
<div class="doc-meta" v-if="doc.page_count != null">{{ doc.page_count }} pages</div>
|
||||
<div class="doc-meta path">{{ shortPath }}</div>
|
||||
|
||||
<IngestProgress
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue"
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from "vue"
|
||||
import { api, type TaskStatus } from "@/api"
|
||||
|
||||
const props = defineProps<{ taskId: string | null }>()
|
||||
|
|
@ -24,13 +24,13 @@ let timer: ReturnType<typeof setInterval> | null = null
|
|||
const visible = computed(() => props.taskId !== null && status.value?.status !== "complete")
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
if (!status.value) return "Queued..."
|
||||
if (!status.value) return "Queued…"
|
||||
const map: Record<string, string> = {
|
||||
running: "Indexing...",
|
||||
running: "Indexing…",
|
||||
complete: "Done",
|
||||
error: "Error",
|
||||
}
|
||||
return map[status.value.status] ?? "Processing..."
|
||||
return map[status.value.status] ?? "Processing…"
|
||||
})
|
||||
|
||||
const barWidth = computed(() => {
|
||||
|
|
@ -55,12 +55,21 @@ function stopPoll() {
|
|||
if (timer) { clearInterval(timer); timer = null }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
function startPoll() {
|
||||
stopPoll()
|
||||
status.value = null
|
||||
if (props.taskId) {
|
||||
poll()
|
||||
timer = setInterval(poll, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.taskId, (newId) => {
|
||||
if (newId) startPoll()
|
||||
else stopPoll()
|
||||
})
|
||||
|
||||
onMounted(startPoll)
|
||||
onUnmounted(stopPoll)
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
</button>
|
||||
</header>
|
||||
|
||||
<p class="error-msg" v-if="error">{{ error }}</p>
|
||||
|
||||
<p class="empty-state" v-if="!loading && docs.length === 0">
|
||||
No books indexed yet. Click "Scan for PDFs" to discover PDFs in your books directory.<br>
|
||||
Make sure your PDF directory is mounted at <code>/books</code> inside the container.
|
||||
|
|
@ -37,32 +39,53 @@ import DocumentCard from "@/components/DocumentCard.vue"
|
|||
const docs = ref<Document[]>([])
|
||||
const loading = ref(true)
|
||||
const scanning = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
const scanResult = ref<{ discovered: number; queued: number } | null>(null)
|
||||
|
||||
async function load() {
|
||||
loading.value = true
|
||||
docs.value = await api.getLibrary().finally(() => (loading.value = false))
|
||||
error.value = null
|
||||
try {
|
||||
docs.value = await api.getLibrary()
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : "Failed to load library"
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function scan() {
|
||||
scanning.value = true
|
||||
error.value = null
|
||||
try {
|
||||
scanResult.value = await api.scanLibrary()
|
||||
await load()
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : "Scan failed"
|
||||
} finally {
|
||||
scanning.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function reingest(id: string) {
|
||||
error.value = null
|
||||
try {
|
||||
await api.reingestDocument(id)
|
||||
await load()
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : "Re-index failed"
|
||||
}
|
||||
}
|
||||
|
||||
async function remove(id: string) {
|
||||
if (!confirm("Remove this book from the library? The PDF file is not deleted.")) return
|
||||
error.value = null
|
||||
try {
|
||||
await api.deleteDocument(id)
|
||||
await load()
|
||||
} catch (e) {
|
||||
error.value = e instanceof Error ? e.message : "Remove failed"
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(load)
|
||||
|
|
@ -81,4 +104,5 @@ h1 { font-size: 1.5rem; }
|
|||
.empty-state { color: var(--color-text-muted); line-height: 1.8; }
|
||||
.empty-state code { font-family: var(--font-mono); background: var(--color-surface-alt); padding: 2px 6px; border-radius: 3px; }
|
||||
.scan-result { margin-top: 1rem; color: var(--color-text-muted); font-size: 0.9rem; }
|
||||
.error-msg { color: var(--color-error); margin-bottom: 1rem; font-size: 0.9rem; }
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in a new issue