Untitled

 avatar
unknown
plain_text
10 days ago
65 kB
3
Indexable
/* eslint-disable eslint-comments/disable-enable-pair */
/* eslint-disable @typescript-eslint/member-delimiter-style */
/* eslint-disable @typescript-eslint/array-type */
/* eslint-disable @nx/workspace/no-useeffect */
/* eslint-disable @typescript-eslint/consistent-indexed-object-style */
/* eslint-disable @typescript-eslint/promise-function-async */
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
/* eslint-disable @typescript-eslint/no-misused-promises */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @nx/workspace/no-number-coercion */
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
/* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable complexity */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
/* eslint-disable @typescript-eslint/no-unnecessary-condition */
import { useEffect, useMemo, useRef, useState, type ChangeEvent } from 'react'
import { Header } from '../../../components/Header'
import { Footer } from '../../../components/Footer'
import LocationSelector from '../../../components/LocationStep/LocationSelector'
import BookingDetails from '../../../components/BookingDetailsStep/BookingDetails'
import BookingConfirmation from '../../../components/BookingConfirmationStep/BookingConfirmation'
import EmployeeSelector from '../../../components/EmployeeStep/EmployeeSelector'
import DateTimeSelector from '../../../components/DateTimeStep/DateTimeSelector'
import styles from '../index.module.less'
import { useTranslationI18 } from '../../../hooks/useTranslationI18'
import {
  useFindServiceMessageTemplateQuery,
  useServicesByIdsQuery,
  useSelectedStaffUserQuery,
  useOnlineBookableLocationsQuery,
  useInsertNotificationBookingMutation,
  useCreateNotificationStateMutation,
  useNotificationsTypesByTypeQuery,
  Notification_Types_Enum_Enum,
  useAllCompanyStaffQuery,
  useMostVisitedBookingLocationLazyQuery,
  useContactsByEmailLazyQuery,
  useSendSmsToUnauthenticatedContactMutation,
  useResendCodeToEmailMutation,
  useAccountRegistrationMutation,
  useVerifySmsLazyQuery,
  usePatientMedicalFormsQuery,
  useCreateOnlineBookingMutation,
  useSendEmailToUnauthenticatedContactMutation,
  useConnectDepositPolicyQuery,
  useGeocodeAddressLazyQuery,
  type OnlineBookableLocationsQuery,
  useSendEmailWithTagsMutation,
  useOnlineBookableLocationsCountQuery,
  useUserWithAlertPermissionQuery,
  useNotificationSettingTemplateQuery,
  useCreateOneUserActivityLogMutation,
  useCreateContactMetaMutation,
  usePackageDataQuery,
  useBookitProGeneralEmailQuery,
  useBookingEmailTemplatesMutation,
  useCancellationPolicyQuery,
  Company_Services_Deposit_Type,
  PaymentType,
  useListSavedCardsQuery,
  useContactPaymentMethodIdQuery,
  useSendAppointmentConfirmationMailMutation,
  useNotificationSettingLazyQuery,
  useCompanyActiveNotificationSettingsQuery,
  useFindFavoriteMedicalFormsCountQuery,
  useMasterCategoryQuery,
} from '@pabau/graphql'
import {
  companyDetailsSelector,
  useCompanyDetailsStore,
  onlineBookingSettingsSelector,
  useOnlineBookingSettingsStore,
  connectUserSelector,
  useConnectUserStore,
} from '@pabau/heap/connect'
import { useSelectedDataStore } from '../../../store/selectedData'
import { useStep, onlineBookingSteps } from '../../../hooks/useStep'
import { PaymentWrapper } from '../../../components/payment/PaymentWrapper'
import dayjs from 'dayjs'
import GoogleAnalytics from '../../../components/Analytics/GoogleAnalytics'
import { shallow } from 'zustand/shallow'
import { useRouter } from 'next/router'
import useLocationPermissions from '../../../hooks/useLocationPermissions'
import { CustomHead } from '../custom-head-details'
import { useBoolean, useDebounce } from 'usehooks-ts'
import PatientInfo from '../../../components/patientinformatioon/PatientInfo'
import { BackButton } from '../../../components/BackButton'
import { useResponsive } from '@pabau/atlas'
import {
  NotificationType,
  Notification,
} from '@pabau/ui/notification/Notification'
import { SuccessfulLottie } from '@pabau/ui/SuccessfulLottie/SuccessfulLottie'
import { SmsVerification } from '@pabau/ui/sms-verification/SmsVerification'
import { concatenateTruthyValues } from '@pabau/ui/helpers/stringUtils'
import { createCdnLink } from '@pabau/quartz'
import classNames from 'classnames'
import { formatDateFromISO } from '../../../helpers/splitHours'
import { useUserLanguage } from '../../../components/hooks/UserLanguage'
import { isValidDateStringInFormat } from '@pabau/yup/lib/dayjsValidation'
import type { BookingData, BookingInfo } from '../../../types/booking'
import { StepNavigator } from '../../../components/StepNavigator'
import { useSearchParams } from 'next/navigation'
import SelectedItemsFooter, {
  handeLastStepFlag,
} from '../../../components/ServicesStep/SelectedItemsFooter/SelectedItemsFooter'
import {
  useCompanyTimeDetailsParse,
  useConnectScrollButton,
  useSendNotification,
} from '@pabau/captain'
import { prepareTemplateTypeWithLanguage } from '@pabau/quartz'
import { StepNavigatorHeader } from '../../../components/StepNavigator/Header'
import {
  areAllServicesFree,
  calculateDeposit,
  servicesHasPolicies,
} from '@pabau/quartz/juno'
import { getDepositPolicyFromType } from '@pabau/quartz/globus'
import { serviceImage } from '../../../helpers/serviceImage'
import {
  tagBookingFormStep,
  sendGoogleTagEvent,
} from '../../../helpers/sendGoogleTagEvent'
import { stepUrlData } from '../../../components/ServicesStep/SelectedItemsFooter/helpers/stepUrlData'
import { calculateStaffCostTiers } from '../../../helpers/calculateStaffCostTiers'
import { calculateTotalCostWithTiers } from '../../../helpers/calculateTotalCostWithTiers'
import { shouldSkipPaymentScreen } from '../../../helpers/shouldSkipPaymentScreen'
import { buzzFeedVariables } from '../../../helpers/createOneBuzzfeedVariables'
import { useLaunchDarklyFlags } from '@pabau/captain/useLaunchDarklyFlags'
import { sendWebNotifications } from '../../../helpers/bookingNotifications'
import { areServicesEligibleForCancellationPolicy } from '../../../helpers/cancellationPolicy'
import { redirectPageAfterBooking } from '../../../helpers/redirectPageAfterBooking'
import pMap from '@cjs-exporter/p-map'
import { totalDuration } from '../../../hooks/useServicesTotalDuration'
import { createEcommerceData } from '../../../helpers/populateEcommerce'
import {
  redirectToDateTimeStep,
  redirectToEmployeeStep,
  redirectToLocationStep,
  redirectToShiftStep,
} from '../../../helpers/redirectToDesiredStep'
import { DownUpArrowIcon } from '@pabau/idol/DownUpArrowIcon'
import { getPrepaidTypeConfig } from '../../../helpers/getPrepaidAnalyticsConfig'
import RevampDateTimeSelector from '../../../components/DateTimeStep/RevampDateTimeSelector'
import { getCookie } from 'react-use-cookie'
import { equals } from 'remeda'
import { GoogleHeader } from '../../../components/StepNavigator/GoogleHeader'
import { LazyLottie } from '@pabau/atlas'

const {
  masterCategoryStep,
  categoryStep,
  servicesStep,
  locationStep,
  employeeStep,
  dateTimeStep,
  bookingStep,
  paymentStep,
  confirmStep,
  patientInformation,
  packageStep,
  accountStep,
  voucherStep,
} = onlineBookingSteps

