From 0a1e6597e52a5b62b354e5c7316256c4fe8a11f6 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Sun, 18 Aug 2024 16:48:44 +1200 Subject: [PATCH 1/3] create basic story and implement component on page --- client/src/app/bookings/page.tsx | 11 ++- .../BookingCreation/BookingCreation.tsx | 2 +- .../BookingInfoComponent.tsx | 3 + .../BookingInformationAndCreation.story.tsx | 28 +++++++ .../BookingInformationAndCreation.tsx | 76 +++++++++++++++++++ .../composite/LodgeInfo/LodgeInfo.tsx | 18 +++-- client/src/models/sanity/LodgeInfo/Utils.ts | 7 +- 7 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.story.tsx create mode 100644 client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.tsx diff --git a/client/src/app/bookings/page.tsx b/client/src/app/bookings/page.tsx index f20a902c5..5187d28ea 100644 --- a/client/src/app/bookings/page.tsx +++ b/client/src/app/bookings/page.tsx @@ -1,11 +1,14 @@ -"use client" +import BookingInformationAndCreation from "@/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation" +import { sanityQuery } from "../../../sanity/lib/utils" +import { LODGE_INFORMATION_GROQ_QUERY } from "@/models/sanity/LodgeInfo/Utils" -import { ProtectedCreateBookingSection } from "@/components/composite/Booking/BookingCreation/ProtectedCreateBookingSection" +const BookingPage = async () => { + const lodgeInfo = await sanityQuery(LODGE_INFORMATION_GROQ_QUERY) + console.log(lodgeInfo) -const BookingPage = () => { return ( <> - + ) } diff --git a/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx b/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx index adfd782c7..705c57a7d 100644 --- a/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx +++ b/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx @@ -40,7 +40,7 @@ export const handleDateRangeInputChange = ( } } -interface ICreateBookingSection { +export interface ICreateBookingSection { /** * The "unfiltered" booking slots for processing */ diff --git a/client/src/components/composite/Booking/BookingInfoComponent/BookingInfoComponent.tsx b/client/src/components/composite/Booking/BookingInfoComponent/BookingInfoComponent.tsx index 28a6fca3c..d78ef3699 100644 --- a/client/src/components/composite/Booking/BookingInfoComponent/BookingInfoComponent.tsx +++ b/client/src/components/composite/Booking/BookingInfoComponent/BookingInfoComponent.tsx @@ -18,6 +18,9 @@ type props = IBookingInfoProps const Divider = () => +/** + * Do **not** confuse with `LodgfeInfoComponent` which is used for display on the `/bookings` route + */ const BookingInfoComponent = ({ pricePerNight, priceSingleFridayOrSaturday diff --git a/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.story.tsx b/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.story.tsx new file mode 100644 index 000000000..bd5fe25ee --- /dev/null +++ b/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.story.tsx @@ -0,0 +1,28 @@ +import type { Meta, StoryObj } from "@storybook/react" +import BookingInformationAndCreation from "./BookingInformationAndCreation" + +const meta: Meta = { + component: BookingInformationAndCreation +} + +export default meta + +type Story = StoryObj + +export const DefaultBookingInformationAndCreation: Story = { + args: { + lodgeInfoProps: { + children: ( + <> +

About our lodge

+

+ 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. +

+ + ) + } + } +} diff --git a/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.tsx b/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.tsx new file mode 100644 index 000000000..1c7503421 --- /dev/null +++ b/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.tsx @@ -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 { ReadonlyURLSearchParams, 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 + + /** + * 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(defaultStage) + + switch (currentStage) { + case "booking-information": + return ( + { + setCurrentStage("booking-creation") + }} + /> + ) + case "booking-creation": + if (enableNetworkRequests) { + return + } else { + return + } + } +} + +export default BookingInformationAndCreation diff --git a/client/src/components/composite/LodgeInfo/LodgeInfo.tsx b/client/src/components/composite/LodgeInfo/LodgeInfo.tsx index c5fb4c77b..4ac3c57a6 100644 --- a/client/src/components/composite/LodgeInfo/LodgeInfo.tsx +++ b/client/src/components/composite/LodgeInfo/LodgeInfo.tsx @@ -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 (
- +
- + handleBookLodgeClick?.()} + > {children}
diff --git a/client/src/models/sanity/LodgeInfo/Utils.ts b/client/src/models/sanity/LodgeInfo/Utils.ts index f38607c26..a0fbf786d 100644 --- a/client/src/models/sanity/LodgeInfo/Utils.ts +++ b/client/src/models/sanity/LodgeInfo/Utils.ts @@ -1,2 +1,7 @@ export const LODGE_INFORMATION_GROQ_QUERY = - `[_type == "lodge-information"]` as const + `*[_type == "lodge-information"]` as const + +export type LodgeInformation = { + images?: string[] + information?: any[] +} From c2fe91f37f0ac1fdfd772122d77efd6340b50c0a Mon Sep 17 00:00:00 2001 From: bcho892 Date: Sun, 18 Aug 2024 20:21:45 +1200 Subject: [PATCH 2/3] fetch data from sanity --- client/package.json | 1 + client/sanity/lib/utils.ts | 4 +- client/src/app/bookings/page.tsx | 42 ++++++++++++++++--- .../BookingInformationAndCreation.tsx | 2 +- client/src/models/sanity/LodgeInfo/Utils.ts | 12 ++++-- yarn.lock | 1 + 6 files changed, 51 insertions(+), 11 deletions(-) diff --git a/client/package.json b/client/package.json index 05f41b32b..89431bcbc 100644 --- a/client/package.json +++ b/client/package.json @@ -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", diff --git a/client/sanity/lib/utils.ts b/client/sanity/lib/utils.ts index 0c75028f0..698c2cf74 100644 --- a/client/sanity/lib/utils.ts +++ b/client/sanity/lib/utils.ts @@ -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()) diff --git a/client/src/app/bookings/page.tsx b/client/src/app/bookings/page.tsx index 5187d28ea..8eca0d1af 100644 --- a/client/src/app/bookings/page.tsx +++ b/client/src/app/bookings/page.tsx @@ -1,14 +1,46 @@ import BookingInformationAndCreation from "@/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation" -import { sanityQuery } from "../../../sanity/lib/utils" -import { LODGE_INFORMATION_GROQ_QUERY } from "@/models/sanity/LodgeInfo/Utils" +import { SanityImageUrl, sanityQuery } from "../../../sanity/lib/utils" +import { + LODGE_INFORMATION_GROQ_QUERY, + LodgeInformation +} from "@/models/sanity/LodgeInfo/Utils" +import { PortableText } from "@portabletext/react" const BookingPage = async () => { - const lodgeInfo = await sanityQuery(LODGE_INFORMATION_GROQ_QUERY) - console.log(lodgeInfo) + const lodgeInfo = await sanityQuery( + 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 && ( + + ) + ) + } + + const processedImages = + lodgeInfoSingleton.imageUrls?.map((item) => + item.url + ? new SanityImageUrl(item.url).autoFormat().width(700).toString() + : "" + ) || [] return ( <> - + , + images: processedImages + }} + /> ) } diff --git a/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.tsx b/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.tsx index 1c7503421..5d89156a4 100644 --- a/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.tsx +++ b/client/src/components/composite/Booking/BookingInformationAndCreation/BookingInformationAndCreation.tsx @@ -7,7 +7,7 @@ import { ICreateBookingSection } from "../BookingCreation/BookingCreation" import { ProtectedCreateBookingSection } from "../BookingCreation/ProtectedCreateBookingSection" -import { ReadonlyURLSearchParams, useSearchParams } from "next/navigation" +import { useSearchParams } from "next/navigation" /** * Utility type determining what should be displayed to the user in {@link BookingInformationAndCreation} diff --git a/client/src/models/sanity/LodgeInfo/Utils.ts b/client/src/models/sanity/LodgeInfo/Utils.ts index a0fbf786d..683d40dd1 100644 --- a/client/src/models/sanity/LodgeInfo/Utils.ts +++ b/client/src/models/sanity/LodgeInfo/Utils.ts @@ -1,7 +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 = { - images?: string[] - information?: any[] + imageUrls?: LodgeInformationImage[] + information?: PortableTextBlock[] } diff --git a/yarn.lock b/yarn.lock index f9caee01a..7f10456c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" From 84acee70ecbd8b4a91bdf195c680ffa1ea186cf4 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Sun, 18 Aug 2024 20:30:43 +1200 Subject: [PATCH 3/3] update docs --- .../composite/Booking/BookingCreation/BookingCreation.tsx | 3 +++ .../Booking/BookingCreation/ProtectedCreateBookingSection.tsx | 3 +++ 2 files changed, 6 insertions(+) diff --git a/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx b/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx index 705c57a7d..019872d77 100644 --- a/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx +++ b/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx @@ -102,6 +102,9 @@ const ActualBookingStayRange = ({ ) } +/** + * @deprecated not for direct consumption on pages + */ export const CreateBookingSection = ({ bookingSlots = [], handleBookingCreation, diff --git a/client/src/components/composite/Booking/BookingCreation/ProtectedCreateBookingSection.tsx b/client/src/components/composite/Booking/BookingCreation/ProtectedCreateBookingSection.tsx index cea4478fe..3c7bd9c11 100644 --- a/client/src/components/composite/Booking/BookingCreation/ProtectedCreateBookingSection.tsx +++ b/client/src/components/composite/Booking/BookingCreation/ProtectedCreateBookingSection.tsx @@ -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()