refactor: userpreferences are now in the DB
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { KanbanBoardContainer } from '@/components/kanban/BoardContainer';
|
||||
import { Header } from '@/components/ui/Header';
|
||||
import { TasksProvider, useTasksContext } from '@/contexts/TasksContext';
|
||||
import { Task, Tag, TaskStats } from '@/lib/types';
|
||||
import { UserPreferencesProvider, useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { Task, Tag, TaskStats, UserPreferences } from '@/lib/types';
|
||||
import { CreateTaskData } from '@/clients/tasks-client';
|
||||
import { userPreferencesService } from '@/services/user-preferences';
|
||||
import { CreateTaskForm } from '@/components/forms/CreateTaskForm';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
|
||||
@@ -14,32 +14,26 @@ interface HomePageClientProps {
|
||||
initialTasks: Task[];
|
||||
initialStats: TaskStats;
|
||||
initialTags: (Tag & { usage: number })[];
|
||||
initialPreferences: UserPreferences;
|
||||
}
|
||||
|
||||
|
||||
function HomePageContent() {
|
||||
const { stats, syncing, createTask } = useTasksContext();
|
||||
const [showFilters, setShowFilters] = useState(true);
|
||||
const [showObjectives, setShowObjectives] = useState(true);
|
||||
const { preferences, updateViewPreferences } = useUserPreferences();
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
|
||||
// Charger les préférences depuis le service
|
||||
useEffect(() => {
|
||||
const viewPreferences = userPreferencesService.getViewPreferences();
|
||||
setShowFilters(viewPreferences.showFilters);
|
||||
setShowObjectives(viewPreferences.showObjectives);
|
||||
}, []);
|
||||
// Extraire les préférences du context
|
||||
const showFilters = preferences.viewPreferences.showFilters;
|
||||
const showObjectives = preferences.viewPreferences.showObjectives;
|
||||
|
||||
// Sauvegarder les préférences via le service
|
||||
// Handlers pour les toggles (sauvegarde automatique via le context)
|
||||
const handleToggleFilters = () => {
|
||||
const newValue = !showFilters;
|
||||
setShowFilters(newValue);
|
||||
userPreferencesService.updateViewPreferences({ showFilters: newValue });
|
||||
updateViewPreferences({ showFilters: !showFilters });
|
||||
};
|
||||
|
||||
const handleToggleObjectives = () => {
|
||||
const newValue = !showObjectives;
|
||||
setShowObjectives(newValue);
|
||||
userPreferencesService.updateViewPreferences({ showObjectives: newValue });
|
||||
updateViewPreferences({ showObjectives: !showObjectives });
|
||||
};
|
||||
|
||||
// Handler pour la création de tâche depuis la barre de contrôles
|
||||
@@ -127,14 +121,16 @@ function HomePageContent() {
|
||||
);
|
||||
}
|
||||
|
||||
export function HomePageClient({ initialTasks, initialStats, initialTags }: HomePageClientProps) {
|
||||
export function HomePageClient({ initialTasks, initialStats, initialTags, initialPreferences }: HomePageClientProps) {
|
||||
return (
|
||||
<TasksProvider
|
||||
initialTasks={initialTasks}
|
||||
initialStats={initialStats}
|
||||
initialTags={initialTags}
|
||||
>
|
||||
<HomePageContent />
|
||||
</TasksProvider>
|
||||
<UserPreferencesProvider initialPreferences={initialPreferences}>
|
||||
<TasksProvider
|
||||
initialTasks={initialTasks}
|
||||
initialStats={initialStats}
|
||||
initialTags={initialTags}
|
||||
>
|
||||
<HomePageContent />
|
||||
</TasksProvider>
|
||||
</UserPreferencesProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,16 +4,14 @@ import { Task, TaskStatus } from '@/lib/types';
|
||||
import { KanbanColumn } from './Column';
|
||||
import { CreateTaskData } from '@/clients/tasks-client';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useColumnVisibility } from '@/hooks/useColumnVisibility';
|
||||
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { useDragAndDrop } from '@/hooks/useDragAndDrop';
|
||||
import { getAllStatuses } from '@/lib/status-config';
|
||||
import {
|
||||
DndContext,
|
||||
DragEndEvent,
|
||||
DragOverlay,
|
||||
DragStartEvent,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
DragStartEvent
|
||||
} from '@dnd-kit/core';
|
||||
import { TaskCard } from './TaskCard';
|
||||
|
||||
@@ -30,18 +28,8 @@ interface KanbanBoardProps {
|
||||
|
||||
export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onUpdateTitle, onUpdateStatus, compactView = false, visibleStatuses }: KanbanBoardProps) {
|
||||
const [activeTask, setActiveTask] = useState<Task | null>(null);
|
||||
|
||||
// Gestion de la visibilité des colonnes (utilise les props si disponibles)
|
||||
const { getVisibleStatuses } = useColumnVisibility();
|
||||
|
||||
// Configuration des capteurs pour le drag & drop
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: {
|
||||
distance: 8, // Évite les clics accidentels
|
||||
},
|
||||
})
|
||||
);
|
||||
const { isColumnVisible } = useUserPreferences();
|
||||
const { isMounted, sensors } = useDragAndDrop();
|
||||
// Organiser les tâches par statut
|
||||
const tasksByStatus = useMemo(() => {
|
||||
const grouped = tasks.reduce((acc, task) => {
|
||||
@@ -66,7 +54,7 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
|
||||
// Filtrer les colonnes visibles
|
||||
const visibleColumns = visibleStatuses ?
|
||||
allColumns.filter(column => visibleStatuses.includes(column.id)) :
|
||||
getVisibleStatuses(allColumns);
|
||||
allColumns.filter(column => isColumnVisible(column.id));
|
||||
|
||||
|
||||
// Gestion du début du drag
|
||||
@@ -94,17 +82,10 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
|
||||
await onUpdateStatus(taskId, newStatus);
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
id="kanban-board"
|
||||
sensors={sensors}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<div className="h-full flex flex-col bg-[var(--background)]">
|
||||
{/* Espacement supérieur */}
|
||||
<div className="pt-4"></div>
|
||||
|
||||
const content = (
|
||||
<div className="h-full flex flex-col bg-[var(--background)]">
|
||||
{/* Espacement supérieur */}
|
||||
<div className="pt-4"></div>
|
||||
|
||||
{/* Board tech dark */}
|
||||
<div className="flex-1 flex gap-6 overflow-x-auto p-6">
|
||||
@@ -121,9 +102,22 @@ export function KanbanBoard({ tasks, onCreateTask, onDeleteTask, onEditTask, onU
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
</div>
|
||||
if (!isMounted) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
id="kanban-board"
|
||||
sensors={sensors}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
{content}
|
||||
|
||||
{/* Overlay pour le drag & drop */}
|
||||
<DragOverlay>
|
||||
{activeTask ? (
|
||||
|
||||
@@ -8,9 +8,9 @@ import { ObjectivesBoard } from './ObjectivesBoard';
|
||||
import { KanbanFilters } from './KanbanFilters';
|
||||
import { EditTaskForm } from '@/components/forms/EditTaskForm';
|
||||
import { useTasksContext } from '@/contexts/TasksContext';
|
||||
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { Task, TaskStatus } from '@/lib/types';
|
||||
import { UpdateTaskData, CreateTaskData } from '@/clients/tasks-client';
|
||||
import { useColumnVisibility } from '@/hooks/useColumnVisibility';
|
||||
import { getAllStatuses } from '@/lib/status-config';
|
||||
|
||||
interface KanbanBoardContainerProps {
|
||||
@@ -20,7 +20,7 @@ interface KanbanBoardContainerProps {
|
||||
|
||||
export function KanbanBoardContainer({
|
||||
showFilters = true,
|
||||
showObjectives = true
|
||||
showObjectives = true
|
||||
}: KanbanBoardContainerProps = {}) {
|
||||
const {
|
||||
filteredTasks,
|
||||
@@ -35,9 +35,10 @@ export function KanbanBoardContainer({
|
||||
tags
|
||||
} = useTasksContext();
|
||||
|
||||
const { hiddenStatuses, toggleStatusVisibility, getVisibleStatuses } = useColumnVisibility();
|
||||
const { preferences, toggleColumnVisibility, isColumnVisible } = useUserPreferences();
|
||||
|
||||
const allStatuses = getAllStatuses();
|
||||
const visibleStatuses = getVisibleStatuses(allStatuses.map(s => ({ id: s.key }))).map(s => s.id);
|
||||
const visibleStatuses = allStatuses.filter(status => isColumnVisible(status.key)).map(s => s.key);
|
||||
const [editingTask, setEditingTask] = useState<Task | null>(null);
|
||||
|
||||
const handleEditTask = (task: Task) => {
|
||||
@@ -79,8 +80,8 @@ export function KanbanBoardContainer({
|
||||
<KanbanFilters
|
||||
filters={kanbanFilters}
|
||||
onFiltersChange={setKanbanFilters}
|
||||
hiddenStatuses={hiddenStatuses}
|
||||
onToggleStatusVisibility={toggleStatusVisibility}
|
||||
hiddenStatuses={new Set(preferences.columnVisibility.hiddenStatuses)}
|
||||
onToggleStatusVisibility={toggleColumnVisibility}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { Input } from '@/components/ui/Input';
|
||||
import { useTasksContext } from '@/contexts/TasksContext';
|
||||
import { getAllPriorities, getPriorityColorHex } from '@/lib/status-config';
|
||||
import { SORT_OPTIONS } from '@/lib/sort-config';
|
||||
import { useColumnVisibility } from '@/hooks/useColumnVisibility';
|
||||
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { ColumnVisibilityToggle } from './ColumnVisibilityToggle';
|
||||
|
||||
export interface KanbanFilters {
|
||||
@@ -32,11 +32,11 @@ interface KanbanFiltersProps {
|
||||
|
||||
export function KanbanFilters({ filters, onFiltersChange, hiddenStatuses: propsHiddenStatuses, onToggleStatusVisibility }: KanbanFiltersProps) {
|
||||
const { tags: availableTags, regularTasks } = useTasksContext();
|
||||
const { hiddenStatuses: localHiddenStatuses, toggleStatusVisibility: localToggleStatusVisibility } = useColumnVisibility();
|
||||
const { preferences, toggleColumnVisibility } = useUserPreferences();
|
||||
|
||||
// Utiliser les props si disponibles, sinon utiliser l'état local
|
||||
const hiddenStatuses = propsHiddenStatuses || localHiddenStatuses;
|
||||
const toggleStatusVisibility = onToggleStatusVisibility || localToggleStatusVisibility;
|
||||
// Utiliser les props si disponibles, sinon utiliser le context
|
||||
const hiddenStatuses = propsHiddenStatuses || new Set(preferences.columnVisibility.hiddenStatuses);
|
||||
const toggleStatusVisibility = onToggleStatusVisibility || toggleColumnVisibility;
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [isSortExpanded, setIsSortExpanded] = useState(false);
|
||||
const [isSwimlaneModeExpanded, setIsSwimlaneModeExpanded] = useState(false);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useObjectivesCollapse } from '@/hooks/useObjectivesCollapse';
|
||||
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { useDragAndDrop } from '@/hooks/useDragAndDrop';
|
||||
import { Task, TaskStatus } from '@/lib/types';
|
||||
import { TaskCard } from './TaskCard';
|
||||
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
|
||||
@@ -10,10 +11,7 @@ import {
|
||||
DndContext,
|
||||
DragEndEvent,
|
||||
DragOverlay,
|
||||
DragStartEvent,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
DragStartEvent
|
||||
} from '@dnd-kit/core';
|
||||
import {
|
||||
SortableContext,
|
||||
@@ -105,18 +103,11 @@ export function ObjectivesBoard({
|
||||
compactView = false,
|
||||
pinnedTagName = "Objectifs"
|
||||
}: ObjectivesBoardProps) {
|
||||
const { isCollapsed, toggleCollapse } = useObjectivesCollapse();
|
||||
const { preferences, toggleObjectivesCollapse } = useUserPreferences();
|
||||
const isCollapsed = preferences.viewPreferences.objectivesCollapsed;
|
||||
const { isMounted, sensors } = useDragAndDrop();
|
||||
const [activeTask, setActiveTask] = useState<Task | null>(null);
|
||||
|
||||
// Configuration des sensors pour le drag & drop
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: {
|
||||
distance: 8, // Évite les clics accidentels
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
// Handlers pour le drag & drop
|
||||
const handleDragStart = (event: DragStartEvent) => {
|
||||
const task = tasks.find(t => t.id === event.active.id);
|
||||
@@ -143,20 +134,14 @@ export function ObjectivesBoard({
|
||||
return null; // Ne rien afficher s'il n'y a pas d'objectifs
|
||||
}
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
id="objectives-board"
|
||||
sensors={sensors}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<div className="bg-[var(--card)]/30 border-b border-[var(--accent)]/30">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
const content = (
|
||||
<div className="bg-[var(--card)]/30 border-b border-[var(--accent)]/30">
|
||||
<div className="container mx-auto px-6 py-4">
|
||||
<Card variant="column" className="border-[var(--accent)]/30 shadow-[var(--accent)]/10">
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<button
|
||||
onClick={toggleCollapse}
|
||||
onClick={toggleObjectivesCollapse}
|
||||
className="flex items-center gap-3 hover:bg-[var(--accent)]/20 rounded-lg p-2 -m-2 transition-colors group"
|
||||
>
|
||||
<div className="w-3 h-3 bg-[var(--accent)] rounded-full animate-pulse shadow-[var(--accent)]/50 shadow-lg"></div>
|
||||
@@ -189,7 +174,7 @@ export function ObjectivesBoard({
|
||||
|
||||
{/* Bouton collapse séparé pour mobile */}
|
||||
<button
|
||||
onClick={toggleCollapse}
|
||||
onClick={toggleObjectivesCollapse}
|
||||
className="lg:hidden p-1 hover:bg-[var(--accent)]/20 rounded transition-colors"
|
||||
aria-label={isCollapsed ? "Développer" : "Réduire"}
|
||||
>
|
||||
@@ -261,7 +246,21 @@ export function ObjectivesBoard({
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (!isMounted) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
id="objectives-board"
|
||||
sensors={sensors}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
{content}
|
||||
|
||||
{/* Overlay pour le drag & drop */}
|
||||
<DragOverlay>
|
||||
{activeTask ? (
|
||||
|
||||
@@ -5,17 +5,15 @@ import { TaskCard } from './TaskCard';
|
||||
import { QuickAddTask } from './QuickAddTask';
|
||||
import { CreateTaskData } from '@/clients/tasks-client';
|
||||
import { useState } from 'react';
|
||||
import { useColumnVisibility } from '@/hooks/useColumnVisibility';
|
||||
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
|
||||
import { useDragAndDrop } from '@/hooks/useDragAndDrop';
|
||||
import { getAllStatuses } from '@/lib/status-config';
|
||||
import {
|
||||
DndContext,
|
||||
DragEndEvent,
|
||||
DragOverlay,
|
||||
DragStartEvent,
|
||||
closestCenter,
|
||||
PointerSensor,
|
||||
useSensor,
|
||||
useSensors,
|
||||
closestCenter
|
||||
} from '@dnd-kit/core';
|
||||
import {
|
||||
SortableContext,
|
||||
@@ -143,19 +141,11 @@ export function SwimlanesBase({
|
||||
const [showQuickAdd, setShowQuickAdd] = useState<{ [key: string]: boolean }>({});
|
||||
|
||||
// Gestion de la visibilité des colonnes
|
||||
const { getVisibleStatuses } = useColumnVisibility();
|
||||
const { isColumnVisible } = useUserPreferences();
|
||||
const { isMounted, sensors } = useDragAndDrop();
|
||||
const allStatuses = getAllStatuses();
|
||||
const statusesToShow = visibleStatuses ||
|
||||
getVisibleStatuses(allStatuses.map(s => ({ id: s.key }))).map(s => s.id);
|
||||
|
||||
// Configuration des sensors pour le drag & drop
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, {
|
||||
activationConstraint: {
|
||||
distance: 8,
|
||||
},
|
||||
})
|
||||
);
|
||||
allStatuses.filter(status => isColumnVisible(status.key)).map(s => s.key);
|
||||
|
||||
// Handlers pour le drag & drop
|
||||
const handleDragStart = (event: DragStartEvent) => {
|
||||
@@ -203,16 +193,10 @@ export function SwimlanesBase({
|
||||
setShowQuickAdd(prev => ({ ...prev, [columnId]: !prev[columnId] }));
|
||||
};
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
<div className="flex flex-col h-full bg-[var(--background)]">
|
||||
{/* Espacement supérieur */}
|
||||
<div className="flex-shrink-0 py-2"></div>
|
||||
const content = (
|
||||
<div className="flex flex-col h-full bg-[var(--background)]">
|
||||
{/* Espacement supérieur */}
|
||||
<div className="flex-shrink-0 py-2"></div>
|
||||
|
||||
|
||||
{/* Headers des colonnes visibles */}
|
||||
@@ -305,7 +289,21 @@ export function SwimlanesBase({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (!isMounted) {
|
||||
return content;
|
||||
}
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
sensors={sensors}
|
||||
collisionDetection={closestCenter}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
>
|
||||
{content}
|
||||
|
||||
{/* Drag overlay */}
|
||||
<DragOverlay>
|
||||
{activeTask && (
|
||||
@@ -315,7 +313,6 @@ export function SwimlanesBase({
|
||||
/>
|
||||
)}
|
||||
</DragOverlay>
|
||||
|
||||
</DndContext>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user