feat: enhance session management with sharing capabilities, real-time event synchronization, and UI updates for session display

This commit is contained in:
Julien Froidefond
2025-11-27 13:34:03 +01:00
parent 9ce2b62bc6
commit 10ff15392f
15 changed files with 1127 additions and 84 deletions

View File

@@ -3,6 +3,7 @@ import Link from 'next/link';
import { auth } from '@/lib/auth';
import { getSessionById } from '@/services/sessions';
import { SwotBoard } from '@/components/swot/SwotBoard';
import { SessionLiveWrapper } from '@/components/collaboration';
import { Badge } from '@/components/ui';
interface SessionPageProps {
@@ -33,6 +34,11 @@ export default async function SessionPage({ params }: SessionPageProps) {
</Link>
<span>/</span>
<span className="text-foreground">{session.title}</span>
{!session.isOwner && (
<Badge variant="accent" className="ml-2">
Partagé par {session.user.name || session.user.email}
</Badge>
)}
</div>
<div className="flex items-start justify-between">
@@ -56,12 +62,20 @@ export default async function SessionPage({ params }: SessionPageProps) {
</div>
</div>
{/* SWOT Board */}
<SwotBoard
{/* Live Session Wrapper */}
<SessionLiveWrapper
sessionId={session.id}
items={session.items}
actions={session.actions}
/>
sessionTitle={session.title}
shares={session.shares}
isOwner={session.isOwner}
canEdit={session.canEdit}
>
<SwotBoard
sessionId={session.id}
items={session.items}
actions={session.actions}
/>
</SessionLiveWrapper>
</main>
);
}

View File

@@ -1,23 +1,8 @@
import Link from 'next/link';
import { auth } from '@/lib/auth';
import { prisma } from '@/services/database';
import { getSessionsByUserId } from '@/services/sessions';
import { Card, CardContent, Badge, Button } from '@/components/ui';
async function getSessions(userId: string) {
return prisma.session.findMany({
where: { userId },
include: {
_count: {
select: {
items: true,
actions: true,
},
},
},
orderBy: { updatedAt: 'desc' },
});
}
export default async function SessionsPage() {
const session = await auth();
@@ -25,7 +10,11 @@ export default async function SessionsPage() {
return null;
}
const sessions = await getSessions(session.user.id);
const sessions = await getSessionsByUserId(session.user.id);
// Separate owned vs shared sessions
const ownedSessions = sessions.filter((s) => s.isOwner);
const sharedSessions = sessions.filter((s) => !s.isOwner);
return (
<main className="mx-auto max-w-7xl px-4 py-8">
@@ -61,45 +50,89 @@ export default async function SessionsPage() {
</Link>
</Card>
) : (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{sessions.map((s) => (
<Link key={s.id} href={`/sessions/${s.id}`}>
<Card hover className="h-full p-6">
<div className="mb-4 flex items-start justify-between">
<div>
<h3 className="font-semibold text-foreground line-clamp-1">
{s.title}
</h3>
<p className="text-sm text-muted">{s.collaborator}</p>
</div>
<span className="text-2xl">📊</span>
</div>
<div className="space-y-8">
{/* My Sessions */}
{ownedSessions.length > 0 && (
<section>
<h2 className="text-lg font-semibold text-foreground mb-4">
📁 Mes sessions ({ownedSessions.length})
</h2>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{ownedSessions.map((s) => (
<SessionCard key={s.id} session={s} />
))}
</div>
</section>
)}
<CardContent className="p-0">
<div className="flex flex-wrap gap-2 mb-4">
<Badge variant="primary">
{s._count.items} items
</Badge>
<Badge variant="success">
{s._count.actions} actions
</Badge>
</div>
<p className="text-xs text-muted">
Mis à jour le{' '}
{new Date(s.updatedAt).toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</p>
</CardContent>
</Card>
</Link>
))}
{/* Shared Sessions */}
{sharedSessions.length > 0 && (
<section>
<h2 className="text-lg font-semibold text-foreground mb-4">
🤝 Sessions partagées avec moi ({sharedSessions.length})
</h2>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{sharedSessions.map((s) => (
<SessionCard key={s.id} session={s} />
))}
</div>
</section>
)}
</div>
)}
</main>
);
}
type SessionWithMeta = Awaited<ReturnType<typeof getSessionsByUserId>>[number];
function SessionCard({ session: s }: { session: SessionWithMeta }) {
return (
<Link href={`/sessions/${s.id}`}>
<Card hover className="h-full p-6">
<div className="mb-4 flex items-start justify-between">
<div className="flex-1 min-w-0">
<h3 className="font-semibold text-foreground line-clamp-1">
{s.title}
</h3>
<p className="text-sm text-muted">{s.collaborator}</p>
{!s.isOwner && (
<p className="text-xs text-muted mt-1">
Par {s.user.name || s.user.email}
</p>
)}
</div>
<div className="flex items-center gap-2">
{!s.isOwner && (
<Badge variant={s.role === 'EDITOR' ? 'primary' : 'warning'}>
{s.role === 'EDITOR' ? '✏️' : '👁️'}
</Badge>
)}
<span className="text-2xl">📊</span>
</div>
</div>
<CardContent className="p-0">
<div className="flex flex-wrap gap-2 mb-4">
<Badge variant="primary">
{s._count.items} items
</Badge>
<Badge variant="success">
{s._count.actions} actions
</Badge>
</div>
<p className="text-xs text-muted">
Mis à jour le{' '}
{new Date(s.updatedAt).toLocaleDateString('fr-FR', {
day: 'numeric',
month: 'long',
year: 'numeric',
})}
</p>
</CardContent>
</Card>
</Link>
);
}