feat: add pinned tag functionality and UI enhancements

- Introduced `isPinned` property to the `Tag` model for marking main objectives.
- Updated `TagForm` to include a checkbox for setting tags as pinned, enhancing user interaction.
- Enhanced `KanbanBoardContainer` to display pinned tasks in a dedicated `ObjectivesBoard`, improving task visibility.
- Modified `KanbanFilters` to support filtering by pinned tags, streamlining task management.
- Adjusted `TasksContext` to separate pinned tasks from regular tasks for better organization.
This commit is contained in:
Julien Froidefond
2025-09-14 22:23:55 +02:00
parent c4f68bb00c
commit a589c0cc2f
10 changed files with 309 additions and 27 deletions

View File

@@ -6,10 +6,11 @@ import { tagsService } from '@/services/tags';
*/
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
{ params }: { params: Promise<{ id: string }> }
) {
try {
const tag = await tagsService.getTagById(params.id);
const { id } = await params;
const tag = await tagsService.getTagById(id);
if (!tag) {
return NextResponse.json(
@@ -40,11 +41,12 @@ export async function GET(
*/
export async function PATCH(
request: NextRequest,
{ params }: { params: { id: string } }
{ params }: { params: Promise<{ id: string }> }
) {
try {
const { id } = await params;
const body = await request.json();
const { name, color } = body;
const { name, color, isPinned } = body;
// Validation
if (name !== undefined && (typeof name !== 'string' || !name.trim())) {
@@ -61,7 +63,7 @@ export async function PATCH(
);
}
const tag = await tagsService.updateTag(params.id, { name, color });
const tag = await tagsService.updateTag(id, { name, color, isPinned });
if (!tag) {
return NextResponse.json(
@@ -107,10 +109,11 @@ export async function PATCH(
*/
export async function DELETE(
request: NextRequest,
{ params }: { params: { id: string } }
{ params }: { params: Promise<{ id: string }> }
) {
try {
await tagsService.deleteTag(params.id);
const { id } = await params;
await tagsService.deleteTag(id);
return NextResponse.json({
message: 'Tag supprimé avec succès'

View File

@@ -29,6 +29,7 @@ interface TasksContextType {
kanbanFilters: KanbanFilters;
setKanbanFilters: (filters: KanbanFilters) => void;
filteredTasks: Task[];
pinnedTasks: Task[]; // Tâches avec tags épinglés (objectifs)
// Tags
tags: Tag[];
tagsLoading: boolean;
@@ -54,9 +55,28 @@ export function TasksProvider({ children, initialTasks, initialStats }: TasksPro
// État des filtres Kanban
const [kanbanFilters, setKanbanFilters] = useState<KanbanFilters>({});
// Filtrage des tâches
// Séparer les tâches épinglées (objectifs) des autres
const { pinnedTasks, regularTasks } = useMemo(() => {
const pinnedTagNames = tags.filter(tag => tag.isPinned).map(tag => tag.name);
const pinned: Task[] = [];
const regular: Task[] = [];
tasksState.tasks.forEach(task => {
const hasPinnedTag = task.tags?.some(tagName => pinnedTagNames.includes(tagName));
if (hasPinnedTag) {
pinned.push(task);
} else {
regular.push(task);
}
});
return { pinnedTasks: pinned, regularTasks: regular };
}, [tasksState.tasks, tags]);
// Filtrage des tâches régulières (pas les épinglées)
const filteredTasks = useMemo(() => {
let filtered = tasksState.tasks;
let filtered = regularTasks;
// Filtre par recherche
if (kanbanFilters.search) {
@@ -85,7 +105,7 @@ export function TasksProvider({ children, initialTasks, initialStats }: TasksPro
}
return filtered;
}, [tasksState.tasks, kanbanFilters]);
}, [regularTasks, kanbanFilters]);
const contextValue: TasksContextType = {
...tasksState,
@@ -94,7 +114,8 @@ export function TasksProvider({ children, initialTasks, initialStats }: TasksPro
tagsError,
kanbanFilters,
setKanbanFilters,
filteredTasks
filteredTasks,
pinnedTasks
};
return (