feat: add group waitlist functionality
- Introduced GroupWaitlistEntry model in Prisma schema to manage waitlisted users for groups. - Implemented API route to add users to the group waitlist with validation. - Enhanced attendee creation logic to handle group capacity and waitlisting scenarios. - Added new problem builders for handling waitlist-related errors. - Updated frontend to support waitlist interactions, including modals for capacity warnings. - Created tests for waitlist functionality, ensuring proper handling of full groups and existing waitlist entries.
This commit is contained in:
@@ -4,6 +4,7 @@ import { useParams, Link } from '@tanstack/react-router';
|
||||
import {
|
||||
CalendarClock,
|
||||
CheckCircle2,
|
||||
Clock,
|
||||
GraduationCap,
|
||||
Loader2,
|
||||
Moon,
|
||||
@@ -32,6 +33,9 @@ export function JoinGroupView() {
|
||||
phone: string | null;
|
||||
} | null>(null);
|
||||
|
||||
// Waitlist state (group is full)
|
||||
const [waitlistInfo, setWaitlistInfo] = useState<{ message: string } | null>(null);
|
||||
|
||||
const inviteQuery = useQuery({
|
||||
queryKey: ['public-invite', token],
|
||||
queryFn: () => getInviteInfo(token),
|
||||
@@ -49,9 +53,15 @@ export function JoinGroupView() {
|
||||
}),
|
||||
onSuccess: (data) => {
|
||||
setErrorMessage(null);
|
||||
if (data.status === 'waitlisted') {
|
||||
setRegisteredAttendee(null);
|
||||
setWaitlistInfo({ message: data.message });
|
||||
return;
|
||||
}
|
||||
setWaitlistInfo(null);
|
||||
setRegisteredAttendee({
|
||||
fullName: data.attendee.fullName,
|
||||
phone: data.attendee.phone,
|
||||
fullName: data.attendee!.fullName,
|
||||
phone: data.attendee!.phone,
|
||||
});
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
@@ -163,8 +173,32 @@ export function JoinGroupView() {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Waitlist Confirmation (group full) */}
|
||||
{waitlistInfo && group ? (
|
||||
<div className="rounded-2xl border border-accent/30 bg-surface p-6 sm:p-8 text-center shadow-xl space-y-5 animate-step-enter">
|
||||
<div className="size-16 rounded-full bg-accent-soft text-accent flex items-center justify-center mx-auto">
|
||||
<Clock className="size-10" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Badge variant="neutral" className="mb-2">
|
||||
Lista de Espera
|
||||
</Badge>
|
||||
<h1 className="text-2xl font-bold text-primary">Cupo completo</h1>
|
||||
<p className="mt-2 text-sm text-foreground/70 leading-relaxed">
|
||||
{waitlistInfo.message}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-foreground/60">
|
||||
El profesor se pondrá en contacto contigo cuando haya un lugar disponible en{' '}
|
||||
<strong className="text-primary font-semibold">{group.name}</strong>.
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Inscription Form */}
|
||||
{!registeredAttendee && group ? (
|
||||
{!registeredAttendee && !waitlistInfo && group ? (
|
||||
<div className="rounded-2xl border border-border bg-surface p-6 sm:p-8 shadow-xl space-y-6">
|
||||
{/* Group Info Header */}
|
||||
<div className="border-b border-border pb-5">
|
||||
|
||||
Reference in New Issue
Block a user