- 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
- Add sort_order column to pages table for drag & drop reordering
- Rewrite _sidebar_data with recursive _build_page_tree for nested hierarchy
- Add tree toggle buttons (▶ collapsed / ▼ expanded) on parent items
- Alpine.js expandedNodes state with toggleTreeNode() for expand/collapse
- Jinja2 recursive macro render_tree_item for proper nesting + indentation
- SortableJS integration on .sidebar-items for drag & drop repositioning
- New PUT /board/api/pages/{id}/move API for persisting moves
- Auto-set sort_order on page creation (max+1 for same parent)
- CSS: .tree-toggle, .tree-toggle-placeholder, grab cursor on page-icon
- 28/28 tests pass, all endpoints healthy
- pages.workspace = owner/repo (ex: bruno/flowdeck) au lieu du nom user
- Sidebar: Kanban board + 4 sous-vues avec URLs distinctes (?view=kanban|table|...)
- Cliquer sur 'Table view' dans sidebar → change la vue à droite
- Dashboard (/): sidebar montre uniquement les projets Gitea
- Board (/board/{o}/{r}): sidebar montre les pages de ce projet
- Changement de projet = changement de contexte (pages isolées)
- newPage/newSubPage passent workspace_key à l'API
- Auto-switch view depuis paramètre URL dans board.html
Gitea API returns 'pull_request': null for regular issues.
Old filter 'pull_request' in issue matched the key even when null,
sending ALL issues to the PRs section instead of the Kanban columns.
Fixed: i.get('pull_request') — null is falsy, real PR dict is truthy.
Also: labels/milestones dropdowns empty = repo has none configured in Gitea.
1. Refresh button no longer doubles header
- Added ?fragment=1 param to filter/refresh HTMX requests
- Board route returns board_fragment.html (columns only) for fragment requests
- Refresh button uses window.location.reload() for full page reload
2. No more raw JSON in header
- base.html: user info rendered server-side via template variable
- board.py: decodes session cookie, passes user to template
- Dashboard/board templates show Login/Deconnexion based on auth state
3. New issue form works
- Replaced HTMX post with JS fetch (handleNewIssue)
- Adds CSRF token to form submission
- Reloads page on successful creation
- Cleaner error handling