diff --git a/prisma/migrations/20251010084245_add_user_to_daily_checkbox/migration.sql b/prisma/migrations/20251010084245_add_user_to_daily_checkbox/migration.sql new file mode 100644 index 0000000..fed0814 --- /dev/null +++ b/prisma/migrations/20251010084245_add_user_to_daily_checkbox/migration.sql @@ -0,0 +1,17 @@ +-- Migration pour ajouter userId aux DailyCheckbox +-- et associer les entrées existantes au premier utilisateur + +-- 1. Ajouter la colonne userId (nullable temporairement) +ALTER TABLE "daily_checkboxes" ADD COLUMN "userId" TEXT; + +-- 2. Migrer les données existantes vers le premier utilisateur +-- (on suppose qu'il y a au moins un utilisateur dans la table users) +UPDATE "daily_checkboxes" +SET "userId" = (SELECT id FROM "users" LIMIT 1) +WHERE "userId" IS NULL; + +-- 3. Créer un index sur userId pour les performances +CREATE INDEX "daily_checkboxes_userId_idx" ON "daily_checkboxes"("userId"); + +-- Note: La contrainte de clé étrangère sera gérée par Prisma +-- SQLite ne supporte pas les contraintes de clé étrangère dans ALTER TABLE diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 448bb23..a42fd95 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -8,20 +8,21 @@ datasource db { } model User { - id String @id @default(cuid()) - email String @unique - name String? - firstName String? - lastName String? - avatar String? // URL de l'avatar - role String @default("user") // user, admin, etc. - isActive Boolean @default(true) - lastLoginAt DateTime? - password String // Hashé avec bcrypt - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - preferences UserPreferences? - notes Note[] + id String @id @default(cuid()) + email String @unique + name String? + firstName String? + lastName String? + avatar String? // URL de l'avatar + role String @default("user") // user, admin, etc. + isActive Boolean @default(true) + lastLoginAt DateTime? + password String // Hashé avec bcrypt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + preferences UserPreferences? + notes Note[] + dailyCheckboxes DailyCheckbox[] @@map("users") } @@ -98,11 +99,14 @@ model DailyCheckbox { type String @default("task") order Int @default(0) taskId String? + userId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt task Task? @relation(fields: [taskId], references: [id]) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([date]) + @@index([userId]) @@map("daily_checkboxes") } diff --git a/src/actions/daily.ts b/src/actions/daily.ts index c06b0f3..ff245ff 100644 --- a/src/actions/daily.ts +++ b/src/actions/daily.ts @@ -13,6 +13,8 @@ import { parseDate, normalizeDate, } from '@/lib/date-utils'; +import { getServerSession } from 'next-auth/next'; +import { authOptions } from '@/lib/auth'; /** * Toggle l'état d'une checkbox @@ -23,6 +25,11 @@ export async function toggleCheckbox(checkboxId: string): Promise<{ error?: string; }> { try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return { success: false, error: 'Non authentifié' }; + } + // Nous devons d'abord récupérer la checkbox pour connaître son état actuel // En absence de getCheckboxById, nous allons essayer de la trouver via une vue daily // Pour l'instant, nous allons simplement toggle via updateCheckbox @@ -30,7 +37,7 @@ export async function toggleCheckbox(checkboxId: string): Promise<{ // Récupérer toutes les checkboxes d'aujourd'hui et hier pour trouver celle à toggle const today = getToday(); - const dailyView = await dailyService.getDailyView(today); + const dailyView = await dailyService.getDailyView(today, session.user.id); let checkbox = dailyView.today.find((cb) => cb.id === checkboxId); if (!checkbox) { @@ -70,8 +77,14 @@ export async function addTodayCheckbox( error?: string; }> { try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return { success: false, error: 'Non authentifié' }; + } + const newCheckbox = await dailyService.addCheckbox({ date: getToday(), + userId: session.user.id, text: content, type: type || 'task', taskId, @@ -101,10 +114,16 @@ export async function addYesterdayCheckbox( error?: string; }> { try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return { success: false, error: 'Non authentifié' }; + } + const yesterday = getPreviousWorkday(getToday()); const newCheckbox = await dailyService.addCheckbox({ date: yesterday, + userId: session.user.id, text: content, type: type || 'task', taskId, @@ -180,10 +199,16 @@ export async function addTodoToTask( error?: string; }> { try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return { success: false, error: 'Non authentifié' }; + } + const targetDate = normalizeDate(date || getToday()); const checkboxData: CreateDailyCheckboxData = { date: targetDate, + userId: session.user.id, text: text.trim(), type: 'task', taskId: taskId, diff --git a/src/app/api/daily/route.ts b/src/app/api/daily/route.ts index 057cf2e..337a241 100644 --- a/src/app/api/daily/route.ts +++ b/src/app/api/daily/route.ts @@ -6,12 +6,19 @@ import { isValidAPIDate, createDateFromParts, } from '@/lib/date-utils'; +import { getServerSession } from 'next-auth/next'; +import { authOptions } from '@/lib/auth'; /** * API route pour récupérer la vue daily (hier + aujourd'hui) */ export async function GET(request: Request) { try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }); + } + const { searchParams } = new URL(request.url); const action = searchParams.get('action'); @@ -20,7 +27,10 @@ export async function GET(request: Request) { if (action === 'history') { // Récupérer l'historique const limit = parseInt(searchParams.get('limit') || '30'); - const history = await dailyService.getCheckboxHistory(limit); + const history = await dailyService.getCheckboxHistory( + session.user.id, + limit + ); return NextResponse.json(history); } @@ -55,7 +65,10 @@ export async function GET(request: Request) { targetDate = getToday(); } - const dailyView = await dailyService.getDailyView(targetDate); + const dailyView = await dailyService.getDailyView( + targetDate, + session.user.id + ); return NextResponse.json(dailyView); } catch (error) { console.error('Erreur lors de la récupération du daily:', error); @@ -71,6 +84,10 @@ export async function GET(request: Request) { */ export async function POST(request: Request) { try { + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }); + } const body = await request.json(); // Validation des données @@ -100,6 +117,7 @@ export async function POST(request: Request) { const checkbox = await dailyService.addCheckbox({ date, + userId: session.user.id, text: body.text, type: body.type, taskId: body.taskId, diff --git a/src/app/daily/page.tsx b/src/app/daily/page.tsx index aa922a8..a8156f1 100644 --- a/src/app/daily/page.tsx +++ b/src/app/daily/page.tsx @@ -3,6 +3,8 @@ import { DailyPageClient } from './DailyPageClient'; import { dailyService } from '@/services/task-management/daily'; import { DeadlineAnalyticsService } from '@/services/analytics/deadline-analytics'; import { getToday } from '@/lib/date-utils'; +import { getServerSession } from 'next-auth/next'; +import { authOptions } from '@/lib/auth'; // Force dynamic rendering (no static generation) export const dynamic = 'force-dynamic'; @@ -13,13 +15,31 @@ export const metadata: Metadata = { }; export default async function DailyPage() { + // Récupérer la session utilisateur + const session = await getServerSession(authOptions); + + if (!session?.user?.id) { + return ( +
+ Vous devez être connecté pour accéder à la page daily. +
+