diff --git a/docs/NOTION_DATABASE_TASKS_GUIDE.md b/docs/NOTION_DATABASE_TASKS_GUIDE.md new file mode 100644 index 0000000..3404f1b --- /dev/null +++ b/docs/NOTION_DATABASE_TASKS_GUIDE.md @@ -0,0 +1,822 @@ +# 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 + +