Merged in fix/STAY-2-GLA-cancelled (pull request #3109)
Fix/STAY-2 GLA cancelled * fix: show toast on cancelling GLA flow * fix: show the ancillary GLA errors as inline alerts Approved-by: Bianca Widstam Approved-by: Erik Tiekstra
This commit is contained in:
@@ -29,6 +29,7 @@ import type { ConfirmationStepProps } from "@/types/components/myPages/myStay/an
|
||||
export default function ConfirmationStep({
|
||||
savedCreditCards,
|
||||
user,
|
||||
error,
|
||||
}: ConfirmationStepProps) {
|
||||
const intl = useIntl()
|
||||
const lang = useLang()
|
||||
@@ -138,14 +139,19 @@ export default function ConfirmationStep({
|
||||
</PaymentOptionsGroup>
|
||||
) : (
|
||||
<>
|
||||
<Alert
|
||||
type={AlertTypeEnum.Info}
|
||||
text={intl.formatMessage({
|
||||
id: "addAncillary.confirmationStep.guaranteeAddCard",
|
||||
defaultMessage:
|
||||
"By adding a card you also guarantee your room booking for late arrival.",
|
||||
})}
|
||||
/>
|
||||
{error ? (
|
||||
<Alert type={error.type} text={error.message} />
|
||||
) : (
|
||||
<Alert
|
||||
type={AlertTypeEnum.Info}
|
||||
text={intl.formatMessage({
|
||||
id: "addAncillary.confirmationStep.guaranteeAddCard",
|
||||
defaultMessage:
|
||||
"By adding a card you also guarantee your room booking for late arrival.",
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
|
||||
<SelectPaymentMethod
|
||||
paymentMethods={(savedCreditCards ?? []).map((card) => ({
|
||||
...card,
|
||||
|
||||
@@ -10,7 +10,7 @@ import SelectQuantityStep from "../SelectQuantityStep"
|
||||
|
||||
import type { StepsProps } from "@/types/components/myPages/myStay/ancillaries"
|
||||
|
||||
export default function Desktop({ user, savedCreditCards }: StepsProps) {
|
||||
export default function Desktop({ user, savedCreditCards, error }: StepsProps) {
|
||||
const currentStep = useAddAncillaryStore((state) => state.currentStep)
|
||||
if (currentStep === AncillaryStepEnum.selectAncillary) {
|
||||
return <SelectAncillaryStep />
|
||||
@@ -22,5 +22,11 @@ export default function Desktop({ user, savedCreditCards }: StepsProps) {
|
||||
return <DeliveryMethodStep />
|
||||
}
|
||||
|
||||
return <ConfirmationStep savedCreditCards={savedCreditCards} user={user} />
|
||||
return (
|
||||
<ConfirmationStep
|
||||
savedCreditCards={savedCreditCards}
|
||||
user={user}
|
||||
error={error}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import SelectQuantityStep from "../SelectQuantityStep"
|
||||
|
||||
import type { StepsProps } from "@/types/components/myPages/myStay/ancillaries"
|
||||
|
||||
export default function Mobile({ user, savedCreditCards }: StepsProps) {
|
||||
export default function Mobile({ user, savedCreditCards, error }: StepsProps) {
|
||||
const { currentStep, selectedAncillary } = useAddAncillaryStore((state) => ({
|
||||
currentStep: state.currentStep,
|
||||
selectedAncillary: state.selectedAncillary,
|
||||
@@ -23,5 +23,11 @@ export default function Mobile({ user, savedCreditCards }: StepsProps) {
|
||||
</>
|
||||
)
|
||||
}
|
||||
return <ConfirmationStep savedCreditCards={savedCreditCards} user={user} />
|
||||
return (
|
||||
<ConfirmationStep
|
||||
savedCreditCards={savedCreditCards}
|
||||
user={user}
|
||||
error={error}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,13 +16,11 @@ import Modal from "@scandic-hotels/design-system/Modal"
|
||||
import { toast } from "@scandic-hotels/design-system/Toast"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
import { trpc } from "@scandic-hotels/trpc/client"
|
||||
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
|
||||
|
||||
import { isWebview } from "@/constants/routes/webviews"
|
||||
import { env } from "@/env/client"
|
||||
import {
|
||||
AncillaryStepEnum,
|
||||
type BreakfastData,
|
||||
useAddAncillaryStore,
|
||||
} from "@/stores/my-stay/add-ancillary-flow"
|
||||
|
||||
@@ -41,16 +39,23 @@ import {
|
||||
trackGlaAncillaryAttempt,
|
||||
} from "@/utils/tracking/myStay"
|
||||
|
||||
import { isAncillaryError } from "../../../utils"
|
||||
import { type AncillaryFormData, ancillaryFormSchema } from "../schema"
|
||||
import ActionButtons from "./ActionButtons"
|
||||
import PriceDetails from "./PriceDetails"
|
||||
import Steps from "./Steps"
|
||||
import {
|
||||
buildBreakfastPackages,
|
||||
calculateBreakfastData,
|
||||
getErrorMessage,
|
||||
} from "./utils"
|
||||
|
||||
import styles from "./addAncillaryFlowModal.module.css"
|
||||
|
||||
import type {
|
||||
AddAncillaryFlowModalProps,
|
||||
Packages,
|
||||
AncillaryErrorMessage,
|
||||
AncillaryItem,
|
||||
} from "@/types/components/myPages/myStay/ancillaries"
|
||||
|
||||
export default function AddAncillaryFlowModal({
|
||||
@@ -81,6 +86,8 @@ export default function AddAncillaryFlowModal({
|
||||
const pathname = usePathname()
|
||||
|
||||
const [isPriceDetailsOpen, setIsPriceDetailsOpen] = useState(false)
|
||||
const [errorMessage, setErrorMessage] =
|
||||
useState<AncillaryErrorMessage | null>(null)
|
||||
const guaranteeRedirectUrl = `${env.NEXT_PUBLIC_NODE_ENV === "development" ? `http://localhost:${env.NEXT_PUBLIC_PORT}` : ""}${guaranteeCallback(lang, isWebview(pathname))}`
|
||||
const deliveryTimeOptions = generateDeliveryOptions()
|
||||
|
||||
@@ -94,7 +101,7 @@ export default function AddAncillaryFlowModal({
|
||||
quantityWithPoints: null,
|
||||
quantityWithCard:
|
||||
!user || hasInsufficientPoints || isBreakfast ? 1 : null,
|
||||
deliveryTime: defaultDeliveryTime,
|
||||
deliveryTime: booking.ancillary?.deliveryTime ?? defaultDeliveryTime,
|
||||
optionalText: "",
|
||||
termsAndConditions: false,
|
||||
paymentMethod: booking.guaranteeInfo
|
||||
@@ -127,16 +134,6 @@ export default function AddAncillaryFlowModal({
|
||||
const { guaranteeBooking, isLoading, handleGuaranteeError } =
|
||||
useGuaranteeBooking(booking.refId, true, booking.hotelId)
|
||||
|
||||
function validateTermsAndConditions(data: AncillaryFormData): boolean {
|
||||
if (!data.termsAndConditions) {
|
||||
formMethods.setError("termsAndConditions", {
|
||||
message: "You must accept the terms",
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function handleAncillarySubmission(
|
||||
data: AncillaryFormData,
|
||||
packages: {
|
||||
@@ -206,8 +203,10 @@ export default function AddAncillaryFlowModal({
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function handleGuaranteePayment(data: AncillaryFormData, packages: any) {
|
||||
function handleGuaranteePayment(
|
||||
data: AncillaryFormData,
|
||||
packages: AncillaryItem[]
|
||||
) {
|
||||
const savedCreditCard = savedCreditCards?.find(
|
||||
(card) => card.id === data.paymentMethod
|
||||
)
|
||||
@@ -239,34 +238,7 @@ export default function AddAncillaryFlowModal({
|
||||
}
|
||||
}
|
||||
|
||||
function buildBreakfastPackages(
|
||||
data: AncillaryFormData,
|
||||
breakfastData: BreakfastData
|
||||
) {
|
||||
const packages = [
|
||||
{
|
||||
code: BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
|
||||
quantity: breakfastData.nrOfAdults,
|
||||
comment: data.optionalText || undefined,
|
||||
},
|
||||
{
|
||||
code: BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
|
||||
quantity: breakfastData.nrOfPayingChildren,
|
||||
comment: data.optionalText || undefined,
|
||||
},
|
||||
{
|
||||
code: BreakfastPackageEnum.FREE_CHILD_BREAKFAST,
|
||||
quantity: breakfastData.nrOfFreeChildren,
|
||||
comment: data.optionalText || undefined,
|
||||
},
|
||||
]
|
||||
|
||||
return packages.filter((pkg) => pkg.quantity > 0)
|
||||
}
|
||||
|
||||
const onSubmit = (data: AncillaryFormData) => {
|
||||
if (!validateTermsAndConditions(data)) return
|
||||
|
||||
const packagesToAdd = !isBreakfast
|
||||
? buildAncillaryPackages(data, selectedAncillary)
|
||||
: breakfastData
|
||||
@@ -300,14 +272,9 @@ export default function AddAncillaryFlowModal({
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const errorCode = searchParams.get("errorCode")
|
||||
const ancillary = searchParams.get("ancillary")
|
||||
if ((errorCode && ancillary) || errorCode === "AncillaryFailed") {
|
||||
if (isAncillaryError(searchParams)) {
|
||||
const errorCode = searchParams.get("errorCode")
|
||||
const queryParams = new URLSearchParams(searchParams.toString())
|
||||
if (ancillary) {
|
||||
queryParams.delete("ancillary")
|
||||
}
|
||||
queryParams.delete("errorCode")
|
||||
const savedData = getAncillarySessionData()
|
||||
if (savedData?.formData) {
|
||||
const updatedFormData = {
|
||||
@@ -318,9 +285,13 @@ export default function AddAncillaryFlowModal({
|
||||
}
|
||||
formMethods.reset(updatedFormData)
|
||||
}
|
||||
|
||||
setErrorMessage(getErrorMessage(intl, errorCode))
|
||||
queryParams.delete("ancillary")
|
||||
queryParams.delete("errorCode")
|
||||
router.replace(`${pathname}?${queryParams.toString()}`)
|
||||
}
|
||||
}, [searchParams, pathname, formMethods, router, booking.guaranteeInfo])
|
||||
}, [searchParams, pathname, formMethods, router, booking.guaranteeInfo, intl])
|
||||
|
||||
useEffect(() => {
|
||||
setBreakfastData(
|
||||
@@ -424,7 +395,11 @@ export default function AddAncillaryFlowModal({
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Steps user={user} savedCreditCards={savedCreditCards} />
|
||||
<Steps
|
||||
user={user}
|
||||
savedCreditCards={savedCreditCards}
|
||||
error={errorMessage}
|
||||
/>
|
||||
{currentStep === AncillaryStepEnum.selectAncillary ? null : (
|
||||
<div
|
||||
className={
|
||||
@@ -520,61 +495,3 @@ function BreakfastPriceList() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This function calculates some breakfast data in the store.
|
||||
* It is used in various places in the add flow, but only needs
|
||||
* to be calculated once.
|
||||
*/
|
||||
function calculateBreakfastData(
|
||||
isBreakfast: boolean,
|
||||
packages: Packages | null,
|
||||
nrOfAdults: number,
|
||||
childrenAges: number[],
|
||||
nrOfNights: number
|
||||
): BreakfastData | null {
|
||||
if (!isBreakfast) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { nrOfPayingChildren, nrOfFreeChildren } = childrenAges.reduce(
|
||||
(total, childAge) => {
|
||||
if (childAge >= 4) {
|
||||
total.nrOfPayingChildren = total.nrOfPayingChildren + 1
|
||||
} else {
|
||||
total.nrOfFreeChildren = total.nrOfFreeChildren + 1
|
||||
}
|
||||
return total
|
||||
},
|
||||
{ nrOfPayingChildren: 0, nrOfFreeChildren: 0 }
|
||||
)
|
||||
|
||||
const adultPackage = packages?.find(
|
||||
(p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
||||
)
|
||||
const childPackage = packages?.find(
|
||||
(p) => p.code === BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST
|
||||
)
|
||||
const priceAdult = adultPackage?.localPrice.price
|
||||
const priceChild = childPackage?.localPrice.price
|
||||
const currency =
|
||||
adultPackage?.localPrice.currency ?? childPackage?.localPrice.currency
|
||||
|
||||
if (
|
||||
typeof priceAdult !== "number" ||
|
||||
typeof priceChild !== "number" ||
|
||||
typeof currency !== "string"
|
||||
) {
|
||||
return null
|
||||
} else {
|
||||
return {
|
||||
nrOfAdults,
|
||||
nrOfPayingChildren,
|
||||
nrOfFreeChildren,
|
||||
nrOfNights,
|
||||
priceAdult,
|
||||
priceChild,
|
||||
currency,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
||||
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
|
||||
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
|
||||
|
||||
import type { Packages } from "@scandic-hotels/trpc/types/packages"
|
||||
import type { IntlShape } from "react-intl"
|
||||
|
||||
import type { AncillaryErrorMessage } from "@/types/components/myPages/myStay/ancillaries"
|
||||
import type { BreakfastData } from "@/stores/my-stay/add-ancillary-flow"
|
||||
import type { AncillaryFormData } from "../schema"
|
||||
|
||||
/**
|
||||
* This function calculates some breakfast data in the store.
|
||||
* It is used in various places in the add flow, but only needs
|
||||
* to be calculated once.
|
||||
*/
|
||||
export function calculateBreakfastData(
|
||||
isBreakfast: boolean,
|
||||
packages: Packages | null,
|
||||
nrOfAdults: number,
|
||||
childrenAges: number[],
|
||||
nrOfNights: number
|
||||
): BreakfastData | null {
|
||||
if (!isBreakfast || !packages) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { nrOfPayingChildren, nrOfFreeChildren } = childrenAges.reduce(
|
||||
(total, childAge) => {
|
||||
if (childAge >= 4) {
|
||||
total.nrOfPayingChildren = total.nrOfPayingChildren + 1
|
||||
} else {
|
||||
total.nrOfFreeChildren = total.nrOfFreeChildren + 1
|
||||
}
|
||||
return total
|
||||
},
|
||||
{ nrOfPayingChildren: 0, nrOfFreeChildren: 0 }
|
||||
)
|
||||
|
||||
const adultPackage = packages.find(
|
||||
(p) => p.code === BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST
|
||||
)
|
||||
const childPackage = packages.find(
|
||||
(p) => p.code === BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST
|
||||
)
|
||||
const priceAdult = adultPackage?.localPrice.price
|
||||
const priceChild = childPackage?.localPrice.price
|
||||
const currency =
|
||||
adultPackage?.localPrice.currency ?? childPackage?.localPrice.currency
|
||||
|
||||
if (
|
||||
typeof priceAdult !== "number" ||
|
||||
typeof priceChild !== "number" ||
|
||||
typeof currency !== "string"
|
||||
) {
|
||||
return null
|
||||
} else {
|
||||
return {
|
||||
nrOfAdults,
|
||||
nrOfPayingChildren,
|
||||
nrOfFreeChildren,
|
||||
nrOfNights,
|
||||
priceAdult,
|
||||
priceChild,
|
||||
currency,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function buildBreakfastPackages(
|
||||
data: AncillaryFormData,
|
||||
breakfastData: BreakfastData
|
||||
) {
|
||||
const packages = [
|
||||
{
|
||||
code: BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
|
||||
quantity: breakfastData.nrOfAdults,
|
||||
comment: data.optionalText || undefined,
|
||||
},
|
||||
{
|
||||
code: BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
|
||||
quantity: breakfastData.nrOfPayingChildren,
|
||||
comment: data.optionalText || undefined,
|
||||
},
|
||||
{
|
||||
code: BreakfastPackageEnum.FREE_CHILD_BREAKFAST,
|
||||
quantity: breakfastData.nrOfFreeChildren,
|
||||
comment: data.optionalText || undefined,
|
||||
},
|
||||
]
|
||||
|
||||
return packages.filter((pkg) => pkg.quantity > 0)
|
||||
}
|
||||
|
||||
export function getErrorMessage(
|
||||
intl: IntlShape,
|
||||
errorCode: string | null
|
||||
): AncillaryErrorMessage {
|
||||
switch (errorCode) {
|
||||
case BookingErrorCodeEnum.TransactionFailed:
|
||||
return {
|
||||
message: intl.formatMessage({
|
||||
id: "guaranteePayment.failed",
|
||||
defaultMessage:
|
||||
"We had an issue guaranteeing your booking. Please try again.",
|
||||
}),
|
||||
type: AlertTypeEnum.Alarm,
|
||||
}
|
||||
case BookingErrorCodeEnum.TransactionCancelled:
|
||||
return {
|
||||
message: intl.formatMessage({
|
||||
id: "guaranteePayment.cancelled",
|
||||
defaultMessage:
|
||||
"You have cancelled the payment. Your booking is not guaranteed.",
|
||||
}),
|
||||
type: AlertTypeEnum.Warning,
|
||||
}
|
||||
case "AncillaryFailed":
|
||||
return {
|
||||
message: intl.formatMessage({
|
||||
id: "guaranteePayment.ancillaryFailed",
|
||||
defaultMessage:
|
||||
"The product could not be added. Your booking is guaranteed. Please try again.",
|
||||
}),
|
||||
type: AlertTypeEnum.Alarm,
|
||||
}
|
||||
default:
|
||||
return {
|
||||
message: intl.formatMessage({
|
||||
id: "guaranteePayment.genericError",
|
||||
defaultMessage: "Something went wrong! Please try again later.",
|
||||
}),
|
||||
type: AlertTypeEnum.Alarm,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
align-items: flex-start;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: var(--Space-x15);
|
||||
|
||||
@@ -69,3 +69,9 @@ export function hasModifiableRate(cancellationRule: string | null): boolean {
|
||||
cancellationRule === CancellationRuleEnum.Changeable
|
||||
)
|
||||
}
|
||||
|
||||
export function isAncillaryError(searchParams: URLSearchParams): boolean {
|
||||
const errorCode = searchParams.get("errorCode")
|
||||
const ancillary = searchParams.get("ancillary")
|
||||
return Boolean((errorCode && ancillary) || errorCode === "AncillaryFailed")
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
"use client"
|
||||
|
||||
import { usePathname, useRouter, useSearchParams } from "next/navigation"
|
||||
import { useCallback, useEffect } from "react"
|
||||
import { useCallback, useEffect, useRef } from "react"
|
||||
import { useIntl } from "react-intl"
|
||||
|
||||
import { toast } from "@scandic-hotels/design-system/Toast"
|
||||
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
|
||||
|
||||
import { isAncillaryError } from "@/components/HotelReservation/MyStay/utils"
|
||||
|
||||
export function useGuaranteePaymentFailedToast() {
|
||||
const hasRunOnce = useRef(false)
|
||||
const intl = useIntl()
|
||||
const searchParams = useSearchParams()
|
||||
const pathname = usePathname()
|
||||
@@ -16,11 +19,11 @@ export function useGuaranteePaymentFailedToast() {
|
||||
const getErrorMessage = useCallback(
|
||||
(errorCode: string | null) => {
|
||||
switch (errorCode) {
|
||||
case "AncillaryFailed":
|
||||
case BookingErrorCodeEnum.TransactionCancelled:
|
||||
return intl.formatMessage({
|
||||
id: "guaranteePayment.ancillaryFailed",
|
||||
id: "guaranteePayment.cancelled",
|
||||
defaultMessage:
|
||||
"The product could not be added. Your booking is guaranteed. Please try again.",
|
||||
"You have cancelled the payment. Your booking is not guaranteed.",
|
||||
})
|
||||
default:
|
||||
return intl.formatMessage({
|
||||
@@ -34,25 +37,33 @@ export function useGuaranteePaymentFailedToast() {
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const errorCode = searchParams.get("errorCode")
|
||||
const errorMessage = getErrorMessage(errorCode)
|
||||
if (!errorCode || errorCode === BookingErrorCodeEnum.TransactionCancelled)
|
||||
// To prevent multiple toasts in strict mode
|
||||
if (hasRunOnce.current) {
|
||||
return
|
||||
}
|
||||
const errorCode = searchParams.get("errorCode")
|
||||
if (!errorCode) {
|
||||
return
|
||||
}
|
||||
|
||||
// Ancillary errors are handled in AddAncillaryFlowModal
|
||||
if (isAncillaryError(searchParams)) {
|
||||
hasRunOnce.current = true
|
||||
return
|
||||
}
|
||||
|
||||
const errorMessage = getErrorMessage(errorCode)
|
||||
const toastType =
|
||||
errorCode === BookingErrorCodeEnum.TransactionCancelled
|
||||
? "warning"
|
||||
: "error"
|
||||
toast[toastType](errorMessage)
|
||||
|
||||
const ancillary = searchParams.get("ancillary")
|
||||
if ((errorCode && ancillary) || errorCode === "AncillaryFailed") {
|
||||
return
|
||||
}
|
||||
toast[toastType](errorMessage)
|
||||
|
||||
const queryParams = new URLSearchParams(searchParams.toString())
|
||||
queryParams.delete("errorCode")
|
||||
|
||||
router.push(`${pathname}?${queryParams.toString()}`)
|
||||
hasRunOnce.current = true
|
||||
}, [searchParams, pathname, router, getErrorMessage])
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { AlertTypeEnum } from "@scandic-hotels/common/constants/alert"
|
||||
import type {
|
||||
ancillaryPackagesSchema,
|
||||
packagesSchema,
|
||||
@@ -24,6 +25,12 @@ export interface AddedAncillariesProps {
|
||||
booking: Room
|
||||
}
|
||||
|
||||
export interface AncillaryItem {
|
||||
code: string
|
||||
quantity: number
|
||||
comment: string | undefined
|
||||
}
|
||||
|
||||
export interface AddAncillaryFlowModalProps {
|
||||
booking: Room
|
||||
packages: Packages | null
|
||||
@@ -34,15 +41,20 @@ export interface AddAncillaryFlowModalProps {
|
||||
export interface SelectQuantityStepProps {
|
||||
user: User | null
|
||||
}
|
||||
|
||||
export interface AncillaryErrorMessage {
|
||||
type: AlertTypeEnum
|
||||
message: string
|
||||
}
|
||||
export interface ConfirmationStepProps {
|
||||
savedCreditCards: CreditCard[] | null
|
||||
user: User | null
|
||||
error: AncillaryErrorMessage | null
|
||||
}
|
||||
|
||||
export interface StepsProps {
|
||||
user: User | null
|
||||
savedCreditCards: CreditCard[] | null
|
||||
error: AncillaryErrorMessage | null
|
||||
}
|
||||
|
||||
export interface ActionButtonsProps {
|
||||
|
||||
Reference in New Issue
Block a user