Skip to content
This repository was archived by the owner on Sep 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ MAILHOG_WEB_PORT=8025

# phpMyAdmin Configuration
PHPMYADMIN_PORT=8080

# .env.local
DATABASE_URL="postgresql://goofyuser:goofypassword@localhost:5432/goofytrack"
264 changes: 264 additions & 0 deletions app/components/talks/MyTalksList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
// components/talks/MyTalksList.tsx
'use client';

import { Button } from '@/components/ui/button';
import {
Card,
CardContent,
CardDescription,
CardFooter,
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';
import DeleteDialog from './DeleteDialog';
import StatusBadge from './StatusBadge';
import TalkDialog from './TalkDialog';

interface MyTalksListProps {
talks: Talk[];
onAddTalk: (talk: Omit<Talk, 'id'>) => 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);

function formatDate(d: Date) {
// YYYYMMDDTHHmmssZ
return d.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z';
}

const params = new URLSearchParams({
action: 'TEMPLATE',
text: talk.title,
details: talk.description || '',
location: 'Goofy Talk',
dates: `${formatDate(start)}/${formatDate(end)}`,
});

return `https://calendar.google.com/calendar/render?${params.toString()}`;
}

export default function MyTalksList({
talks,
onAddTalk,
onUpdateTalk,
onDeleteTalk,
// scheduledTalks,
rooms = [],
// topics,
}: MyTalksListProps) {
const session = useSession();

const [isDialogOpen, setIsDialogOpen] = useState(false);
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [currentTalk, setCurrentTalk] = useState<Talk | null>(null);
const [talkToDelete, setTalkToDelete] = useState<Talk | null>(null);
const [isNewTalk, setIsNewTalk] = useState(true);

const [filterTopic, setFilterTopic] = useState('');
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 (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]);

const handleCreateTalk = () => {
setIsNewTalk(true);
setCurrentTalk(null);
setIsDialogOpen(true);
};

const handleEditTalk = (talk: Talk) => {
setCurrentTalk(talk);
setIsNewTalk(false);
setIsDialogOpen(true);
};

const handleDeleteTalk = (talk: Talk) => {
setTalkToDelete(talk);
setIsDeleteDialogOpen(true);
};

const confirmDeleteTalk = () => {
if (talkToDelete) {
onDeleteTalk(talkToDelete.id);
setIsDeleteDialogOpen(false);
setTalkToDelete(null);
}
};

const saveTalk = (talk: Omit<Talk, 'id'> & { id?: string }) => {
if (isNewTalk) {
onAddTalk(talk);
} else {
onUpdateTalk(talk as Talk);
}
setIsDialogOpen(false);
};

