127 lines
4.8 KiB
TypeScript
127 lines
4.8 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
|
|
import { Card, CardBody, CardHeader } from '@/components/heroui/Card'
|
|
import { formatNumber, formatPersonName } from '@/helpers'
|
|
import { formatIranianMobile, formatPersianDate } from '@/lib/formatters'
|
|
import {
|
|
fetchEventGuestListLinks,
|
|
fetchEventInvitedGuests,
|
|
type EventGuestListLink,
|
|
type EventInvitedGuest,
|
|
} from '@/services/eventGuestListAccess'
|
|
import { format, texts } from '@/texts'
|
|
|
|
interface EventGuestListAccessPanelProps {
|
|
eventId: string
|
|
}
|
|
|
|
const EventGuestListAccessPanel = ({ eventId }: EventGuestListAccessPanelProps) => {
|
|
const [links, setLinks] = useState<EventGuestListLink[]>([])
|
|
const [invitedGuests, setInvitedGuests] = useState<EventInvitedGuest[]>([])
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
let cancelled = false
|
|
|
|
const load = async () => {
|
|
try {
|
|
setIsLoading(true)
|
|
setError(null)
|
|
const [nextLinks, nextInvitedGuests] = await Promise.all([fetchEventGuestListLinks(eventId), fetchEventInvitedGuests(eventId)])
|
|
|
|
if (!cancelled) {
|
|
setLinks(nextLinks)
|
|
setInvitedGuests(nextInvitedGuests)
|
|
}
|
|
} catch {
|
|
if (!cancelled) setError(texts.events.guestAccessLoadFailed)
|
|
} finally {
|
|
if (!cancelled) setIsLoading(false)
|
|
}
|
|
}
|
|
|
|
void load()
|
|
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
}, [eventId])
|
|
|
|
return (
|
|
<Card className="admin-surface">
|
|
<CardHeader className="border-b border-consumer-border px-4 py-3 text-sm font-bold text-consumer-text">
|
|
{texts.events.guestAccessTitle}
|
|
</CardHeader>
|
|
<CardBody className="space-y-5 p-3">
|
|
{isLoading ? <p className="text-sm text-secondary-20">{texts.events.guestAccessLoading}</p> : null}
|
|
{error ? <p className="text-sm text-fourth-900">{error}</p> : null}
|
|
{!isLoading && !error ? (
|
|
<>
|
|
<section>
|
|
<div className="mb-2 flex items-center justify-between gap-3">
|
|
<h2 className="text-sm font-bold text-consumer-text">{texts.events.guestListsConnected}</h2>
|
|
<span className="text-xs text-secondary-20">
|
|
{format(texts.events.guestListsCount, { count: formatNumber(links.length) })}
|
|
</span>
|
|
</div>
|
|
{links.length ? (
|
|
<div className="grid gap-2">
|
|
{links.map((link) => (
|
|
<div
|
|
key={link.listId}
|
|
className="rounded-consumer-control border border-consumer-border bg-consumer-surface-muted/60 p-3"
|
|
>
|
|
<p className="text-sm font-semibold text-consumer-text">{link.listName ?? texts.events.guestListFallback}</p>
|
|
<p className="mt-1 text-xs text-secondary-20">
|
|
{link.notifiedAt
|
|
? format(texts.events.guestListNotifiedAt, { date: formatPersianDate(String(link.notifiedAt)) })
|
|
: texts.events.guestListNotNotified}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-secondary-20">{texts.events.guestListsEmpty}</p>
|
|
)}
|
|
</section>
|
|
|
|
<section>
|
|
<div className="mb-2 flex items-center justify-between gap-3">
|
|
<h2 className="text-sm font-bold text-consumer-text">{texts.events.invitedGuestsTitle}</h2>
|
|
<span className="text-xs text-secondary-20">
|
|
{format(texts.events.personCount, { count: formatNumber(invitedGuests.length) })}
|
|
</span>
|
|
</div>
|
|
{invitedGuests.length ? (
|
|
<div className="grid gap-2">
|
|
{invitedGuests.map((guest, index) => (
|
|
<div
|
|
key={guest.userId ?? `${guest.mobile}-${index}`}
|
|
className="rounded-consumer-control border border-consumer-border bg-consumer-surface-muted/60 p-3"
|
|
>
|
|
<p className="text-sm font-semibold text-consumer-text">{formatPersonName(guest.firstName, guest.lastName)}</p>
|
|
<p
|
|
className="mt-1 text-xs text-secondary-20"
|
|
dir="ltr"
|
|
>
|
|
{formatIranianMobile(guest.mobile)}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<p className="text-sm text-secondary-20">{texts.events.invitedGuestsEmpty}</p>
|
|
)}
|
|
</section>
|
|
</>
|
|
) : null}
|
|
</CardBody>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
export default EventGuestListAccessPanel
|