Show synced to qdrant status on Browse Jobs

This commit is contained in:
Aaron Roberts
2026-07-03 10:14:35 +01:00
parent 02185bef46
commit bcd8960ea9
2 changed files with 38 additions and 3 deletions

View File

@@ -4,7 +4,7 @@ import { useModels } from '../hooks/useModels'
import { motion, AnimatePresence } from 'framer-motion'
import {
Search, ChevronLeft, ChevronRight, CheckCircle2, Clock,
FileText, Loader2, Save, RefreshCw, Trash2, Sparkles,
FileText, Loader2, Save, RefreshCw, Trash2, Sparkles, Cloud, CloudOff,
} from 'lucide-react'
import axios from 'axios'
@@ -29,6 +29,38 @@ function StatusBadge({ status }) {
)
}
// A job is synced "in its most recent state" when it was pushed to Qdrant at or
// after its last update. `updated_at` is bumped by a DB trigger on every change,
// so any edit after a sync makes the job stale until it's re-synced.
function isSynced(job) {
if (!job.qdrant_synced_at) return false
if (!job.updated_at) return true
return new Date(job.qdrant_synced_at).getTime() >= new Date(job.updated_at).getTime()
}
function SyncBadge({ job }) {
const synced = isSynced(job)
const Icon = synced ? Cloud : CloudOff
const title = synced
? 'Synced with Qdrant'
: job.qdrant_synced_at
? 'Changed since last Qdrant sync — needs re-sync'
: 'Never synced with Qdrant'
return (
<span
title={title}
className={`inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs border ${
synced
? 'text-green-400 bg-green-400/10 border-green-400/30'
: 'text-orange-400 bg-orange-400/10 border-orange-400/30'
}`}
>
<Icon className="w-3 h-3" />
{synced ? 'Synced' : 'Not synced'}
</span>
)
}
// ─────────────────────────────────────────────────────────────
// Full-screen Job Detail
// ─────────────────────────────────────────────────────────────
@@ -210,6 +242,7 @@ function JobDetail({ jobId, onClose, onReviewed, onDeleted, suggestions = {} })
{job && (
<>
<StatusBadge status={job.status} />
<SyncBadge job={job} />
<motion.button
onClick={handleToggleStatus}
disabled={togglingStatus}
@@ -631,8 +664,9 @@ export default function JobsPanel() {
whileTap={{ scale: 0.98 }}
layout
>
<div className="flex items-start justify-between gap-2 mb-2">
<div className="flex items-center flex-wrap gap-2 mb-2">
<StatusBadge status={job.status} />
<SyncBadge job={job} />
</div>
{job.book && <p className="text-sm font-medium text-gray-200 truncate">{job.book}</p>}
<div className="flex items-center gap-2 mt-0.5">