feat: overhaul TODO.md and enhance Kanban components
- Updated TODO.md to reflect the new project structure and phases, marking several tasks as completed. - Enhanced Kanban components with a tech-inspired design, including new styles for columns and task cards. - Removed the obsolete reminders service and task processor, streamlining the codebase for better maintainability. - Introduced a modern API for task management, including CRUD operations and improved error handling. - Updated global styles for a cohesive dark theme and added custom scrollbar styles.
This commit is contained in:
@@ -1,100 +0,0 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { getConfig, getTargetRemindersList, getEnabledRemindersLists } from '@/lib/config';
|
||||
import { remindersService } from '@/services/reminders';
|
||||
|
||||
/**
|
||||
* API route pour récupérer la configuration actuelle
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const config = getConfig();
|
||||
const availableLists = await remindersService.getReminderLists();
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
config,
|
||||
availableLists,
|
||||
currentTarget: getTargetRemindersList(),
|
||||
enabledLists: getEnabledRemindersLists()
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de la récupération de la config:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API route pour tester l'accès à une liste spécifique
|
||||
*/
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { listName, action } = body;
|
||||
|
||||
if (!listName) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: 'listName est requis'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
let result: any = {};
|
||||
|
||||
switch (action) {
|
||||
case 'test':
|
||||
// Tester l'accès à une liste spécifique
|
||||
const reminders = await remindersService.getRemindersByList(listName);
|
||||
result = {
|
||||
listName,
|
||||
accessible: true,
|
||||
reminderCount: reminders.length,
|
||||
sample: reminders.slice(0, 3).map(r => ({
|
||||
title: r.title,
|
||||
completed: r.completed,
|
||||
priority: r.priority
|
||||
}))
|
||||
};
|
||||
break;
|
||||
|
||||
case 'preview':
|
||||
// Prévisualiser les rappels d'une liste
|
||||
const previewReminders = await remindersService.getRemindersByList(listName);
|
||||
result = {
|
||||
listName,
|
||||
reminders: previewReminders.map(r => ({
|
||||
id: r.id,
|
||||
title: r.title,
|
||||
completed: r.completed,
|
||||
priority: r.priority,
|
||||
tags: r.tags || []
|
||||
}))
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: 'Action non supportée. Utilisez "test" ou "preview"'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
action,
|
||||
result
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors du test de liste:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { taskProcessorService } from '@/services/task-processor';
|
||||
|
||||
/**
|
||||
* API route pour synchroniser les rappels macOS avec la base de données
|
||||
*/
|
||||
export async function POST() {
|
||||
try {
|
||||
console.log('🔄 Début de la synchronisation des rappels...');
|
||||
|
||||
const syncResult = await taskProcessorService.syncRemindersToDatabase();
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Synchronisation des rappels terminée',
|
||||
syncLog: syncResult
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de la synchronisation:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Erreur inconnue lors de la synchronisation'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API route pour obtenir le statut de la dernière synchronisation
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
// Récupérer les derniers logs de sync
|
||||
const { prisma } = await import('@/services/database');
|
||||
|
||||
const lastSyncLogs = await prisma.syncLog.findMany({
|
||||
where: { source: 'reminders' },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 5
|
||||
});
|
||||
|
||||
const taskStats = await taskProcessorService.getTaskStats();
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
lastSyncLogs,
|
||||
taskStats,
|
||||
message: 'Statut de synchronisation récupéré'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de la récupération du statut:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { taskProcessorService } from '@/services/task-processor';
|
||||
import { TaskStatus } from '@/lib/types';
|
||||
import { tasksService } from '@/services/tasks';
|
||||
import { TaskStatus, TaskPriority } from '@/lib/types';
|
||||
|
||||
/**
|
||||
* API route pour récupérer les tâches avec filtres optionnels
|
||||
@@ -10,7 +10,13 @@ export async function GET(request: Request) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
|
||||
// Extraire les paramètres de filtre
|
||||
const filters: any = {};
|
||||
const filters: {
|
||||
status?: TaskStatus[];
|
||||
source?: string[];
|
||||
search?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
} = {};
|
||||
|
||||
const status = searchParams.get('status');
|
||||
if (status) {
|
||||
@@ -38,8 +44,8 @@ export async function GET(request: Request) {
|
||||
}
|
||||
|
||||
// Récupérer les tâches
|
||||
const tasks = await taskProcessorService.getTasks(filters);
|
||||
const stats = await taskProcessorService.getTaskStats();
|
||||
const tasks = await tasksService.getTasks(filters);
|
||||
const stats = await tasksService.getTaskStats();
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
@@ -60,26 +66,71 @@ export async function GET(request: Request) {
|
||||
}
|
||||
|
||||
/**
|
||||
* API route pour mettre à jour le statut d'une tâche
|
||||
* API route pour créer une nouvelle tâche
|
||||
*/
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { title, description, status, priority, tags, dueDate } = body;
|
||||
|
||||
if (!title) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: 'Le titre est requis'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
const task = await tasksService.createTask({
|
||||
title,
|
||||
description,
|
||||
status: status as TaskStatus,
|
||||
priority: priority as TaskPriority,
|
||||
tags,
|
||||
dueDate: dueDate ? new Date(dueDate) : undefined
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: task,
|
||||
message: 'Tâche créée avec succès'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de la création de la tâche:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API route pour mettre à jour une tâche
|
||||
*/
|
||||
export async function PATCH(request: Request) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { taskId, status } = body;
|
||||
const { taskId, ...updates } = body;
|
||||
|
||||
if (!taskId || !status) {
|
||||
if (!taskId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: 'taskId et status sont requis'
|
||||
error: 'taskId est requis'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
const updatedTask = await taskProcessorService.updateTaskStatus(taskId, status);
|
||||
// Convertir dueDate si présent
|
||||
if (updates.dueDate) {
|
||||
updates.dueDate = new Date(updates.dueDate);
|
||||
}
|
||||
|
||||
const updatedTask = await tasksService.updateTask(taskId, updates);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
data: updatedTask,
|
||||
message: `Tâche ${taskId} mise à jour avec le statut ${status}`
|
||||
message: 'Tâche mise à jour avec succès'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
@@ -91,3 +142,35 @@ export async function PATCH(request: Request) {
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API route pour supprimer une tâche
|
||||
*/
|
||||
export async function DELETE(request: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const taskId = searchParams.get('taskId');
|
||||
|
||||
if (!taskId) {
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: 'taskId est requis'
|
||||
}, { status: 400 });
|
||||
}
|
||||
|
||||
await tasksService.deleteTask(taskId);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Tâche supprimée avec succès'
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Erreur lors de la suppression de la tâche:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { testDatabaseConnection } from '@/services/database';
|
||||
import { remindersService } from '@/services/reminders';
|
||||
import { taskProcessorService } from '@/services/task-processor';
|
||||
|
||||
/**
|
||||
* API route de test pour vérifier que tous les services fonctionnent
|
||||
*/
|
||||
export async function GET() {
|
||||
try {
|
||||
const results = {
|
||||
timestamp: new Date().toISOString(),
|
||||
database: false,
|
||||
reminders: false,
|
||||
taskProcessor: false,
|
||||
reminderLists: [] as string[],
|
||||
taskStats: null as any
|
||||
};
|
||||
|
||||
// Test de la base de données
|
||||
try {
|
||||
results.database = await testDatabaseConnection();
|
||||
} catch (error) {
|
||||
console.error('Test DB failed:', error);
|
||||
}
|
||||
|
||||
// Test de l'accès aux rappels
|
||||
try {
|
||||
results.reminders = await remindersService.testRemindersAccess();
|
||||
if (results.reminders) {
|
||||
results.reminderLists = await remindersService.getReminderLists();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Test Reminders failed:', error);
|
||||
}
|
||||
|
||||
// Test du service de traitement des tâches
|
||||
try {
|
||||
results.taskStats = await taskProcessorService.getTaskStats();
|
||||
results.taskProcessor = true;
|
||||
} catch (error) {
|
||||
console.error('Test TaskProcessor failed:', error);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Tests des services terminés',
|
||||
results
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur dans l\'API de test:', error);
|
||||
|
||||
return NextResponse.json({
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Erreur inconnue'
|
||||
}, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
--background: #020617; /* slate-950 */
|
||||
--foreground: #f1f5f9; /* slate-100 */
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
@@ -12,15 +12,38 @@
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-family: var(--font-geist-mono), 'Courier New', monospace;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
/* Scrollbar tech style */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: #1e293b; /* slate-800 */
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #475569; /* slate-600 */
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #06b6d4; /* cyan-500 */
|
||||
}
|
||||
|
||||
/* Animations tech */
|
||||
@keyframes glow {
|
||||
0%, 100% { box-shadow: 0 0 5px rgba(6, 182, 212, 0.3); }
|
||||
50% { box-shadow: 0 0 20px rgba(6, 182, 212, 0.6); }
|
||||
}
|
||||
|
||||
.animate-glow {
|
||||
animation: glow 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
import { taskProcessorService } from '@/services/task-processor';
|
||||
import { getTargetRemindersList } from '@/lib/config';
|
||||
import { tasksService } from '@/services/tasks';
|
||||
import { KanbanBoard } from '../../components/kanban/Board';
|
||||
import { Header } from '../../components/ui/Header';
|
||||
|
||||
export default async function HomePage() {
|
||||
// SSR - Récupération des données côté serveur
|
||||
// SSR - Récupération des données côté serveur (focus sur les tâches récentes)
|
||||
const [tasks, stats] = await Promise.all([
|
||||
taskProcessorService.getTasks({ limit: 100 }),
|
||||
taskProcessorService.getTaskStats()
|
||||
tasksService.getTasks({ limit: 20 }), // Réduire pour voir les nouvelles tâches
|
||||
tasksService.getTaskStats()
|
||||
]);
|
||||
|
||||
const targetList = getTargetRemindersList();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
||||
<div className="min-h-screen bg-slate-950">
|
||||
<Header
|
||||
title="TowerControl"
|
||||
subtitle={`Tâches synchronisées depuis "${targetList}"`}
|
||||
subtitle="Gestionnaire de tâches moderne"
|
||||
stats={stats}
|
||||
/>
|
||||
|
||||
<main className="container mx-auto px-4 py-6">
|
||||
<main className="h-[calc(100vh-120px)]">
|
||||
<KanbanBoard tasks={tasks} />
|
||||
</main>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user