feat: enhance Kanban functionality and update TODO.md

- Completed the creation and validation forms for tasks in the Kanban board, improving task management capabilities.
- Integrated new task creation and deletion functionalities in the `KanbanBoard` and `KanbanColumn` components.
- Added quick task addition feature in `Column` component for better user experience.
- Updated `TaskCard` to support task deletion with a new button.
- Marked several tasks as completed in `TODO.md` to reflect the progress on Kanban features.
- Updated TypeScript types to include 'manual' as a new task source.
This commit is contained in:
Julien Froidefond
2025-09-14 08:48:39 +02:00
parent 79f8035d18
commit 0b7e0edb2f
14 changed files with 1056 additions and 37 deletions

View File

@@ -0,0 +1,72 @@
/**
* Client HTTP de base pour toutes les requêtes API
*/
export class HttpClient {
private baseUrl: string;
constructor(baseUrl: string = '') {
this.baseUrl = baseUrl;
}
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const url = `${this.baseUrl}${endpoint}`;
const config: RequestInit = {
headers: {
'Content-Type': 'application/json',
...options.headers,
},
...options,
};
try {
const response = await fetch(url, config);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error(`HTTP Request failed: ${url}`, error);
throw error;
}
}
async get<T>(endpoint: string, params?: Record<string, string>): Promise<T> {
const url = params
? `${endpoint}?${new URLSearchParams(params)}`
: endpoint;
return this.request<T>(url, { method: 'GET' });
}
async post<T>(endpoint: string, data?: unknown): Promise<T> {
return this.request<T>(endpoint, {
method: 'POST',
body: data ? JSON.stringify(data) : undefined,
});
}
async patch<T>(endpoint: string, data?: unknown): Promise<T> {
return this.request<T>(endpoint, {
method: 'PATCH',
body: data ? JSON.stringify(data) : undefined,
});
}
async delete<T>(endpoint: string, params?: Record<string, string>): Promise<T> {
const url = params
? `${endpoint}?${new URLSearchParams(params)}`
: endpoint;
return this.request<T>(url, { method: 'DELETE' });
}
}
// Instance par défaut
export const httpClient = new HttpClient('/api');

114
clients/tasks-client.ts Normal file
View File

@@ -0,0 +1,114 @@
import { httpClient } from './base/http-client';
import { Task, TaskStatus, TaskPriority } from '@/lib/types';
export interface TaskFilters {
status?: TaskStatus[];
source?: string[];
search?: string;
limit?: number;
offset?: number;
}
export interface TasksResponse {
success: boolean;
data: Task[];
stats: {
total: number;
completed: number;
inProgress: number;
todo: number;
completionRate: number;
};
count: number;
}
export interface CreateTaskData {
title: string;
description?: string;
status?: TaskStatus;
priority?: TaskPriority;
tags?: string[];
dueDate?: Date;
}
export interface UpdateTaskData {
taskId: string;
title?: string;
description?: string;
status?: TaskStatus;
priority?: TaskPriority;
tags?: string[];
dueDate?: Date;
}
/**
* Client pour la gestion des tâches
*/
export class TasksClient {
/**
* Récupère toutes les tâches avec filtres
*/
async getTasks(filters?: TaskFilters): Promise<TasksResponse> {
const params: Record<string, string> = {};
if (filters?.status) {
params.status = filters.status.join(',');
}
if (filters?.source) {
params.source = filters.source.join(',');
}
if (filters?.search) {
params.search = filters.search;
}
if (filters?.limit) {
params.limit = filters.limit.toString();
}
if (filters?.offset) {
params.offset = filters.offset.toString();
}
return httpClient.get<TasksResponse>('/tasks', params);
}
/**
* Crée une nouvelle tâche
*/
async createTask(data: CreateTaskData): Promise<{ success: boolean; data: Task; message: string }> {
const payload = {
...data,
dueDate: data.dueDate?.toISOString()
};
return httpClient.post('/tasks', payload);
}
/**
* Met à jour une tâche
*/
async updateTask(data: UpdateTaskData): Promise<{ success: boolean; data: Task; message: string }> {
const payload = {
...data,
dueDate: data.dueDate?.toISOString()
};
return httpClient.patch('/tasks', payload);
}
/**
* Supprime une tâche
*/
async deleteTask(taskId: string): Promise<{ success: boolean; message: string }> {
return httpClient.delete('/tasks', { taskId });
}
/**
* Met à jour le statut d'une tâche
*/
async updateTaskStatus(taskId: string, status: TaskStatus): Promise<{ success: boolean; data: Task; message: string }> {
return this.updateTask({ taskId, status });
}
}
// Instance singleton
export const tasksClient = new TasksClient();