Skip to content

Commit

Permalink
660 frontend put lodge schemas on bookings page (#763)
Browse files Browse the repository at this point in the history
* create basic story and implement component on page

* fetch data from sanity

* update docs
  • Loading branch information
choden-dev authored Aug 18, 2024
1 parent cafd4e8 commit ea8b816
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 13 deletions.
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"generate-types": "openapi-typescript ../server/src/middleware/__generated__/swagger.json -o ./src/models/__generated__/schema.d.ts "
},
"dependencies": {
"@portabletext/react": "^3.1.0",
"@sanity/image-url": "1",
"@sanity/vision": "3",
"@stripe/react-stripe-js": "^2.7.3",
Expand Down
4 changes: 2 additions & 2 deletions client/sanity/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ export class SanityImageUrl {
}

/**
* Appends the height query parameter to the URL.
* Appends the width query parameter to the URL.
*
* @param h - The height in pixels to set the image to.
* @param w - width height in pixels to set the image to.
*/
public width(w: number) {
this.url.searchParams.append("w", w.toString())
Expand Down
43 changes: 39 additions & 4 deletions client/src/app/bookings/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,46 @@
"use client"
import BookingInformationAndCreation from "@/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation"
import { SanityImageUrl, sanityQuery } from "../../../sanity/lib/utils"
import {
LODGE_INFORMATION_GROQ_QUERY,
LodgeInformation
} from "@/models/sanity/LodgeInfo/Utils"
import { PortableText } from "@portabletext/react"

import { ProtectedCreateBookingSection } from "@/components/composite/Booking/BookingCreation/ProtectedCreateBookingSection"
const BookingPage = async () => {
const lodgeInfo = await sanityQuery<LodgeInformation[]>(
LODGE_INFORMATION_GROQ_QUERY
)

/**
* We assume there will be only one based on the way {@link LodgeInformation}
* is set up in sanity
*/
const lodgeInfoSingleton = lodgeInfo[0]

const RenderedContent = () => {
return (
lodgeInfoSingleton.information && (
<PortableText value={lodgeInfoSingleton.information} />
)
)
}

const processedImages =
lodgeInfoSingleton.imageUrls?.map((item) =>
item.url
? new SanityImageUrl(item.url).autoFormat().width(700).toString()
: ""
) || []

const BookingPage = () => {
return (
<>
<ProtectedCreateBookingSection />
<BookingInformationAndCreation
enableNetworkRequests
lodgeInfoProps={{
children: <RenderedContent />,
images: processedImages
}}
/>
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const handleDateRangeInputChange = (
}
}

interface ICreateBookingSection {
export interface ICreateBookingSection {
/**
* The "unfiltered" booking slots for processing
*/
Expand Down Expand Up @@ -102,6 +102,9 @@ const ActualBookingStayRange = ({
)
}

/**
* @deprecated not for direct consumption on pages
*/
export const CreateBookingSection = ({
bookingSlots = [],
handleBookingCreation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { CreateBookingSection } from "./BookingCreation"
import { useContext, useEffect } from "react"
import { BookingContext } from "../BookingContext"

/**
* @deprecated not for direct consumption on pages, use `BookingInformationAndCreation` instead
*/
export const ProtectedCreateBookingSection = () => {
const [{ currentUser, currentUserClaims }] = useAppData()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ type props = IBookingInfoProps

const Divider = () => <span className="bg-dark-blue-100 my-3 h-[1px] w-full" />

/**
* Do **not** confuse with `LodgfeInfoComponent` which is used for display on the `/bookings` route
*/
const BookingInfoComponent = ({
pricePerNight,
priceSingleFridayOrSaturday
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Meta, StoryObj } from "@storybook/react"
import BookingInformationAndCreation from "./BookingInformationAndCreation"

const meta: Meta<typeof BookingInformationAndCreation> = {
component: BookingInformationAndCreation
}

export default meta

type Story = StoryObj<typeof meta>

export const DefaultBookingInformationAndCreation: Story = {
args: {
lodgeInfoProps: {
children: (
<>
<h2 className="italic">About our lodge</h2>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sit
molestiae repellendus nulla voluptatibus iure! Quasi earum quis
velit facilis mollitia minus a consequuntur blanditiis, excepturi
omnis harum laudantium ad dolores.
</p>
</>
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
"use client"

import { useState } from "react"
import LodgeInfo, { ILodgeInfo } from "../../LodgeInfo/LodgeInfo"
import {
CreateBookingSection,
ICreateBookingSection
} from "../BookingCreation/BookingCreation"
import { ProtectedCreateBookingSection } from "../BookingCreation/ProtectedCreateBookingSection"
import { useSearchParams } from "next/navigation"

/**
* Utility type determining what should be displayed to the user in {@link BookingInformationAndCreation}
*/
type BookingStages = "booking-information" | "booking-creation"

interface IBookingInformationAndCreation {
/**
* The required props for {@link CreateBookingSection}
*
* Optional if {@link enableNetworkRequests} is set to `false`
*/
bookingCreationProps?: ICreateBookingSection

/**
* The required props for {@link LodgeInfo}
*/
lodgeInfoProps?: Omit<ILodgeInfo, "handleBookLodgeClick">

/**
* Only set to `true` if embedding on page, for storybook purposes keep as false.
*
* Uses the {@link ProtectedCreateBookingSection} component if set to `true` otherwise
* uses the implementation of{@link CreateBookingSection}
*/
enableNetworkRequests?: boolean
}

/**
* Wrapper component that handles presentation for the booking creation page,
* with the information screen to allow users to know more about the lodge
*/
const BookingInformationAndCreation = ({
bookingCreationProps,
lodgeInfoProps,
enableNetworkRequests
}: IBookingInformationAndCreation) => {
const params = useSearchParams()

const defaultStage: BookingStages =
params.get("skip-info") === "true"
? "booking-creation"
: "booking-information"

const [currentStage, setCurrentStage] = useState<BookingStages>(defaultStage)

switch (currentStage) {
case "booking-information":
return (
<LodgeInfo
{...lodgeInfoProps}
handleBookLodgeClick={() => {
setCurrentStage("booking-creation")
}}
/>
)
case "booking-creation":
if (enableNetworkRequests) {
return <ProtectedCreateBookingSection />
} else {
return <CreateBookingSection {...bookingCreationProps} />
}
}
}

export default BookingInformationAndCreation
18 changes: 13 additions & 5 deletions client/src/components/composite/LodgeInfo/LodgeInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@ import LodgeInfoComponent from "./LodgeInfoComponent/LodgeInfoComponent"
import LodgeInfoGallery from "./LodgeInfoGallery/LodgeInfoGallery"
import { ReactNode } from "react"

interface ILodgeInfo {
export interface ILodgeInfo {
/**
* **Pre-formatted** content that should be displayed to the user
*/
children?: ReactNode
/**
* List of image srcs
*/
images: string[]
handleBookLodgeClick: () => void
images?: string[]
/**
* Handler to be called once the user clicks the call to action
*/
handleBookLodgeClick?: () => void
}

const LodgeInfo = ({ children, images, handleBookLodgeClick }: ILodgeInfo) => {
return (
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<LodgeInfoGallery images={images}></LodgeInfoGallery>
<LodgeInfoGallery images={images || []}></LodgeInfoGallery>
</div>
<div>
<LodgeInfoComponent handleBookLodgeClick={handleBookLodgeClick}>
<LodgeInfoComponent
handleBookLodgeClick={() => handleBookLodgeClick?.()}
>
{children}
</LodgeInfoComponent>
</div>
Expand Down
13 changes: 12 additions & 1 deletion client/src/models/sanity/LodgeInfo/Utils.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,13 @@
import { PortableTextBlock } from "@portabletext/types"

export const LODGE_INFORMATION_GROQ_QUERY =
`[_type == "lodge-information"]` as const
`*[_type == "lodge-information"]{"imageUrls": images[]{"url": asset->url}, ...}` as const

type LodgeInformationImage = {
url?: string
}

export type LodgeInformation = {
imageUrls?: LodgeInformationImage[]
information?: PortableTextBlock[]
}
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10909,6 +10909,7 @@ __metadata:
resolution: "client@workspace:client"
dependencies:
"@chromatic-com/storybook": "npm:1.6.1"
"@portabletext/react": "npm:^3.1.0"
"@sanity/image-url": "npm:1"
"@sanity/vision": "npm:3"
"@storybook/addon-essentials": "npm:^8.1.11"
Expand Down

0 comments on commit ea8b816

Please sign in to comment.