diff --git a/TODO.md b/TODO.md index 8787eaf..68c7674 100644 --- a/TODO.md +++ b/TODO.md @@ -124,43 +124,6 @@ - **Performance** : Index sur `userId`, pagination pour gros volumes - **Migration** : Script de migration des données existantes -### 📱 Interface mobile adaptée (PROJET MAJEUR) - -#### **Problème actuel** -- Kanban non adapté aux écrans tactiles petits -- Drag & drop difficile sur mobile -- Interface desktop-first - -#### **Solution : Interface mobile dédiée** -- [ ] **Phase 1: Détection et responsive** - - [ ] Détection mobile/desktop (useMediaQuery) - - [ ] Composant de switch automatique d'interface - - [ ] Breakpoints adaptés pour tablettes - -- [ ] **Phase 2: Interface mobile pour les tâches** - - [ ] **Vue liste simple** : Kanban simple OK, mais swimlane KO. Ajouter une autre interface plus simple pour mobile en plus du Kanban Simple - - [ ] Liste verticale avec statuts en badges - - [ ] Actions par swipe (marquer terminé, changer statut) - - [ ] Filtres simplifiés (dropdown au lieu de sidebar) - - [ ] **Actions tactiles** - - [ ] Tap pour voir détails - - [ ] Long press pour menu contextuel - - [ ] Swipe left/right pour actions rapides - - [ ] **Navigation mobile** - - [ ] Bottom navigation bar - - [ ] Sections : Tâches, Daily, Jira, Profil - -- [ ] **Phase 3: Daily mobile optimisé** - - [ ] Checkboxes plus grandes (touch-friendly) - - [ ] Ajout rapide par bouton flottant - - [ ] On ne voit que aujourd'hui et au swipe on va en avant en arrière par jour - -#### **Considérations UX mobile** -- **Simplicité** : Moins d'options visibles, plus de navigation -- **Tactile** : Boutons plus grands, zones de touch optimisées -- **Performance** : Lazy loading, virtualisation pour longues listes -- **Offline** : Cache local pour usage sans réseau (PWA) - --- *Focus sur l'expérience utilisateur et le design moderne. App standalone prête pour évoluer vers une plateforme d'intégration complète.* diff --git a/src/app/daily/DailyPageClient.tsx b/src/app/daily/DailyPageClient.tsx index 47c2228..ca7ee90 100644 --- a/src/app/daily/DailyPageClient.tsx +++ b/src/app/daily/DailyPageClient.tsx @@ -212,52 +212,84 @@ export function DailyPageClient({ {/* Contenu principal */}
-
- {/* Calendrier - toujours visible */} -
- -
- - {/* Sections daily */} + {/* Layout Mobile uniquement - Section Aujourd'hui en premier */} +
{dailyView && ( -
- {/* Section Hier */} - - - {/* Section Aujourd'hui */} - +
+ {/* Section Aujourd'hui - Mobile First */} + + + {/* Calendrier en bas sur mobile */} +
)}
+ {/* Layout Tablette/Desktop - Layout original */} +
+
+ {/* Calendrier - Desktop */} +
+ +
+ + {/* Sections daily - Desktop */} + {dailyView && ( +
+ {/* Section Hier - Desktop seulement */} + + + {/* Section Aujourd'hui - Desktop */} + +
+ )} +
+
+ {/* Section des tâches en attente */} - {/* Barre de contrôles de visibilité */} -
-
-
-
-
- + {/* Barre de contrĂ´les responsive */} + {isMobile ? ( + setIsCreateModalOpen(true)} + /> + ) : ( + /* Barre de contrĂ´les desktop */ +
+
+
+
+
+ + + +
+ +
+ {/* Raccourcis Jira */} + + + + + + + {/* Font Size Toggle */} + +
-
- -
- {/* Raccourcis Jira */} - - - - - - - {/* Font Size Toggle */} - -
- -
- {/* Bouton d'ajout de tâche */} - + {/* Bouton d'ajout de tâche */} + +
-
+ )}
-
onToggle(checkbox.id)} disabled={saving} - className="w-3.5 h-3.5 rounded border border-[var(--border)] text-[var(--primary)] focus:ring-[var(--primary)]/20 focus:ring-1" + className="w-4 h-4 md:w-3.5 md:h-3.5 rounded border border-[var(--border)] text-[var(--primary)] focus:ring-[var(--primary)]/20 focus:ring-1" /> {/* Contenu principal */} @@ -102,7 +102,7 @@ export function DailyCheckboxItem({
{/* Texte cliquable pour édition inline */} - + {/* Header */}
diff --git a/src/components/kanban/BoardRouter.tsx b/src/components/kanban/BoardRouter.tsx index 798c885..ddd1898 100644 --- a/src/components/kanban/BoardRouter.tsx +++ b/src/components/kanban/BoardRouter.tsx @@ -6,6 +6,7 @@ import { PrioritySwimlanesBoard } from './PrioritySwimlanesBoard'; import { Task, TaskStatus } from '@/lib/types'; import { CreateTaskData } from '@/clients/tasks-client'; import { KanbanFilters } from './KanbanFilters'; +import { useIsMobile } from '@/hooks/useIsMobile'; interface BoardRouterProps { tasks: Task[]; @@ -26,8 +27,13 @@ export function BoardRouter({ visibleStatuses, loading }: BoardRouterProps) { + const isMobile = useIsMobile(768); // Tailwind md breakpoint + + // Sur mobile, toujours utiliser le board standard pour une meilleure UX + const shouldUseSwimlanes = kanbanFilters.swimlanesByTags && !isMobile; + // Logique de routage des boards selon les filtres - if (kanbanFilters.swimlanesByTags) { + if (shouldUseSwimlanes) { if (kanbanFilters.swimlanesMode === 'priority') { return ( (null); const swimlaneModeDropdownRef = useRef(null); const sortButtonRef = useRef(null); @@ -262,52 +264,54 @@ export function KanbanFilters({ filters, onFiltersChange, hiddenStatuses: propsH />
- {/* Menu swimlanes */} -
- - - {/* Bouton pour changer le mode des swimlanes */} - {filters.swimlanesByTags && ( + {/* Menu swimlanes - masqué sur mobile */} + {!isMobile && ( +
- )} -
+ + {/* Bouton pour changer le mode des swimlanes */} + {filters.swimlanesByTags && ( + + )} +
+ )} {/* Bouton de tri */} @@ -600,8 +604,8 @@ export function KanbanFilters({ filters, onFiltersChange, hiddenStatuses: propsH document.body )} - {/* Dropdown des modes swimlanes rendu via portail pour éviter les problèmes de z-index */} - {isSwimlaneModeExpanded && typeof window !== 'undefined' && createPortal( + {/* Dropdown des modes swimlanes rendu via portail pour éviter les problèmes de z-index - masqué sur mobile */} + {!isMobile && isSwimlaneModeExpanded && typeof window !== 'undefined' && createPortal(
void; + onToggleObjectives: () => void; + onToggleCompactView: () => void; + onFiltersChange: (filters: KanbanFilters) => void; + onCreateTask: () => void; +} + +export function MobileControls({ + showFilters, + showObjectives, + compactView, + activeFiltersCount, + kanbanFilters, + onToggleFilters, + onToggleObjectives, + onToggleCompactView, + onFiltersChange, + onCreateTask, +}: MobileControlsProps) { + const [isMenuOpen, setIsMenuOpen] = useState(false); + + return ( +
+
+ {/* Barre principale mobile */} +
+ {/* Bouton menu hamburger */} + + + {/* Bouton d'ajout de tâche */} + +
+ + {/* Menu déroulant */} + {isMenuOpen && ( +
+ {/* Section Affichage */} +
+

+ Affichage +

+
+ + + +
+
+ + {/* Section Paramètres */} +
+

+ Paramètres +

+
+ + +
+ Taille police + +
+
+
+ + {/* Section Jira */} +
+

+ Raccourcis Jira +

+ +
+
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/src/hooks/useIsMobile.ts b/src/hooks/useIsMobile.ts new file mode 100644 index 0000000..8f24c7d --- /dev/null +++ b/src/hooks/useIsMobile.ts @@ -0,0 +1,29 @@ +'use client'; + +import { useState, useEffect } from 'react'; + +/** + * Hook pour détecter si l'utilisateur est sur mobile + * Utilise un breakpoint à 640px (sm en Tailwind) + */ +export function useIsMobile(breakpoint: number = 640): boolean { + const [isMobile, setIsMobile] = useState(false); + + useEffect(() => { + const checkIsMobile = () => { + setIsMobile(window.innerWidth < breakpoint); + }; + + // Check initial + checkIsMobile(); + + // Écouter les changements de taille + window.addEventListener('resize', checkIsMobile); + + return () => { + window.removeEventListener('resize', checkIsMobile); + }; + }, [breakpoint]); + + return isMobile; +} \ No newline at end of file