diff --git a/app/agenda/page.tsx b/app/agenda/page.tsx index 2784c3e..91f8852 100644 --- a/app/agenda/page.tsx +++ b/app/agenda/page.tsx @@ -54,6 +54,11 @@ export default async function CalendarPage() { orderBy: { start: 'asc' } + }, + mission: { + include: { + missionUsers: true + } } } }); diff --git a/app/api/events/[id]/route.ts b/app/api/events/[id]/route.ts index d4a0239..33597ff 100644 --- a/app/api/events/[id]/route.ts +++ b/app/api/events/[id]/route.ts @@ -3,6 +3,39 @@ import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/options"; import { prisma } from "@/lib/prisma"; +// Helper function to check if user can manage events in a mission calendar +async function canManageMissionCalendar(userId: string, calendarId: string): Promise { + const calendar = await prisma.calendar.findUnique({ + where: { id: calendarId }, + include: { + mission: { + include: { + missionUsers: true + } + } + } + }); + + // If calendar is not linked to a mission, allow (it's a regular calendar) + if (!calendar?.missionId || !calendar.mission) { + return true; + } + + const mission = calendar.mission; + + // Check if user is the creator of the mission + if (mission.creatorId === userId) { + return true; + } + + // Check if user is a gardien-temps (time guardian) for this mission + const isGardienTemps = mission.missionUsers.some( + (mu) => mu.userId === userId && mu.role === 'gardien-temps' + ); + + return isGardienTemps; +} + export async function DELETE(req: NextRequest, props: { params: Promise<{ id: string }> }) { const params = await props.params; const session = await getServerSession(authOptions); @@ -33,6 +66,17 @@ export async function DELETE(req: NextRequest, props: { params: Promise<{ id: st ); } + // If calendar is linked to a mission, check permissions + if (event.calendar.missionId) { + const canManage = await canManageMissionCalendar(session.user.id, event.calendarId); + if (!canManage) { + return NextResponse.json( + { error: "Seul le créateur de la mission ou le gardien-temps peut supprimer des événements" }, + { status: 403 } + ); + } + } + // Delete the event await prisma.event.delete({ where: { id: params.id }, diff --git a/app/api/events/route.ts b/app/api/events/route.ts index 318babd..212d4b5 100644 --- a/app/api/events/route.ts +++ b/app/api/events/route.ts @@ -3,6 +3,39 @@ import { getServerSession } from "next-auth/next"; import { authOptions } from "@/app/api/auth/options"; import { prisma } from "@/lib/prisma"; +// Helper function to check if user can manage events in a mission calendar +async function canManageMissionCalendar(userId: string, calendarId: string): Promise { + const calendar = await prisma.calendar.findUnique({ + where: { id: calendarId }, + include: { + mission: { + include: { + missionUsers: true + } + } + } + }); + + // If calendar is not linked to a mission, allow (it's a regular calendar) + if (!calendar?.missionId || !calendar.mission) { + return true; + } + + const mission = calendar.mission; + + // Check if user is the creator of the mission + if (mission.creatorId === userId) { + return true; + } + + // Check if user is a gardien-temps (time guardian) for this mission + const isGardienTemps = mission.missionUsers.some( + (mu) => mu.userId === userId && mu.role === 'gardien-temps' + ); + + return isGardienTemps; +} + export async function POST(req: NextRequest) { const session = await getServerSession(authOptions); @@ -36,6 +69,17 @@ export async function POST(req: NextRequest) { ); } + // If calendar is linked to a mission, check permissions + if (calendar.missionId) { + const canManage = await canManageMissionCalendar(session.user.id, calendarId); + if (!canManage) { + return NextResponse.json( + { error: "Seul le créateur de la mission ou le gardien-temps peut créer des événements" }, + { status: 403 } + ); + } + } + // Create event with all required fields const event = await prisma.event.create({ data: { @@ -114,6 +158,17 @@ export async function PUT(req: NextRequest) { ); } + // If calendar is linked to a mission, check permissions + if (calendar.missionId) { + const canManage = await canManageMissionCalendar(session.user.id, calendarId); + if (!canManage) { + return NextResponse.json( + { error: "Seul le créateur de la mission ou le gardien-temps peut modifier des événements" }, + { status: 403 } + ); + } + } + const event = await prisma.event.update({ where: { id }, data: { diff --git a/app/api/missions/route.ts b/app/api/missions/route.ts index a657ff1..d37996d 100644 --- a/app/api/missions/route.ts +++ b/app/api/missions/route.ts @@ -317,6 +317,7 @@ export async function POST(request: Request) { color: calendarColor, description: `Calendrier pour la mission "${mission.name}"`, userId: memberId, + missionId: mission.id, // Link calendar to mission }, }); }); diff --git a/components/calendar/calendar-client.tsx b/components/calendar/calendar-client.tsx index 916707f..5faa345 100644 --- a/components/calendar/calendar-client.tsx +++ b/components/calendar/calendar-client.tsx @@ -62,8 +62,20 @@ const colorPalette = [ "#2563eb", // Blue ]; +interface CalendarWithMission extends Calendar { + missionId?: string | null; + mission?: { + id: string; + creatorId: string; + missionUsers: Array<{ + userId: string; + role: string; + }>; + } | null; +} + interface CalendarClientProps { - initialCalendars: (Calendar & { events: Event[] })[]; + initialCalendars: (CalendarWithMission & { events: Event[] })[]; userId: string; userProfile: { name: string; @@ -949,25 +961,63 @@ export function CalendarClient({ initialCalendars, userId, userProfile }: Calend ); })()} - + {(() => { + // Check if user can create events in the selected calendar(s) + const canCreateEvent = () => { + // If multiple calendars are visible, allow creation + if (visibleCalendarIds.length !== 1) { + return true; + } + + const selectedCal = calendars.find(cal => cal.id === visibleCalendarIds[0]); + if (!selectedCal) return true; + + // If calendar is not linked to a mission, allow + if (!selectedCal.missionId || !selectedCal.mission) { + return true; + } + + const mission = selectedCal.mission; + + // Check if user is the creator + if (mission.creatorId === userId) { + return true; + } + + // Check if user is gardien-temps + const isGardienTemps = mission.missionUsers.some( + (mu) => mu.userId === userId && mu.role === 'gardien-temps' + ); + + return isGardienTemps; + }; + + if (!canCreateEvent()) { + return null; + } + + return ( + + ); + })()} diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 98294da..37cb70d 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -33,12 +33,15 @@ model Calendar { color String @default("#0082c9") description String? userId String + missionId String? // Optional: link to mission if this is a mission calendar createdAt DateTime @default(now()) updatedAt DateTime @updatedAt events Event[] user User @relation(fields: [userId], references: [id], onDelete: Cascade) + mission Mission? @relation(fields: [missionId], references: [id], onDelete: Cascade) @@index([userId]) + @@index([missionId]) } model Event { @@ -142,6 +145,7 @@ model Mission { creatorId String attachments Attachment[] missionUsers MissionUser[] + calendars Calendar[] // Calendars linked to this mission // External integration fields leantimeProjectId String?