'use client'; import { FunctionComponent, useState } from 'react'; import Chip from '@mui/material/Chip'; import Skeleton from '@mui/material/Skeleton'; import Stack from '@mui/material/Stack'; import Typography from '@mui/material/Typography'; import { useLocale, useTranslations } from 'next-intl'; import { useRouter } from 'next/navigation'; import AppButton from '@/components/common/AppButton'; import { AppIcon } from '@/components/common'; import { ticketThreadPath } from '@/constants'; import { useMyTickets } from '@/services/tickets'; import { TICKETS_PAGE_SIZE } from '@/services/tickets/constants'; import type { TicketStatus } from '@/services/tickets/types'; import { formatRelativeTime, formatShamsiDate } from '@/utils'; import { authorLabelKey } from './authorLabel'; import ContactSupportDialog from './ContactSupportDialog'; import EmergencyPlaybookRow from './EmergencyPlaybookRow'; import TicketListCard from './TicketListCard'; export interface TicketInboxScreenProps { role: 'customer' | 'nurse'; } /** Status filter chips — `undefined` (همه) plus every `TicketStatus`, in display order. */ const STATUS_FILTERS: Array = [undefined, 'open', 'closed']; /** * The "My Tickets" inbox — a status filter chip row, the compact emergency playbook row, a "Contact * support" CTA that opens a new ticket (and shows its `referenceCode`), and the paginated ticket list. * Cards show the **`referenceCode`** prominently, the status chip, an unread indicator + last-message * preview + relative last-activity time (mock-tolerant: they degrade gracefully when the real API hasn't * shipped the enrichment fields yet, REQ-059), and a null-safe linked-booking/refund hint. Empty / * loading-skeleton / error→retry states. Shared by the customer and nurse inbox pages (role decides the * thread route + the ticket shell, not the components). * @component TicketInboxScreen */ const TicketInboxScreen: FunctionComponent = ({ role }) => { const t = useTranslations('tickets'); const locale = useLocale(); const router = useRouter(); const [dialogOpen, setDialogOpen] = useState(false); const [status, setStatus] = useState(undefined); const [limit, setLimit] = useState(TICKETS_PAGE_SIZE); const { data, isLoading, isError, isFetching, refetch } = useMyTickets({ status, pageSize: limit, page: 1 }); const tickets = data?.items ?? []; const total = data?.total ?? 0; const selectStatus = (next: TicketStatus | undefined) => { setStatus(next); setLimit(TICKETS_PAGE_SIZE); }; const openThread = (ticketId: number) => router.push(`/${locale}${ticketThreadPath(role, ticketId)}`); return ( {t('title')} setDialogOpen(true)} > {t('contact_support')} setDialogOpen(true)} /> {STATUS_FILTERS.map((filter) => ( selectStatus(filter)} color={status === filter ? 'primary' : 'default'} variant={status === filter ? 'filled' : 'outlined'} /> ))} {isLoading ? ( {[0, 1, 2].map((i) => ( ))} ) : isError ? ( {t('error_body')} refetch()}> {t('retry')} ) : tickets.length === 0 ? ( {t('empty_title')} {t('empty_body')} ) : ( {tickets.map((ticket) => ( openThread(ticket.id)} /> ))} {total > tickets.length ? ( setLimit((current) => current + TICKETS_PAGE_SIZE)} disabled={isFetching} sx={{ alignSelf: 'center' }} > {t('load_more')} ) : null} )} setDialogOpen(false)} role={role} defaultCategory="support" /> ); }; export default TicketInboxScreen;