feat(diag): Advanced Search — dynamisch Felder hinzufuegen mit + Button
Statt fest 3 Felder gibt's jetzt eine erweiterbare Reihen-Liste: - "+ Feld"-Button fuegt eine Reihe hinzu (UND/ODER + Eingabe) - ✕-Button pro Reihe (ausser der ersten) entfernt sie - Erste Reihe ist immer "Start" ohne Operator - syncAdvancedRowsFromDOM rettet Eingaben vor jedem Re-Render - runAdvancedSearch iteriert ueber alle Reihen mit Inhalt, leere werden ignoriert Damit ist die Boolean-Suche so lang wie noetig — Stefan kann auch 5-6 Begriffe verknuepfen ohne UI-Hack. Min. 1 Feld bleibt immer (clearAdvancedSearch reseted auf eine leere Start-Reihe). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+84
-31
@@ -857,25 +857,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="brain-advanced-panel" style="display:none;margin-top:10px;padding:10px;background:#080810;border:1px solid #1E1E2E;border-radius:6px;">
|
<div id="brain-advanced-panel" style="display:none;margin-top:10px;padding:10px;background:#080810;border:1px solid #1E1E2E;border-radius:6px;">
|
||||||
<div style="color:#8888AA;font-size:11px;margin-bottom:6px;">
|
<div style="color:#8888AA;font-size:11px;margin-bottom:6px;">
|
||||||
Mehrere Begriffe mit AND/OR verknuepfen — Volltext-Substring, case-insensitive, link-nach-rechts ausgewertet.
|
Mehrere Begriffe mit AND/OR verknuepfen — Volltext-Substring, case-insensitive, links-nach-rechts ausgewertet.
|
||||||
</div>
|
</div>
|
||||||
<div style="display:grid;grid-template-columns:1fr 60px 1fr 60px 1fr;gap:6px;align-items:center;">
|
<div id="adv-rows-container" style="display:flex;flex-direction:column;gap:6px;">
|
||||||
<input type="text" id="adv-term-1" placeholder="z.B. flugzeug" style="background:#080810;color:#E0E0F0;border:1px solid #1E1E2E;padding:6px;border-radius:4px;font-family:inherit;font-size:12px;">
|
<!-- Reihen werden dynamisch via JS gerendert (renderAdvancedRows) -->
|
||||||
<select id="adv-op-1" style="background:#080810;color:#E0E0F0;border:1px solid #1E1E2E;padding:6px;border-radius:4px;font-family:inherit;font-size:11px;">
|
|
||||||
<option value="AND" selected>UND</option>
|
|
||||||
<option value="OR">ODER</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" id="adv-term-2" placeholder="z.B. cessna" style="background:#080810;color:#E0E0F0;border:1px solid #1E1E2E;padding:6px;border-radius:4px;font-family:inherit;font-size:12px;">
|
|
||||||
<select id="adv-op-2" style="background:#080810;color:#E0E0F0;border:1px solid #1E1E2E;padding:6px;border-radius:4px;font-family:inherit;font-size:11px;">
|
|
||||||
<option value="AND" selected>UND</option>
|
|
||||||
<option value="OR">ODER</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" id="adv-term-3" placeholder="(optional)" style="background:#080810;color:#E0E0F0;border:1px solid #1E1E2E;padding:6px;border-radius:4px;font-family:inherit;font-size:12px;">
|
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:6px;margin-top:8px;align-items:center;">
|
<div style="display:flex;gap:6px;margin-top:8px;align-items:center;flex-wrap:wrap;">
|
||||||
<button class="btn" onclick="runAdvancedSearch()" style="padding:4px 12px;font-size:11px;">Suchen</button>
|
<button class="btn" onclick="runAdvancedSearch()" style="padding:4px 12px;font-size:11px;">Suchen</button>
|
||||||
<button class="btn secondary" onclick="clearAdvancedSearch()" style="padding:4px 10px;font-size:11px;color:#8888AA;">Felder leeren</button>
|
<button class="btn secondary" onclick="addAdvancedRow()" style="padding:4px 10px;font-size:11px;" title="Weiteres Suchfeld hinzufuegen">+ Feld</button>
|
||||||
<span style="color:#555570;font-size:10px;margin-left:auto;">Tipp: Felder mit Inhalt werden zusammengeführt — leere Felder werden ignoriert</span>
|
<button class="btn secondary" onclick="clearAdvancedSearch()" style="padding:4px 10px;font-size:11px;color:#8888AA;">Alle leeren</button>
|
||||||
|
<span style="color:#555570;font-size:10px;margin-left:auto;">Leere Felder werden ignoriert · Min. 1 Feld · ✕ entfernt ein Feld</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="brain-search-info" style="margin-top:6px;font-size:10px;color:#8888AA;display:none;"></div>
|
<div id="brain-search-info" style="margin-top:6px;font-size:10px;color:#8888AA;display:none;"></div>
|
||||||
@@ -3485,32 +3476,94 @@
|
|||||||
const open = panel.style.display !== 'none';
|
const open = panel.style.display !== 'none';
|
||||||
panel.style.display = open ? 'none' : 'block';
|
panel.style.display = open ? 'none' : 'block';
|
||||||
if (btn) btn.textContent = open ? '⌃ Erweitert' : '⌄ Einklappen';
|
if (btn) btn.textContent = open ? '⌃ Erweitert' : '⌄ Einklappen';
|
||||||
|
if (!open) ensureAdvancedRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamische Such-Reihen-Struktur:
|
||||||
|
// advRows = [{term, op}, ...] — die erste Reihe hat op=null,
|
||||||
|
// jede weitere bekommt einen UND/ODER-Selektor links und einen ✕ rechts.
|
||||||
|
let advRows = [{ term: '', op: null }];
|
||||||
|
|
||||||
|
function ensureAdvancedRows() {
|
||||||
|
if (!advRows.length) advRows = [{ term: '', op: null }];
|
||||||
|
renderAdvancedRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAdvancedRow() {
|
||||||
|
// Vor dem Re-render aktuelle Werte aus DOM uebernehmen damit nichts verloren geht
|
||||||
|
syncAdvancedRowsFromDOM();
|
||||||
|
advRows.push({ term: '', op: 'AND' });
|
||||||
|
renderAdvancedRows();
|
||||||
|
// Fokus auf das neue Feld
|
||||||
|
const last = document.querySelector(`#adv-rows-container .adv-row:last-child input.adv-term`);
|
||||||
|
if (last) last.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAdvancedRow(idx) {
|
||||||
|
syncAdvancedRowsFromDOM();
|
||||||
|
if (advRows.length <= 1) return; // erste bleibt
|
||||||
|
advRows.splice(idx, 1);
|
||||||
|
// Erste Reihe hat immer op=null
|
||||||
|
if (advRows[0]) advRows[0].op = null;
|
||||||
|
renderAdvancedRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncAdvancedRowsFromDOM() {
|
||||||
|
const rows = document.querySelectorAll('#adv-rows-container .adv-row');
|
||||||
|
const next = [];
|
||||||
|
rows.forEach((row, i) => {
|
||||||
|
const term = (row.querySelector('input.adv-term')?.value || '');
|
||||||
|
const op = i === 0 ? null : (row.querySelector('select.adv-op')?.value || 'AND');
|
||||||
|
next.push({ term, op });
|
||||||
|
});
|
||||||
|
if (next.length) advRows = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderAdvancedRows() {
|
||||||
|
const container = document.getElementById('adv-rows-container');
|
||||||
|
if (!container) return;
|
||||||
|
const inputStyle = 'flex:1;min-width:0;background:#080810;color:#E0E0F0;border:1px solid #1E1E2E;padding:6px;border-radius:4px;font-family:inherit;font-size:12px;';
|
||||||
|
const selectStyle = 'background:#080810;color:#E0E0F0;border:1px solid #1E1E2E;padding:6px;border-radius:4px;font-family:inherit;font-size:11px;width:70px;';
|
||||||
|
container.innerHTML = advRows.map((r, i) => {
|
||||||
|
const ph = i === 0 ? 'z.B. flugzeug' : 'z.B. cessna';
|
||||||
|
const term = (r.term || '').replace(/"/g, '"');
|
||||||
|
if (i === 0) {
|
||||||
|
return `<div class="adv-row" style="display:flex;gap:6px;align-items:center;">
|
||||||
|
<span style="width:70px;color:#555570;font-size:11px;text-align:center;">Start</span>
|
||||||
|
<input type="text" class="adv-term" placeholder="${ph}" value="${term}" style="${inputStyle}">
|
||||||
|
<span style="width:24px;"></span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
const op = r.op || 'AND';
|
||||||
|
return `<div class="adv-row" style="display:flex;gap:6px;align-items:center;">
|
||||||
|
<select class="adv-op" style="${selectStyle}">
|
||||||
|
<option value="AND"${op === 'AND' ? ' selected' : ''}>UND</option>
|
||||||
|
<option value="OR"${op === 'OR' ? ' selected' : ''}>ODER</option>
|
||||||
|
</select>
|
||||||
|
<input type="text" class="adv-term" placeholder="${ph}" value="${term}" style="${inputStyle}">
|
||||||
|
<button class="btn secondary" onclick="removeAdvancedRow(${i})" title="Diese Zeile entfernen" style="width:24px;height:24px;padding:0;line-height:20px;font-size:11px;color:#FF6B6B;">✕</button>
|
||||||
|
</div>`;
|
||||||
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearAdvancedSearch() {
|
function clearAdvancedSearch() {
|
||||||
['adv-term-1','adv-term-2','adv-term-3'].forEach(id => {
|
advRows = [{ term: '', op: null }];
|
||||||
const el = document.getElementById(id); if (el) el.value = '';
|
renderAdvancedRows();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Mehrere Volltext-Suchen + Boolean-Kombination (links nach rechts).
|
/** Mehrere Volltext-Suchen + Boolean-Kombination (links nach rechts).
|
||||||
* Backend bleibt simpel — wir machen N parallele search-text-Calls
|
* Backend bleibt simpel — wir machen N parallele search-text-Calls
|
||||||
* und kombinieren die ID-Mengen client-seitig per AND/OR. */
|
* und kombinieren die ID-Mengen client-seitig per AND/OR. */
|
||||||
async function runAdvancedSearch() {
|
async function runAdvancedSearch() {
|
||||||
const terms = [
|
syncAdvancedRowsFromDOM();
|
||||||
document.getElementById('adv-term-1')?.value?.trim() || '',
|
|
||||||
document.getElementById('adv-term-2')?.value?.trim() || '',
|
|
||||||
document.getElementById('adv-term-3')?.value?.trim() || '',
|
|
||||||
];
|
|
||||||
const ops = [
|
|
||||||
document.getElementById('adv-op-1')?.value || 'AND',
|
|
||||||
document.getElementById('adv-op-2')?.value || 'AND',
|
|
||||||
];
|
|
||||||
const info = document.getElementById('brain-search-info');
|
const info = document.getElementById('brain-search-info');
|
||||||
// Felder mit Inhalt zusammen mit dem ops-Op DAVOR sammeln
|
// Nur Reihen mit Inhalt einsammeln. Die erste belegte Reihe wird zum
|
||||||
|
// Start-Term (op=null), egal an welchem Index sie ursprünglich war.
|
||||||
const active = [];
|
const active = [];
|
||||||
for (let i = 0; i < 3; i++) {
|
for (const r of advRows) {
|
||||||
if (terms[i]) active.push({ term: terms[i], op: i === 0 ? null : ops[i - 1] });
|
const t = (r.term || '').trim();
|
||||||
|
if (!t) continue;
|
||||||
|
active.push({ term: t, op: active.length === 0 ? null : (r.op || 'AND') });
|
||||||
}
|
}
|
||||||
if (active.length === 0) {
|
if (active.length === 0) {
|
||||||
if (info) info.style.display = 'none';
|
if (info) info.style.display = 'none';
|
||||||
|
|||||||
Reference in New Issue
Block a user