return (
<div className="space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold">Tous mes talks</h2>
{session.data?.user &&
(isOrganizer(session.data.user.roleId) || isSpeaker(session.data.user.roleId)) && (
<Button onClick={handleCreateTalk}>
<Plus className="mr-2 h-4 w-4" /> Nouveau Talk
</Button>
)}
</div>

<div className="mb-2 flex flex-wrap gap-4">
<select
className="rounded border px-2 py-1"
value={filterTopic}
onChange={(e) => setFilterTopic(e.target.value)}
>
<option value="">Sujet</option>
{topics.map((topic) => (
<option key={topic} value={topic}>
{topic}
</option>
))}
</select>
<select
className="rounded border px-2 py-1"
value={filterDuration}
onChange={(e) => setFilterDuration(e.target.value)}
>
<option value="">Durée</option>
{[...new Set(talks.map((talk) => talk.durationMinutes))].map((d) => (
<option key={d} value={d}>
{d} min
</option>
))}
</select>
<select
className="rounded border px-2 py-1"
value={filterLevel}
onChange={(e) => setFilterLevel(e.target.value)}
>
<option value="">Niveau</option>
{levels.map((l) => (
<option key={l.value} value={l.value}>
{l.label}
</option>
))}
</select>
<select
className="rounded border px-2 py-1"
value={filterRoom}
onChange={(e) => setFilterRoom(e.target.value)}
>
<option value="">Salle</option>
{rooms.map((room) => (
<option key={room.id} value={room.id}>
{room.name}
</option>
))}
</select>
<input
className="rounded border px-2 py-1"
min={new Date().toISOString().slice(0, 10)}
type="date"
value={filterDate}
onChange={(e) => setFilterDate(e.target.value)}
/>
</div>

{filteredScheduledTalks.length === 0 ? (
<div className="text-muted-foreground rounded-lg border p-8 text-center">
Aucun talk ne correspond aux filtres.
</div>
) : (
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{filteredScheduledTalks.map((talk) => (
<Card key={talk.id} className="flex h-full flex-col">
<CardHeader className="pb-2">
<div className="flex items-start justify-between">
<CardTitle className="text-lg">{talk.title}</CardTitle>
<StatusBadge status={talk.status} />
</div>
<CardDescription className="text-muted-foreground flex space-x-2 text-sm">
<span>{talk.topic}</span>
<span>•</span>
<span>{levels.find((l) => l.value === talk.level)?.label}</span>
<span>•</span>
<span>{talk.durationMinutes} min</span>
</CardDescription>
</CardHeader>
<CardContent className="flex-grow">
<p className="text-muted-foreground line-clamp-3 text-sm">{talk.description}</p>
</CardContent>
<CardFooter className="flex justify-between pt-2">
<div className="flex space-x-2">
<>
<Button size="sm" variant="outline" onClick={() => handleEditTalk(talk)}>
<Pencil className="mr-1 h-4 w-4" /> Modifier
</Button>
<Button size="sm" variant="outline" onClick={() => handleDeleteTalk(talk)}>
<Trash2 className="mr-1 h-4 w-4" /> Supprimer
</Button>
</>
</div>
{talk.status === 'accepted' && (
<a href={getGoogleCalendarUrl(talk)} rel="noopener noreferrer" target="_blank">
<Button size="sm" variant="outline">
<CalendarPlus className="mr-1 h-4 w-4" /> Ajouter à Google Calendar
</Button>
</a>
)}
</CardFooter>
</Card>
))}
</div>
)}

<TalkDialog
isNew={isNewTalk}
isOpen={isDialogOpen}
setIsOpen={setIsDialogOpen}
talk={currentTalk}
onSave={saveTalk}
/>

<DeleteDialog
isOpen={isDeleteDialogOpen}
setIsOpen={setIsDeleteDialogOpen}
talk={talkToDelete}
onConfirm={confirmDeleteTalk}
/>
</div>
);
}
35 changes: 28 additions & 7 deletions app/components/talks/PendingTalksList.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// components/talks/PendingTalksList.tsx - Modified version
// components/talks/PendingTalksList.tsx
'use client';

import { Button } from '@/components/ui/button';
Expand All @@ -22,11 +22,33 @@ import StatusDialog from './StatusDialog';
import TalkDialog from './TalkDialog';

// Mock data for rooms (you should replace this with your actual data)
const rooms = [
{ id: 'room1', name: 'Salle A', capacity: 100 },
{ id: 'room2', name: 'Salle B', capacity: 50 },
{ id: 'room3', name: 'Salle C', capacity: 200 },
];
// const rooms = [
// {
// name: 'Salle Amphithéâtre',
// capacity: 300,
// description: 'Grande salle principale pour les keynotes et sessions populaires',
// },
// {
// name: 'Salle Ateliers',
// capacity: 100,
// description: 'Salle équipée pour les ateliers pratiques et hands-on labs',
// },
// {
// name: 'Salle Conférences A',
// capacity: 150,
// description: 'Salle de conférence standard pour les présentations techniques',
// },
// {
// name: 'Salle Conférences B',
// capacity: 150,
// description: 'Salle de conférence standard pour les présentations techniques',
// },
// {
// name: 'Salle Innovation',
// capacity: 80,
// description: 'Espace dédié aux démonstrations et nouvelles technologies',
// },
// ];

interface PendingTalksListProps {
talks: Talk[];
Expand Down Expand Up @@ -192,7 +214,6 @@ export default function PendingTalksList({
{/* Dialog for status change */}
<StatusDialog
isOpen={isStatusDialogOpen}
rooms={rooms}
setIsOpen={setIsStatusDialogOpen}
talk={talkToChangeStatus}
onChangeTalkStatus={onChangeTalkStatus}
Expand Down
Loading
Loading