refactor: add SessionPageHeader and apply to all 6 session detail pages

- Create SessionPageHeader component (breadcrumb + editable title + collaborator + badges + date)
- Embed UPDATE_FN map internally, keyed by workshopType — no prop drilling
- Replace duplicated header blocks in sessions, motivators, year-review, weather, weekly-checkin, gif-mood

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 14:15:43 +01:00
parent b1ba43fd30
commit 09a849279b
9 changed files with 191 additions and 281 deletions

View File

@@ -0,0 +1,95 @@
'use client';
import Link from 'next/link';
import { ReactNode } from 'react';
import { getWorkshop, getSessionsTabUrl, WorkshopTypeId } from '@/lib/workshops';
import { Badge } from './Badge';
import { EditableTitle } from './EditableTitle';
import { CollaboratorDisplay } from './CollaboratorDisplay';
import type { ResolvedCollaborator } from '@/services/auth';
import { updateSessionTitle } from '@/actions/session';
import { updateMotivatorSession } from '@/actions/moving-motivators';
import { updateYearReviewSession } from '@/actions/year-review';
import { updateWeatherSession } from '@/actions/weather';
import { updateWeeklyCheckInSession } from '@/actions/weekly-checkin';
import { updateGifMoodSession } from '@/actions/gif-mood';
type UpdateFn = (id: string, title: string) => Promise<{ success: boolean; error?: string }>;
const UPDATE_FN: Record<WorkshopTypeId, UpdateFn> = {
'swot': updateSessionTitle,
'motivators': (id, title) => updateMotivatorSession(id, { title }),
'year-review': (id, title) => updateYearReviewSession(id, { title }),
'weekly-checkin': (id, title) => updateWeeklyCheckInSession(id, { title }),
'weather': (id, title) => updateWeatherSession(id, { title }),
'gif-mood': (id, title) => updateGifMoodSession(id, { title }),
};
interface SessionPageHeaderProps {
workshopType: WorkshopTypeId;
sessionId: string;
sessionTitle: string;
isOwner: boolean;
canEdit: boolean;
ownerUser: { name: string | null; email: string };
date: Date | string;
collaborator?: ResolvedCollaborator | null;
badges?: ReactNode;
}
export function SessionPageHeader({
workshopType,
sessionId,
sessionTitle,
isOwner,
canEdit,
ownerUser,
date,
collaborator,
badges,
}: SessionPageHeaderProps) {
const workshop = getWorkshop(workshopType);
return (
<div className="mb-8">
<div className="flex items-center gap-2 text-sm text-muted mb-2">
<Link href={getSessionsTabUrl(workshopType)} className="hover:text-foreground">
{workshop.labelShort}
</Link>
<span>/</span>
<span className="text-foreground">{sessionTitle}</span>
{!isOwner && (
<Badge variant="accent" className="ml-2">
Partagé par {ownerUser.name || ownerUser.email}
</Badge>
)}
</div>
<div className="flex items-start justify-between">
<div>
<EditableTitle
sessionId={sessionId}
initialTitle={sessionTitle}
canEdit={canEdit}
onUpdate={UPDATE_FN[workshopType]}
/>
{collaborator && (
<div className="mt-2">
<CollaboratorDisplay collaborator={collaborator} size="lg" showEmail />
</div>
)}
</div>
<div className="flex items-center gap-3">
{badges}
<span className="text-sm text-muted">
{new Date(date).toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</span>
</div>
</div>
</div>
);
}

View File

@@ -13,6 +13,7 @@ export {
EditableGifMoodTitle,
} from './EditableTitles';
export { PageHeader } from './PageHeader';
export { SessionPageHeader } from './SessionPageHeader';
export { Input } from './Input';
export { ParticipantInput } from './ParticipantInput';
export { Modal, ModalFooter } from './Modal';