feat(corpus): add save-to-corpus + private corpus search scope
- POST /api/save-to-corpus.php — saves tool output text to user's default CaveauAI corpus via ClientRagPipeline
- api/case/upload.php — dual-writes uploaded PDFs to CaveauAI client_documents (best-effort)
- assets/js/corpus-save.js — shared <dialog> handler for .js-save-corpus buttons on all tool pages
- includes/layout_footer.php — injects corpus-save.js + shared save dialog markup
- korrespond/deep-research/barnevernet/discrepancy JS — save-to-corpus buttons on output sections
- api/search.php + LegalTools::search() — corpus_scope param ('shared'|'private'|'both'), merges personal CaveauAI corpus with shared legal library when 'both'
- includes/tool_form.php + assets/js/tools.js — corpus scope radio toggle shown on search tab
- api/user-docs.php — add POST upload method for non-SSO authenticated users
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* corpus-save.js — "Save to corpus" shared handler for all DBN tool pages.
|
||||
*
|
||||
* Buttons that trigger a save must have:
|
||||
* class="js-save-corpus"
|
||||
* data-content-id="<id of element containing the text to save>"
|
||||
* data-tool="<source_tool slug, e.g. korrespond>"
|
||||
* data-suggested-title="<pre-filled title string>" (optional)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const dlg = document.getElementById('save-corpus-dialog');
|
||||
const form = document.getElementById('save-corpus-form');
|
||||
const titleIn = document.getElementById('save-corpus-title');
|
||||
const tagsIn = document.getElementById('save-corpus-tags');
|
||||
const cancelBtn = document.getElementById('save-corpus-cancel');
|
||||
|
||||
if (!dlg || !form) return; // dialog not present (e.g. not logged in)
|
||||
|
||||
cancelBtn?.addEventListener('click', () => dlg.close());
|
||||
|
||||
let _pendingBtn = null;
|
||||
let _pendingContent = '';
|
||||
let _pendingTool = '';
|
||||
|
||||
// Delegated click — catches buttons added dynamically by tool JS
|
||||
document.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.js-save-corpus');
|
||||
if (!btn) return;
|
||||
|
||||
const contentId = btn.dataset.contentId;
|
||||
const el = contentId ? document.getElementById(contentId) : null;
|
||||
const content = (el ? (el.value ?? el.textContent) : '').trim();
|
||||
|
||||
if (!content || content.length < 30) {
|
||||
btn.textContent = 'Nothing to save';
|
||||
setTimeout(() => { btn.textContent = 'Save to corpus'; }, 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingBtn = btn;
|
||||
_pendingContent = content;
|
||||
_pendingTool = btn.dataset.tool ?? '';
|
||||
|
||||
titleIn.value = btn.dataset.suggestedTitle ?? '';
|
||||
tagsIn.value = '';
|
||||
dlg.showModal();
|
||||
titleIn.focus();
|
||||
titleIn.select();
|
||||
});
|
||||
|
||||
// Form submit inside dialog
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
dlg.close();
|
||||
|
||||
const btn = _pendingBtn;
|
||||
const content = _pendingContent;
|
||||
const title = titleIn.value.trim();
|
||||
const tags = tagsIn.value.trim();
|
||||
const tool = _pendingTool;
|
||||
|
||||
if (!title || !content) return;
|
||||
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Saving…';
|
||||
}
|
||||
|
||||
try {
|
||||
const resp = await fetch('api/save-to-corpus.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title, content, source_tool: tool, tags }),
|
||||
});
|
||||
|
||||
const data = await resp.json().catch(() => ({}));
|
||||
|
||||
if (resp.ok && data.ok) {
|
||||
if (btn) {
|
||||
btn.textContent = '✓ Saved to corpus';
|
||||
btn.classList.add('js-save-corpus--saved');
|
||||
}
|
||||
} else {
|
||||
const msg = data.error ?? `Error ${resp.status}`;
|
||||
if (btn) {
|
||||
btn.textContent = 'Save failed';
|
||||
btn.disabled = false;
|
||||
btn.title = msg;
|
||||
}
|
||||
console.error('[corpus-save] Save failed:', msg);
|
||||
}
|
||||
} catch (err) {
|
||||
if (btn) {
|
||||
btn.textContent = 'Network error';
|
||||
btn.disabled = false;
|
||||
}
|
||||
console.error('[corpus-save] Network error:', err);
|
||||
}
|
||||
|
||||
_pendingBtn = null;
|
||||
_pendingContent = '';
|
||||
});
|
||||
}());
|
||||
Reference in New Issue
Block a user