export const Index = () => {
  const { t } = useTranslationI18()
  const router = useRouter()
  const searchParam = useSearchParams()
  const prepaidType = searchParam.get('prepaidType')
  const {
    location,
    discover,
    services,
    category,
    staff,
    date: urlDate = '',
    bookingId: bookingIdUrl,
    rescheduleBookingId,
    packageId,
    voucherDetails,
    shiftId,
    packageClickedId,
    withGoogle,
  } = router.query
  const { companyDetails, setDepositPolicy, depositPolicy } =
    useCompanyDetailsStore(companyDetailsSelector, shallow)
  const isFlagActive = useLaunchDarklyFlags()
  const isVoucherActive = isFlagActive(
    'Online Bookings - Gift voucher purchase for someone else'
  )
  const isGTMEventsActive = isFlagActive('GTM events')

  const stripeKey = companyDetails?.BookitProGeneral?.stripe_public_key

  const { onlineBookingSettings } = useOnlineBookingSettingsStore(
    onlineBookingSettingsSelector,
    shallow
  )
  const [CreateOneContactMeta] = useCreateContactMetaMutation()

  const { mobile, tablet, laptop, desktop } = useResponsive()

  const { selectedData, setSelectedData, actionTypes, resetSelectedData } =
    useSelectedDataStore()
  const { connectUser, connectLogin } = useConnectUserStore(
    connectUserSelector,
    shallow
  )
  const {
    currentStep,
    goToNextStep,
    goToPrevStep,
    setStep,
    changeTotalSteps,
    totalSteps,
    setCurrentStep,
  } = useStep(confirmStep, onlineBookingSettings?.show_service_groups)

  const { value: skipLocationStep, setTrue: setSkipLocationStep } =
    useBoolean(false)
  const { value: skipPaymentStep, setTrue: skipPaymentStepTrue } =
    useBoolean(false)

  const [smsPasscodeInput, setSmsPasscodeInput] = useState('')

  const [locationSearchString, setLocationSearchString] = useState('')
  const debounceTypedKey = useDebounce(locationSearchString, 1000)

  const { language } = useUserLanguage()

  const { value: categorySelect, setFalse: setCategorySelectFalse } =
    useBoolean(false)

  const { data: masterCategories } = useMasterCategoryQuery()

  const {
    data: { findManyServiceMessageTemplate: precareTemplates } = {},
  } = useFindServiceMessageTemplateQuery({
    variables: {
      serviceId: selectedData?.services.map((service) => service.id),
    },
    skip: !selectedData?.services?.[0]?.id,
  })

  const [sendTemplate] = useBookingEmailTemplatesMutation()
  const { value: smsVerificationModal, setTrue: openSmsVerificationModal } =
    useBoolean(false)

  const {
    setFalse: smsSendErrorDisable,
    setTrue: smsSendErrorEnable,
    value: smsSend,
  } = useBoolean(false)

  const { setTrue: setOnBackClickTrue } = useBoolean(false)

  const serviceIdMedicalForm = () => {
    const serviceIds = selectedData.services.map((service) => service.id)
    return serviceIds.map((id) => ({ service_id: { contains: String(id) } }))
  }
  const {
    data: { findManyMedicalFormCount = 0 } = {},
  } = useFindFavoriteMedicalFormsCountQuery()
  const {
    data: { findManyMedicalForm: medicalForms = [] } = {},
    loading: isFetchingMedicalForms,
  } = usePatientMedicalFormsQuery({
    skip:
      !onlineBookingSettings?.require_form ||
      (serviceIdMedicalForm().length === 0 && findManyMedicalFormCount === 0) ||
      selectedData.prepaidItems.length > 0,
    variables: {
      where: {
        AND: [
          { deleted_at: { equals: null } },
          {
            OR: [
              {
                AND: [
                  { form_type: { equals: 'questionnaire' } },
                  { is_fav: { equals: 2 } },
                ],
              },
              {
                AND: [
                  { OR: serviceIdMedicalForm() },
                  { bookingRequired: { equals: '1' } },
                ],
              },
            ],
          },
        ],
      },
    },
    onCompleted({ findManyMedicalForm }) {
      if (findManyMedicalForm.length === 0) {
        params.delete('medicalForms')
      } else {
        params.set('medicalForms', 'true')
      }
      router.replace({
        pathname: router.pathname,
        query: params.toString(),
      })
    },
  })
  const [createOneBuzzFeed] = useCreateOneUserActivityLogMutation()
  const [
    fetchMatchedContact,
    { data: cmContactData, loading: cmContactLoading },
  ] = useContactsByEmailLazyQuery()

  const existingClientMatched =
    !cmContactLoading && Boolean(cmContactData?.findFirstCmContact)
  const [sendSms] = useSendSmsToUnauthenticatedContactMutation({
    onCompleted({ sendSmsToUnauthenticatedContact }) {
      // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'.
      if (sendSmsToUnauthenticatedContact.success) {
        Notification(
          NotificationType.success,
          t('notifications.connect.sms.send.successMessage')
        )
        smsSendErrorDisable()
      } else {
        handleResendCodeToEmail()
        smsSendErrorEnable()
      }
    },
    onError() {
      handleResendCodeToEmail()
      smsSendErrorEnable()
    },
  })
  const [resendCodeToEmail] = useResendCodeToEmailMutation()
  const handleResendCodeToEmail = () => {
    if (!cmContactData?.findFirstCmContact?.Email) return
    void resendCodeToEmail({
      variables: {
        companyId: companyDetails?.id,
        contactEmail: cmContactData.findFirstCmContact.Email,
        contactId: cmContactData.findFirstCmContact.ID,
      },
      onCompleted() {
        Notification(
          NotificationType.success,
          t('notifications.connect.email.send.successMessage')
        )
      },
    })
  }
  usePackageDataQuery({
    variables: {
      packageId: Number.parseInt(packageId?.toString() ?? '0.00'),
    },
    skip: !packageId,
    onCompleted({ findFirstPackage }) {
      if (findFirstPackage?.id == null || findFirstPackage?.product_id == null)
        return
      setSelectedData(actionTypes.setPrepaidItems, [
        {
          id: findFirstPackage.product_id,
          name: findFirstPackage.name,
          amount: findFirstPackage.price,
          type: 'package',
          package_id: findFirstPackage?.id,
          tax: findFirstPackage?.Product?.Tax,
          packageData: {
            count: Number.parseInt(findFirstPackage.session_count),
            categoryId:
              // @ts-expect-error unchecked index migration
              findFirstPackage.ServiceProduct?.CompanyService[0].group_id,
            masterCategoryId:
              findFirstPackage?.ServiceProduct?.InvCategory?.MasterCategory?.id,
            serviceId: findFirstPackage?.ServiceProduct?.CompanyService[0]?.id,
          },
        },
      ])
      setSelectedData(actionTypes.SET_CATEGORY_ID, undefined)
      setSelectedData(actionTypes.SET_COST_TIERS, [])
      setSelectedData(actionTypes.SET_SELECTED_SERVICES, [])
      setSelectedData(actionTypes.SET_TOTAL_COST, 0)
      setSelectedData(
        actionTypes.setTotalPrepaidItemsCost,
        findFirstPackage.price
      )
      nextStep()
    },
  })

  const {
    data: { findFirstBookitProGeneral } = {},
  } = useBookitProGeneralEmailQuery({
    onError: (error) => {
      console.error(error)
    },
  })

  const [sendEmail] = useSendEmailToUnauthenticatedContactMutation({
    onCompleted({ sendEmailToUnauthenticatedContact }) {
      if (sendEmailToUnauthenticatedContact?.success) {
        Notification(
          NotificationType.success,
          t('notifications.connect.email.send.successMessage')
        )
      } else {
        Notification(
          NotificationType.error,
          t('notifications.connect.email.send.failedMessage')
        )
      }
    },
    onError() {
      Notification(
        NotificationType.error,
        t('notifications.connect.email.send.failedMessage')
      )
    },
  })

  const [
    runSmsVerification,
    { error: verificationStatus, loading: verificationLoading },
  ] = useVerifySmsLazyQuery()

  const verifySmsCode = () => {
    void runSmsVerification({
      variables: {
        // @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'.
        contactId: cmContactData?.findFirstCmContact.ID,
        passcode: smsPasscodeInput,
        companyId: companyDetails?.id,
      },
      onCompleted(data) {
        if (data.VerifySms?.success) {
          void createAccountAndSendNotification(selectedData.members)
        }
      },
    })
  }

  const onConfirmBookingDetails = async (members: BookingData['members']) => {
    if (!members?.[0]) {
      return
    }

    const { last_name, email, mobile } = members[0]

    if (!last_name) {
      return
    }

    const { data } = await fetchMatchedContact({
      variables: {
        companyId: companyDetails?.id,
        lastName: last_name,
        email,
        mobile,
      },
    })

    if (data?.findFirstCmContact) {
      return
    }

    await createAccountAndSendNotification(members)
  }

  const createAccountAndSendNotification = async (
    members: BookingData['members']
  ) => {
    if (!members?.[0]) {
      return
    }

    const createdAccount = await createAccount({
      variables: {
        input: {
          accountData: {
            ...members[0],
            location_id:
              typeof location === 'string' ? Number.parseInt(location) : 0,
          },
        },
      },
    })
    if (
      createdAccount.data?.AccountRegistration?.CreatedAccount.status === 409
    ) {
      return Notification(
        NotificationType.error,
        createdAccount.data?.AccountRegistration?.CreatedAccount
          .responseMessage ?? ''
      )
    }

    if (!createdAccount.data?.AccountRegistration?.CreatedAccount) {
      return
    }

    const { status, jwtToken, id } =
      createdAccount.data.AccountRegistration.CreatedAccount

    setSelectedData(actionTypes.SET_MEMBERS, [
      { ...members[0], contact_id: id },
    ])

    if (!jwtToken) {
      return
    }

    if (status === 409) {
      console.log('error 409')
      return
    }

    if (status === 200) {
      await connectLogin(jwtToken)
    }

    const { data: notificationMessageData } = await getNotificationMessage()

    if (connectRegistrationNotification) {
      await insertNotificationsOneMutation({
        variables: {
          destination: companyDetails?.id.toString(),
          template: 'connect_registration',
          variables: {
            client_name: members[0].first_name,
            client_lastname: members[0].last_name,
          },
          sentBy: id,
        },
      })
    }
    await CreateOneContactMeta({
      variables: {
        data: {
          Contact: {
            connect: {
              ID: id,
            },
          },
          meta_name: 'connect_account_confirmed',
          meta_value: '0',
        },
      },
    })
    await sendAppointmentConfirmation({
      variables: {
        to: createdAccount?.data?.AccountRegistration?.CreatedAccount?.email,
        subject: t('connect.account.emailVerification'),
        html: notificationMessageData?.default.at(0)?.email_template ?? '',
        text: notificationMessageData?.default.at(0)?.email_template ?? '',
        contactId:
          createdAccount?.data?.AccountRegistration?.CreatedAccount?.id,
        replyTo: {
          email: '',
          name: '',
        },
        fromConnect: true,
      },
    })
  }

  const queryVariables = {
    where: {
      payment_protection: { equals: 1 },
      is_active: { equals: 1 },
    },
  }
  const {
    data: { findFirstCancellationPolicy } = {},
  } = useCancellationPolicyQuery({
    variables: queryVariables,
  })

  const handleSmsPasscodeInput = (
    event: ChangeEvent<HTMLInputElement>
  ): void => {
    setSmsPasscodeInput(event.target.value)
  }

  const sendSmsToUnauthenticatedContact = () => {
    if (!cmContactData?.findFirstCmContact) return
    return sendSms({
      variables: {
        to: cmContactData?.findFirstCmContact.Mobile,
        from: 'Pabau',
        companyId: companyDetails?.id,
        contactId: cmContactData?.findFirstCmContact.ID,
      },
      fetchPolicy: 'no-cache',
    })
  }

  const sendEmailToUnauthenticatedContact = async () => {
    if (!cmContactData?.findFirstCmContact) return
    await sendEmail({
      variables: {
        contactId: cmContactData.findFirstCmContact.ID,
      },
      fetchPolicy: 'no-cache',
    })
  }

  const { canLocationPerformService } = useLocationPermissions()

  const [connectRegistrationNotification, setConnectRegistrationNotification] =
    useState(false)
  const [allCompanyStaff, setAllCompanyStaff] = useState([])

  if (!selectedData?.categoryID && category) {
    setSelectedData(actionTypes.SET_CATEGORY_ID, Number(category))
  }
  if (
    urlDate &&
    !selectedData?.dateTime &&
    isValidDateStringInFormat(String(urlDate), 'YYYY-MM-DDTHH:mm:ssZ')
  ) {
    const urlFullDate = dayjs(String(urlDate), 'YYYY-MM-DDTHH:mm:ss')
    if (!urlFullDate?.isBefore(dayjs())) {
      setSelectedData(actionTypes.SET_DATETIME, urlFullDate)
    }
  }
  const handleSmsVerificationModal = () => {
    openSmsVerificationModal()
  }

  const [insertNotificationsOneMutation] = useInsertNotificationBookingMutation(
    {
      onCompleted({ insert_notifications }) {
        // @ts-expect-error TS(2339) FIXME: Property 'template' does not exist on type
        const { id, template } = insert_notifications?.returning[0]
        if (template === 'connect_registration') {
          for (const staff of allCompanyStaff) {
            insertNotificationState({
              variables: {
                companyId: connectUser?.company,
                // @ts-expect-error TS(2339) FIXME: Property 'pabau_id' does not exist on type 'never'.
                userId: staff.pabau_id,
                notificationId: id,
                createdAt: dayjs().utc(),
              },
            })
          }
        } else {
          insertNotificationState({
            variables: {
              companyId: connectUser?.company,
              userId: selectedData?.employee?.User?.id,
              notificationId: id,
              createdAt: dayjs().utc(),
            },
          })
        }
      },
    }
  )
  const [insertNotificationState] = useCreateNotificationStateMutation()

  const handleStepUrl = (route: { [key: string]: string }) => {
    for (const [key, value] of Object.entries(route)) {
      router.query[key] = value
    }
    router.push(router)
  }

  useNotificationsTypesByTypeQuery({
    variables: {
      type: 'connect_registration' as Notification_Types_Enum_Enum,
    },
    onCompleted({ notification_types }) {
      setConnectRegistrationNotification(
        // @ts-expect-error unchecked index migration
        notification_types[0]?.notification_toggles[0]?.enabled
      )
    },
  })
  const mainBodyRef = useRef<HTMLDivElement>(null)
  const { iconUp } = useConnectScrollButton(mainBodyRef, desktop, laptop)
  const [isEmployeePicked, setIsEmployeePicked] = useState(false)
  const handleEmployeePicked = (value: boolean) => {
    setIsEmployeePicked(value)
  }

  const handleArrowClick = (): void => {
    mainBodyRef.current?.scroll({
      top: iconUp ? 0 : mainBodyRef.current?.scrollHeight,
      behavior: 'smooth',
    })
  }

  useEffect(() => {
    if (connectUser?.contact_id && bookingIdUrl) {
      setStep(paymentStep)
      return
    }
    if (prepaidType) {
      // @ts-expect-error TODO CONNECT BIG FIX
      setSelectedData(actionTypes.setPrepaidType, prepaidType)
      onSelected(paymentStep)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  useEffect(() => {
    if (currentStep === confirmStep) {
      mainBodyRef.current?.scrollTo(0, 0)

      const { items, type, stage, pathway, appointmentId } =
        getPrepaidTypeConfig(prepaidType, selectedData)

      const ecommerceData = createEcommerceData(
        items,
        companyDetails.details.currency,
        type
      )

      isGTMEventsActive
        ? sendGoogleTagEvent(
            {
              event: 'purchase',
              stage: stage,
              pathway: pathway,
              clientId: connectUser?.contact_id?.toString() ?? '',
              ...(appointmentId && { appointmentId: bookingIdUrl?.toString() }),
            },
            undefined,
            undefined,
            ecommerceData
          )
        : tagBookingFormStep(
            currentStep,
            connectUser?.contact_id?.toString() ?? '',
            selectedData.services,
            selectedData.location?.name,
            bookingIdUrl?.toString()
          )
    } else {
      !isGTMEventsActive &&
        tagBookingFormStep(
          currentStep,
          connectUser?.contact_id?.toString() ?? '',
          selectedData.services,
          selectedData.location?.name
        )
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [currentStep])

  // to be investigated, causing refresh issue
  // useEffect(() => {
  //   const totalQueryParams = Object.keys(router.query).length
  //   if (totalQueryParams === 1 && 'company_slug' in router.query) {
  //     setCurrentStep(masterCategoryStep)
  //   }
  // }, [currentStep, router.query, setCurrentStep])

  const nextStep = () => {
    !isGTMEventsActive &&
      sendGoogleTagEvent(
        {
          event: 'stepInteraction',
          formStep: 'service',
          stepAction: `next button`,
        },
        selectedData.services
      )
    handleStepUrl(
      stepUrlData({
        services: '',
        categoryId: 0,
        canLocationPerformService,
        isPrepaidItem: Boolean(prepaidType),
        bookableLocations: selectedData.bookableLocations,
        location: String(selectedData?.bookableLocations?.[0]?.id.toString()),
      })
    )
    if (prepaidType && connectUser?.id) {
      onSelected(paymentStep)
      return
    }
    if (!connectUser?.id) {
      handeLastStepFlag()
      onSelected(bookingStep)
      return
    }
    onSelected()
  }

  const googleTagId =
    companyDetails?.CompanyMeta.find(
      ({ meta_name }) => meta_name === 'google_tag_manager_id'
    )?.meta_value ?? ''
  const googleAnalyticsId =
    companyDetails?.CompanyMeta.find(
      ({ meta_name }) => meta_name === 'google_analytics_id'
    )?.meta_value ?? ''
  const metaPixelId =
    companyDetails?.CompanyMeta.find(
      ({ meta_name }) => meta_name === 'meta_pixel_id'
    )?.meta_value ?? ''
  const metaPixelEvent =
    companyDetails?.CompanyMeta.find(
      ({ meta_name }) => meta_name === 'meta_pixel_event'
    )?.meta_value ?? ''

  const [getFavoriteLocation, { data: favoriteLocation }] =
    useMostVisitedBookingLocationLazyQuery()

  const [getNotificationMessage] = useNotificationSettingLazyQuery({
    variables: {
      type: 'verified-email',
      companyID: 0,
    },
  })

  const { data: requestFeedbackNotifications } =
    useCompanyActiveNotificationSettingsQuery({
      skip: !companyDetails?.id || urlDate === '' || currentStep < bookingStep,
      variables: {
        type: 'request-feedback',
      },
    })

  const [createAccount] = useAccountRegistrationMutation()

  const {
    data: { findFirstPaymentProtectionStripe } = {},
  } = useContactPaymentMethodIdQuery({
    skip: !connectUser?.contact_id,
    ...(connectUser?.contact_id !== undefined && {
      variables: {
        contactId: connectUser.contact_id,
      },
    }),
  })

  const [bookingData, setBookingData] = useState<BookingInfo[]>(
    bookingIdUrl === null || bookingIdUrl === undefined || bookingIdUrl === ''
      ? []
      : [{ id: Number.parseInt(String(bookingIdUrl)) }]
  )
  const [contactId, setContactId] = useState<number>()
  const {
    data: { listSavedCards = [] } = {},
  } = useListSavedCardsQuery({
    skip: !connectUser?.contact_id,
    variables: {
      type: PaymentType.Stripe,
      contactId: connectUser?.contact_id ?? 0,
    },
  })
  const isActiveCancellationPolicy = Boolean(
    findFirstCancellationPolicy?.is_active &&
      selectedData.services.length > 0 &&
      isFlagActive('Cancellation Policy') &&
      areServicesEligibleForCancellationPolicy(
        selectedData.services,
        depositPolicy
      ) &&
      (listSavedCards.length === 0 ||
        (!listSavedCards.some((card) => card?.is_expired === false) &&
          (!findFirstPaymentProtectionStripe?.paymentMethodId ||
            findFirstPaymentProtectionStripe?.paymentMethodId === '')))
  )
  const handleError = (error: Error) => {
    const errorHandlers = {
      'User is suspended': () => {
        Notification(
          NotificationType.error,
          t('connect.onlinebooking.user.suspended.message')
        )
      },
      'Package not found': () => {
        Notification(
          NotificationType.error,
          t('connect.onlinebooking.user.suspended.message')
        )
      },
      'Package Service conflict': () => {
        Notification(
          NotificationType.error,
          t('connect.onlinebooking.user.suspended.message')
        )
      },
      'Package is expired': () => {
        Notification(
          NotificationType.error,
          t('connect.onlinebooking.user.suspended.message')
        )
      },
      Location: redirectToLocationStep,
      Employee: redirectToEmployeeStep,
      Datetime: redirectToDateTimeStep,
      Shift: redirectToShiftStep,
    }

    for (const [key, handler] of Object.entries(errorHandlers)) {
      if (error.message.includes(key)) {
        handler()
        break
      }
    }
  }

  const { parseDate, parseTime } = useCompanyTimeDetailsParse()
  const sendNotification = useSendNotification()
  const [createBooking] = useCreateOnlineBookingMutation({
    async onCompleted(data) {
      setContactId(contactId)
      setBookingData(data.public_createOnlineBooking)
      if (
        shouldSkipPaymentScreen({
          calculatedDeposit: calculateDeposit(
            selectedData,
            isFlagActive('Business detail tax setting'),
            companyDetails?.details?.is_tax_included,
            depositPolicy
          ),
          areAllServicesFree: areAllServicesFree(selectedData.services),
          depositAmount: depositPolicy?.amount,
          isActiveCancellationPolicy: Boolean(isActiveCancellationPolicy),
          hasStripeKey: Boolean(stripeKey),
          servicesPolicies: servicesHasPolicies(selectedData.services),
          flatDepositZero: selectedData.services.every(
            (service) =>
              service.deposit_type === Company_Services_Deposit_Type.Amount &&
              service.deposit_amount === 0
          ),
          hasPackageClicked: Boolean(packageClickedId),
        }) &&
        data.public_createOnlineBooking?.length > 0
      ) {
        await skippedPaymentScreen(data.public_createOnlineBooking)
        if (medicalForms.length > 0) {
          setStep(patientInformation)
        }
      } else {
        handleStepUrl({
          bookingId: String(data.public_createOnlineBooking?.[0]?.id),
        })
        onSelected()
      }
    },
    onError(error) {
      handleError(error)
    },
  })
  const createBookingAndNavigate = async () => {
    const loggedInContactId =
      selectedData.members?.at(0)?.contact_id ?? connectUser?.contact_id
    const skipPaymentStep = shouldSkipPaymentScreen({
      calculatedDeposit: calculateDeposit(
        selectedData,
        isFlagActive('Business detail tax setting'),
        companyDetails?.details?.is_tax_included,
        depositPolicy
      ),
      areAllServicesFree: areAllServicesFree(selectedData.services),
      depositAmount: depositPolicy?.amount,
      isActiveCancellationPolicy: Boolean(isActiveCancellationPolicy),
      hasStripeKey: Boolean(stripeKey),
      servicesPolicies: servicesHasPolicies(selectedData.services),
      flatDepositZero: selectedData.services.every(
        (service) =>
          service.deposit_type === Company_Services_Deposit_Type.Amount &&
          service.deposit_amount === 0
      ),
      hasPackageClicked: Boolean(packageClickedId),
    })
    if (
      !loggedInContactId ||
      !selectedData?.employee?.User?.id ||
      !selectedData.location?.id
    )
      return
    await createBooking({
      variables: {
        bookingId: bookingData[0]?.id ?? 0,
        companyId: companyDetails?.id,
        userId: selectedData?.employee?.User?.id,
        serviceIds: selectedData.services.map((service) => service.id),
        startDate: selectedData.dateTime?.utc(true).format(),
        endDate: selectedData.dateTime
          ?.utc(true)
          .add(
            totalDuration(
              selectedData?.services,
              selectedData?.employee?.User?.id
            ),
            'minutes'
          )
          .format(),
        locationId: selectedData.location?.id,
        comment: selectedData.comment,
        contactId: loggedInContactId,
        ...(withGoogle === 'true' && getCookie('_rwg_token')
          ? { rwgToken: getCookie('_rwg_token') }
          : {}),
        sendSurvey:
          requestFeedbackNotifications?.notification_settings.at(-1)
            ?.email_on ||
          requestFeedbackNotifications?.notification_settings.at(-1)?.sms_on
            ? 1
            : 0,
        skipPaymentStep,
        shiftId: Number.parseInt(shiftId?.toString() ?? '0'),
        createdBy: selectedData?.employee?.User?.id,
        ...(packageClickedId && {
          packageClickedId: Number.parseInt(packageClickedId.toString()),
        }),
        where:
          discover === 'true'
            ? 'discover'
            : withGoogle === 'true'
              ? 'google-reserve'
              : '',
      },
    })
  }

  useAllCompanyStaffQuery({
    variables: {
      companyId: companyDetails?.id,
    },
    onCompleted({ findFirstCompany }) {
      // @ts-expect-error TS(2345) FIXME: Type 'undefined' is not assignable to type 'SetStateAction<never[]>'.
      setAllCompanyStaff(findFirstCompany?.CmStaffGeneral)
    },
  })

  const [geoCodeAddress] = useGeocodeAddressLazyQuery()

  const handleLocations = async (
    companyBranches: OnlineBookableLocationsQuery['findManyCompanyBranch']
  ) =>
    pMap(
      companyBranches,
      async (item) => {
        const { address, city, region, county } = item
        const fullAddress = `${address ?? ''} ${city ?? ''} ${
          region ?? ''
        } ${county}`

        const { data } = await geoCodeAddress({
          variables: {
            address: fullAddress,
          },
        })

        return {
          ...item,
          lat: data?.getAddressGeoData?.latitude,
          lng: data?.getAddressGeoData?.longitude,
        }
      },
      { concurrency: 2 }
    )

  const {
    data: { findManyCompanyBranchCount: locationsCount = 0 } = {},
  } = useOnlineBookableLocationsCountQuery()

  const { value: showButton, setFalse: setFalseShowButton } = useBoolean(true)
  const {
    value: isLoadingLocations,
    setTrue: startLoadingLocations,
    setFalse: stopLoadingLocations,
  } = useBoolean(true)
  const [sendAppointmentConfirmation] = useSendEmailWithTagsMutation()
  const [sendEmailSmsConfirmation] =
    useSendAppointmentConfirmationMailMutation()

  const [getAppointmentNotification] = useNotificationSettingLazyQuery({
    variables: {
      type: prepareTemplateTypeWithLanguage(
        'new-appointment',
        companyDetails?.details?.language ?? 'english'
      ),
      companyID: companyDetails?.id,
    },
  })

  const [locationsLimit, setLocationsLimit] = useState<number>(50)
  const { value: hideFindNearestButton, setValue: setHideFindNearestButton } =
    useBoolean(false)

  const getQueryVariables = useMemo(
    () => ({
      limit: locationsLimit,
      nameFilter: {
        contains: debounceTypedKey,
      },
    }),
    [locationsLimit, debounceTypedKey]
  )

  const handleShowMore = () => {
    setFalseShowButton()
    if (locationsLimit < locationsCount && !showButton) {
      setLocationsLimit((prevState) => prevState + 50)
    }
  }

  useOnlineBookableLocationsQuery({
    variables: {
      ...getQueryVariables,
    },
    async onCompleted({ findManyCompanyBranch }) {
      const updatedLocations = await handleLocations(findManyCompanyBranch)

      if (!equals(updatedLocations, selectedData.bookableLocations)) {
        const geoAddress = updatedLocations.every(
          (item) => item.lat === 0 && item.lng === 0
        )
        setHideFindNearestButton(geoAddress)
        // @ts-expect-error TODO CONNECT BIG FIX
        setSelectedData(actionTypes.SET_BOOKABLE_LOCATIONS, updatedLocations)
      }
      stopLoadingLocations()
      if (!selectedData?.location) {
        const urlLocation = findManyCompanyBranch?.find(
          ({ id }) => id === Number(location)
        )
        if (
          !urlLocation &&
          locationsLimit !== 0 &&
          currentStep > locationStep
        ) {
          setLocationsLimit(0)
          startLoadingLocations()
        }
        // @ts-expect-error TODO CONNECT BIG FIX
        if (urlLocation) setSelectedData(actionTypes.SET_LOCATION, urlLocation)
      }
      if (
        findManyCompanyBranch.filter((location) =>
          canLocationPerformService(location.id)
        ).length === 1
      ) {
        changeTotalSteps()
        setSkipLocationStep()
      }
      if (connectUser && onlineBookingSettings?.most_visited_location) {
        getFavoriteLocation({
          variables: {
            contactId: connectUser.contact_id ?? 0,
          },
        })
      }
    },
  })

  useSelectedStaffUserQuery({
    variables: {
      companyId: companyDetails?.id,
      employeeId: Number(staff),
    },
    skip: !!selectedData?.employee || !staff || currentStep < employeeStep,
    onCompleted({ findFirstCmStaffGeneral }) {
      if (!findFirstCmStaffGeneral || selectedData?.employee) return
      // @ts-expect-error TODO CONNECT BIG FIX
      setSelectedData(actionTypes.SET_EMPLOYEE, findFirstCmStaffGeneral)
      if (
        findFirstCmStaffGeneral.User?.id &&
        selectedData?.services?.length > 0
      ) {
        const staffCostTier = calculateStaffCostTiers(
          findFirstCmStaffGeneral.User.id,
          selectedData?.services,
          selectedData?.costTiers
        )
        setSelectedData(actionTypes.SET_COST_TIERS, staffCostTier)
        setSelectedData(
          actionTypes.SET_TOTAL_COST,
          (selectedData.totalPrepaidItemsCost ?? 0) +
            calculateTotalCostWithTiers(staffCostTier)
        )
      }
    },
  })

  useServicesByIdsQuery({
    variables: {
      companyId: companyDetails?.id,
      serviceIds: String(services)?.split(',')?.map(Number),
    },
    skip:
      !services ||
      !!selectedData?.services?.length ||
      currentStep < locationStep,
    onCompleted({ findManyCompanyService }) {
      setSelectedData(
        actionTypes.SET_SELECTED_SERVICES,
        // @ts-expect-error TODO CONNECT BIG FIX
        findManyCompanyService.map((item) => ({
          ...item,
          deposit_amount: packageClickedId ? 0 : item.deposit_amount,
          Product: {
            ...item.Product,
            image: serviceImage(
              item.Product?.image,
              item.category?.image,
              item.category?.masterCategory?.image
            ),
          },
        }))
      )
      setSelectedData(actionTypes.SET_COST_TIERS, [
        ...(selectedData.costTiers ?? []),
        ...(findManyCompanyService?.map(
          ({
            id,
            price: servicePrice,
            locationPrices: locations,
            staffPrices: staffs,
          }) => {
            const locationPrice =
              locations?.find(
                ({ location_id }) =>
                  Number.parseInt(String(location)) === location_id
              )?.price ?? 0
            const staffPrice =
              staffs?.find(
                ({ user_id }) => selectedData?.employee?.User?.id === user_id
              )?.price ?? 0
            return {
              service_id: id,
              regular_price: servicePrice,
              location_price: locationPrice,
              staff_price: staffPrice,
            }
          }
        ) ?? []),
      ])
      setSelectedData(
        actionTypes.SET_TOTAL_COST,
        (selectedData?.totalCost ?? 0) +
          findManyCompanyService?.reduce(
            (final, { price = 0, staffPrices, locationPrices }) => {
              let nextPrice = price
              if (locationPrices.length > 0 && location) {
                const locationPrice =
                  locationPrices.find(
                    ({ location_id }) =>
                      location_id === Number.parseFloat(String(location))
                  )?.price ?? 0
                if (locationPrice && locationPrice > 0)
                  nextPrice = locationPrice
              }
              if (staffPrices.length > 0 && staff) {
                const staffPrice =
                  staffPrices.find(
                    ({ user_id }) => user_id === selectedData.employee?.User.id
                  )?.price ?? 0
                if (staffPrice && staffPrice > 0) nextPrice = staffPrice
              }
              return final + nextPrice
            },
            0
          )
      )
    },
  })

  useConnectDepositPolicyQuery({
    onCompleted(data) {
      const depositPolicyData = getDepositPolicyFromType(
        data?.deposit_policy[0]?.policy_type,
        data?.deposit_policy[0]?.custom_rate
      )
      if (!equals(depositPolicyData, depositPolicy)) {
        setDepositPolicy(data)
      }
    },
  })

  const params = new URLSearchParams(router.query as unknown as URLSearchParams)
  let currentStepUrl = ''
  const removeQueryParamOnBackClick = async (queryParam: string) => {
    const { pathname, query = {} } = router
    const params = new URLSearchParams(query as unknown as URLSearchParams)
    const lastStep = params.get('lastStep')
    const category = params.get('category')
    params.delete('hidePrepaid')
    params.delete('viewAll')
    params.delete('subCategory')
    params.delete('cursorCategory')
    params.delete('prepaidFlow')
    params.delete('waitList')
    if (queryParam === 'prepaidFlow' && !query?.voucherDetails?.toString()) {
      params.delete('prepaidType')
    }

    if (query?.voucherDetails?.toString()) {
      params.delete('voucherDetails')
    }

    if (queryParam === 'category') {
      params.delete('category')
      params.delete('groupCategory')
      params.delete('hideButton')
      params.delete('cursorService')
      params.delete('cursorMasterCategory')
      params.delete('hideCategoryButton')
      params.delete('hideMasterCategoryButton')
    }

    if (queryParam === 'staff' && selectedData?.staff?.length === 1) {
      params.delete('date')
      params.delete('location')
    }
    if (queryParam === 'staff') params.delete('date')

    if (
      queryParam === 'staff' &&
      onlineBookingSettings?.skip_employee_step === true
    )
      params.delete('location')

    if (queryParam === 'service') {
      params.delete('location')
      if (onlineBookingSettings?.enable_quick_service) {
        params.delete('services')
      }
    }
    params.delete(queryParam)

    if (lastStep === '6') {
      params.delete('lastStep')
      if (category) {
        params.delete('prepaidType')
        params.delete('courseUpsell')
      }
      await router.replace({ pathname, query: params.toString() })
    }

    if (Object.entries(query).length > 1) {
      await router.replace({ pathname, query: params.toString() })
    }
  }

  const getServiceAvailableLocations = useMemo(() => {
    const availableLocations = selectedData.bookableLocations.map(
      (location) => location.id
    )

    const servicesDisabledLocation = selectedData.services.reduce<number[]>(
      (accumulator, service) => {
        const locations = service.disabled_locations?.split(',').map(Number)

        if (!locations) {
          return accumulator
        }

        return [...new Set([...accumulator, ...locations])]
      },
      []
    )

    return availableLocations.filter(
      (locationId) => !servicesDisabledLocation.includes(locationId)
    )
  }, [selectedData.bookableLocations, selectedData.services])

  const arrowBack = async () => {
    const waitList = params.get('waitList')

    if (waitList) {
      setCurrentStep(dateTimeStep)
      await removeQueryParamOnBackClick(currentStepUrl)
      return
    }
    await removeQueryParamOnBackClick(currentStepUrl)

    const serviceEnabledLocation = getServiceAvailableLocations
    const courseUpsell = params.get('courseUpsell')
    if (courseUpsell) {
      setCurrentStep(servicesStep)
      return
    }

    if (
      selectedData.services.length === 0 &&
      selectedData.prepaidItems.length > 0 &&
      (currentStep === bookingStep || currentStep === paymentStep)
    ) {
      setSelectedData(actionTypes.setPrepaidItems, [])
      setSelectedData(actionTypes.setTotalPrepaidItemsCost, 0)
      // @ts-expect-error TODO CONNECT BIG FIX
      setSelectedData(actionTypes.setPrepaidType, undefined)
      setCurrentStep(categoryStep)
      return
    }
    if (currentStep === dateTimeStep) {
      setSelectedData(actionTypes.SET_DATETIME, dayjs())
    }
    const skipMedicalForm =
      medicalForms.length === 0 || !onlineBookingSettings?.require_form
    goToPrevStep(
      currentStep,
      skipLocationStep || serviceEnabledLocation.length === 1,
      onlineBookingSettings?.skip_employee_step ||
        selectedData.staff?.length === 1,
      skipMedicalForm,
      isEmployeePicked
    )
    if (categorySelect) {
      setOnBackClickTrue()
      setCategorySelectFalse()
    }
    if (selectedData.prepaidType) {
      // @ts-expect-error TODO CONNECT BIG FIX
      setSelectedData(actionTypes.setPrepaidType, null)
    }
    if (
      (currentStep === locationStep ||
        (currentStep === employeeStep &&
          (skipLocationStep || serviceEnabledLocation.length === 1)) ||
        (currentStep === dateTimeStep &&
          (onlineBookingSettings?.skip_employee_step ||
            selectedData.staff?.length === 1))) &&
      onlineBookingSettings?.enable_quick_service
    ) {
      setSelectedData(actionTypes.SET_SELECTED_SERVICES, [])
      setSelectedData(actionTypes.SET_COST_TIERS, [])
      setSelectedData(actionTypes.SET_TOTAL_COST, 0)
    }
    if (currentStep === confirmStep && prepaidType === 'voucher') {
      router.reload()
    }
  }

  const goBackButton = () => {
    switch (currentStep) {
      case categoryStep: {
        currentStepUrl = 'groupCategory'
        break
      }
      case servicesStep: {
        currentStepUrl = 'category'
        break
      }
      case locationStep: {
        currentStepUrl = 'service'
        break
      }
      case employeeStep: {
        const serviceEnabledLocation = getServiceAvailableLocations

        currentStepUrl =
          skipLocationStep || serviceEnabledLocation.length === 1
            ? 'service'
            : 'location'
        break
      }
      case dateTimeStep: {
        sessionStorage.removeItem('timeLeft') //this is used to clear the session for the countdown timer at PaymentAppointment.tsx
        currentStepUrl = 'staff'
        break
      }
      case bookingStep: {
        currentStepUrl = 'shiftId'
        break
      }
      case patientInformation: {
        break
      }
      case paymentStep: {
        currentStepUrl = 'bookingId'
        break
      }
      case confirmStep: {
        currentStepUrl = 'bookingId'
        break
      }

      case packageStep:
      case accountStep:
      case voucherStep: {
        currentStepUrl = 'prepaidFlow'
        break
      }
      // No default
    }

    if (prepaidType && currentStep !== confirmStep) {
      const hasUrlVoucherDetails = Boolean(voucherDetails?.toString())
      return (
        <BackButton
          onClick={() => {
            if (!hasUrlVoucherDetails) {
              resetSelectedData()
            }
            arrowBack()
          }}
          text={
            hasUrlVoucherDetails
              ? t('gift.voucher.purchase.details.back.to.choose.gift')
              : ''
          }
        />
      )
    }
    return (
      (currentStep > masterCategoryStep || categorySelect) &&
      currentStep < totalSteps &&
      !existingClientMatched &&
      currentStep !== confirmStep &&
      !(
        currentStep === categoryStep &&
        Object.keys(masterCategories ?? {}).length < 2
      ) &&
      !tablet &&
      !rescheduleBookingId && <BackButton onClick={arrowBack} />
    )
  }

  const {
    data: { findUniqueUser: staffUser } = {},
  } = useUserWithAlertPermissionQuery({
    variables: {
      userId: selectedData?.employee?.User?.id ?? 0,
      alertTitle: 'Appointment Booked',
    },
    skip: !selectedData?.employee?.User?.id,
  })

  const { data: appointmentUserNotification } =
    useNotificationSettingTemplateQuery({
      variables: {
        type: prepareTemplateTypeWithLanguage(
          'new-appointment-user',
          staffUser?.locale ?? 'english'
        ),
      },
    })

  const { data: onlineBookingNotifications } =
    useNotificationSettingTemplateQuery({
      variables: {
        type: prepareTemplateTypeWithLanguage(
          'new-online-booking',
          language ?? 'english'
        ),
      },
    })

  const sendUserWebNotification = (
    bookingData: { id?: number; start_date?: number }[] = []
  ) => {
    const contactId =
      connectUser?.contact_id ?? selectedData?.members?.[0]?.contact_id ?? 0
    const userId = selectedData?.employee?.User?.id ?? 0
    const who = connectUser?.contact_id
      ? concatenateTruthyValues([connectUser?.fname, connectUser?.lname])
      : concatenateTruthyValues([
          // @ts-expect-error unchecked index migration
          selectedData?.members?.[0].first_name,
          // @ts-expect-error unchecked index migration
          selectedData?.members?.[0].last_name,
        ])

    sendWebNotifications(
      bookingData,
      selectedData?.services,
      Notification_Types_Enum_Enum.NewAppointmentViaPabau,
      Notification_Types_Enum_Enum.NewAppointmentViaCalendar,
      who,
      companyDetails?.id,
      userId,
      `/clients/${contactId}/dashboard`,
      sendNotification,
      parseDate,
      parseTime
    )
  }

  const sendBookingNotification = (bookingId: { id: number }[]) => {
    const email = findFirstBookitProGeneral?.booking_emails

    if (!email) return

    const emailTemplate =
      onlineBookingNotifications?.notification_settings[0]?.email_template ?? ''
    void sendAppointmentConfirmation({
      variables: {
        to: email,
        subject: t('connect.subject.email'),
        html: emailTemplate,
        text: emailTemplate,
        contactId: connectUser?.contact_id,
        bookingId: bookingId[0]?.id,
        staffId: selectedData.employee?.User?.id,
        fromConnect: true,
        isForStaff: true,
      },
    })
  }

  const skippedPaymentScreen = async (
    bookingIds: { id: number; start_date?: number }[]
  ) => {
    const { data: appointmentNotification } = await getAppointmentNotification()
    skipPaymentStepTrue()

    void createOneBuzzFeed({
      variables: {
        data: buzzFeedVariables(
          selectedData,
          companyDetails,
          connectUser,
          onlineBookingSettings
        ),
      },
    })

    handleStepUrl({
      bookingIdAfterPayment: String(
        bookingIds.map((booking) => booking.id ?? 0)[0]
      ),
    })
    if (precareTemplates !== undefined) {
      void sendTemplate({
        variables: {
          bookingId: bookingIds.at(0)?.id ?? 0,
          templateIds:
            precareTemplates?.map((template) => template.template_id ?? 0) ??
            [],
        },
      })
    }
    if (
      appointmentNotification?.notification_settings[0]?.email_on ??
      appointmentNotification?.default[0]?.email_on
    ) {
      const isVirtualService =
        selectedData.services?.[0]?.online_only_service === 1

      void sendEmailSmsConfirmation({
        variables: {
          bookingId: bookingIds.at(0)?.id ?? 0,
          isVirtualService: isVirtualService,
          saveCommunication: true,
          sendSms:
            appointmentNotification?.notification_settings[0]?.sms_on ?? false,
        },
      })
    }
    if (staffUser?.UserAlertPermission?.at(0)?.email_notification === 1) {
      const notificationUserEmailMessage =
        appointmentUserNotification?.notification_settings?.at(0)
          ?.email_template ||
        appointmentUserNotification?.default?.at(0)?.email_template ||
        ''
      void sendAppointmentConfirmation({
        variables: {
          to: staffUser?.email,
          subject: t('connect.booking.created.success.email'),
          html: notificationUserEmailMessage,
          text: notificationUserEmailMessage,
          contactId: connectUser?.contact_id,
          bookingId: bookingIds.at(0)?.id,
          staffId: selectedData.employee?.User?.id,
          relatedType: 'APPOINTMENT',
          fromConnect: true,
          isForStaff: true,
        },
      })
    }

    sendUserWebNotification(bookingIds)
    if (bookingIds[0]?.id) sendBookingNotification([{ id: bookingIds[0].id }])
    if (medicalForms.length === 0)
      void redirectPageAfterBooking(
        router,
        onlineBookingSettings?.skip_timely_booking,
        onlineBookingSettings?.page_url
      )
  }

  const onSelected = (step?: number) => {
    if (step) {
      return setStep(step, true) //overriding maxStep control if we want to go to specific page
    }
    if (currentStep === patientInformation) {
      setStep(skipPaymentStep ? paymentStep + 1 : paymentStep)
      return
    }

    if (currentStep === bookingStep) {
      if (medicalForms.length > 0) {
        setStep(patientInformation)
        return
      }
      return setStep(skipPaymentStep ? paymentStep + 1 : paymentStep)
    }
    if (
      currentStep === servicesStep &&
      getServiceAvailableLocations.length === 1
    ) {
      const getAvailableLocation = selectedData.bookableLocations.find(
        (item) => item.id === getServiceAvailableLocations[0]
      )
      setSelectedData(actionTypes.SET_LOCATION, getAvailableLocation)
      setStep(locationStep)
    }
    if (
      (currentStep === servicesStep &&
        onlineBookingSettings?.skip_employee_step &&
        skipLocationStep) ||
      currentStep === employeeStep
    ) {
      setStep(dateTimeStep)
      return
    }
    goToNextStep()
  }

  if (
    currentStep === paymentStep &&
    (!stripeKey || stripeKey.trim() === '') &&
    selectedData.totalCost >= 0
  ) {
    setStep(confirmStep)
    return
  }

  const isBlocked = false

  return (
    <>
      <CustomHead businessName={companyDetails?.slug} link={router.basePath} />
      <GoogleAnalytics
        googleAnalyticsId={googleAnalyticsId}
        googleTagId={googleTagId}
        metaPixelId={metaPixelId}
        metaPixelEvent={metaPixelEvent}
      />
      <div className={styles.onlineBooking}>
        <Header
          currentStep={
            currentStep > masterCategoryStep ? currentStep : servicesStep
          }
          back={arrowBack}
          visible={
            (currentStep > masterCategoryStep || !!categorySelect) &&
            currentStep < totalSteps &&
            !existingClientMatched &&
            currentStep !== confirmStep &&
            !(
              currentStep === categoryStep &&
              Object.keys(
                masterCategories?.findManyServicesMasterCategory ?? {}
              ).length < 2
            ) &&
            !tablet &&
            !rescheduleBookingId
          }
          confirmStep={confirmStep}
        />

        <div
          ref={mainBodyRef}
          className={classNames(styles.mainBody, {
            // @ts-expect-error unchecked index migration
            [styles.noHeader]: currentStep === confirmStep,
          })}
          data-cy="mainbody"
          aria-label="Main Body"
          style={{
            backgroundColor:
              onlineBookingSettings?.brand_color === 'white' ||
              onlineBookingSettings?.brand_color === '#ffffff'
                ? '#f7f7f9'
                : (onlineBookingSettings?.brand_color ?? '#f7f7f9'),
          }}
        >
          {/* {isBlocked ? (
            <div> */}
          {goBackButton()}
          {[
            masterCategoryStep,
            categoryStep,
            servicesStep,
            employeeStep,
            dateTimeStep,
          ].includes(currentStep) &&
            withGoogle !== undefined &&
            String(withGoogle) === 'true' && <GoogleHeader />}
          <div
            className={classNames({
              // @ts-expect-error unchecked index migration
              [styles.slide]: !mobile,
              // @ts-expect-error unchecked index migration
              [styles.slideMobile]: mobile,
            })}
            aria-label="Steps Navigator"
          >
            {currentStep < 6 &&
            mainBodyRef.current?.clientHeight !==
              mainBodyRef.current?.scrollHeight ? (
              <DownUpArrowIcon
                onClick={handleArrowClick}
                className={classNames(styles.fixedDynamicArrow, {
                  [styles.iconUp ?? '']:
                    iconUp ||
                    ((selectedData.services.length > 0 ||
                      selectedData.vouchers.length > 0 ||
                      selectedData.prepaidItems.length > 0) &&
                      currentStep < locationStep),
                })}
                rotate={iconUp ? 0 : 180}
                aria-label="Scroll Arrow"
              />
            ) : null}
            <StepNavigator
              currentStep={currentStep}
              onSelected={onSelected}
              onHandleStepUrl={handleStepUrl}
            />
            {currentStep === locationStep && (
              <div className={styles.slide2} data-cy="Location Step">
                <StepNavigatorHeader />
                <LocationSelector
                  onSelected={onSelected}
                  favoriteLocation={favoriteLocation}
                  handleStepUrl={handleStepUrl}
                  loadingLocations={isLoadingLocations}
                  handleShowMore={handleShowMore}
                  showButton={showButton}
                  hideFindNearestButton={hideFindNearestButton}
                  searchString={locationSearchString}
                  setSearchString={setLocationSearchString}
                />
              </div>
            )}
            {currentStep === employeeStep && (
              <div className={styles.slide3} data-cy="employee step">
                <StepNavigatorHeader />
                <EmployeeSelector
                  onSelected={onSelected}
                  handleStepUrl={handleStepUrl}
                  loading={isLoadingLocations}
                  isEmployeePicked={handleEmployeePicked}
                />
              </div>
            )}
            {currentStep === dateTimeStep && (
              <div className={styles.slide4} data-cy="date time step">
                {isFlagActive('Connect - Date and time step revamp') ? (
                  <RevampDateTimeSelector
                    onSelected={onSelected}
                    handleStepUrl={handleStepUrl}
                    setStep={setStep}
                    minimumAdvance={onlineBookingSettings?.minimum_advance ?? 0}
                  />
                ) : (
                  <DateTimeSelector
                    onSelected={onSelected}
                    handleStepUrl={handleStepUrl}
                    setStep={setStep}
                    minimumAdvance={onlineBookingSettings?.minimum_advance ?? 0}
                  />
                )}
              </div>
            )}
            {currentStep === bookingStep && !smsVerificationModal && (
              <div className={styles.slide5} data-cy="booking step">
                {existingClientMatched ? (
                  <SuccessfulLottie
                    name={`${cmContactData?.findFirstCmContact?.Fname} ${cmContactData?.findFirstCmContact?.Lname}`}
                    email={cmContactData?.findFirstCmContact?.Email}
                    mobilePhone={cmContactData?.findFirstCmContact?.Mobile}
                    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-type-assertion
                    dob={formatDateFromISO(
                      dayjs(cmContactData?.findFirstCmContact?.DOB).unix(),
                      companyDetails?.details?.date_format,
                      // eslint-disable-next-line @typescript-eslint/no-explicit-any
                      undefined as any,
                      language,
                      'sms-code'
                    )!.toString()}
                    onSendSmsToUnauthenticatedContact={
                      sendSmsToUnauthenticatedContact
                    }
                    onSendEmailToUnauthenticatedContact={
                      sendEmailToUnauthenticatedContact
                    }
                    onOpenSmsVerificationModal={handleSmsVerificationModal}
                  />
                ) : (
                  <BookingDetails
                    data-cy="booking details"
                    onConfirmed={onConfirmBookingDetails}
                    onSelected={onSelected}
                    backToStep={setStep}
                    stripeKey={stripeKey ?? ''}
                    createBookingAndNavigate={createBookingAndNavigate}
                    isActiveCancellationPolicy={isActiveCancellationPolicy}
                    isFetchingMedicalForms={isFetchingMedicalForms}
                  />
                )}
              </div>
            )}
            {currentStep === patientInformation && (
              <PatientInfo
                data-cy="patient info"
                onSelected={onSelected}
                onlineBookingSettings={onlineBookingSettings}
                skipPaymentStep={skipPaymentStep}
                patientId={connectUser?.contact_id}
                medicalForms={medicalForms}
              />
            )}
            {currentStep === bookingStep && smsVerificationModal ? (
              <SmsVerification
                handleSmsPasscodeInput={handleSmsPasscodeInput}
                smsPasscodeInput={smsPasscodeInput}
                verificationStatus={verificationStatus?.message}
                verifySmsCode={verifySmsCode}
                verificationLoading={verificationLoading}
                onSendSmsToUnauthenticatedContact={
                  sendSmsToUnauthenticatedContact
                }
                onSendEmailToUnauthenticatedContact={
                  sendEmailToUnauthenticatedContact
                }
                clinicLogo={createCdnLink(companyDetails?.details?.logo, false)}
                phoneNumber={cmContactData?.findFirstCmContact?.Mobile}
                clinicName={companyDetails?.details?.company_name}
                email={cmContactData?.findFirstCmContact?.Email}
                handleResendCodeToEmail={handleResendCodeToEmail}
                smsSend={smsSend}
              />
            ) : null}
            {currentStep === paymentStep && (
              <div
                className={classNames(styles.paymentWrapper, {
                  // @ts-expect-error unchecked index migration
                  [styles.prepaidPayment]: prepaidType && (laptop || desktop),
                })}
                data-cy="payment step"
              >
                <PaymentWrapper
                  onSelected={onSelected}
                  bookingId={bookingData}
                  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
                  contactId={contactId!}
                  brandColor={onlineBookingSettings?.brand_color}
                  isActiveCancellationPolicy={isActiveCancellationPolicy}
                  hoursAhead={
                    ['8', '24', '48', '72'][
                      findFirstCancellationPolicy?.policy_notice ?? 1
                    ]
                  }
                  noShowFee={findFirstCancellationPolicy?.no_show_fee}
                  advancedCancelFee={
                    findFirstCancellationPolicy?.advanced_cancellation_fee
                  }
                />
              </div>
            )}
            {Boolean(currentStep === confirmStep) && (
              <BookingConfirmation
                online
                onSelected={onSelected}
                stripeKey={stripeKey}
                showPrices={onlineBookingSettings?.show_prices}
                currentStep={currentStep}
                bookingIds={bookingData}
                sendUserWebNotification={sendUserWebNotification}
                sendBookingNotification={sendBookingNotification}
              />
            )}
          </div>
          {[servicesStep, packageStep, voucherStep, accountStep]
            .filter((step) => (isVoucherActive ? step !== voucherStep : step))
            .includes(currentStep) && (
            <SelectedItemsFooter
              onSelected={onSelected}
              onHandleStepUrl={handleStepUrl}
            />
          )}
          <Footer />
        </div>
      </div>
    </>
  )
}

export default Index
Editor is loading...
Leave a Comment