# Guide Technique — Recréer Database & Tasks de Notion dans FlowDeck > **Version:** 1.0 · **Date:** 2026-07-10 > **Auteur:** Hermes-Deepin · **Cible:** FlowDeck v1.2+ --- ## Table des matières 1. [Introduction](#introduction) 2. [Comprendre Notion : le modèle « Blocs & Collections »](#1-comprendre-notion--le-modèle-blocs--collections) 3. [La Database Notion : anatomie complète](#2-la-database-notion--anatomie-complète) 4. [Les Tasks Notion : un cas spécial de database](#3-les-tasks-notion--un-cas-spécial-de-database) 5. [Comment Notion connecte Database et Tasks](#4-comment-notion-connecte-database-et-tasks) 6. [Implémentation dans FlowDeck : plan pas à pas](#5-implémentation-dans-flowdeck--plan-pas-à-pas) 7. [Références](#6-références) --- ## Introduction L'objectif est de **recréer dans FlowDeck** les deux fonctionnalités les plus puissantes de Notion : - **Les Databases** — tables structurées avec propriétés typées, vues multiples, filtres et tris - **Les Tasks** — sous-tâches hiérarchiques avec dépendances, liées aux databases Ces deux systèmes sont **profondément interconnectés** dans Notion : les tâches ne sont qu'une forme spécialisée de database, avec un parent auto-référencé et des propriétés de dépendance. ### État actuel de FlowDeck (v1.2) FlowDeck a déjà une base solide : ``` ✅ Boards (Kanban lié à Gitea) → table `boards` ✅ Cards (issues → colonnes) → table `cards` ✅ Colonnes custom → `project_properties` + `property_values` ✅ Checklists par issue → `checklists` + `checklist_items` ✅ Vues multiples → Kanban, Table, Status, TeamLoad, Detailed ✅ Notes Markdown → `notes` ✅ Pages (ébauche Notion editor) → `pages` (content, parent_id, sort_order, content_format) ⚠️ Database concept → ABSENT (pas de notion de collection/row) ❌ Relations entre projets → ABSENT ❌ Sub-tasks / Parent-item → ABSENT ❌ Rollups / Formules → ABSENT ❌ Dependencies (bloque/bloqué par) → ABSENT ``` --- ## 1. Comprendre Notion : le modèle « Blocs & Collections » ### 1.1 Le principe fondateur : « Everything is a Block » Dans Notion, **chaque élément de contenu est un bloc**. Un bloc peut être : ``` ┌──────────────────────────────────────┐ │ BLOC │ │ ├─ id: UUID │ │ ├─ type: "paragraph" │ │ │ "heading_1" │ │ │ "to_do" │ │ │ "bulleted_list_item" │ │ │ "child_page" │ │ │ "child_database" │ │ │ "collection_view_page" │ ← Database │ │ ... │ │ ├─ content: [...] (blocs enfants) │ │ ├─ properties: {} (metadata) │ │ └─ parent: {type, id} │ └──────────────────────────────────────┘ ``` ### 1.2 Les 3 couches d'une Database Notion Une database Notion est un assemblage de **3 concepts distincts** : ``` ┌────────────────────────────────────────────────────────────┐ │ DATABASE NOTION │ │ │ │ ┌─────────────┐ ┌──────────────────┐ ┌────────────┐│ │ │ COLLECTION │ │ COLLECTION_VIEW │ │ PAGES ││ │ │ (schema) │◄───│ (présentation) │◄───│ (data) ││ │ │ │ │ │ │ ││ │ │ properties │ │ type: table │ │ row 1 ││ │ │ - Name │ │ board │ │ row 2 ││ │ │ - Status │ │ calendar │ │ row 3 ││ │ │ - Priority │ │ gallery │ │ ... ││ │ │ - Due Date │ │ list │ │ ││ │ │ - Assignee │ │ timeline │ │ ││ │ │ │ │ │ │ ││ │ │ │ │ filters │ │ ││ │ │ │ │ sorts │ │ ││ │ │ │ │ grouping │ │ ││ │ │ │ │ format options │ │ ││ │ └─────────────┘ └──────────────────┘ └────────────┘│ │ │ │ 1 SCHÉMA N VUES (filtres N LIGNES │ │ partagé + tris différents) (pages filles) │ └────────────────────────────────────────────────────────────┘ ``` **Concept clé** : Une database n'est PAS une table SQL. C'est une **collection de pages**, chaque page ayant un schéma de propriétés commun, affiché via des vues configurables. ### 1.3 Le cycle de vie d'une Database ``` Création Consultation Modification ───────── ──────────── ──────────── 1. L'utilisateur choisit 4. Les vues lisent le 7. Édition inline : un type de vue schéma + les pages - click → édite valeur (table, board...) - drag → réorganise 5. Filtres/sorts/tri - shortcuts → ajoute ligne 2. Notion crée : appliqués - 1 Collection 8. Changement de vue = - 1 CollectionView 6. Rendu en temps réel même données, - 1 CollectionViewPage autre présentation - + pages vides optionnelles 9. Ajout propriété = 3. L'utilisateur définit nouveau champ sur les propriétés (schéma) TOUTES les pages ``` --- ## 2. La Database Notion : anatomie complète ### 2.1 Propriétés (le schéma) Notion supporte **21 types de propriétés**. Toutes sont définies au niveau de la Collection : ``` Type │ Description │ Exemple de valeur ──────────────────┼────────────────────────────┼────────────────── Title │ Le titre de la page (obligatoire, unique) Text │ Texte libre │ "Description..." Number │ Nombre (décimales, format) │ 42 / 12.5% Select │ Choix unique │ "En cours" Multi-select │ Choix multiples │ ["Frontend","Backend"] Status │ Select avec couleur forcée │ "Done" (vert) Date │ Date + heure optionnelle │ 2026-07-15 Person │ Mention utilisateur │ ["bruno"] Files & media │ Uploads │ [url1, url2] Checkbox │ Booléen │ true / false URL │ Lien cliquable │ "https://..." Email │ Email cliquable │ "a@b.com" Phone │ Téléphone cliquable │ "+1..." Formula │ Calcul (JS-like) │ prop("Prix")*prop("Qté") ← CLÉ Relation │ Lien vers autre database │ [page_id_1, page_id_2] ← CLÉ Rollup │ Agrégation via relation │ sum, avg, count, min, max ← CLÉ Created time │ Date de création (auto) │ 2026-01-15T10:30:00Z Created by │ Auteur (auto) │ "bruno" Last edited time │ Dernière édition (auto) │ 2026-07-09T18:00:00Z Last edited by │ Dernier éditeur (auto) │ "bruno" Unique ID │ ID incrémental (auto) │ 42 Button │ Déclenche action (nouveau) │ — AI Summary │ Résumé AI (nouveau) │ "Ce projet..." ``` ### 2.2 Views (les vues) Une database peut avoir **N vues**, chaque vue avec : ``` ┌─────────────────────────────────────────┐ │ Vue "Kanban" │ │ ├─ type: board │ │ ├─ group_by: "Status" │ ← Groupement │ ├─ filter: {Status ≠ "Archivé"} │ ← Filtres │ ├─ sort: [Due Date ASC, Priority DESC] │ ← Tris │ ├─ visible_properties: [Name, Assignee, Due Date] │ │ ├─ card_size: medium │ │ └─ cover_image_property: "Files" │ └─────────────────────────────────────────┘ ``` **6 types de vues :** | Vue | Usage | Particularité | |-----|-------|---------------| | **Table** | Liste classique | Colonnes = propriétés | | **Board** | Kanban | Groupé par propriété Select/Status | | **Timeline** | Gantt | Axe X = propriété Date | | **Calendar** | Calendrier | Groupé par propriété Date (jour/semaine/mois) | | **Gallery** | Cartes visuelles | Cover image + preview | | **List** | Liste compacte | Comme table mais 1 colonne + preview | **Filtres avancés :** ``` Filtre 1 : Status IS "En cours" → filtre simple Filtre 2 : Due Date IS BEFORE "today" → filtre date Filtre 3 : Priority CONTAINS "P1" → filtre multi-select Groupe : AND / OR entre les filtres Sous-groupe : (FiltreA OR FiltreB) AND FiltreC ``` ### 2.3 Relations et Rollups (les super-pouvoirs) C'est LE mécanisme qui rend Notion puissant : ``` ┌─────────────────────┐ ┌──────────────────────┐ │ DATABASE A │ │ DATABASE B │ │ « Projets » │ │ « Tâches » │ │ │ │ │ │ ┌─────────────┐ │ relation│ ┌──────────────┐ │ │ │ Nom │ │◄───────►│ │ Titre │ │ │ │ Description │ │ │ │ Priorité │ │ │ │ Statut │ │ │ │ Projet ──────┼──┐ │ │ └─────────────┘ │ │ │ Assignee │ │ │ │ │ │ └──────────────┘ │ │ │ │ │ │ │ │ ROLLUP « Nb tâches»│◄────────┼─── count(Tâches) │ │ │ ROLLUP « Avancement│◄────────┼─── avg(%Complété) │ │ │ ROLLUP « Prochaine │◄────────┼─── min(Due Date) │ │ └─────────────────────┘ └───────────────────┘──┘ ``` **Relation** = pointeur bidirectionnel entre pages de 2 databases différentes **Rollup** = agrégation (COUNT, SUM, AVG, MIN, MAX, RANGE, UNIQUE) sur une propriété de la database liée --- ## 3. Les Tasks Notion : un cas spécial de database ### 3.1 Architecture des tâches Contrairement à ce qu'on pourrait penser, **Notion n'a pas de module « Tasks » séparé**. ``` Les tâches sont simplement des PAGES dans une DATABASE qui possède des propriétés spécifiques de type tâche. ``` Voici comment Notion les distingue : ``` ┌─────────────────────────────────────────────────────────────┐ │ DATABASE « Tasks » (ou n'importe quelle DB) │ │ │ │ Propriétés « tâche » │ Propriétés « standard » │ │ ────────────────────────── │ ────────────────────────── │ │ ✅ Sub-item (parent) │ • Titre (obligatoire) │ │ ✅ Dependencies (bloque) │ • Status (Select) │ │ ✅ Due Date │ • Priority (Select) │ │ ✅ Assignee │ • Tags (Multi-select) │ │ ✅ Reminder │ • ... │ │ │ │ La magie : ces propriétés SONT la database. │ │ Pas de modèle « Task » séparé. │ └─────────────────────────────────────────────────────────────┘ ``` ### 3.2 Sub-items (sous-tâches) Les sous-tâches utilisent une **relation auto-référencée** : ``` ┌──────────────────────────────────────┐ │ Table « tasks » (dans la DB) │ │ │ │ id │ title │ parent_id │ │ ───┼────────────────┼───────────────│ │ 1 │ Refonte UI │ NULL │ ← Tâche parent │ 2 │ Nouveau header │ 1 │ ← Sous-tâche de 1 │ 3 │ Dark mode │ 1 │ ← Sous-tâche de 1 │ 4 │ Tests header │ 2 │ ← Sous-sous-tâche de 2 │ 5 │ Déploiement │ NULL │ ← Autre tâche parent │ │ │ Propriétés spéciales : │ │ ├─ « Sub-item » = relation vers │ │ │ la même database │ │ └─ « Parent item » = l'inverse │ │ (auto-créé par Notion) │ └──────────────────────────────────────┘ ``` **Comment ça fonctionne techniquement :** 1. L'utilisateur active « Sub-items » dans une database 2. Notion crée automatiquement 2 propriétés de type **Relation** pointant vers la même database : - `Sub-item` (relation → même DB) - `Parent item` (relation inverse, auto-générée) 3. Dans l'interface Kanban, les sous-tâches apparaissent imbriquées sous le parent 4. La récursion est illimitée (tâche → sous-tâche → sous-sous-tâche → ...) ### 3.3 Dependencies (dépendances) Les dépendances bloquantes sont une **seconde relation auto-référencée** : ``` ┌──────────────────────────────────────┐ │ Relation « Bloque » │ │ │ │ Tâche A ──bloque──► Tâche B │ │ Tâche B ──bloquée par──► Tâche A │ │ │ │ Règles métier : │ │ ├─ B ne peut pas être « Done » │ │ │ tant que A n'est pas « Done » │ │ ├─ Si A est déplacée, B suit │ │ │ (optionnel, selon config) │ │ └─ Timeline affiche les flèches │ │ A ────────► B │ └──────────────────────────────────────┘ ``` ### 3.4 La vue « My Tasks » (vue agrégée) La killer feature de Notion : un dashboard qui regroupe TOUTES les tâches, peu importe leur database d'origine. ``` ┌──────────────────────────────────────────────────┐ │ MY TASKS │ │ ┌───────────────────────────────────────────┐ │ │ │ Projet Alpha ┌──────────────────────┐│ │ │ │ ├─ Design maquette│ Assignee: Bruno ││ │ │ │ └─ Tests UI │ Due: 15 juillet ││ │ │ │ └──────────────────────┘│ │ │ │ Projet Beta │ │ │ │ ├─ API endpoint │ │ │ │ └─ Documentation │ │ │ │ Daily Tasks │ │ │ │ └─ Réunion standup │ │ │ └───────────────────────────────────────────┘ │ │ │ │ Agrège TOUTES les databases ayant : │ │ ├─ Une propriété « Person » = current user │ │ └─ Un Status ≠ « Done » │ └──────────────────────────────────────────────────┘ ``` **Fonctionnement interne :** 1. Notion scanne TOUTES les databases du workspace 2. Filtre les pages où `Assignee = current_user` ET `Status ≠ Done/Completed` 3. Regroupe par database d'origine (ou par projet parent) 4. Affiche avec tri par `Due Date` ASC 5. Cette vue est **read-only logique** (pas une vraie database) --- ## 4. Comment Notion connecte Database et Tasks ### 4.1 Le diagramme de dépendance conceptuel ``` DATABASE (concept racine) │ ┌──────────────┼──────────────┐ ▼ ▼ ▼ COLLECTION PAGES (rows) COLLECTION_VIEWS (schéma) │ (présentations) │ │ │ │ ├─ Title ├─ Table ├─ Text ├─ Properties ├─ Board ├─ Select │ (valeurs ├─ Calendar ├─ Date │ par page) ├─ Timeline ├─ Person │ ├─ Gallery ├─ Relation ────┼──► SELF └─ List │ └── Sub-items │ ┌── Parent │ └── Deps │ └── Blocked by ├─ Rollup ←──────┼──┘ (count, sum, avg via relation) ├─ Formula │ └─ ... │ │ ┌──────┴──────┐ │ TASKS │ (surcouche métier) │ │ │ Ne sont PAS │ │ un modèle │ │ séparé — │ │ ce sont des │ │ PAGES avec │ │ propriétés │ │ spécifiques │ └─────────────┘ ``` ### 4.2 Les invariants du modèle 1. **Une database = 1 Collection** (schéma de propriétés) 2. **1 Collection = N CollectionViews** (vues) 3. **1 CollectionView = 1 type** (table | board | calendar | timeline | gallery | list) 4. **Les pages d'une database sont des blocs** (comme toute page Notion) 5. **Une propriété « Relation » pointe vers une autre database** (pas une table SQL) 6. **Une propriété « Rollup » agrège via une Relation existante** 7. **Les sub-items sont une Relation auto-référencée** 8. **Les dépendances sont une Relation auto-référencée avec contrainte métier** --- ## 5. Implémentation dans FlowDeck : plan pas à pas ### 5.1 Phase 1 — Le concept de Database (semaine 1) **Objectif :** Abstraire le concept de « board Gitea » en « database » réutilisable. #### Nouvelle table : `collections` ```sql CREATE TABLE collections ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, description TEXT DEFAULT '', icon TEXT DEFAULT '📋', -- Peut être liée à un projet Gitea (optionnel) gitea_owner TEXT, gitea_repo TEXT, -- Schéma de propriétés en JSON (flexible, évolutif) schema_json TEXT NOT NULL DEFAULT '[]', -- Format: [{"name":"Status","type":"select","options":["Todo","Done"],"position":0}, ...] created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` #### Nouvelle table : `collection_pages` (remplace `cards`) ```sql CREATE TABLE collection_pages ( id INTEGER PRIMARY KEY AUTOINCREMENT, collection_id INTEGER NOT NULL REFERENCES collections(id) ON DELETE CASCADE, -- Peut être liée à une issue Gitea gitea_issue_id INTEGER, gitea_issue_number INTEGER, -- Titre de la page (obligatoire) title TEXT NOT NULL DEFAULT '', -- Position dans la collection position INTEGER NOT NULL DEFAULT 0, -- Sub-items (auto-référence) parent_id INTEGER REFERENCES collection_pages(id), -- Valeurs des propriétés en JSON property_values_json TEXT NOT NULL DEFAULT '{}', -- Format: {"Status":"Done","Priority":"P1","DueDate":"2026-07-15"} -- Métadonnées temporelles created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_cp_collection ON collection_pages(collection_id, position); CREATE INDEX idx_cp_parent ON collection_pages(parent_id); CREATE INDEX idx_cp_gitea ON collection_pages(gitea_issue_id); ``` #### Nouvelle table : `collection_views` ```sql CREATE TABLE collection_views ( id INTEGER PRIMARY KEY AUTOINCREMENT, collection_id INTEGER NOT NULL REFERENCES collections(id) ON DELETE CASCADE, name TEXT NOT NULL DEFAULT 'Default View', view_type TEXT NOT NULL DEFAULT 'table', -- table, board, calendar, timeline, gallery, list -- Configuration JSON config_json TEXT NOT NULL DEFAULT '{}', -- Format: -- { -- "group_by": "Status", ← board/calendar uniquement -- "filters": [ ← tous types -- {"property":"Status","operator":"is_not","value":"Done"}, -- {"property":"DueDate","operator":"is_before","value":"today"} -- ], -- "filter_conjunction": "and", ← "and" | "or" -- "sorts": [ ← tous types -- {"property":"Priority","direction":"asc"}, -- {"property":"DueDate","direction":"desc"} -- ], -- "visible_properties": ["Title","Status","Assignee","DueDate"], -- "card_size": "medium", ← board/gallery -- "cover_property": "Files", ← board/gallery -- "date_property": "DueDate", ← calendar/timeline -- "date_range_property": "EndDate" ← timeline (optionnel) -- } position INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); ``` ### 5.2 Phase 2 — Le système de propriétés (semaine 1-2) #### Nouvelle table : `collection_properties` (remplace `project_properties`) ```sql CREATE TABLE collection_properties ( id INTEGER PRIMARY KEY AUTOINCREMENT, collection_id INTEGER NOT NULL REFERENCES collections(id) ON DELETE CASCADE, name TEXT NOT NULL, prop_type TEXT NOT NULL DEFAULT 'text', -- Types: title, text, number, select, multi_select, status, -- date, person, checkbox, url, email, phone, -- relation, rollup, formula, -- created_time, created_by, last_edited_time, last_edited_by -- Options pour select/multi_select/status options_json TEXT DEFAULT '[]', -- Format: [{"name":"Todo","color":"gray"},{"name":"Done","color":"green"}] -- Pour les relations related_collection_id INTEGER REFERENCES collections(id), reverse_name TEXT, -- nom de la relation inverse (auto-généré) -- Pour les rollups relation_property_id INTEGER REFERENCES collection_properties(id), target_property_id INTEGER REFERENCES collection_properties(id), rollup_function TEXT, -- count, sum, avg, min, max, range, unique -- Pour les formules formula_expression TEXT, -- Position dans la liste des propriétés position INTEGER NOT NULL DEFAULT 0, required BOOLEAN NOT NULL DEFAULT 0, visible_in_views BOOLEAN NOT NULL DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(collection_id, name) ); ``` **Mapping des types Notion → FlowDeck :** ```python PROPERTY_TYPES = { "title": { "storage": "string", "validation": "max 2000 chars", "indexed": True, "unique_per_collection": False, }, "text": { "storage": "string", "validation": "any text", }, "number": { "storage": "float", "validation": "numeric", "format_options": ["number", "percent", "currency", "dollar", "euro", "pound", "yen"], }, "select": { "storage": "string (from options_json)", "validation": "must match one option", }, "multi_select": { "storage": "JSON array of strings", "validation": "each must match option", }, "status": { "storage": "string (from options_json) + color", "validation": "must match one option", "colors": ["gray","brown","orange","yellow","green","blue","purple","pink","red"], }, "date": { "storage": "ISO 8601 string or range", "validation": "valid datetime", "include_time": True, # configurable }, "person": { "storage": "JSON array of {id, login, avatar_url}", "validation": "must be workspace user", }, "checkbox": { "storage": "boolean", "validation": "true/false", }, "relation": { "storage": "JSON array of page IDs", "validation": "must be existing page in related_collection", "bidirectional": True, }, "rollup": { "storage": "computed — not stored directly", "functions": ["count","count_values","empty","not_empty", "sum","average","median","min","max","range"], "depends_on": ["relation_property_id","target_property_id"], }, "formula": { "storage": "computed — not stored directly", "expression": "JavaScript-like DSL", "functions_available": [ "prop()", "now()", "today()", "if()", "concat()", "round()", "dateAdd()", "dateSubtract()", "formatDate()", "toNumber()", "contains()", "length()", "replace()", "replaceAll()", ], }, } ``` ### 5.3 Phase 3 — Sub-items et Dépendances (semaine 2) #### Implémentation des sub-items (relation auto-référencée) ```python # Dans le schema d'une database, activer Sub-items crée automatiquement : def enable_sub_items(collection_id: int): """Active les sub-items sur une collection.""" # 1. Créer la propriété relation « Parent item » (si pas déjà) parent_prop = create_property( collection_id=collection_id, name="Parent item", prop_type="relation", related_collection_id=collection_id, # AUTO-RÉFÉRENCE reverse_name="Sub-item", # relation inverse visible_in_views=False, # cachée dans les vues ) # 2. Marquer la collection comme ayant des sub-items mark_collection_feature(collection_id, "has_sub_items", True) # 3. Mettre à jour la vue board pour afficher l'imbrication # Le rendu Kanban doit maintenant supporter : # - Groupe parent → sous-tâches indentées # - Bouton « + Add sub-item » sous chaque carte # - État du parent = agrégation des enfants ``` #### Rendu des sub-items dans le board ```html
{{ page.title }}
{% for sub in page.sub_items %}
├─ {{ sub.title }} {{ sub.properties.Status }}
{% endfor %}
``` #### Implémentation des dépendances ```python def enable_dependencies(collection_id: int): """Active les dépendances bloquantes.""" # Création de 2 propriétés relation blocks = create_property( collection_id=collection_id, name="Blocks", prop_type="relation", related_collection_id=collection_id, reverse_name="Blocked by", ) # Contrainte métier : au niveau applicatif # (pas de contrainte SQL pour garder la flexibilité) def check_dependency_constraint(page_id: int, new_status: str): """Vérifie qu'une page peut passer à 'Done' seulement si toutes les pages qu'elle bloque sont Done.""" blocked_pages = get_blocked_pages(page_id) if new_status == "Done" and blocked_pages: not_done = [p for p in blocked_pages if p.status not in ("Done", "Cancelled")] if not_done: raise DependencyError( f"Cannot mark as Done: still blocking {len(not_done)} tasks" ) ``` ### 5.4 Phase 4 — « My Tasks » (vue agrégée) (semaine 2-3) ```python # GET /my-tasks async def my_tasks(request: Request): """Dashboard personnel : toutes les tâches assignées à l'utilisateur.""" user = get_current_user(request) # 1. Scanner TOUTES les collections collections = db.fetch_all("SELECT * FROM collections") my_tasks = [] for col in collections: schema = json.loads(col.schema_json) # 2. Vérifier si la collection a une propriété Person person_props = [p for p in schema if p["type"] == "person"] if not person_props: continue # 3. Récupérer les pages assignées à l'utilisateur pages = db.fetch_all(""" SELECT cp.*, cpv.value as property_values FROM collection_pages cp JOIN collection_properties cprop ON cprop.collection_id = cp.collection_id AND cprop.prop_type = 'person' WHERE cp.collection_id = ? AND cp.property_values_json LIKE ? """, (col.id, f'%"{user.login}"%')) my_tasks.extend([ {"collection": col, "page": p, "schema": schema} for p in pages ]) # 4. Grouper par collection grouped = {} for task in my_tasks: col_name = task["collection"].name if col_name not in grouped: grouped[col_name] = [] grouped[col_name].append(task) # 5. Trier par Due Date for tasks in grouped.values(): tasks.sort(key=lambda t: t["page"].get_property("DueDate") or "9999") return render_template("my_tasks.html", tasks=grouped) ``` ### 5.5 Phase 5 — Rétrocompatibilité avec Gitea **Stratégie de migration progressive :** ```python # Phase 5a : Wrapper — les boards Gitea existants # deviennent des collections avec une couche de compatibilité class GiteaBoardCompat: """Adaptateur : ancien board Gitea → nouvelle Collection.""" @staticmethod def from_board(board_row): """Convertit un board existant en collection.""" return { "collection_id": f"gitea:{board_row.id}", "name": f"{board_row.project_owner}/{board_row.project_name}", "gitea_owner": board_row.project_owner, "gitea_repo": board_row.project_name, "schema": [ {"name": "Title", "type": "title"}, {"name": "Status", "type": "select", "options": json.loads(board_row.columns_json)}, {"name": "Priority", "type": "select", "options": ["P1","P2","P3","P4"]}, ], "is_gitea_linked": True, } # Phase 5b : Nouvelle route hybride # GET /board/{owner}/{repo} → détecte si c'est un board Gitea legacy # ou une collection pure, et route vers le bon renderer ``` ### 5.6 Résumé du plan d'implémentation ``` Semaine 1 Semaine 2 Semaine 3 ──────── ───────── ───────── ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ Database │ │ Properties │ │ My Tasks │ │ Concept │ ──► │ Avancées │ ──► │ Dashboard │ │ │ │ │ │ │ │ • collections│ │ • Relations │ │ • Vue agrégée│ │ • pages │ │ • Rollups │ │ • Filtres │ │ • views │ │ • Formules │ │ globaux │ │ • filters │ │ • Auto-prop │ │ • Calendrier │ │ • sorts │ │ computed │ │ intégré │ │ │ │ │ │ │ │ Sub-items │ │ Dependencies │ │ UI polish │ │ (parent_id) │ ──► │ + contraintes│ ──► │ + tests │ └──────────────┘ └──────────────┘ └──────────────┘ ``` --- ## 6. Références - [Notion API — Property Object](https://developers.notion.com/reference/property-object) - [Notion Help — Intro to Databases](https://www.notion.com/help/intro-to-databases) - [Notion Help — Tasks & Dependencies](https://www.notion.com/help/tasks-and-dependencies) - [Notion Help — Relations & Rollups](https://www.notion.com/help/relations-and-rollups) - [react-notion-x — Block types reference](https://github.com/NotionX/react-notion-x) - [Notion Data Sources update (2025)](https://www.notionapps.com/blog/notion-data-sources-update-2025/) ### Différences clés FlowDeck vs Notion | Concept | Notion | FlowDeck (cible) | |---------|--------|------------------| | **Database** | Collection + Pages + Views | `collections` + `collection_pages` + `collection_views` | | **Schéma** | Properties sur la Collection | `collection_properties` au niveau collection | | **Valeurs** | Dans chaque page (bloc) | `property_values_json` dans chaque `collection_page` | | **Sub-items** | Relation auto-référencée | `parent_id` auto-référence dans `collection_pages` | | **Dependencies** | Relation auto-référencée + contrainte | Idem + check applicatif au changement de statut | | **My Tasks** | Scan cross-database + filtre | Route `/my-tasks` + JOIN sur `collection_pages` | | **Gitea sync** | N/A (pas de Gitea) | Les pages peuvent être liées à `gitea_issue_id` | | **Stockage** | Cloud distribué (PostgreSQL + cache) | SQLite local (1 fichier) |