feat(gui): Ordner-Ebene eines Projekts umbenennbar
Der erste Reiter hieß fest "Ordner" und trägt jetzt einen frei wählbaren Namen. Er steht in der neuen Spalte projects.root_tab_name (NULL = "Ordner") statt als Zeile in project_tabs: die Ordner-Ebene ist kein echter Reiter -- es gibt sie immer, sie hat keine eigene id und ist nicht löschbar. Eine eigene Zeile hätte eine id gebraucht, die in project_pins.tab_id landen müsste, womit die "0 = Ordner-Ebene"-Regel und damit der UNIQUE-Index gefallen wären. PATCH /api/projects/:id/tabs/0 fängt die 0 ab und schreibt ins Projekt; leerer Name setzt auf "Ordner" zurück. Ebenen-Auswahl im Pin-Dialog und die Liste bestehender Anheftungen zeigen den Namen mit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
772385713e
commit
d0e02eb976
+12
-7
@@ -575,7 +575,9 @@ async function renderProjectDetail(v) {
|
||||
if (state.projectTabId && !p.tabs.some(t => t.id === state.projectTabId)) state.projectTabId = 0;
|
||||
const cur = state.projectTabId;
|
||||
const pins = p.pins.filter(x => x.tab_id === cur);
|
||||
const levelName = cur ? (p.tabs.find(t => t.id === cur) || {}).name : p.name;
|
||||
// Die Ordner-Ebene ist umbenennbar; ohne eigenen Namen heißt sie „Ordner".
|
||||
const rootName = p.root_tab_name || 'Ordner';
|
||||
const levelName = cur ? (p.tabs.find(t => t.id === cur) || {}).name : rootName;
|
||||
v.innerHTML = `
|
||||
<div class="card">
|
||||
<div class="proj-head">
|
||||
@@ -584,7 +586,8 @@ async function renderProjectDetail(v) {
|
||||
<button id="prename" class="btn icon" title="Ordner umbenennen">✏️</button>
|
||||
</div>
|
||||
<div class="proj-tabs">
|
||||
<button class="ptab ${cur === 0 ? 'active' : ''}" data-ptab="0">Ordner</button>
|
||||
<button class="ptab ${cur === 0 ? 'active' : ''}" data-ptab="0">${escapeHtml(rootName)}
|
||||
<span class="ptab-n">${p.pins.filter(x => x.tab_id === 0).length}</span></button>
|
||||
${p.tabs.map(t => `<button class="ptab ${cur === t.id ? 'active' : ''}" data-ptab="${t.id}">${escapeHtml(t.name)}
|
||||
<span class="ptab-n">${p.pins.filter(x => x.tab_id === t.id).length}</span></button>`).join('')}
|
||||
<button id="ptabnew" class="ptab add" title="Unterreiter anlegen">+ Reiter</button>
|
||||
@@ -594,8 +597,8 @@ async function renderProjectDetail(v) {
|
||||
<b>${escapeHtml(levelName)}</b>
|
||||
<span class="muted small">${pins.length} angeheftete Aufnahme${pins.length === 1 ? '' : 'n'}</span>
|
||||
<span class="grow"></span>
|
||||
${cur ? `<button id="ptabren" class="btn small">✏️ Reiter umbenennen</button>
|
||||
<button id="ptabdel" class="btn small danger">🗑 Reiter löschen</button>` : ''}
|
||||
<button id="ptabren" class="btn small">✏️ ${cur ? 'Reiter' : 'Ebene'} umbenennen</button>
|
||||
${cur ? '<button id="ptabdel" class="btn small danger">🗑 Reiter löschen</button>' : ''}
|
||||
<button id="ppin" class="btn primary small">📌 Aufnahme anheften</button>
|
||||
</div>
|
||||
<div id="ppins" class="list">${pins.length ? pins.map(r => `
|
||||
@@ -630,8 +633,10 @@ async function renderProjectDetail(v) {
|
||||
};
|
||||
const renBtn = v.querySelector('#ptabren');
|
||||
if (renBtn) renBtn.onclick = async () => {
|
||||
const name = prompt('Unterreiter umbenennen:', levelName);
|
||||
if (name === null || !name.trim()) return;
|
||||
// Auf der Ordner-Ebene ist ein leeres Feld erlaubt: es setzt die
|
||||
// Beschriftung auf die Vorgabe „Ordner" zurück.
|
||||
const name = prompt(cur ? 'Unterreiter umbenennen:' : 'Ordner-Ebene umbenennen (leer = „Ordner"):', levelName);
|
||||
if (name === null || (cur && !name.trim())) return;
|
||||
await api.patch(`/api/projects/${p.id}/tabs/${cur}`, { name: name.trim() });
|
||||
renderProjectDetail(v);
|
||||
};
|
||||
@@ -719,7 +724,7 @@ async function pinToProjectDialog(rec, done) {
|
||||
const loadTabs = async () => {
|
||||
if (pp.value === 'new') { pt.innerHTML = '<option value="0">Ordner-Ebene</option>'; return; }
|
||||
const p = await api.get('/api/projects/' + pp.value);
|
||||
pt.innerHTML = '<option value="0">Ordner-Ebene</option>' +
|
||||
pt.innerHTML = `<option value="0">${escapeHtml(p.root_tab_name || 'Ordner-Ebene')}</option>` +
|
||||
p.tabs.map(t => `<option value="${t.id}">${escapeHtml(t.name)}</option>`).join('');
|
||||
};
|
||||
pp.onchange = async () => {
|
||||
|
||||
Reference in New Issue
Block a user