diff --git a/files/client/custom/modules/knowledge-base/src/views/kb/index.js b/files/client/custom/modules/knowledge-base/src/views/kb/index.js index 937aff0..161d744 100644 --- a/files/client/custom/modules/knowledge-base/src/views/kb/index.js +++ b/files/client/custom/modules/knowledge-base/src/views/kb/index.js @@ -833,18 +833,15 @@ define('modules/knowledge-base/views/kb/index', ['view'], function (Dep) { const src = this._findSourceById(id); if (!src) return; const titleShort = (src.title || '').slice(0, 60); - if (!confirm(`לעבד מחדש את "${titleShort}"?\n\nהקטעים הקיימים יימחקו והקובץ יעובר parse + chunk + embed מחדש.\nהמטא-דאטה (כותרת, מזהה, תאריכים) נשמרים.`)) return; + if (!confirm(`לעבד מחדש את "${titleShort}"?\n\nהקובץ ינותח מחדש על ידי AI; תקבל הצעות מטא-דאטה (כותרת, סיכום, תוויות) לעריכה במסך הסקירה.\nהקטעים יוחלפו רק לאחר שתאשר את ההצעות.`)) return; const self = this; Espo.Ajax.postRequest('KnowledgeBase/action/reingestSource', {id}) .then(res => { - if (res && res.job_id) { - // Surface in the upload-status banner + jobs list, like an upload would. - self._renderJobStatus({ - original_filename: src.title || ('source #' + id), - status: res.status || 'queued', - }); - self._startJobPolling(res.job_id); - // Refresh sources after the polling reports done. + if (res && res.batch_id) { + // Open the same review UI as a fresh upload — the + // backend created a single-file batch wrapping + // this re-ingest job. + self._openBatch(res.batch_id, src.topic_id || self.topicId); } }) .catch(err => { @@ -1003,6 +1000,18 @@ define('modules/knowledge-base/views/kb/index', ['view'], function (Dep) { if (typeof ai === 'string') { try { ai = JSON.parse(ai); } catch (e) { ai = {}; } } + // For re-ingest jobs, metadata_json carries the + // existing source metadata so the form can pre-fill + // values (title/labels/etc.) before the AI classifier + // returns. AI suggestions take precedence once they + // arrive, but the fallback prevents an empty form + // during the classify wait — and keeps existing + // labels visible if the user wants to retain them. + let md = it.metadata_json; + if (typeof md === 'string') { + try { md = JSON.parse(md); } catch (e) { md = {}; } + } + const isReingest = !!(md && md.reingest); let s = it.status; if (s === 'processing' && it.processing_stage === 'classifying') s = 'classifying'; else if (s === 'processing' && it.processing_stage === 'embedding') s = 'committing'; @@ -1011,6 +1020,9 @@ define('modules/knowledge-base/views/kb/index', ['view'], function (Dep) { filename: it.original_filename, status: s, ai: (ai && Object.keys(ai).length) ? ai : null, + defaults: isReingest ? md : null, + isReingest: isReingest, + sourceId: it.source_id || null, edits: null, error: it.error_message || null, }; @@ -1158,9 +1170,19 @@ define('modules/knowledge-base/views/kb/index', ['view'], function (Dep) { const html = _activeBatch.cards.map((c, idx) => { if (c.status === 'awaiting_review') readyCount++; const ai = c.ai || {}; + const defaults = c.defaults || {}; const e = c.edits || {}; - const val = (k) => this.escape(e[k] != null ? e[k] : (ai[k] || '')); - const labelsStr = e.labelsStr != null ? e.labelsStr : ((ai.labels || []).join(', ')); + // Precedence: user edits > AI suggestion > existing source + // (only set for re-ingest cards). For fresh uploads `defaults` + // is empty so this collapses to the previous behavior. + const val = (k) => this.escape( + e[k] != null ? e[k] : + (ai[k] != null && ai[k] !== '' ? ai[k] : + (defaults[k] || '')) + ); + const aiLabels = (ai.labels || []); + const fallbackLabels = aiLabels.length ? aiLabels : (defaults.labels || []); + const labelsStr = e.labelsStr != null ? e.labelsStr : fallbackLabels.join(', '); let body = ''; if (c.status === 'failed') { body = `