From 188dc35fdb3955bec0c8e8e203e5b468c659ed3c Mon Sep 17 00:00:00 2001 From: Sebastian Holesz Date: Mon, 12 Aug 2024 15:06:59 +0200 Subject: [PATCH] added opening hours to packetery selected transport label - additionally, instead of TypeListedStoreFragment, StoreOrPacketeryPoint is now used as it may contain extra data --- .../Blocks/OpeningHours/OpeningHours.tsx | 105 ++++++++++++------ .../Blocks/Popup/PickupPlacePopup.tsx | 4 +- .../contactInformationUtils.ts | 4 +- .../deliveryAddressUtils.ts | 4 +- .../TransportAndPaymentSelect/StoreSelect.tsx | 4 +- .../TransportAndPaymentSelect.tsx | 4 +- .../TransportAndPaymentSelectItemLabel.tsx | 6 +- .../transportAndPaymentUtils.tsx | 18 +-- .../components/Pages/Stores/StoreInfoBox.tsx | 4 +- .../components/Pages/Stores/StoresContent.tsx | 4 +- .../factories/getGtmTransportChangeEvent.ts | 4 +- .../onGtmTransportChangeEventHandler.ts | 4 +- .../mappers/getGtmPickupPlaceFromLastOrder.ts | 4 +- .../gtm/mappers/getGtmPickupPlaceFromStore.ts | 4 +- storefront/gtm/mappers/mapGtmShippingInfo.ts | 4 +- .../store/slices/createPacketerySlice.ts | 6 +- storefront/types/cart.ts | 4 +- .../utils/cart/useChangeTransportInCart.ts | 4 +- storefront/utils/cart/useCurrentCart.ts | 6 +- storefront/utils/packetery/index.ts | 73 ++++++++++-- storefront/utils/packetery/types.ts | 20 ++++ 21 files changed, 203 insertions(+), 87 deletions(-) diff --git a/storefront/components/Blocks/OpeningHours/OpeningHours.tsx b/storefront/components/Blocks/OpeningHours/OpeningHours.tsx index 5354cd88b3..28e2fe886b 100644 --- a/storefront/components/Blocks/OpeningHours/OpeningHours.tsx +++ b/storefront/components/Blocks/OpeningHours/OpeningHours.tsx @@ -3,9 +3,13 @@ import useTranslation from 'next-translate/useTranslation'; import { Fragment } from 'react'; import { twJoin } from 'tailwind-merge'; import { useFormatDate } from 'utils/formatting/useFormatDate'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; import { twMergeCustom } from 'utils/twMerge'; -export const OpeningHours: FC<{ openingHours: TypeOpeningHours }> = ({ openingHours, className }) => { +export const OpeningHours: FC<{ openingHours: StoreOrPacketeryPoint['openingHours'] | TypeOpeningHours }> = ({ + openingHours, + className, +}) => { const { t } = useTranslation(); const { formatDate } = useFormatDate(); @@ -32,39 +36,76 @@ export const OpeningHours: FC<{ openingHours: TypeOpeningHours }> = ({ openingHo } }; + if (openingHours.openingHoursOfDays.length === 0) { + return null; + } + return ( -
- {openingHours.openingHoursOfDays.map(({ date, dayOfWeek, openingHoursRanges }) => { - const isToday = openingHours.dayOfWeek === dayOfWeek; - const isClosedWholeDay = openingHoursRanges.length === 0; + <> +
{t('Open') + ': '}
+ {'exceptionDays' in openingHours && + openingHours.exceptionDays?.map((exceptionDay, index) => { + let exceptionDayText = formatDate(exceptionDay.from, 'D.M.'); + + if (exceptionDay.to) { + exceptionDayText += ` - ${formatDate(exceptionDay.to, 'D.M.')}`; + } + + if (exceptionDay.times.length) { + for (let index = 0; index < exceptionDay.times.length; index++) { + if (index === 0) { + exceptionDayText += ` ${exceptionDay.times[index].open} - ${exceptionDay.times[index].close}`; + } else { + exceptionDayText += `, ${exceptionDay.times[index].open} - ${exceptionDay.times[index].close}`; + } + } + } else { + exceptionDayText += ` ${t('Closed')}`; + } + + return ( +
+ {exceptionDayText} +
+ ); + })} + +
+ {openingHours.openingHoursOfDays.map(({ date, dayOfWeek, openingHoursRanges }) => { + const isToday = openingHours.dayOfWeek === dayOfWeek; + const isClosedWholeDay = openingHoursRanges.length === 0; - return ( -
- - {getDayName(openingHours.dayOfWeek, dayOfWeek)} {formatDate(date, 'D.M.')} - - - {isClosedWholeDay ? ( - <> {t('Closed')} - ) : ( - openingHoursRanges.map(({ openingTime, closingTime }, index) => ( - - {index > 0 && ','} {openingTime} - {closingTime} - - )) + return ( +
-
- ); - })} -
+ > + + {getDayName(openingHours.dayOfWeek, dayOfWeek)} {formatDate(date, 'D.M.')} + + + {isClosedWholeDay ? ( + <> {t('Closed')} + ) : ( + openingHoursRanges.map(({ openingTime, closingTime }, index) => ( + + {index > 0 && ','} {openingTime} - {closingTime} + + )) + )} + +
+ ); + })} +
+ ); }; diff --git a/storefront/components/Blocks/Popup/PickupPlacePopup.tsx b/storefront/components/Blocks/Popup/PickupPlacePopup.tsx index 13281c8f8d..7140f4f30b 100644 --- a/storefront/components/Blocks/Popup/PickupPlacePopup.tsx +++ b/storefront/components/Blocks/Popup/PickupPlacePopup.tsx @@ -3,15 +3,15 @@ import { Button } from 'components/Forms/Button/Button'; import { Popup } from 'components/Layout/Popup/Popup'; import { StoreSelect } from 'components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/StoreSelect'; import { TIDs } from 'cypress/tids'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { useTransportStoresQuery } from 'graphql/requests/transports/queries/TransportStoresQuery.generated'; import useTranslation from 'next-translate/useTranslation'; import { useState } from 'react'; import { useSessionStore } from 'store/useSessionStore'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; type PickupPlacePopupProps = { transportUuid: string; - onChangePickupPlaceCallback: (transportUuid: string, selectedPickupPlace: TypeListedStoreFragment | null) => void; + onChangePickupPlaceCallback: (transportUuid: string, selectedPickupPlace: StoreOrPacketeryPoint | null) => void; }; export const PickupPlacePopup: FC = ({ transportUuid, onChangePickupPlaceCallback }) => { diff --git a/storefront/components/Pages/Order/ContactInformation/contactInformationUtils.ts b/storefront/components/Pages/Order/ContactInformation/contactInformationUtils.ts index e9453e9502..021632f150 100644 --- a/storefront/components/Pages/Order/ContactInformation/contactInformationUtils.ts +++ b/storefront/components/Pages/Order/ContactInformation/contactInformationUtils.ts @@ -15,7 +15,6 @@ import { TypeCreateOrderMutationVariables, useCreateOrderMutation, } from 'graphql/requests/orders/mutations/CreateOrderMutation.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { GtmMessageOriginType } from 'gtm/enums/GtmMessageOriginType'; import { getGtmCreateOrderEventOrderPart, getGtmCreateOrderEventUserPart } from 'gtm/factories/getGtmCreateOrderEvent'; import { onGtmCreateOrderEventHandler } from 'gtm/handlers/onGtmCreateOrderEventHandler'; @@ -34,6 +33,7 @@ import { useChangePaymentInCart } from 'utils/cart/useChangePaymentInCart'; import { useCurrentCart } from 'utils/cart/useCurrentCart'; import { handleFormErrors } from 'utils/forms/handleFormErrors'; import { getIsPaymentWithPaymentGate } from 'utils/mappers/payment'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; import { getInternationalizedStaticUrls } from 'utils/staticUrls/getInternationalizedStaticUrls'; import { useCurrentUserContactInformation } from 'utils/user/useCurrentUserContactInformation'; @@ -88,7 +88,7 @@ export const useCreateOrder = ( const getCreateOrderMutationVariables = ( cartUuid: string | null, formValues: ContactInformation, - selectedPickupPlace: TypeListedStoreFragment | null, + selectedPickupPlace: StoreOrPacketeryPoint | null, user: CurrentCustomerType | undefined | null, ) => { const country = formValues.country.value; diff --git a/storefront/components/Pages/Order/ContactInformation/deliveryAddressUtils.ts b/storefront/components/Pages/Order/ContactInformation/deliveryAddressUtils.ts index c2484c138a..ea2d818814 100644 --- a/storefront/components/Pages/Order/ContactInformation/deliveryAddressUtils.ts +++ b/storefront/components/Pages/Order/ContactInformation/deliveryAddressUtils.ts @@ -1,7 +1,7 @@ import { TypeCreateOrderMutationVariables } from 'graphql/requests/orders/mutations/CreateOrderMutation.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { ContactInformation } from 'store/slices/createContactInformationSlice'; import { CurrentCustomerType } from 'types/customer'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; type DeliveryInfo = Pick< TypeCreateOrderMutationVariables, @@ -47,7 +47,7 @@ export const getSelectedDeliveryAddressForLoggedInUser = ( export const getDeliveryInfoFromSelectedPickupPlace = ( formValues: ContactInformation, - selectedPickupPlace: TypeListedStoreFragment, + selectedPickupPlace: StoreOrPacketeryPoint, ): DeliveryInfo => ({ deliveryFirstName: formValues.isDeliveryAddressDifferentFromBilling ? formValues.deliveryFirstName diff --git a/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/StoreSelect.tsx b/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/StoreSelect.tsx index a64cac3e19..47b70f99a0 100644 --- a/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/StoreSelect.tsx +++ b/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/StoreSelect.tsx @@ -2,9 +2,9 @@ import { TransportAndPaymentListItem } from './TransportAndPaymentListItem'; import { Radiobutton } from 'components/Forms/Radiobutton/Radiobutton'; import { TransportAndPaymentSelectItemLabel } from 'components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelectItemLabel'; import { TypeListedStoreConnectionFragment } from 'graphql/requests/stores/fragments/ListedStoreConnectionFragment.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { useMemo } from 'react'; import { mapConnectionEdges } from 'utils/mappers/connection'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; type StoreSelectProps = { selectedStoreUuid: string; @@ -13,7 +13,7 @@ type StoreSelectProps = { }; export const StoreSelect: FC = ({ selectedStoreUuid, stores, onSelectStoreCallback }) => { - const mappedStores = useMemo(() => mapConnectionEdges(stores.edges), [stores.edges]); + const mappedStores = useMemo(() => mapConnectionEdges(stores.edges), [stores.edges]); return (
    diff --git a/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelect.tsx b/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelect.tsx index d6a6877b68..4333aab155 100644 --- a/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelect.tsx +++ b/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelect.tsx @@ -13,7 +13,6 @@ import { useDomainConfig } from 'components/providers/DomainConfigProvider'; import { TIDs } from 'cypress/tids'; import { TypeSimplePaymentFragment } from 'graphql/requests/payments/fragments/SimplePaymentFragment.generated'; import { useGoPaySwiftsQuery } from 'graphql/requests/payments/queries/GoPaySwiftsQuery.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { TypeTransportStoresFragment } from 'graphql/requests/transports/fragments/TransportStoresFragment.generated'; import { TypeTransportWithAvailablePaymentsFragment } from 'graphql/requests/transports/fragments/TransportWithAvailablePaymentsFragment.generated'; import useTranslation from 'next-translate/useTranslation'; @@ -22,10 +21,11 @@ import { createEmptyArray } from 'utils/arrays/createEmptyArray'; import { ChangePaymentInCart } from 'utils/cart/useChangePaymentInCart'; import { ChangeTransportInCart } from 'utils/cart/useChangeTransportInCart'; import { useCurrentCart } from 'utils/cart/useCurrentCart'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; type TransportAndPaymentSelectProps = { transports: TypeTransportWithAvailablePaymentsFragment[]; - lastOrderPickupPlace: TypeListedStoreFragment | null; + lastOrderPickupPlace: StoreOrPacketeryPoint | null; changeTransportInCart: ChangeTransportInCart; changePaymentInCart: ChangePaymentInCart; isTransportSelectionLoading: boolean; diff --git a/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelectItemLabel.tsx b/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelectItemLabel.tsx index e83d5941c8..0126ed63de 100644 --- a/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelectItemLabel.tsx +++ b/storefront/components/Pages/Order/TransportAndPayment/TransportAndPaymentSelect/TransportAndPaymentSelectItemLabel.tsx @@ -2,10 +2,10 @@ import { Image } from 'components/Basic/Image/Image'; import { OpeningHours } from 'components/Blocks/OpeningHours/OpeningHours'; import { TIDs } from 'cypress/tids'; import { TypeImageFragment } from 'graphql/requests/images/fragments/ImageFragment.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { Translate } from 'next-translate'; import useTranslation from 'next-translate/useTranslation'; import { useFormatPrice } from 'utils/formatting/useFormatPrice'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; type TransportAndPaymentSelectItemLabelProps = { name: string; @@ -13,7 +13,7 @@ type TransportAndPaymentSelectItemLabelProps = { daysUntilDelivery?: number; description?: string | null; image?: TypeImageFragment | null; - pickupPlaceDetail?: TypeListedStoreFragment; + pickupPlaceDetail?: StoreOrPacketeryPoint; }; export const TransportAndPaymentSelectItemLabel: FC = ({ @@ -52,8 +52,6 @@ export const TransportAndPaymentSelectItemLabel: FC -
    {t('Open') + ': '}
    - )} diff --git a/storefront/components/Pages/Order/TransportAndPayment/transportAndPaymentUtils.tsx b/storefront/components/Pages/Order/TransportAndPayment/transportAndPaymentUtils.tsx index fc872fdded..1e3551c0ff 100644 --- a/storefront/components/Pages/Order/TransportAndPayment/transportAndPaymentUtils.tsx +++ b/storefront/components/Pages/Order/TransportAndPayment/transportAndPaymentUtils.tsx @@ -6,7 +6,6 @@ import { LastOrderQueryDocument, } from 'graphql/requests/orders/queries/LastOrderQuery.generated'; import { TypeSimplePaymentFragment } from 'graphql/requests/payments/fragments/SimplePaymentFragment.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { TypeStoreQuery, TypeStoreQueryVariables, @@ -32,6 +31,7 @@ import { useCurrentCart } from 'utils/cart/useCurrentCart'; import { hasValidationErrors } from 'utils/errors/hasValidationErrors'; import { logException } from 'utils/errors/logException'; import { mapPacketeryExtendedPoint, packeteryPick } from 'utils/packetery'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; import { getInternationalizedStaticUrls } from 'utils/staticUrls/getInternationalizedStaticUrls'; const PickupPlacePopup = dynamic( @@ -66,7 +66,7 @@ export const usePaymentChangeInSelect = (changePaymentHandler: ChangePaymentInCa export const useTransportChangeInSelect = ( transports: TypeTransportWithAvailablePaymentsFragment[] | undefined, - lastOrderPickupPlace: TypeListedStoreFragment | null, + lastOrderPickupPlace: StoreOrPacketeryPoint | null, changeTransportHandler: ChangeTransportInCart, changePaymentHandler: ChangePaymentInCart, ) => { @@ -151,7 +151,7 @@ export const useTransportChangeInSelect = ( ); }; - const changePickupPlace = (transportUuid: string, selectedPickupPlace: TypeListedStoreFragment | null) => { + const changePickupPlace = (transportUuid: string, selectedPickupPlace: StoreOrPacketeryPoint | null) => { if (selectedPickupPlace) { changeTransportHandler(transportUuid, selectedPickupPlace); } else { @@ -171,9 +171,9 @@ export const useTransportChangeInSelect = ( const getLastOrderPickupPlace = ( lastOrder: TypeLastOrderFragment, lastOrderPickupPlaceIdentifier: string, - lastOrderPickupPlaceFromApi: TypeListedStoreFragment | undefined | null, - packeteryPickupPoint: TypeListedStoreFragment | null, -): TypeListedStoreFragment | null => { + lastOrderPickupPlaceFromApi: StoreOrPacketeryPoint | undefined | null, + packeteryPickupPoint: StoreOrPacketeryPoint | null, +): StoreOrPacketeryPoint | null => { if (packeteryPickupPoint?.identifier === lastOrderPickupPlaceIdentifier) { return packeteryPickupPoint; } @@ -205,7 +205,7 @@ type TransportAndPaymentErrorsType = { export const getTransportAndPaymentValidationMessages = ( transport: Maybe, - pickupPlace: Maybe, + pickupPlace: Maybe, payment: Maybe, paymentGoPayBankSwift: Maybe, t: Translate, @@ -250,12 +250,12 @@ export const getTransportAndPaymentValidationMessages = ( export const useLoadTransportAndPaymentFromLastOrder = ( changeTransportInCart: ChangeTransportInCart, changePaymentInCart: ChangePaymentInCart, -): [boolean, TypeListedStoreFragment | null] => { +): [boolean, StoreOrPacketeryPoint | null] => { const client = useClient(); const isUserLoggedIn = useIsUserLoggedIn(); const { transport: currentTransport, payment: currentPayment, cart } = useCurrentCart(); - const [lastOrderPickupPlace, setLastOrderPickupPlace] = useState(null); + const [lastOrderPickupPlace, setLastOrderPickupPlace] = useState(null); const [isLoadingTransportAndPaymentFromLastOrder, setIsLoadingTransportAndPaymentFromLastOrder] = useState(false); const packeteryPickupPoint = usePersistStore((store) => store.packeteryPickupPoint); diff --git a/storefront/components/Pages/Stores/StoreInfoBox.tsx b/storefront/components/Pages/Stores/StoreInfoBox.tsx index b6e7950c35..091d531ab2 100644 --- a/storefront/components/Pages/Stores/StoreInfoBox.tsx +++ b/storefront/components/Pages/Stores/StoreInfoBox.tsx @@ -2,11 +2,11 @@ import { RemoveIcon } from 'components/Basic/Icon/RemoveIcon'; import { Link } from 'components/Basic/Link/Link'; import { OpeningHours } from 'components/Blocks/OpeningHours/OpeningHours'; import { OpeningStatus } from 'components/Blocks/OpeningHours/OpeningStatus'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import useTranslation from 'next-translate/useTranslation'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; type StoreInfoBoxProps = { - store: TypeListedStoreFragment; + store: StoreOrPacketeryPoint; closeInfoBoxCallback: () => void; }; diff --git a/storefront/components/Pages/Stores/StoresContent.tsx b/storefront/components/Pages/Stores/StoresContent.tsx index 8a26ff8d8d..e3726fd346 100644 --- a/storefront/components/Pages/Stores/StoresContent.tsx +++ b/storefront/components/Pages/Stores/StoresContent.tsx @@ -7,10 +7,10 @@ import { SimpleLayout } from 'components/Layout/SimpleLayout/SimpleLayout'; import { useDomainConfig } from 'components/providers/DomainConfigProvider'; import { TIDs } from 'cypress/tids'; import { TypeListedStoreConnectionFragment } from 'graphql/requests/stores/fragments/ListedStoreConnectionFragment.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import useTranslation from 'next-translate/useTranslation'; import { useMemo, useState } from 'react'; import { mapConnectionEdges } from 'utils/mappers/connection'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; type StoresContentProps = { stores: TypeListedStoreConnectionFragment; @@ -20,7 +20,7 @@ export const StoresContent: FC = ({ stores }) => { const { t } = useTranslation(); const { defaultLocale } = useDomainConfig(); const [activeStoreIdentifier, setActiveStoreIdentifier] = useState(); - const mappedStores = useMemo(() => mapConnectionEdges(stores.edges), [stores.edges]); + const mappedStores = useMemo(() => mapConnectionEdges(stores.edges), [stores.edges]); const activeMarkerHandler = (id: string) => setActiveStoreIdentifier(activeStoreIdentifier !== id ? id : undefined); diff --git a/storefront/gtm/factories/getGtmTransportChangeEvent.ts b/storefront/gtm/factories/getGtmTransportChangeEvent.ts index 4c656d9c60..1fd2654070 100644 --- a/storefront/gtm/factories/getGtmTransportChangeEvent.ts +++ b/storefront/gtm/factories/getGtmTransportChangeEvent.ts @@ -1,14 +1,14 @@ -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { TypeTransportWithAvailablePaymentsAndStoresFragment } from 'graphql/requests/transports/fragments/TransportWithAvailablePaymentsAndStoresFragment.generated'; import { GtmEventType } from 'gtm/enums/GtmEventType'; import { mapGtmShippingInfo } from 'gtm/mappers/mapGtmShippingInfo'; import { GtmTransportChangeEventType } from 'gtm/types/events'; import { GtmCartInfoType } from 'gtm/types/objects'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; export const getGtmTransportChangeEvent = ( gtmCartInfo: GtmCartInfoType, updatedTransport: TypeTransportWithAvailablePaymentsAndStoresFragment, - updatedPickupPlace: TypeListedStoreFragment | null, + updatedPickupPlace: StoreOrPacketeryPoint | null, paymentName: string | undefined, ): GtmTransportChangeEventType => { const { transportDetail, transportExtra } = mapGtmShippingInfo(updatedPickupPlace); diff --git a/storefront/gtm/handlers/onGtmTransportChangeEventHandler.ts b/storefront/gtm/handlers/onGtmTransportChangeEventHandler.ts index 85d5ca10d3..e4793a9c28 100644 --- a/storefront/gtm/handlers/onGtmTransportChangeEventHandler.ts +++ b/storefront/gtm/handlers/onGtmTransportChangeEventHandler.ts @@ -1,13 +1,13 @@ -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { TypeTransportWithAvailablePaymentsAndStoresFragment } from 'graphql/requests/transports/fragments/TransportWithAvailablePaymentsAndStoresFragment.generated'; import { getGtmTransportChangeEvent } from 'gtm/factories/getGtmTransportChangeEvent'; import { GtmCartInfoType } from 'gtm/types/objects'; import { gtmSafePushEvent } from 'gtm/utils/gtmSafePushEvent'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; export const onGtmTransportChangeEventHandler = ( gtmCartInfo: GtmCartInfoType | undefined | null, updatedTransport: TypeTransportWithAvailablePaymentsAndStoresFragment | null, - updatedPickupPlace: TypeListedStoreFragment | null, + updatedPickupPlace: StoreOrPacketeryPoint | null, paymentName: string | undefined, ): void => { if (gtmCartInfo && updatedTransport !== null) { diff --git a/storefront/gtm/mappers/getGtmPickupPlaceFromLastOrder.ts b/storefront/gtm/mappers/getGtmPickupPlaceFromLastOrder.ts index 63f28797fc..7cc9399fea 100644 --- a/storefront/gtm/mappers/getGtmPickupPlaceFromLastOrder.ts +++ b/storefront/gtm/mappers/getGtmPickupPlaceFromLastOrder.ts @@ -1,10 +1,10 @@ import { TypeLastOrderFragment } from 'graphql/requests/orders/fragments/LastOrderFragment.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; export const getGtmPickupPlaceFromLastOrder = ( pickupPlaceIdentifier: string, lastOrder: TypeLastOrderFragment, -): TypeListedStoreFragment => ({ +): StoreOrPacketeryPoint => ({ __typename: 'Store', latitude: null, longitude: null, diff --git a/storefront/gtm/mappers/getGtmPickupPlaceFromStore.ts b/storefront/gtm/mappers/getGtmPickupPlaceFromStore.ts index 6115bab548..aa99e210ff 100644 --- a/storefront/gtm/mappers/getGtmPickupPlaceFromStore.ts +++ b/storefront/gtm/mappers/getGtmPickupPlaceFromStore.ts @@ -1,6 +1,6 @@ -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; -export const getGtmPickupPlaceFromStore = (store: TypeListedStoreFragment): TypeListedStoreFragment => ({ +export const getGtmPickupPlaceFromStore = (store: StoreOrPacketeryPoint): StoreOrPacketeryPoint => ({ __typename: 'Store', latitude: null, longitude: null, diff --git a/storefront/gtm/mappers/mapGtmShippingInfo.ts b/storefront/gtm/mappers/mapGtmShippingInfo.ts index 755c93a930..ca03c5ebf2 100644 --- a/storefront/gtm/mappers/mapGtmShippingInfo.ts +++ b/storefront/gtm/mappers/mapGtmShippingInfo.ts @@ -1,7 +1,7 @@ -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { GtmShippingInfoType } from 'gtm/types/objects'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; -export const mapGtmShippingInfo = (pickupPlace: TypeListedStoreFragment | null): GtmShippingInfoType => { +export const mapGtmShippingInfo = (pickupPlace: StoreOrPacketeryPoint | null): GtmShippingInfoType => { let transportDetail = ''; const transportExtra = []; diff --git a/storefront/store/slices/createPacketerySlice.ts b/storefront/store/slices/createPacketerySlice.ts index de833cbc5f..17cfb4ba5b 100644 --- a/storefront/store/slices/createPacketerySlice.ts +++ b/storefront/store/slices/createPacketerySlice.ts @@ -1,12 +1,12 @@ -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; import { StateCreator } from 'zustand'; type PacketeryState = { - packeteryPickupPoint: TypeListedStoreFragment | null; + packeteryPickupPoint: StoreOrPacketeryPoint | null; }; export type PacketerySlice = PacketeryState & { - setPacketeryPickupPoint: (mappedPacketeryPoint: TypeListedStoreFragment) => void; + setPacketeryPickupPoint: (mappedPacketeryPoint: StoreOrPacketeryPoint) => void; clearPacketeryPickupPoint: () => void; }; diff --git a/storefront/types/cart.ts b/storefront/types/cart.ts index 4675d49c12..fcab9d9669 100644 --- a/storefront/types/cart.ts +++ b/storefront/types/cart.ts @@ -2,15 +2,15 @@ import { TypeCartFragment } from 'graphql/requests/cart/fragments/CartFragment.g import { TypeCartModificationsFragment } from 'graphql/requests/cart/fragments/CartModificationsFragment.generated'; import { TypeSimplePaymentFragment } from 'graphql/requests/payments/fragments/SimplePaymentFragment.generated'; import { TypePriceFragment } from 'graphql/requests/prices/fragments/PriceFragment.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { TypeTransportWithAvailablePaymentsAndStoresFragment } from 'graphql/requests/transports/fragments/TransportWithAvailablePaymentsAndStoresFragment.generated'; import { Maybe } from 'graphql/types'; import { UseQueryExecute } from 'urql'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; export type CurrentCartType = { cart: Maybe | undefined; transport: Maybe; - pickupPlace: Maybe; + pickupPlace: Maybe; payment: Maybe; paymentGoPayBankSwift: Maybe; promoCode: Maybe; diff --git a/storefront/utils/cart/useChangeTransportInCart.ts b/storefront/utils/cart/useChangeTransportInCart.ts index 0dbe9b970a..22fb1d544b 100644 --- a/storefront/utils/cart/useChangeTransportInCart.ts +++ b/storefront/utils/cart/useChangeTransportInCart.ts @@ -1,17 +1,17 @@ import { TypeCartFragment } from 'graphql/requests/cart/fragments/CartFragment.generated'; import { useChangeTransportInCartMutation } from 'graphql/requests/cart/mutations/ChangeTransportInCartMutation.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { GtmMessageOriginType } from 'gtm/enums/GtmMessageOriginType'; import { useGtmCartInfo } from 'gtm/utils/useGtmCartInfo'; import useTranslation from 'next-translate/useTranslation'; import { usePersistStore } from 'store/usePersistStore'; import { getUserFriendlyErrors } from 'utils/errors/friendlyErrorMessageParser'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; import { showErrorMessage } from 'utils/toasts/showErrorMessage'; import { useLatest } from 'utils/ui/useLatest'; export type ChangeTransportInCart = ( newTransportUuid: string | null, - newPickupPlace: TypeListedStoreFragment | null, + newPickupPlace: StoreOrPacketeryPoint | null, ) => Promise; export const useChangeTransportInCart = () => { diff --git a/storefront/utils/cart/useCurrentCart.ts b/storefront/utils/cart/useCurrentCart.ts index dab80df12b..e722d522cf 100644 --- a/storefront/utils/cart/useCurrentCart.ts +++ b/storefront/utils/cart/useCurrentCart.ts @@ -1,5 +1,4 @@ import { useCartQuery } from 'graphql/requests/cart/queries/CartQuery.generated'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; import { TypeTransportWithAvailablePaymentsAndStoresFragment } from 'graphql/requests/transports/fragments/TransportWithAvailablePaymentsAndStoresFragment.generated'; import { Maybe } from 'graphql/types'; import { useEffect } from 'react'; @@ -7,6 +6,7 @@ import { usePersistStore } from 'store/usePersistStore'; import { useSessionStore } from 'store/useSessionStore'; import { CurrentCartType } from 'types/cart'; import { useIsUserLoggedIn } from 'utils/auth/useIsUserLoggedIn'; +import { StoreOrPacketeryPoint } from 'utils/packetery/types'; export const useCurrentCart = (fromCache = true): CurrentCartType => { const isUserLoggedIn = useIsUserLoggedIn(); @@ -57,8 +57,8 @@ export const useCurrentCart = (fromCache = true): CurrentCartType => { const getSelectedPickupPlace = ( transport: Maybe | undefined, pickupPlaceIdentifier: string | null | undefined, - packeteryPickupPoint: TypeListedStoreFragment | null, -): TypeListedStoreFragment | null => { + packeteryPickupPoint: StoreOrPacketeryPoint | null, +): StoreOrPacketeryPoint | null => { if (!transport || !pickupPlaceIdentifier) { return null; } diff --git a/storefront/utils/packetery/index.ts b/storefront/utils/packetery/index.ts index e72480dfca..15986b6708 100644 --- a/storefront/utils/packetery/index.ts +++ b/storefront/utils/packetery/index.ts @@ -1,5 +1,5 @@ -import { PacketeryExtendedPoint, PacketeryPickFunction } from './types'; -import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; +import { PacketeryExtendedPoint, PacketeryPickFunction, StoreOrPacketeryPoint } from './types'; +import { TypeOpeningHours } from 'graphql/types'; /** * @see https://docs.packetery.com/01-pickup-point-selection/02-widget-v6.html @@ -19,14 +19,14 @@ export const packeteryPick: PacketeryPickFunction = (apiKey, callback, opts, inE window.Packeta.Widget.pick(apiKey, callback, opts, inElement); }; -export const mapPacketeryExtendedPoint = (packeteryExtendedPoint: PacketeryExtendedPoint): TypeListedStoreFragment => ({ +export const mapPacketeryExtendedPoint = (packeteryExtendedPoint: PacketeryExtendedPoint): StoreOrPacketeryPoint => ({ __typename: 'Store', slug: '', latitude: null, longitude: null, identifier: packeteryExtendedPoint.id.toString(), description: packeteryExtendedPoint.directions, - name: packeteryExtendedPoint.name, + name: packeteryExtendedPoint.place, city: packeteryExtendedPoint.city, street: packeteryExtendedPoint.street, country: { @@ -35,9 +35,66 @@ export const mapPacketeryExtendedPoint = (packeteryExtendedPoint: PacketeryExten name: packeteryExtendedPoint.country.toUpperCase(), }, postcode: packeteryExtendedPoint.zip.replaceAll(' ', ''), - openingHours: { + openingHours: mapPacketeryOpeningHoursToInternalOpeningHours(packeteryExtendedPoint), +}); + +// {date: '2024-08-29', hours: '12:00–22:00'} +const mapPacketeryOpeningHoursToInternalOpeningHours = ( + packeteryExtendedPoint: PacketeryExtendedPoint, +): StoreOrPacketeryPoint['openingHours'] => { + const daysMap = { + monday: 1, + tuesday: 2, + wednesday: 3, + thursday: 4, + friday: 5, + saturday: 6, + sunday: 7, + }; + + const internalOpeningHours: TypeOpeningHours = { isOpen: false, - dayOfWeek: 0, + dayOfWeek: new Date().getDay(), openingHoursOfDays: [], - }, -}); + }; + + const parseTimeRange = (range: string) => { + const [openingTime, closingTime] = range.split('–'); + return { openingTime, closingTime }; + }; + + const parseOpeningHours = (hours: string) => { + if (hours.toLowerCase() === 'nonstop') { + return [ + { + openingTime: '00:00', + closingTime: '23:59', + }, + ]; + } + + return hours.split(', ').map(parseTimeRange); + }; + + // Process regular hours and fill internalOpeningHours + for (const [day, hours] of Object.entries(packeteryExtendedPoint.openingHours.regular)) { + const openingHoursRanges = parseOpeningHours(hours as string); + const date = new Date(); + date.setDate(date.getDate() + ((daysMap[day as keyof typeof daysMap] - date.getDay() + 7) % 7)); + const isoDate = date.toISOString().split('T')[0]; + + internalOpeningHours.openingHoursOfDays.push({ + date: `${isoDate}T00:00:00+02:00`, + dayOfWeek: daysMap[day as keyof typeof daysMap], + openingHoursRanges, + }); + } + + // Sort the days so that the current day is first and the rest follow sequentially + const currentDayOfWeek = new Date().getDay(); + internalOpeningHours.openingHoursOfDays.sort((a, b) => { + return ((a.dayOfWeek - currentDayOfWeek + 7) % 7) - ((b.dayOfWeek - currentDayOfWeek + 7) % 7); + }); + + return { ...internalOpeningHours, exceptionDays: packeteryExtendedPoint.exceptionDays }; +}; diff --git a/storefront/utils/packetery/types.ts b/storefront/utils/packetery/types.ts index ab941c919d..2e94c87b82 100644 --- a/storefront/utils/packetery/types.ts +++ b/storefront/utils/packetery/types.ts @@ -1,3 +1,5 @@ +import { TypeListedStoreFragment } from 'graphql/requests/stores/fragments/ListedStoreFragment.generated'; + export {}; /** @@ -41,12 +43,24 @@ export type PacketeryExtendedPoint = { tableLong: string; regular: string; }; + exceptionDays: PacketeryExceptionDay[]; pickupPointType: string; routingCode: string; carrierId: string; carrierPickupPointId: string; }; +type PacketeryExceptionDay = { + from: string; + to: string | null; + times: PacketeryTime[]; +}; + +type PacketeryTime = { + open: string; + close: string; +}; + export type PacketeryOptions = { webUrl?: string; appIdentity?: string; @@ -71,3 +85,9 @@ export type PacketeryPickFunction = ( opts?: PacketeryOptions, inElement?: HTMLElement, ) => void; + +export type StoreOrPacketeryPoint = TypeListedStoreFragment & { + openingHours: TypeListedStoreFragment['openingHours'] & { + exceptionDays?: PacketeryExceptionDay[]; + }; +};