import { useMemo, useState } from 'react'; import { useRouter } from 'next/navigation'; import { formatCurrency } from '@/packages/shared/src/utils'; import { addHours, isAfter, isSameDay } from 'date-fns'; import { orderBy } from 'lodash'; import { useTranslation } from 'react-i18next'; import { pathsConfig } from '@kit/shared/config'; import { formatDateAndTime } from '@kit/shared/utils'; import { Button } from '@kit/ui/shadcn/button'; import { Card } from '@kit/ui/shadcn/card'; import { toast } from '@kit/ui/sonner'; import { Trans } from '@kit/ui/trans'; import { cn } from '@kit/ui/utils'; import { updateReservationTime } from '~/lib/services/reservation.service'; import { createInitialReservationAction } from '../../_lib/server/actions'; import { EnrichedCartItem } from '../cart/types'; import { ServiceProvider, TimeSlot } from './booking.context'; import { useBooking } from './booking.provider'; const getServiceProviderTitle = ( currentLocale: string, serviceProvider?: ServiceProvider, ) => { if (!serviceProvider) return null; if (currentLocale === 'en') return serviceProvider.jobTitleEn; if (currentLocale === 'ru') return serviceProvider.jobTitleRu; return serviceProvider.jobTitleEt; }; const PAGE_SIZE = 7; const TimeSlots = ({ countryCode, cartItem, onComplete, }: { countryCode: string; cartItem?: EnrichedCartItem; onComplete?: () => void; }) => { const [currentPage, setCurrentPage] = useState(1); const { t, i18n: { language: currentLocale }, } = useTranslation(); const booking = useBooking(); const router = useRouter(); const selectedDate = booking.selectedDate ?? new Date(); const filteredBookings = useMemo( () => orderBy( booking?.timeSlots?.filter(({ StartTime }) => { const firstAvailableTimeToSelect = isSameDay(selectedDate, new Date()) ? addHours(new Date(), 0.5) : selectedDate; return isAfter(StartTime, firstAvailableTimeToSelect); }) ?? [], 'StartTime', 'asc', ), [booking.timeSlots, selectedDate], ); const totalPages = Math.ceil(filteredBookings.length / PAGE_SIZE); const paginatedBookings = useMemo(() => { const startIndex = (currentPage - 1) * PAGE_SIZE; const endIndex = startIndex + PAGE_SIZE; return filteredBookings.slice(startIndex, endIndex); }, [filteredBookings, currentPage, PAGE_SIZE]); const handlePageChange = (page: number) => { setCurrentPage(page); }; const generatePageNumbers = () => { const pages = []; const maxVisiblePages = 5; if (totalPages <= maxVisiblePages) { for (let i = 1; i <= totalPages; i++) { pages.push(i); } } else { if (currentPage <= 3) { for (let i = 1; i <= 4; i++) { pages.push(i); } pages.push('...'); pages.push(totalPages); } else if (currentPage >= totalPages - 2) { pages.push(1); pages.push('...'); for (let i = totalPages - 3; i <= totalPages; i++) { pages.push(i); } } else { pages.push(1); pages.push('...'); for (let i = currentPage - 1; i <= currentPage + 1; i++) { pages.push(i); } pages.push('...'); pages.push(totalPages); } } return pages; }; if (!booking?.timeSlots?.length) { return null; } const handleBookTime = async (timeSlot: TimeSlot, comments?: string) => { const selectedService = booking.selectedService; const selectedVariant = selectedService?.variants?.[0]; const syncedService = timeSlot.syncedService; if (!syncedService || !selectedVariant) { return toast.error(t('booking:serviceNotFound')); } const bookTimePromise = createInitialReservationAction( selectedVariant, countryCode, Number(syncedService.id), syncedService?.clinic_id, timeSlot.UserID, timeSlot.SyncUserID, timeSlot.StartTime, booking.selectedLocationId ? booking.selectedLocationId : null, comments, ).then(() => { if (onComplete) { onComplete(); } router.push(pathsConfig.app.cart); }); toast.promise(() => bookTimePromise, { success: , error: , loading: , }); }; const handleChangeTime = async ( timeSlot: TimeSlot, reservationId: number, cartId: string, ) => { const syncedService = timeSlot.syncedService; if (!syncedService) { return toast.error(t('booking:serviceNotFound')); } const bookTimePromise = updateReservationTime({ reservationId, newStartTime: timeSlot.StartTime, newServiceId: Number(syncedService.id), newAppointmentUserId: timeSlot.UserID, newSyncUserId: timeSlot.SyncUserID, newLocationId: booking.selectedLocationId ? booking.selectedLocationId : null, cartId, }); toast.promise(() => bookTimePromise, { success: , error: , loading: , }); if (onComplete) { onComplete(); } }; const handleTimeSelect = async (timeSlot: TimeSlot) => { if (cartItem?.reservation.id) { return handleChangeTime( timeSlot, cartItem.reservation.id, cartItem.cart_id, ); } return handleBookTime(timeSlot); }; return (
{paginatedBookings.map((timeSlot, index) => { const isEHIF = timeSlot.HKServiceID > 0; const serviceProviderTitle = getServiceProviderTitle( currentLocale, timeSlot.serviceProvider, ); const price = booking.selectedService?.variants?.[0]?.calculated_price ?.calculated_amount ?? cartItem?.unit_price; return (
{formatDateAndTime(timeSlot.StartTime.toString())}
{timeSlot.serviceProvider?.name}
{serviceProviderTitle && ( {serviceProviderTitle} )} {isEHIF && {t('booking:ehifBooking')}}
{timeSlot.location?.address}
{formatCurrency({ currencyCode: 'EUR', locale: 'et-EE', value: price ?? '', })}
); })} {!paginatedBookings.length && (

{t('booking:noResults')}

)}
{totalPages > 1 && (
{t('common:pageOfPages', { page: currentPage, total: totalPages, })}
{generatePageNumbers().map((page, index) => ( ))}
)}
); }; export default TimeSlots;