fix(glossary-list): show real embedding progress in glossary list view

- Add current_version_embedding_status/embedded_count/term_count to GlossaryResponse
- Batch-fetch current versions in list endpoint (single extra query, not N queries)
- Add get_versions_by_ids() helper to glossary_service
- Fix GlossaryList.tsx: embeddingBadge('') → embeddingBadge(g) with real status + pct

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-05-13 19:00:56 +01:00
parent e70a67718e
commit 4645e67611
5 changed files with 33 additions and 7 deletions

View file

@ -47,7 +47,8 @@ async def list_glossaries(
"""List all active glossaries for a client."""
assert_user_in_org(ctx, client_id, OrgRole.VIEWER)
glossaries = await svc.get_glossaries_for_client(client_id)
return [_to_response(g) for g in glossaries]
version_map = await svc.get_versions_by_ids([g.current_version_id for g in glossaries if g.current_version_id])
return [_to_response(g, version_map.get(g.current_version_id)) for g in glossaries]
# ── Upload new glossary ───────────────────────────────────────────────────────
@ -286,7 +287,7 @@ def _validate_xlsx(file: UploadFile) -> None:
)
def _to_response(g) -> GlossaryResponse:
def _to_response(g, current_version=None) -> GlossaryResponse:
return GlossaryResponse(
id=str(g.id),
client_id=g.client_id,
@ -296,6 +297,9 @@ def _to_response(g) -> GlossaryResponse:
source=g.source,
status=g.status,
current_version_id=g.current_version_id,
current_version_embedding_status=current_version.embedding_status if current_version else None,
current_version_embedded_count=current_version.embedded_count if current_version else None,
current_version_term_count=current_version.term_count if current_version else None,
created_at=g.created_at,
created_by=g.created_by,
)

View file

@ -91,6 +91,9 @@ class GlossaryResponse(BaseModel):
source: GlossarySource
status: GlossaryStatus
current_version_id: str | None = None
current_version_embedding_status: EmbeddingStatus | None = None
current_version_embedded_count: int | None = None
current_version_term_count: int | None = None
created_at: datetime
created_by: str

View file

@ -728,6 +728,17 @@ async def get_glossary(glossary_id: str) -> Glossary | None:
return glossary_from_doc(doc) if doc else None
async def get_versions_by_ids(version_ids: list[str]) -> dict[str, GlossaryVersion]:
"""Batch-fetch versions by ID, returns {version_id: GlossaryVersion}."""
if not version_ids:
return {}
db = await get_database()
docs = await db[_COLL_VERSIONS].find(
{"_id": {"$in": [ObjectId(vid) for vid in version_ids]}}
).to_list(length=len(version_ids))
return {str(d["_id"]): glossary_version_from_doc(d) for d in docs}
async def get_versions(glossary_id: str) -> list[GlossaryVersion]:
db = await get_database()
cursor = db[_COLL_VERSIONS].find(

View file

@ -11,12 +11,17 @@ function statusBadge(status: string) {
: 'bg-gray-100 text-gray-500';
}
function embeddingBadge(status: string) {
function embeddingBadge(g: import('../../../types/api').Glossary) {
const status = g.current_version_embedding_status;
const pct = g.current_version_term_count && g.current_version_term_count > 0
? Math.round(((g.current_version_embedded_count ?? 0) / g.current_version_term_count) * 100)
: 0;
switch (status) {
case 'done': return <span className="text-xs text-green-600">Embedded </span>;
case 'in_progress': return <span className="text-xs text-blue-600 animate-pulse">Embedding</span>;
case 'done': return <span className="text-xs text-green-600">Embedded ({g.current_version_term_count?.toLocaleString()})</span>;
case 'in_progress': return <span className="text-xs text-blue-600 animate-pulse">Embedding {pct}%</span>;
case 'failed': return <span className="text-xs text-red-500">Embed failed</span>;
default: return <span className="text-xs text-gray-400">Pending embed</span>;
case 'pending': return <span className="text-xs text-gray-400">Pending embed</span>;
default: return null;
}
}
@ -107,7 +112,7 @@ export function GlossaryList() {
</div>
<div className="flex items-center gap-4 shrink-0">
<div className="text-right text-xs text-gray-400">
{g.current_version_id ? embeddingBadge('') : null}
{g.current_version_id ? embeddingBadge(g) : null}
</div>
{isAdmin && g.status === 'active' && (
<button

View file

@ -822,6 +822,9 @@ export interface Glossary {
source: string;
status: GlossaryStatus;
current_version_id?: string;
current_version_embedding_status?: EmbeddingStatus;
current_version_embedded_count?: number;
current_version_term_count?: number;
created_at: string;
created_by: string;
}