From e40b5ba0a5b1500e928340a019d1b6c73a79e2a1 Mon Sep 17 00:00:00 2001 From: Pier-Jean Malandrino Date: Thu, 30 Apr 2026 10:04:01 +0200 Subject: [PATCH] =?UTF-8?q?feat(#225,#224):=20vocabulary=20rename=20index?= =?UTF-8?q?=E2=86=92ingest=20+=20stale=20stores=20strip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #225: rename all push/index/pousser/indexé labels to ingest/ingérer/ingéré across status tooltips, docs/chunks push modals, and legacy ingestion panel - #224: add DocStoreLink type + storeLinks on Document; StaleStoresStrip component shows per-store state badges with one-click re-ingest buttons --- .../features/chunks/ui/StaleStoresStrip.vue | 143 ++++++++++++++++++ frontend/src/pages/DocChunksTab.vue | 9 +- frontend/src/pages/DocWorkspacePage.vue | 1 + frontend/src/shared/i18n.ts | 84 +++++----- frontend/src/shared/types.ts | 9 ++ 5 files changed, 209 insertions(+), 37 deletions(-) create mode 100644 frontend/src/features/chunks/ui/StaleStoresStrip.vue diff --git a/frontend/src/features/chunks/ui/StaleStoresStrip.vue b/frontend/src/features/chunks/ui/StaleStoresStrip.vue new file mode 100644 index 0000000..34bc886 --- /dev/null +++ b/frontend/src/features/chunks/ui/StaleStoresStrip.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/frontend/src/pages/DocChunksTab.vue b/frontend/src/pages/DocChunksTab.vue index ba44fa5..443ca06 100644 --- a/frontend/src/pages/DocChunksTab.vue +++ b/frontend/src/pages/DocChunksTab.vue @@ -25,6 +25,11 @@
+ import { ref, onMounted } from 'vue' -import type { DocTreeNode } from '../shared/types' +import type { DocTreeNode, DocStoreLink } from '../shared/types' import { fetchDocumentTree } from '../features/document/api' import DocTreeRail from '../features/document/ui/DocTreeRail.vue' import ChunksEditor from '../features/chunks/ui/ChunksEditor.vue' +import StaleStoresStrip from '../features/chunks/ui/StaleStoresStrip.vue' import { useI18n } from '../shared/i18n' const props = defineProps<{ docId: string availableStores: string[] + storeLinks?: DocStoreLink[] }>() const { t } = useI18n() diff --git a/frontend/src/pages/DocWorkspacePage.vue b/frontend/src/pages/DocWorkspacePage.vue index 9951a59..85b67a8 100644 --- a/frontend/src/pages/DocWorkspacePage.vue +++ b/frontend/src/pages/DocWorkspacePage.vue @@ -41,6 +41,7 @@ v-if="activeMode === 'chunks'" :doc-id="id" :available-stores="doc.stores ?? []" + :store-links="doc.storeLinks" /> diff --git a/frontend/src/shared/i18n.ts b/frontend/src/shared/i18n.ts index 52aa0a1..103e777 100644 --- a/frontend/src/shared/i18n.ts +++ b/frontend/src/shared/i18n.ts @@ -46,9 +46,9 @@ const messages: Messages = { 'status.Failed': 'Échoué', 'status.tooltip.Uploaded': 'Fichier reçu, en attente de parsing.', 'status.tooltip.Parsed': 'Document parsé avec succès.', - 'status.tooltip.Chunked': 'Chunks générés, prêts à être poussés vers un store.', - 'status.tooltip.Ingested': 'Indexé dans au moins un store.', - 'status.tooltip.Stale': 'Chunks modifiés depuis le dernier push — re-ingestion requise.', + 'status.tooltip.Chunked': 'Chunks générés, prêts à être ingérés dans un store.', + 'status.tooltip.Ingested': 'Ingéré dans au moins un store.', + 'status.tooltip.Stale': 'Chunks modifiés depuis la dernière ingestion — ré-ingestion requise.', 'status.tooltip.Failed': 'Une étape du pipeline a échoué. Réessayez.', // Document library (#211, #212, #213) @@ -66,14 +66,14 @@ const messages: Messages = { 'docs.filterClear': 'Effacer les filtres', 'docs.selected': '{n} sélectionné(s)', 'docs.bulkRechunk': 'Re-chunker', - 'docs.bulkPush': 'Pousser vers un store…', + 'docs.bulkPush': 'Ingérer dans un store…', 'docs.bulkDelete': 'Supprimer', 'docs.bulkCancel': 'Annuler la sélection', 'docs.deleteConfirm': 'Supprimer {n} document(s) ? Cette action est irréversible.', - 'docs.pushTitle': 'Pousser vers un store', + 'docs.pushTitle': 'Ingérer dans un store', 'docs.pushLabel': 'Store cible', 'docs.pushPlaceholder': 'Nom du store…', - 'docs.pushSubmit': 'Pousser', + 'docs.pushSubmit': 'Ingérer', 'docs.pushCancel': 'Annuler', 'docs.jobDispatched': 'Job lancé ({jobId})', @@ -316,33 +316,33 @@ const messages: Messages = { 'Le chunking n\u2019est pas disponible pour cette analyse. Les documents volumineux trait\u00e9s par batch ne g\u00e9n\u00e8rent pas la structure interne n\u00e9cessaire au d\u00e9coupage. Coming soon !', // Search (legacy nav label moved to the top with the other legacy keys) - 'search.hint': 'Saisissez un terme pour rechercher dans les chunks indexés.', + 'search.hint': 'Saisissez un terme pour rechercher dans les chunks ingérés.', // Ingestion / My Documents 'ingestion.ingest': 'Ingérer', 'ingestion.document': 'Document', 'ingestion.chunkCount': 'Chunks prêts', - 'ingestion.successMessage': 'Indexation terminée avec succès !', + 'ingestion.successMessage': 'Ingestion terminée avec succès !', 'ingestion.ingesting': 'Ingestion...', - 'ingestion.reindex': 'Ré-indexer', - 'ingestion.indexed': 'Indexé', - 'ingestion.notIndexed': 'Non indexé', - 'ingestion.chunksIndexed': '{n} chunks indexés', + 'ingestion.reindex': 'Ré-ingérer', + 'ingestion.indexed': 'Ingéré', + 'ingestion.notIndexed': 'Non ingéré', + 'ingestion.chunksIndexed': '{n} chunks ingérés', 'ingestion.openInStudio': 'Ouvrir dans le Studio', - 'ingestion.deleteIndex': "Supprimer de l'index", + 'ingestion.deleteIndex': 'Retirer du store', 'ingestion.deleteConfirm': 'Retirer ce document de l\u2019index ? Les chunks seront supprimés mais le document source restera.', 'ingestion.unavailable': 'Ingestion non disponible', 'ingestion.filterAll': 'Tous', - 'ingestion.filterIndexed': 'Indexés', - 'ingestion.filterNotIndexed': 'Non indexés', + 'ingestion.filterIndexed': 'Ingérés', + 'ingestion.filterNotIndexed': 'Non ingérés', 'ingestion.sortName': 'Nom', 'ingestion.sortDate': 'Date', 'ingestion.search': 'Rechercher...', 'ingestion.searchChunks': 'Rechercher dans les chunks…', 'ingestion.noResults': 'Aucun résultat pour « {q} ».', 'ingestion.stepEmbedding': 'Embedding…', - 'ingestion.stepIndexing': 'Indexation…', + 'ingestion.stepIndexing': 'Ingestion…', 'ingestion.stepDone': 'Terminé', 'ingestion.opensearchConnected': 'OpenSearch connecté', 'ingestion.opensearchDisconnected': 'OpenSearch déconnecté', @@ -396,13 +396,19 @@ const messages: Messages = { 'chunks.diffAdded': 'ajout\u00e9(s)', 'chunks.diffModified': 'modifi\u00e9(s)', 'chunks.diffRemoved': 'supprim\u00e9(s)', - 'chunks.pushTitle': 'Pousser vers un store', + 'chunks.pushTitle': 'Ing\u00e9rer dans un store', 'chunks.pushStore': 'Store cible', 'chunks.pushPlaceholder': 'Nom du store\u2026', 'chunks.pushSummary': '{embeds} chunks \u00b7 ~{tokens} tokens', - 'chunks.pushConfirm': 'Pousser', + 'chunks.pushConfirm': 'Ing\u00e9rer', 'chunks.pushCancel': 'Annuler', 'chunks.pushedJob': 'Job lanc\u00e9 : {jobId}', + // Stale stores strip (#224) + 'chunks.stale.pushedAt': 'ing\u00e9r\u00e9 le {date}', + 'chunks.stale.reingest': 'R\u00e9-ing\u00e9rer', + 'chunks.stale.reingestAll': 'R\u00e9-ing\u00e9rer tous les obsol\u00e8tes', + 'chunks.stale.jobDispatched': 'Job lanc\u00e9 : {jobId}', + 'chunks.selected': '{n} s\u00e9lectionn\u00e9(s)', 'chunks.bulkDrop': 'Supprimer la s\u00e9lection', 'chunks.bulkMerge': 'Fusionner la s\u00e9lection', @@ -450,9 +456,9 @@ const messages: Messages = { 'status.Failed': 'Failed', 'status.tooltip.Uploaded': 'File received, awaiting parsing.', 'status.tooltip.Parsed': 'Document parsed successfully.', - 'status.tooltip.Chunked': 'Chunks generated, ready to push to a store.', - 'status.tooltip.Ingested': 'Indexed in at least one store.', - 'status.tooltip.Stale': 'Chunks modified since last push — re-ingestion required.', + 'status.tooltip.Chunked': 'Chunks generated, ready to ingest into a store.', + 'status.tooltip.Ingested': 'Ingested into at least one store.', + 'status.tooltip.Stale': 'Chunks modified since last ingestion — re-ingestion required.', 'status.tooltip.Failed': 'A pipeline step failed. Retry to recover.', // Document library (#211, #212, #213) @@ -470,14 +476,14 @@ const messages: Messages = { 'docs.filterClear': 'Clear filters', 'docs.selected': '{n} selected', 'docs.bulkRechunk': 'Re-chunk', - 'docs.bulkPush': 'Push to store…', + 'docs.bulkPush': 'Ingest into store…', 'docs.bulkDelete': 'Delete', 'docs.bulkCancel': 'Cancel selection', 'docs.deleteConfirm': 'Delete {n} document(s)? This action cannot be undone.', - 'docs.pushTitle': 'Push to store', + 'docs.pushTitle': 'Ingest into store', 'docs.pushLabel': 'Target store', 'docs.pushPlaceholder': 'Store name…', - 'docs.pushSubmit': 'Push', + 'docs.pushSubmit': 'Ingest', 'docs.pushCancel': 'Cancel', 'docs.jobDispatched': 'Job dispatched ({jobId})', @@ -710,32 +716,32 @@ const messages: Messages = { 'chunking.batchNotice': 'Chunking is not available for this analysis. Large documents processed in batch mode do not generate the internal structure required for chunking. Coming soon!', - 'search.hint': 'Enter a term to search through indexed chunks.', + 'search.hint': 'Enter a term to search through ingested chunks.', 'ingestion.ingest': 'Ingest', 'ingestion.document': 'Document', 'ingestion.chunkCount': 'Chunks ready', - 'ingestion.successMessage': 'Indexing completed successfully!', + 'ingestion.successMessage': 'Ingestion completed successfully!', 'ingestion.ingesting': 'Ingesting...', - 'ingestion.reindex': 'Re-index', - 'ingestion.indexed': 'Indexed', - 'ingestion.notIndexed': 'Not indexed', - 'ingestion.chunksIndexed': '{n} chunks indexed', + 'ingestion.reindex': 'Re-ingest', + 'ingestion.indexed': 'Ingested', + 'ingestion.notIndexed': 'Not ingested', + 'ingestion.chunksIndexed': '{n} chunks ingested', 'ingestion.openInStudio': 'Open in Studio', - 'ingestion.deleteIndex': 'Remove from index', + 'ingestion.deleteIndex': 'Remove from store', 'ingestion.deleteConfirm': 'Remove this document from the index? Chunks will be deleted but the source document will remain.', 'ingestion.unavailable': 'Ingestion unavailable', 'ingestion.filterAll': 'All', - 'ingestion.filterIndexed': 'Indexed', - 'ingestion.filterNotIndexed': 'Not indexed', + 'ingestion.filterIndexed': 'Ingested', + 'ingestion.filterNotIndexed': 'Not ingested', 'ingestion.sortName': 'Name', 'ingestion.sortDate': 'Date', 'ingestion.search': 'Search...', 'ingestion.searchChunks': 'Search indexed chunks…', 'ingestion.noResults': 'No results for "{q}".', 'ingestion.stepEmbedding': 'Embedding…', - 'ingestion.stepIndexing': 'Indexing…', + 'ingestion.stepIndexing': 'Ingesting…', 'ingestion.stepDone': 'Done', 'ingestion.opensearchConnected': 'OpenSearch connected', 'ingestion.opensearchDisconnected': 'OpenSearch unreachable', @@ -786,13 +792,19 @@ const messages: Messages = { 'chunks.diffAdded': 'added', 'chunks.diffModified': 'modified', 'chunks.diffRemoved': 'removed', - 'chunks.pushTitle': 'Push to store', + 'chunks.pushTitle': 'Ingest into store', 'chunks.pushStore': 'Target store', 'chunks.pushPlaceholder': 'Store name\u2026', 'chunks.pushSummary': '{embeds} chunks \u00b7 ~{tokens} tokens', - 'chunks.pushConfirm': 'Push', + 'chunks.pushConfirm': 'Ingest', 'chunks.pushCancel': 'Cancel', 'chunks.pushedJob': 'Job dispatched: {jobId}', + // Stale stores strip (#224) + 'chunks.stale.pushedAt': 'ingested on {date}', + 'chunks.stale.reingest': 'Re-ingest', + 'chunks.stale.reingestAll': 'Re-ingest all stale', + 'chunks.stale.jobDispatched': 'Job dispatched: {jobId}', + 'chunks.selected': '{n} selected', 'chunks.bulkDrop': 'Drop selected', 'chunks.bulkMerge': 'Merge selected', diff --git a/frontend/src/shared/types.ts b/frontend/src/shared/types.ts index bd8fdd5..065472d 100644 --- a/frontend/src/shared/types.ts +++ b/frontend/src/shared/types.ts @@ -17,6 +17,13 @@ export type DocumentLifecycleState = | 'Stale' | 'Failed' +/** Per-store ingestion state for a document (#224). */ +export interface DocStoreLink { + store: string + state: DocumentLifecycleState + pushedAt: string | null +} + export interface Document { id: string filename: string @@ -30,6 +37,8 @@ export interface Document { lifecycleStateAt: string | null /** Stores this document has been pushed to (added in E1 #203). */ stores?: string[] + /** Per-store state detail (#224). Present when backend returns storeLinks. */ + storeLinks?: DocStoreLink[] } export interface PipelineOptions {