Merged in feat/SW-431-payment-flow (pull request #635)
Feat/SW-431 payment flow * feat(SW-431): Update mock hotel data * feat(SW-431): Added route handler and trpc routes * feat(SW-431): List payment methods and handle booking status and redirection * feat(SW-431): Updated booking page to poll for booking status * feat(SW-431): Updated create booking contract * feat(SW-431): small fix * fix(SW-431): Added intl string and sorted dictionaries * fix(SW-431): Changes from PR * fix(SW-431): fixes from PR * fix(SW-431): add todo comments * fix(SW-431): update schema prop Approved-by: Simon.Emanuelsson
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { serverClient } from "@/lib/trpc/server"
|
||||
import { getHotelDataSchema } from "@/server/routers/hotels/output"
|
||||
import tempHotelData from "@/server/routers/hotels/tempHotelData.json"
|
||||
|
||||
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
|
||||
import BedSelection from "@/components/HotelReservation/SelectRate/BedSelection"
|
||||
@@ -80,12 +80,19 @@ export default async function SectionsPage({
|
||||
}: PageArgs<LangParams & { section: string }, SectionPageProps>) {
|
||||
setLang(params.lang)
|
||||
|
||||
// TODO: Use real endpoint.
|
||||
const hotel = getHotelDataSchema.parse(tempHotelData)
|
||||
const hotel = await serverClient().hotel.hotelData.get({
|
||||
hotelId: "811",
|
||||
language: params.lang,
|
||||
})
|
||||
|
||||
if (!hotel) {
|
||||
// TODO: handle case with hotel missing
|
||||
return notFound()
|
||||
}
|
||||
|
||||
const rooms = await serverClient().hotel.rates.get({
|
||||
// TODO: pass the correct hotel ID and all other parameters that should be included in the search
|
||||
hotelId: "1",
|
||||
hotelId: hotel.data.id,
|
||||
})
|
||||
const intl = await getIntl()
|
||||
|
||||
@@ -170,7 +177,9 @@ export default async function SectionsPage({
|
||||
header={intl.formatMessage({ id: "Payment info" })}
|
||||
path={`payment?${currentSearchParams}`}
|
||||
>
|
||||
{params.section === "payment" && <Payment />}
|
||||
{params.section === "payment" && (
|
||||
<Payment hotel={hotel.data.attributes} />
|
||||
)}
|
||||
</SectionAccordion>
|
||||
</div>
|
||||
<div className={styles.summary}>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
|
||||
export default function Loading() {
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
@@ -1,20 +1,67 @@
|
||||
"use client"
|
||||
|
||||
import { useMemo } from "react"
|
||||
|
||||
import {
|
||||
BOOKING_CONFIRMATION_NUMBER,
|
||||
BookingStatusEnum,
|
||||
} from "@/constants/booking"
|
||||
|
||||
import IntroSection from "@/components/HotelReservation/BookingConfirmation/IntroSection"
|
||||
import StaySection from "@/components/HotelReservation/BookingConfirmation/StaySection"
|
||||
import SummarySection from "@/components/HotelReservation/BookingConfirmation/SummarySection"
|
||||
import { tempConfirmationData } from "@/components/HotelReservation/BookingConfirmation/tempConfirmationData"
|
||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||
import { useHandleBookingStatus } from "@/hooks/booking/useHandleBookingStatus"
|
||||
|
||||
import styles from "./page.module.css"
|
||||
|
||||
const maxRetries = 10
|
||||
const retryInterval = 2000
|
||||
|
||||
export default function BookingConfirmationPage() {
|
||||
const { email, hotel, stay, summary } = tempConfirmationData
|
||||
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<section className={styles.section}>
|
||||
<IntroSection email={email} />
|
||||
<StaySection hotel={hotel} stay={stay} />
|
||||
<SummarySection summary={summary} />
|
||||
</section>
|
||||
</main>
|
||||
const confirmationNumber = useMemo(() => {
|
||||
if (typeof window === "undefined") return ""
|
||||
|
||||
const storedConfirmationNumber = sessionStorage.getItem(
|
||||
BOOKING_CONFIRMATION_NUMBER
|
||||
)
|
||||
// TODO: cleanup stored values
|
||||
// sessionStorage.removeItem(BOOKING_CONFIRMATION_NUMBER)
|
||||
return storedConfirmationNumber
|
||||
}, [])
|
||||
|
||||
const bookingStatus = useHandleBookingStatus(
|
||||
confirmationNumber,
|
||||
BookingStatusEnum.BookingCompleted,
|
||||
maxRetries,
|
||||
retryInterval
|
||||
)
|
||||
|
||||
if (
|
||||
confirmationNumber === null ||
|
||||
bookingStatus.isError ||
|
||||
(bookingStatus.isFetched && !bookingStatus.data)
|
||||
) {
|
||||
// TODO: handle error
|
||||
throw new Error("Error fetching booking status")
|
||||
}
|
||||
|
||||
if (
|
||||
bookingStatus.data?.reservationStatus === BookingStatusEnum.BookingCompleted
|
||||
) {
|
||||
return (
|
||||
<main className={styles.main}>
|
||||
<section className={styles.section}>
|
||||
<IntroSection email={email} />
|
||||
<StaySection hotel={hotel} stay={stay} />
|
||||
<SummarySection summary={summary} />
|
||||
</section>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
return <LoadingSpinner />
|
||||
}
|
||||
|
||||
37
app/api/web/payment-callback/[lang]/[status]/route.ts
Normal file
37
app/api/web/payment-callback/[lang]/[status]/route.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { NextRequest, NextResponse } from "next/server"
|
||||
import { env } from "process"
|
||||
|
||||
import { Lang } from "@/constants/languages"
|
||||
import {
|
||||
bookingConfirmation,
|
||||
payment,
|
||||
} from "@/constants/routes/hotelReservation"
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: { lang: string; status: string } }
|
||||
): Promise<NextResponse> {
|
||||
console.log(`[payment-callback] callback started`)
|
||||
const lang = params.lang as Lang
|
||||
const status = params.status
|
||||
const returnUrl = new URL(`${env.PUBLIC_URL}/${payment[lang]}`)
|
||||
|
||||
if (status === "success") {
|
||||
const confirmationUrl = new URL(
|
||||
`${env.PUBLIC_URL}/${bookingConfirmation[lang]}`
|
||||
)
|
||||
console.log(`[payment-callback] redirecting to: ${confirmationUrl}`)
|
||||
return NextResponse.redirect(confirmationUrl)
|
||||
}
|
||||
|
||||
if (status === "cancel") {
|
||||
returnUrl.searchParams.set("cancel", "true")
|
||||
}
|
||||
|
||||
if (status === "error") {
|
||||
returnUrl.searchParams.set("error", "true")
|
||||
}
|
||||
|
||||
console.log(`[payment-callback] redirecting to: ${returnUrl}`)
|
||||
return NextResponse.redirect(returnUrl)
|
||||
}
|
||||
Reference in New Issue
Block a user