feat: implement Moving Motivators feature with session management, real-time event handling, and UI components for enhanced user experience
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import Link from 'next/link';
|
||||
import { auth } from '@/lib/auth';
|
||||
import { getSessionsByUserId } from '@/services/sessions';
|
||||
import { getMotivatorSessionsByUserId } from '@/services/moving-motivators';
|
||||
import { Card, CardContent, Badge, Button } from '@/components/ui';
|
||||
import { WorkshopTabs } from './WorkshopTabs';
|
||||
|
||||
export default async function SessionsPage() {
|
||||
const session = await auth();
|
||||
@@ -10,129 +12,87 @@ export default async function SessionsPage() {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sessions = await getSessionsByUserId(session.user.id);
|
||||
// Fetch both SWOT and Moving Motivators sessions
|
||||
const [swotSessions, motivatorSessions] = await Promise.all([
|
||||
getSessionsByUserId(session.user.id),
|
||||
getMotivatorSessionsByUserId(session.user.id),
|
||||
]);
|
||||
|
||||
// Separate owned vs shared sessions
|
||||
const ownedSessions = sessions.filter((s) => s.isOwner);
|
||||
const sharedSessions = sessions.filter((s) => !s.isOwner);
|
||||
// Add type to each session for unified display
|
||||
const allSwotSessions = swotSessions.map((s) => ({
|
||||
...s,
|
||||
workshopType: 'swot' as const,
|
||||
}));
|
||||
|
||||
const allMotivatorSessions = motivatorSessions.map((s) => ({
|
||||
...s,
|
||||
workshopType: 'motivators' as const,
|
||||
}));
|
||||
|
||||
// Combine and sort by updatedAt
|
||||
const allSessions = [...allSwotSessions, ...allMotivatorSessions].sort(
|
||||
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
||||
);
|
||||
|
||||
const hasNoSessions = allSessions.length === 0;
|
||||
|
||||
return (
|
||||
<main className="mx-auto max-w-7xl px-4 py-8">
|
||||
{/* Header */}
|
||||
<div className="mb-8 flex items-center justify-between">
|
||||
<div className="mb-8 flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-foreground">Mes Sessions SWOT</h1>
|
||||
<h1 className="text-3xl font-bold text-foreground">Mes Ateliers</h1>
|
||||
<p className="mt-1 text-muted">
|
||||
Gérez vos ateliers SWOT avec vos collaborateurs
|
||||
Tous vos ateliers en un seul endroit
|
||||
</p>
|
||||
</div>
|
||||
<Link href="/sessions/new">
|
||||
<Button>
|
||||
<span>✨</span>
|
||||
Nouvelle Session
|
||||
</Button>
|
||||
</Link>
|
||||
<div className="flex gap-2">
|
||||
<Link href="/sessions/new">
|
||||
<Button variant="outline">
|
||||
<span>📊</span>
|
||||
Nouveau SWOT
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/motivators/new">
|
||||
<Button>
|
||||
<span>🎯</span>
|
||||
Nouveau Motivators
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sessions Grid */}
|
||||
{sessions.length === 0 ? (
|
||||
{/* Content */}
|
||||
{hasNoSessions ? (
|
||||
<Card className="p-12 text-center">
|
||||
<div className="text-5xl mb-4">📋</div>
|
||||
<div className="text-5xl mb-4">🚀</div>
|
||||
<h2 className="text-xl font-semibold text-foreground mb-2">
|
||||
Aucune session pour le moment
|
||||
Commencez votre premier atelier
|
||||
</h2>
|
||||
<p className="text-muted mb-6">
|
||||
Créez votre première session SWOT pour commencer à analyser les forces,
|
||||
faiblesses, opportunités et menaces de vos collaborateurs.
|
||||
<p className="text-muted mb-6 max-w-md mx-auto">
|
||||
Créez un atelier SWOT pour analyser les forces et faiblesses, ou un Moving Motivators pour découvrir les motivations de vos collaborateurs.
|
||||
</p>
|
||||
<Link href="/sessions/new">
|
||||
<Button>Créer ma première session</Button>
|
||||
</Link>
|
||||
<div className="flex gap-3 justify-center">
|
||||
<Link href="/sessions/new">
|
||||
<Button variant="outline">
|
||||
<span>📊</span>
|
||||
Créer un SWOT
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/motivators/new">
|
||||
<Button>
|
||||
<span>🎯</span>
|
||||
Créer un Moving Motivators
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</Card>
|
||||
) : (
|
||||
<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>
|
||||
)}
|
||||
|
||||
{/* 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>
|
||||
<WorkshopTabs
|
||||
swotSessions={allSwotSessions}
|
||||
motivatorSessions={allMotivatorSessions}
|
||||
/>
|
||||
)}
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user