Files
gruperly/apps/web/src/features/join/JoinGroupProvider.tsx
Jose Selesan 94c353f4f2 Added routes
2026-09-25 10:05:56 -03:00

128 lines
3.8 KiB
TypeScript

import { createContext, useContext, useState } from 'react'
import type { ReactNode } from 'react'
import { useMutation, useQuery } from '@tanstack/react-query'
import type { GroupInviteInfoDto } from '@gruperly/shared'
import { ApiError, getInviteInfo, joinViaInvite } from '../../lib/api'
type RegisteredAttendee = {
fullName: string
phone: string | null
}
type JoinGroupContextValue = {
token: string
group: GroupInviteInfoDto | undefined
invitePending: boolean
inviteFailed: boolean
firstName: string
setFirstName: (value: string) => void
lastName: string
setLastName: (value: string) => void
phone: string
setPhone: (value: string) => void
email: string
setEmail: (value: string) => void
errorMessage: string | null
registeredAttendee: RegisteredAttendee | null
waitlistMessage: string | null
submit: () => void
isSubmitting: boolean
}
const JoinGroupContext = createContext<JoinGroupContextValue | null>(null)
export function JoinGroupProvider({ token, children }: { token: string; children: ReactNode }) {
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [phone, setPhone] = useState('')
const [email, setEmail] = useState('')
const [errorMessage, setErrorMessage] = useState<string | null>(null)
const [registeredAttendee, setRegisteredAttendee] = useState<RegisteredAttendee | null>(null)
const [waitlistMessage, setWaitlistMessage] = useState<string | null>(null)
const inviteQuery = useQuery({
queryKey: ['public-invite', token],
queryFn: () => getInviteInfo(token),
enabled: Boolean(token),
retry: 1,
})
const joinMutation = useMutation({
mutationFn: () =>
joinViaInvite(token, {
firstName: firstName.trim(),
lastName: lastName.trim(),
phone: phone.trim(),
email: email.trim() || undefined,
}),
onSuccess: (data) => {
setErrorMessage(null)
if (data.status === 'waitlisted') {
setRegisteredAttendee(null)
setWaitlistMessage(data.message)
return
}
setWaitlistMessage(null)
setRegisteredAttendee({
fullName: data.attendee?.fullName ?? `${firstName.trim()} ${lastName.trim()}`.trim(),
phone: data.attendee?.phone ?? null,
})
},
onError: (error: unknown) => {
if (error instanceof ApiError) {
if (error.problem?.code === 'attendee_already_registered') {
setErrorMessage(
'Ya te encuentras registrado en este grupo con ese número de teléfono. El profesor ya tiene tus datos.',
)
return
}
setErrorMessage(error.message || 'Error al procesar la inscripción.')
return
}
if (error instanceof Error) {
setErrorMessage(error.message)
return
}
setErrorMessage('Ocurrió un error inesperado. Por favor, intenta de nuevo.')
},
})
const submit = () => {
setErrorMessage(null)
if (!firstName.trim() || !lastName.trim() || !phone.trim()) {
setErrorMessage('Por favor completa los campos requeridos (Nombre, Apellido y Teléfono).')
return
}
joinMutation.mutate()
}
const value: JoinGroupContextValue = {
token,
group: inviteQuery.data,
invitePending: inviteQuery.isPending,
inviteFailed: inviteQuery.isError,
firstName,
setFirstName,
lastName,
setLastName,
phone,
setPhone,
email,
setEmail,
errorMessage,
registeredAttendee,
waitlistMessage,
submit,
isSubmitting: joinMutation.isPending,
}
return <JoinGroupContext.Provider value={value}>{children}</JoinGroupContext.Provider>
}
export function useJoinGroup() {
const context = useContext(JoinGroupContext)
if (!context) {
throw new Error('useJoinGroup debe usarse dentro de <JoinGroupProvider>')
}
return context
}