import { useEffect, useMemo, useState } from 'react' import { createPortal } from 'react-dom' import { Plus, Trash2, X, Clock, Check, XCircle } from 'lucide-react' import { useLocale } from '@meshkee/dashboard-ui' import { ApiError } from '../lib/api' import { formatCellForDisplay } from '../lib/cellNumber' import { addWorkshopRegistration, formatWorkshopDate, formatWorkshopRegistrationName, listWorkshopRegistrations, removeWorkshopRegistration, updateWorkshopRegistrationStatus, type WorkshopRegistration, type WorkshopRegistrationStatus, } from '../services/workshopService' import type { BusinessCustomer } from '../services/customerService' import { useToast } from '../context/ToastContext' import { useT } from '../i18n/useT' import { AddWorkshopRegistrationModal } from './AddWorkshopRegistrationModal' import { Tooltip } from './Tooltip' import modalStyles from './VariationsModal.module.css' import styles from './WorkshopRegistrationsModal.module.css' interface WorkshopRegistrationsModalProps { open: boolean workshopId: string workshopTitle: string onClose: () => void onRegistrationAdded?: () => void onRegistrationRemoved?: () => void } const ANIMATION_MS = 220 const STATUS_CONTROLS: { status: WorkshopRegistrationStatus icon: typeof Clock labelKey: 'workshop.registrations.status.pending' | 'workshop.registrations.status.approved' | 'workshop.registrations.status.rejected' className: 'statusBtnPending' | 'statusBtnApprove' | 'statusBtnReject' }[] = [ { status: 'pending', icon: Clock, labelKey: 'workshop.registrations.status.pending', className: 'statusBtnPending', }, { status: 'approved', icon: Check, labelKey: 'workshop.registrations.status.approved', className: 'statusBtnApprove', }, { status: 'rejected', icon: XCircle, labelKey: 'workshop.registrations.status.rejected', className: 'statusBtnReject', }, ] export function WorkshopRegistrationsModal({ open, workshopId, workshopTitle, onClose, onRegistrationAdded, onRegistrationRemoved, }: WorkshopRegistrationsModalProps) { const t = useT() const { showToast } = useToast() const { locale } = useLocale() const dateLocale = locale === 'fa' ? 'fa' : 'en' const [mounted, setMounted] = useState(open) const [closing, setClosing] = useState(false) const [registrations, setRegistrations] = useState([]) const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState('') const [addError, setAddError] = useState('') const [addOpen, setAddOpen] = useState(false) const [isAdding, setIsAdding] = useState(false) const [updatingId, setUpdatingId] = useState(null) const [removingId, setRemovingId] = useState(null) const registeredUserIds = useMemo( () => registrations.map((registration) => registration.userId), [registrations], ) useEffect(() => { if (open) { setMounted(true) setClosing(false) } else if (mounted) { setClosing(true) const timer = setTimeout(() => { setMounted(false) setClosing(false) setAddOpen(false) setAddError('') }, ANIMATION_MS) return () => clearTimeout(timer) } }, [open, mounted]) useEffect(() => { if (!open || !workshopId) return const controller = new AbortController() void loadRegistrations(controller.signal) return () => controller.abort() }, [open, workshopId]) async function loadRegistrations(signal?: AbortSignal) { setIsLoading(true) setError('') try { const data = await listWorkshopRegistrations(workshopId, signal) setRegistrations(data.items) } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return if (err instanceof ApiError) { setError(err.message) } else { setError(t('workshop.registrations.errorLoad')) } } finally { setIsLoading(false) } } async function handleAddCustomer(customer: BusinessCustomer) { setIsAdding(true) setAddError('') try { const result = await addWorkshopRegistration(workshopId, customer.id) setRegistrations((current) => [result.registration, ...current]) setAddOpen(false) showToast(t('workshop.registrations.toast.added'), 'success') onRegistrationAdded?.() } catch (err) { if (err instanceof ApiError) { setAddError(err.message) } else { setAddError(t('workshop.registrations.errorAdd')) } } finally { setIsAdding(false) } } function openAddModal() { setAddError('') setAddOpen(true) } function closeAddModal() { if (isAdding) return setAddOpen(false) setAddError('') } async function handleStatusChange( registrationId: string, status: WorkshopRegistrationStatus, ) { setUpdatingId(registrationId) setError('') try { const result = await updateWorkshopRegistrationStatus( workshopId, registrationId, status, ) setRegistrations((current) => current.map((item) => item.id === registrationId ? result.registration : item, ), ) showToast(t('workshop.registrations.toast.statusUpdated'), 'success') } catch (err) { if (err instanceof ApiError) { setError(err.message) } else { setError(t('workshop.registrations.errorStatus')) } } finally { setUpdatingId(null) } } async function handleRemove(registrationId: string) { setRemovingId(registrationId) setError('') try { await removeWorkshopRegistration(workshopId, registrationId) setRegistrations((current) => current.filter((item) => item.id !== registrationId)) showToast(t('workshop.registrations.toast.removed'), 'success') onRegistrationRemoved?.() } catch (err) { if (err instanceof ApiError) { setError(err.message) } else { setError(t('workshop.registrations.errorRemove')) } } finally { setRemovingId(null) } } useEffect(() => { if (!mounted || closing) return const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape' && !addOpen) onClose() } document.addEventListener('keydown', onKey) return () => document.removeEventListener('keydown', onKey) }, [mounted, closing, addOpen, onClose]) if (!mounted) return null return createPortal( <>
e.stopPropagation()} role="dialog" aria-modal="true" aria-labelledby="workshop-registrations-title" lang={locale === 'fa' ? 'fa' : undefined} dir={locale === 'fa' ? 'rtl' : undefined} >

{t('workshop.registrations.title')}

{workshopTitle}

{error && (

{error}

)} {isLoading ? (

{t('workshop.registrations.loading')}

) : registrations.length === 0 ? (

{t('workshop.registrations.empty')}

) : (
{registrations.map((registration) => ( ))}
{t('workshop.registrations.col.name')} {t('workshop.registrations.col.cell')} {t('workshop.registrations.col.date')} {t('workshop.registrations.col.status')}
{formatWorkshopRegistrationName(registration.user, dateLocale)} {registration.user.cellNumber ? formatCellForDisplay(registration.user.cellNumber) : '—'} {formatWorkshopDate(registration.createdAt, dateLocale)}
{STATUS_CONTROLS.map(({ status, icon: Icon, labelKey, className }) => { const isActive = registration.status === status const isBusy = updatingId === registration.id || removingId === registration.id return ( ) })}
)}
, document.body, ) }