diff --git a/app/components/talks/MyTalksList.tsx b/app/components/talks/MyTalksList.tsx index 65682b4..d6f3444 100644 --- a/app/components/talks/MyTalksList.tsx +++ b/app/components/talks/MyTalksList.tsx @@ -10,9 +10,7 @@ import { CardHeader, CardTitle, } from '@/components/ui/card'; -import { levels } from '@/lib/mock-data'; import { Talk, Room } from '@/lib/types'; -import { isOrganizer, isSpeaker } from '@/utils/auth.utils'; import { Pencil, Plus, Trash2, CalendarPlus } from 'lucide-react'; import { useSession } from 'next-auth/react'; import { useState, useMemo } from 'react'; @@ -25,16 +23,13 @@ interface MyTalksListProps { onAddTalk: (talk: Omit) => void; onUpdateTalk: (talk: Talk) => void; onDeleteTalk: (talkId: string) => void; - // scheduledTalks: ScheduledTalk[]; - rooms?: Room[]; - // topics: string[]; } // Helper to generate Google Calendar event link function getGoogleCalendarUrl(talk: Talk) { // For demo, use current date/time as start, and add duration const start = new Date(); - const end = new Date(start.getTime() + (talk.durationMinutes || 30) * 60000); + const end = new Date(start.getTime() + (talk.duration || 30) * 60000); function formatDate(d: Date) { // YYYYMMDDTHHmmssZ @@ -57,9 +52,6 @@ export default function MyTalksList({ onAddTalk, onUpdateTalk, onDeleteTalk, - // scheduledTalks, - rooms = [], - // topics, }: MyTalksListProps) { const session = useSession(); @@ -73,25 +65,17 @@ export default function MyTalksList({ const [filterDuration, setFilterDuration] = useState(''); const [filterLevel, setFilterLevel] = useState(''); const [filterRoom, setFilterRoom] = useState(''); - const [filterDate, setFilterDate] = useState(''); const filteredScheduledTalks = useMemo(() => { return talks.filter((talk) => { let ok = true; - if (filterTopic && talk.topic !== filterTopic) ok = false; - if (filterDuration && String(talk.durationMinutes) !== filterDuration) ok = false; + if (filterTopic && talk.subjects?.name !== filterTopic) ok = false; + if (filterDuration && String(talk.duration) !== filterDuration) ok = false; if (filterLevel && talk.level !== filterLevel) ok = false; return ok; }); // }, [scheduledTalks, filterTopic, filterDuration, filterLevel, filterRoom, filterDate]); - }, [talks, filterTopic, filterDuration, filterLevel, filterRoom, filterDate]); - - const topics = useMemo(() => { - const allTopics = talks.map((talk) => talk.topic); - const uniqueTopics = Array.from(new Set(allTopics)); - - return uniqueTopics; - }, [talks]); + }, [talks, filterTopic, filterDuration, filterLevel, filterRoom]); const handleCreateTalk = () => { setIsNewTalk(true); @@ -112,31 +96,46 @@ export default function MyTalksList({ const confirmDeleteTalk = () => { if (talkToDelete) { - onDeleteTalk(talkToDelete.id); + onDeleteTalk(talkToDelete.id.toString()); setIsDeleteDialogOpen(false); setTalkToDelete(null); } }; - const saveTalk = (talk: Omit & { id?: string }) => { + const saveTalk = (talk: Talk) => { if (isNewTalk) { - onAddTalk(talk); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, ...talkWithoutId } = talk; + onAddTalk(talkWithoutId); } else { - onUpdateTalk(talk as Talk); + onUpdateTalk(talk); } setIsDialogOpen(false); }; + const [levels, rooms, topics] = useMemo(() => { + const uniqueLevels = Array.from(new Set(talks.map((talk) => talk.level))); + const uniqueTopics = Array.from(new Set(talks.map((talk) => talk.subjects?.name))); + + const allSchedules = talks.flatMap((talk) => talk.schedules || []); + const uniqueRoomIds = new Set(); + allSchedules.forEach((schedule) => { + if (schedule.room_id) { + uniqueRoomIds.add(schedule.room_id); + } + }); + const uniqueRooms = Array.from(uniqueRoomIds); + + return [uniqueLevels, uniqueRooms, uniqueTopics]; + }, []); + return (

Tous mes talks

- {session.data?.user && - (isOrganizer(session.data.user.roleId) || isSpeaker(session.data.user.roleId)) && ( - - )} +
@@ -158,7 +157,7 @@ export default function MyTalksList({ onChange={(e) => setFilterDuration(e.target.value)} > - {[...new Set(talks.map((talk) => talk.durationMinutes))].map((d) => ( + {[...new Set(talks.map((talk) => talk.duration))].map((d) => ( @@ -171,8 +170,8 @@ export default function MyTalksList({ > {levels.map((l) => ( - ))} @@ -183,18 +182,11 @@ export default function MyTalksList({ > {rooms.map((room) => ( - ))} - setFilterDate(e.target.value)} - />
{filteredScheduledTalks.length === 0 ? ( @@ -211,27 +203,27 @@ export default function MyTalksList({
- {talk.topic} + {talk.subjects?.name} - {levels.find((l) => l.value === talk.level)?.label} + {talk.level.charAt(0).toUpperCase() + talk.level.slice(1)} - {talk.durationMinutes} min + {talk.duration} min

{talk.description}

-
- <> + {talk.speakerId.toString() === session.data?.user.id && ( +
- -
+
+ )} {talk.status === 'scheduled' && ( - - - )} + + - {talk.status === 'scheduled' && ( - - - - )} -
- - ))} + )} + {talk.status === 'scheduled' && ( + + + + )} + + + ))} )} diff --git a/app/lib/types.ts b/app/lib/types.ts index df4db51..c18e53c 100644 --- a/app/lib/types.ts +++ b/app/lib/types.ts @@ -7,14 +7,37 @@ export type TalkStatus = 'pending' | 'accepted' | 'rejected' | 'scheduled'; export type TalkLevel = 'beginner' | 'intermediate' | 'advanced'; export interface Talk { - id: string; + id: number; title: string; - topic: string; description: string; - durationMinutes: number; + duration: number; level: TalkLevel; status: TalkStatus; - speakerId: string; + speakerId: number; + subjectId: number; + createdAt: string; + updatedAt: string; + subjects?: { + id: number; + name: string; + created_at: string; + }; + schedules?: Array<{ + id: number; + talk_id: number; + room_id: number; + start_time: string; + end_time: string; + created_at: string; + updated_at: string; + }>; + // feedback?: any[]; + // favorites?: any[]; + users?: { + id: number; + username: string; + email: string; + }; } export interface Room { diff --git a/app/pages/api/schedules.ts b/app/pages/api/schedules.ts deleted file mode 100644 index ac72222..0000000 --- a/app/pages/api/schedules.ts +++ /dev/null @@ -1,55 +0,0 @@ -// pages/api/schedules.ts -import { prisma } from '@/lib/prisma'; -import type { NextApiRequest, NextApiResponse } from 'next'; - -type RawSchedule = { - id: number; - talk_id: number; - room_id: number; - start_time: Date; - end_time: Date; - // **Here** we use `speaker_id`, not `speakerId` - talk: { - id: number; - title: string; - speaker_id: number; - // …other fields if you need them - }; -}; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - const { date } = req.query as { date?: string }; - if (req.method !== 'GET' || !date) { - return res.status(400).json({ error: 'Use GET & provide ?date=YYYY-MM-DD' }); - } - - const day = new Date(date); - const start = new Date(day); - start.setHours(9, 0, 0, 0); - const end = new Date(day); - end.setHours(18, 0, 0, 0); - - // no more type‐assertion here: let TS infer the raw type - const raw: RawSchedule[] = await prisma.schedules.findMany({ - where: { - start_time: { gte: start, lt: end }, - }, - include: { - talk: true, - }, - }); - - const schedules = raw.map((s) => ({ - id: s.id, - roomId: s.room_id, // ← note room_id → roomId - startTime: s.start_time.toISOString(), - endTime: s.end_time.toISOString(), - talk: { - id: s.talk.id, - title: s.talk.title, - speakerId: s.talk.speaker_id, // ← speaker_id → speakerId - }, - })); - - return res.status(200).json({ schedules }); -} diff --git a/app/pages/api/schedules/index.ts b/app/pages/api/schedules/index.ts index 2151cf5..ac72222 100644 --- a/app/pages/api/schedules/index.ts +++ b/app/pages/api/schedules/index.ts @@ -1,28 +1,55 @@ -// pages/api/schedules/index.ts -import type { NextApiRequest, NextApiResponse } from 'next'; +// pages/api/schedules.ts import { prisma } from '@/lib/prisma'; +import type { NextApiRequest, NextApiResponse } from 'next'; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - // Only GET is allowed - if (req.method === 'GET') { - try { - const schedules = await prisma.schedules.findMany({ - include: { - rooms: true, // include room details for each schedule - }, - orderBy: { - start_time: 'asc', // optional: sort by start time - }, - }); +type RawSchedule = { + id: number; + talk_id: number; + room_id: number; + start_time: Date; + end_time: Date; + // **Here** we use `speaker_id`, not `speakerId` + talk: { + id: number; + title: string; + speaker_id: number; + // …other fields if you need them + }; +}; - return res.status(200).json({ schedules }); - } catch (error) { - console.error('Error fetching schedules:', error); - return res.status(500).json({ error: 'Unable to load schedules' }); - } +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const { date } = req.query as { date?: string }; + if (req.method !== 'GET' || !date) { + return res.status(400).json({ error: 'Use GET & provide ?date=YYYY-MM-DD' }); } - // Method not allowed - res.setHeader('Allow', ['GET']); - return res.status(405).end(`Method ${req.method} Not Allowed`); + const day = new Date(date); + const start = new Date(day); + start.setHours(9, 0, 0, 0); + const end = new Date(day); + end.setHours(18, 0, 0, 0); + + // no more type‐assertion here: let TS infer the raw type + const raw: RawSchedule[] = await prisma.schedules.findMany({ + where: { + start_time: { gte: start, lt: end }, + }, + include: { + talk: true, + }, + }); + + const schedules = raw.map((s) => ({ + id: s.id, + roomId: s.room_id, // ← note room_id → roomId + startTime: s.start_time.toISOString(), + endTime: s.end_time.toISOString(), + talk: { + id: s.talk.id, + title: s.talk.title, + speakerId: s.talk.speaker_id, // ← speaker_id → speakerId + }, + })); + + return res.status(200).json({ schedules }); } diff --git a/app/pages/index.tsx b/app/pages/index.tsx index b60c93e..48c1ebe 100644 --- a/app/pages/index.tsx +++ b/app/pages/index.tsx @@ -25,12 +25,9 @@ export default function TalksPage() { setLoading(true); setError(null); - // choose endpoint: authenticated speakers -> /api/talks/me; everything else -> public /api/talks let endpoint = '/api/talks'; - if (status === 'authenticated') { - if (isSpeaker(session!.user.roleId)) { - endpoint = '/api/talks/me'; - } + if (isSpeaker(session?.user.roleId) || isOrganizer(session?.user.roleId)) { + endpoint = '/api/talks/me'; } try { @@ -38,8 +35,6 @@ export default function TalksPage() { if (!res.ok) throw new Error(`Error: ${res.status}`); const { talks: fetched } = await res.json(); setTalks(fetched); - // console.log('session:', session); - // console.log('fetched talks:', fetched); } catch (err) { if (err instanceof Error) { console.error(err); @@ -58,7 +53,7 @@ export default function TalksPage() { // Fonction pour programmer un talk const scheduleTalk = (talkId: string, slotId: string) => { - const talk = talks.find((t) => t.id === talkId); + const talk = talks.find((t) => String(t.id) === talkId); const slot = mockData.slots.find((s) => s.id === slotId); if (!talk || !slot) return; @@ -75,7 +70,7 @@ export default function TalksPage() { const addTalk = (newTalk: Omit) => { const talkWithId = { ...newTalk, - id: Date.now().toString(), + id: Date.now(), } as Talk; setTalks((prev) => [...prev, talkWithId]); }; @@ -87,7 +82,7 @@ export default function TalksPage() { const deleteTalk = async (talkId: string) => { const res = await fetch(`/api/talks/${talkId}`, { method: 'DELETE' }); if (res.ok) { - setTalks((prev) => prev.filter((t) => t.id !== talkId)); + setTalks((prev) => prev.filter((t) => String(t.id) !== talkId)); } else { const err = await res.json(); console.error('Failed to delete talk:', err.error); @@ -153,7 +148,7 @@ export default function TalksPage() { {/* Tab: Liste des talks */} - {isSpeaker(session?.user?.roleId) ? ( + {isSpeaker(session?.user?.roleId) || isOrganizer(session?.user?.roleId) ? (