128 lines
3.8 KiB
TypeScript
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
|
|
} |