'use client'; import { useState, useTransition, useRef } from 'react'; import Link from 'next/link'; import { useSearchParams, useRouter } from 'next/navigation'; import { Card, Badge, Button, Modal, ModalFooter, Input, CollaboratorDisplay, } from '@/components/ui'; import { deleteSwotSession, updateSwotSession } from '@/actions/session'; import { deleteMotivatorSession, updateMotivatorSession } from '@/actions/moving-motivators'; import { deleteYearReviewSession, updateYearReviewSession } from '@/actions/year-review'; import { deleteWeeklyCheckInSession, updateWeeklyCheckInSession } from '@/actions/weekly-checkin'; import { deleteWeatherSession, updateWeatherSession } from '@/actions/weather'; import { type WorkshopTabType, type WorkshopTypeId, WORKSHOPS, VALID_TAB_PARAMS, getWorkshop, getSessionPath, } from '@/lib/workshops'; import { useClickOutside } from '@/hooks/useClickOutside'; import type { Share } from '@/lib/share-utils'; const TYPE_TABS = [ { value: 'all' as const, icon: '📋', label: 'Tous' }, { value: 'team' as const, icon: '🏢', label: 'Équipe' }, ...WORKSHOPS.map((w) => ({ value: w.id, icon: w.icon, label: w.labelShort })), ]; interface ResolvedCollaborator { raw: string; matchedUser: { id: string; email: string; name: string | null; } | null; } interface SwotSession { id: string; title: string; collaborator: string; resolvedCollaborator: ResolvedCollaborator; updatedAt: Date; isOwner: boolean; role: 'OWNER' | 'VIEWER' | 'EDITOR'; user: { id: string; name: string | null; email: string }; shares: Share[]; _count: { items: number; actions: number }; workshopType: 'swot'; isTeamCollab?: true; canEdit?: boolean; } interface MotivatorSession { id: string; title: string; participant: string; resolvedParticipant: ResolvedCollaborator; updatedAt: Date; isOwner: boolean; role: 'OWNER' | 'VIEWER' | 'EDITOR'; user: { id: string; name: string | null; email: string }; shares: Share[]; _count: { cards: number }; workshopType: 'motivators'; isTeamCollab?: true; canEdit?: boolean; } interface YearReviewSession { id: string; title: string; participant: string; resolvedParticipant: ResolvedCollaborator; year: number; updatedAt: Date; isOwner: boolean; role: 'OWNER' | 'VIEWER' | 'EDITOR'; user: { id: string; name: string | null; email: string }; shares: Share[]; _count: { items: number }; workshopType: 'year-review'; isTeamCollab?: true; canEdit?: boolean; } interface WeeklyCheckInSession { id: string; title: string; participant: string; resolvedParticipant: ResolvedCollaborator; date: Date; updatedAt: Date; isOwner: boolean; role: 'OWNER' | 'VIEWER' | 'EDITOR'; user: { id: string; name: string | null; email: string }; shares: Share[]; _count: { items: number }; workshopType: 'weekly-checkin'; isTeamCollab?: true; canEdit?: boolean; } interface WeatherSession { id: string; title: string; date: Date; updatedAt: Date; isOwner: boolean; role: 'OWNER' | 'VIEWER' | 'EDITOR'; user: { id: string; name: string | null; email: string }; shares: Share[]; _count: { entries: number }; workshopType: 'weather'; isTeamCollab?: true; canEdit?: boolean; } type AnySession = SwotSession | MotivatorSession | YearReviewSession | WeeklyCheckInSession | WeatherSession; interface WorkshopTabsProps { swotSessions: SwotSession[]; motivatorSessions: MotivatorSession[]; yearReviewSessions: YearReviewSession[]; weeklyCheckInSessions: WeeklyCheckInSession[]; weatherSessions: WeatherSession[]; teamCollabSessions?: (AnySession & { isTeamCollab?: true })[]; } // Helper to get resolved collaborator from any session function getResolvedCollaborator(session: AnySession): ResolvedCollaborator { if (session.workshopType === 'swot') { return (session as SwotSession).resolvedCollaborator; } else if (session.workshopType === 'year-review') { return (session as YearReviewSession).resolvedParticipant; } else if (session.workshopType === 'weekly-checkin') { return (session as WeeklyCheckInSession).resolvedParticipant; } else if (session.workshopType === 'weather') { // For weather sessions, use the owner as the "participant" since it's a personal weather const weatherSession = session as WeatherSession; return { raw: weatherSession.user.name || weatherSession.user.email, matchedUser: { id: weatherSession.user.id, email: weatherSession.user.email, name: weatherSession.user.name, }, }; } else { return (session as MotivatorSession).resolvedParticipant; } } // Get grouping key - use matched user ID if available, otherwise normalized raw string function getGroupKey(session: AnySession): string { const resolved = getResolvedCollaborator(session); // If we have a matched user, use their ID as key (ensures same person = same group) if (resolved.matchedUser) { return `user:${resolved.matchedUser.id}`; } // Otherwise, normalize the raw string return `raw:${resolved.raw.trim().toLowerCase()}`; } // Group sessions by participant (using matched user ID when available) function groupByPerson(sessions: AnySession[]): Map { const grouped = new Map(); sessions.forEach((session) => { const key = getGroupKey(session); const existing = grouped.get(key); if (existing) { existing.push(session); } else { grouped.set(key, [session]); } }); // Sort sessions within each group by date grouped.forEach((sessions) => { sessions.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()); }); return grouped; } export function WorkshopTabs({ swotSessions, motivatorSessions, yearReviewSessions, weeklyCheckInSessions, weatherSessions, teamCollabSessions = [], }: WorkshopTabsProps) { const searchParams = useSearchParams(); const router = useRouter(); const [typeDropdownOpen, setTypeDropdownOpen] = useState(false); // Get tab from URL or default to 'all' const tabParam = searchParams.get('tab'); const activeTab: WorkshopTabType = tabParam && VALID_TAB_PARAMS.includes(tabParam as WorkshopTabType) ? (tabParam as WorkshopTabType) : 'all'; const setActiveTab = (tab: WorkshopTabType) => { const params = new URLSearchParams(searchParams.toString()); if (tab === 'all') { params.delete('tab'); } else { params.set('tab', tab); } router.push(`/sessions${params.toString() ? `?${params.toString()}` : ''}`); }; // Combine and sort all sessions (exclude team collab from main list - they're shown separately) const allSessions: AnySession[] = [ ...swotSessions, ...motivatorSessions, ...yearReviewSessions, ...weeklyCheckInSessions, ...weatherSessions, ].sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()); // Filter based on active tab (for non-byPerson tabs) const filteredSessions = activeTab === 'all' || activeTab === 'byPerson' ? allSessions : activeTab === 'team' ? teamCollabSessions : activeTab === 'swot' ? swotSessions : activeTab === 'motivators' ? motivatorSessions : activeTab === 'year-review' ? yearReviewSessions : activeTab === 'weekly-checkin' ? weeklyCheckInSessions : weatherSessions; // Separate by ownership (for non-team tab: owned, shared, teamCollab) const ownedSessions = filteredSessions.filter((s) => s.isOwner); const sharedSessions = filteredSessions.filter((s) => !s.isOwner && !(s as AnySession & { isTeamCollab?: boolean }).isTeamCollab); const teamCollabFiltered = activeTab === 'all' ? teamCollabSessions : activeTab === 'team' ? teamCollabSessions : []; // Group by person (all sessions - owned and shared) const sessionsByPerson = groupByPerson(allSessions); const sortedPersons = Array.from(sessionsByPerson.entries()).sort((a, b) => a[0].localeCompare(b[0], 'fr') ); return (
{/* Tabs */}
setActiveTab('all')} icon="📋" label="Tous" count={allSessions.length} /> setActiveTab('byPerson')} icon="👥" label="Par personne" count={sessionsByPerson.size} /> {teamCollabSessions.length > 0 && ( setActiveTab('team')} icon="🏢" label="Équipe" count={teamCollabSessions.length} /> )}
{/* Sessions */} {activeTab === 'byPerson' ? ( // By Person View sortedPersons.length === 0 ? (
Aucun atelier pour le moment
) : (
{sortedPersons.map(([personKey, sessions]) => { const resolved = getResolvedCollaborator(sessions[0]); return (

{sessions.length} atelier{sessions.length > 1 ? 's' : ''}

{sessions.map((s) => ( ))}
); })}
) ) : activeTab === 'team' ? ( teamCollabSessions.length === 0 ? (
Aucun atelier de vos collaborateurs d'équipe (non partagés avec vous)
) : (

🏢 Ateliers de l'équipe – non partagés ({teamCollabSessions.length})

En tant qu'admin d'équipe, vous voyez les ateliers de vos collaborateurs qui ne vous sont pas encore partagés.

{teamCollabSessions.map((s) => ( ))}
) ) : filteredSessions.length === 0 ? (
Aucun atelier de ce type pour le moment
) : (
{/* My Sessions */} {ownedSessions.length > 0 && (

📁 Mes ateliers ({ownedSessions.length})

{ownedSessions.map((s) => ( ))}
)} {/* Shared Sessions */} {sharedSessions.length > 0 && (

🤝 Partagés avec moi ({sharedSessions.length})

{sharedSessions.map((s) => ( ))}
)} {/* Team collab sessions (non-shared) - grayed out, admin view only */} {activeTab === 'all' && teamCollabFiltered.length > 0 && (

🏢 Équipe – non partagés ({teamCollabFiltered.length})

{teamCollabFiltered.map((s) => ( ))}
)}
)}
); } function TypeFilterDropdown({ activeTab, setActiveTab, open, onOpenChange, counts, }: { activeTab: WorkshopTabType; setActiveTab: (t: WorkshopTabType) => void; open: boolean; onOpenChange: (v: boolean) => void; counts: Record; }) { const typeTabs = TYPE_TABS.filter((t) => t.value !== 'all'); const current = TYPE_TABS.find((t) => t.value === activeTab) ?? TYPE_TABS[0]; const isTypeSelected = activeTab !== 'all' && activeTab !== 'byPerson'; const totalCount = typeTabs.reduce((s, t) => s + (counts[t.value] ?? 0), 0); const containerRef = useRef(null); useClickOutside(containerRef, () => onOpenChange(false), open); return (
{open && (
{typeTabs.map((t) => ( ))}
)}
); } function TabButton({ active, onClick, icon, label, count, }: { active: boolean; onClick: () => void; icon: string; label: string; count: number; }) { return ( ); } function SessionCard({ session, isTeamCollab = false }: { session: AnySession; isTeamCollab?: boolean }) { const [showDeleteModal, setShowDeleteModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [isPending, startTransition] = useTransition(); // Edit form state const [editTitle, setEditTitle] = useState(session.title); const [editParticipant, setEditParticipant] = useState( session.workshopType === 'swot' ? (session as SwotSession).collaborator : session.workshopType === 'year-review' ? (session as YearReviewSession).participant : session.workshopType === 'weather' ? '' : (session as MotivatorSession).participant ); const workshop = getWorkshop(session.workshopType as WorkshopTypeId); const isSwot = session.workshopType === 'swot'; const isYearReview = session.workshopType === 'year-review'; const isWeeklyCheckIn = session.workshopType === 'weekly-checkin'; const isWeather = session.workshopType === 'weather'; const href = getSessionPath(session.workshopType as WorkshopTypeId, session.id); const participant = isSwot ? (session as SwotSession).collaborator : isYearReview ? (session as YearReviewSession).participant : isWeeklyCheckIn ? (session as WeeklyCheckInSession).participant : isWeather ? (session as WeatherSession).user.name || (session as WeatherSession).user.email : (session as MotivatorSession).participant; const accentColor = workshop.accentColor; const handleDelete = () => { startTransition(async () => { const result = isSwot ? await deleteSwotSession(session.id) : isYearReview ? await deleteYearReviewSession(session.id) : isWeeklyCheckIn ? await deleteWeeklyCheckInSession(session.id) : isWeather ? await deleteWeatherSession(session.id) : await deleteMotivatorSession(session.id); if (result.success) { setShowDeleteModal(false); } else { console.error('Error deleting session:', result.error); } }); }; const handleEdit = () => { startTransition(async () => { const result = isSwot ? await updateSwotSession(session.id, { title: editTitle, collaborator: editParticipant }) : isYearReview ? await updateYearReviewSession(session.id, { title: editTitle, participant: editParticipant, }) : isWeeklyCheckIn ? await updateWeeklyCheckInSession(session.id, { title: editTitle, participant: editParticipant, }) : isWeather ? await updateWeatherSession(session.id, { title: editTitle }) : await updateMotivatorSession(session.id, { title: editTitle, participant: editParticipant, }); if (result.success) { setShowEditModal(false); } else { console.error('Error updating session:', result.error); } }); }; const openEditModal = () => { // Reset form values when opening setEditTitle(session.title); setEditParticipant(participant); setShowEditModal(true); }; const editParticipantLabel = workshop.participantLabel; const cardContent = ( {/* Accent bar */}
{/* Header: Icon + Title + Role badge */}
{workshop.icon}

{session.title}

{!session.isOwner && ( {session.role === 'EDITOR' ? '✏️' : '👁️'} )}
{/* Participant + Owner info */}
{!session.isOwner && ( · par {session.user.name || session.user.email} )}
{/* Footer: Stats + Avatars + Date */}
{/* Stats */}
{isSwot ? ( <> {(session as SwotSession)._count.items} items · {(session as SwotSession)._count.actions} actions ) : isYearReview ? ( <> {(session as YearReviewSession)._count.items} items · Année {(session as YearReviewSession).year} ) : isWeeklyCheckIn ? ( <> {(session as WeeklyCheckInSession)._count.items} items · {new Date((session as WeeklyCheckInSession).date).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', })} ) : isWeather ? ( <> {(session as WeatherSession)._count.entries} membres · {new Date((session as WeatherSession).date).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', })} ) : ( {(session as MotivatorSession)._count.cards}/10 )}
{/* Date */} {new Date(session.updatedAt).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', })}
{/* Shared with */} {session.isOwner && session.shares.length > 0 && (
Partagé
{session.shares.slice(0, 3).map((share) => (
{share.user.name?.split(' ')[0] || share.user.email.split('@')[0]} {share.role === 'EDITOR' ? '✏️' : '👁️'}
))} {session.shares.length > 3 && ( +{session.shares.length - 3} )}
)} ); return ( <>
{cardContent} {/* Edit: owner, EDITOR, or team admin | Delete: owner or team admin only (not EDITOR) */} {(session.isOwner || session.role === 'EDITOR' || session.isTeamCollab) && (
{(session.isOwner || session.isTeamCollab) && ( )}
)}
{/* Edit modal */} setShowEditModal(false)} title="Modifier l'atelier" size="sm" >
{ e.preventDefault(); handleEdit(); }} className="space-y-4" >
setEditTitle(e.target.value)} placeholder="Titre de l'atelier" required />
{!isWeather && ( setEditParticipant(e.target.value)} placeholder={isSwot ? 'Nom du collaborateur' : 'Nom du participant'} required /> )}
{/* Delete confirmation modal */} setShowDeleteModal(false)} title="Supprimer l'atelier" size="sm" >

Êtes-vous sûr de vouloir supprimer l'atelier{' '} "{session.title}" ?

Cette action est irréversible. Toutes les données seront perdues.

); }