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
@@ -89,6 +89,10 @@ export async function ensureSchema() {
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(190) NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)`);
|
||||
// Beschriftung der Ordner-Ebene (tab_id 0). Die liegt am Projekt statt in
|
||||
// project_tabs, weil sie keine echte Reiter-Zeile ist — es gibt sie immer,
|
||||
// sie lässt sich nicht löschen und hat keine eigene id. NULL = „Ordner".
|
||||
await pool.query('ALTER TABLE projects ADD COLUMN IF NOT EXISTS root_tab_name VARCHAR(190) NULL');
|
||||
await pool.query(`CREATE TABLE IF NOT EXISTS project_tabs (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
project_id INT NOT NULL,
|
||||
@@ -113,7 +117,7 @@ export async function ensureSchema() {
|
||||
// der (nicht gelöschten) Pins über alle Ebenen.
|
||||
export async function listProjects() {
|
||||
const [rows] = await pool.query(`
|
||||
SELECT p.id, p.name, p.created_at,
|
||||
SELECT p.id, p.name, p.root_tab_name, p.created_at,
|
||||
(SELECT COUNT(*) FROM project_tabs t WHERE t.project_id = p.id) AS tabs,
|
||||
(SELECT COUNT(*) FROM project_pins pin JOIN recordings r ON r.id = pin.recording_id
|
||||
WHERE pin.project_id = p.id AND r.deleted_at IS NULL) AS pins
|
||||
@@ -128,6 +132,12 @@ export async function createProject(name) {
|
||||
export async function renameProject(id, name) {
|
||||
await pool.execute('UPDATE projects SET name=? WHERE id=?', [String(name).slice(0, 190), id]);
|
||||
}
|
||||
// Ordner-Ebene umbenennen. Leerer Name setzt auf die Vorgabe „Ordner" zurück.
|
||||
export async function renameProjectRoot(id, name) {
|
||||
const n = String(name || '').trim().slice(0, 190);
|
||||
await pool.execute('UPDATE projects SET root_tab_name=? WHERE id=?', [n || null, id]);
|
||||
return n || null;
|
||||
}
|
||||
// Der Ordner verschwindet mitsamt Reitern und Pins — die Aufnahmen selbst
|
||||
// bleiben unberührt (ein Pin ist nur eine Referenz).
|
||||
export async function deleteProject(id) {
|
||||
@@ -153,7 +163,7 @@ export async function deleteProjectTab(id) {
|
||||
// (im Papierkorb liegende) Aufnahmen werden ausgeblendet, nicht entfernt: eine
|
||||
// Wiederherstellung bringt den Pin ohne Zutun zurück.
|
||||
export async function getProject(id) {
|
||||
const [pr] = await pool.query('SELECT id, name, created_at FROM projects WHERE id=?', [id]);
|
||||
const [pr] = await pool.query('SELECT id, name, root_tab_name, created_at FROM projects WHERE id=?', [id]);
|
||||
if (!pr.length) return null;
|
||||
const [tabs] = await pool.query(
|
||||
'SELECT id, name, created_at FROM project_tabs WHERE project_id=? ORDER BY id', [id]);
|
||||
@@ -179,7 +189,7 @@ export async function unpinRecording(pinId) {
|
||||
export async function listPinsForRecording(recId) {
|
||||
const [rows] = await pool.query(`
|
||||
SELECT pin.id AS pin_id, pin.project_id, p.name AS project_name,
|
||||
pin.tab_id, t.name AS tab_name
|
||||
pin.tab_id, COALESCE(t.name, p.root_tab_name) AS tab_name
|
||||
FROM project_pins pin
|
||||
JOIN projects p ON p.id = pin.project_id
|
||||
LEFT JOIN project_tabs t ON t.id = pin.tab_id
|
||||
|
||||
Reference in New Issue
Block a user