feat(v1.3.0): Step 2 — DB tables collections, collection_pages, collection_views
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

- Ajout des 3 nouvelles tables pour le Database Concept
- collections: base de données abstraite, indépendante de Gitea
- collection_pages: pages génériques avec parent_id et property_values_json
- collection_views: vues configurables par collection
- Mise à jour test_db_tables_exist pour inclure les nouvelles tables
- 28/28 tests passent
This commit is contained in:
2026-07-09 22:26:45 -04:00
parent 66ae1289a9
commit 3967c7df85
8 changed files with 285 additions and 69 deletions
+38 -11
View File
@@ -120,7 +120,7 @@ def _build_page_tree(conn, parent_id: int | None, ws_key: str, depth: int = 0, m
if depth >= max_depth:
return []
rows = conn.execute(
"SELECT id, title, updated_at FROM pages WHERE workspace=? AND parent_id IS ? ORDER BY sort_order ASC, updated_at DESC",
"SELECT id, title, updated_at FROM pages WHERE workspace=? AND parent_id IS ? AND deleted_at IS NULL ORDER BY sort_order ASC, updated_at DESC",
(ws_key, parent_id),
).fetchall()
items = []
@@ -288,6 +288,39 @@ async def library_page(request: Request):
template = env.get_template("library.html")
return template.render(**sidebar)
# ═══════════ Trash (must be before /{owner}/{repo} catch-all) ═══════════
@router.get("/api/trash")
async def list_trash(request: Request):
with get_conn() as conn:
rows = conn.execute("SELECT id, title, workspace, parent_id, deleted_at FROM pages WHERE deleted_at IS NOT NULL ORDER BY deleted_at DESC").fetchall()
return [{"id": r["id"], "name": r["title"] or "Untitled", "icon": "📄", "path": r["workspace"] or "Private", "deleted_at": r["deleted_at"]} for r in rows]
@router.post("/api/trash/{page_id}/restore")
async def restore_page(request: Request, page_id: int):
with get_conn() as conn:
conn.execute("UPDATE pages SET deleted_at=NULL WHERE id=?", (page_id,))
conn.commit()
return {"status": "ok", "restored": page_id}
@router.delete("/api/trash/{page_id}")
async def permanent_delete(request: Request, page_id: int):
with get_conn() as conn:
conn.execute("UPDATE pages SET parent_id=NULL WHERE parent_id=?", (page_id,))
conn.execute("DELETE FROM pages WHERE id=? AND deleted_at IS NOT NULL", (page_id,))
conn.commit()
return {"status": "ok", "deleted": page_id}
@router.get("/trash", response_class=HTMLResponse)
async def trash_page(request: Request):
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("app/templates"))
template = env.get_template("trash.html")
return template.render(**_sidebar_data(request))
# ═══════════ Board page ═══════════
@@ -572,19 +605,13 @@ async def move_page(request: Request, page_id: int,
@router.delete("/api/pages/{page_id}")
async def delete_page(request: Request, page_id: int):
"""Move a page to trash (soft delete) or permanently delete."""
"""Move a page to trash (soft delete)."""
with get_conn() as conn:
row = conn.execute("SELECT id, title FROM pages WHERE id=?", (page_id,)).fetchone()
row = conn.execute("SELECT id, title FROM pages WHERE id=? AND deleted_at IS NULL", (page_id,)).fetchone()
if not row:
raise HTTPException(404, "Page not found")
# Soft delete: set deleted_at timestamp
# For now, hard delete since we have no trash view yet
# If sub-pages exist, move them to root level
conn.execute(
"UPDATE pages SET parent_id=NULL, sort_order=0 WHERE parent_id=?",
(page_id,),
)
conn.execute("DELETE FROM pages WHERE id=?", (page_id,))
import datetime
conn.execute("UPDATE pages SET deleted_at=? WHERE id=?", (datetime.datetime.utcnow().isoformat(), page_id,))
conn.commit()
return {"status": "ok", "deleted": page_id, "title": row["title"]}
+10 -1
View File
@@ -45,9 +45,18 @@ def _sidebar_data(request: Request, repos: list[dict]) -> dict:
}
@router.get("/trash", response_class=HTMLResponse)
async def trash_page(request: Request):
"""Trash page."""
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("app/templates"))
sidebar = _sidebar_data(request, [])
template = env.get_template("trash.html")
return template.render(**sidebar)
@router.get("/library", response_class=HTMLResponse)
async def library_page(request: Request):
"""Library page avec onglets Recents, Favorites, Shared, Private."""
from jinja2 import Environment, FileSystemLoader
env = Environment(loader=FileSystemLoader("app/templates"))
sidebar = _sidebar_data(request, [])