feat(SW-3151): Added original to imageSchema and added transform to a more generic image type
Approved-by: Bianca Widstam Approved-by: Chuma Mcphoy (We Ahead) Approved-by: Matilda Landström
This commit is contained in:
@@ -45,8 +45,11 @@ function CardContent({ stay }: StayCardProps) {
|
|||||||
<article className={styles.stay}>
|
<article className={styles.stay}>
|
||||||
<Image
|
<Image
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
alt={hotelInformation.hotelContent.images.metaData.altText}
|
alt={
|
||||||
src={hotelInformation.hotelContent.images.imageSizes.small}
|
hotelInformation.hotelContent.images.altText ||
|
||||||
|
hotelInformation.hotelContent.images.altText_En
|
||||||
|
}
|
||||||
|
src={hotelInformation.hotelContent.images.src}
|
||||||
width={420}
|
width={420}
|
||||||
height={240}
|
height={240}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ export default async function HotelListingItem({
|
|||||||
return (
|
return (
|
||||||
<article className={styles.container}>
|
<article className={styles.container}>
|
||||||
<Image
|
<Image
|
||||||
src={image.imageSizes.large}
|
src={image.src}
|
||||||
alt={image.metaData.altText || image.metaData.altText_En}
|
alt={image.altText || image.altText_En}
|
||||||
width={400}
|
width={400}
|
||||||
height={300}
|
height={300}
|
||||||
sizes="(min-width: 768px) 400px, 100vw"
|
sizes="(min-width: 768px) 400px, 100vw"
|
||||||
|
|||||||
@@ -63,10 +63,12 @@ export default function HotelCardCarousel({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getImage({ hotel }: Pick<HotelListingHotelData, "hotel">) {
|
function getImage({ hotel }: Pick<HotelListingHotelData, "hotel">) {
|
||||||
|
if (hotel.galleryImages?.length) {
|
||||||
|
const image = hotel.galleryImages[0]
|
||||||
return {
|
return {
|
||||||
src: hotel.galleryImages?.[0]?.imageSizes.large,
|
src: image.src,
|
||||||
alt:
|
alt: image.altText || image.altText_En,
|
||||||
hotel.galleryImages?.[0]?.metaData.altText ||
|
|
||||||
hotel.galleryImages?.[0]?.metaData.altText_En,
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,10 +53,12 @@ export function getHotelMapMarkers(hotels: HotelListingHotelData[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getImage({ hotel }: Pick<HotelListingHotelData, "hotel">) {
|
function getImage({ hotel }: Pick<HotelListingHotelData, "hotel">) {
|
||||||
|
if (hotel.galleryImages?.length) {
|
||||||
|
const image = hotel.galleryImages[0]
|
||||||
return {
|
return {
|
||||||
src: hotel.galleryImages?.[0]?.imageSizes.large,
|
src: image.src,
|
||||||
alt:
|
alt: image.altText || image.altText_En,
|
||||||
hotel.galleryImages?.[0]?.metaData.altText ||
|
|
||||||
hotel.galleryImages?.[0]?.metaData.altText_En,
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ export default function SidePeekImages({ images }: SidePeekImagesProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.sidePeekImages}>
|
<div className={styles.sidePeekImages}>
|
||||||
{images.map(({ metaData, imageSizes }) => (
|
{images.map(({ src, altText, altText_En }) => (
|
||||||
<Image
|
<Image
|
||||||
key={imageSizes.tiny}
|
key={src}
|
||||||
src={imageSizes.tiny}
|
src={src}
|
||||||
alt={metaData.altText}
|
alt={altText || altText_En}
|
||||||
height={240}
|
height={240}
|
||||||
width={imageWidth}
|
width={imageWidth}
|
||||||
sizes={sizesString}
|
sizes={sizesString}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ export default async function HeroHeader({
|
|||||||
{heroImage ? (
|
{heroImage ? (
|
||||||
<div className={styles.heroWrapper}>
|
<div className={styles.heroWrapper}>
|
||||||
<Hero
|
<Hero
|
||||||
src={heroImage.imageSizes.medium}
|
src={heroImage.src}
|
||||||
alt={heroImage.metaData.altText || ""}
|
alt={heroImage.altText || heroImage.altText_En}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export default async function HotelHeader({
|
|||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<Image
|
<Image
|
||||||
className={styles.hero}
|
className={styles.hero}
|
||||||
alt={image.metaData.altText || image.metaData.altText_En || ""}
|
alt={image.altText || image.altText_En || ""}
|
||||||
src={image.imageSizes.large}
|
src={image.src}
|
||||||
height={200}
|
height={200}
|
||||||
width={1196}
|
width={1196}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ export default function Promo({
|
|||||||
<div className={styles.imageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
<Image
|
<Image
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
src={image.imageSizes.large}
|
src={image.src}
|
||||||
alt={image.metaData.altText}
|
alt={image.altText || image.altText_En}
|
||||||
fill
|
fill
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
|||||||
import IconChip from "@scandic-hotels/design-system/IconChip"
|
import IconChip from "@scandic-hotels/design-system/IconChip"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
import Image from "@scandic-hotels/design-system/Image"
|
import Image from "@scandic-hotels/design-system/Image"
|
||||||
|
import ImageFallback from "@scandic-hotels/design-system/ImageFallback"
|
||||||
import Modal from "@scandic-hotels/design-system/Modal"
|
import Modal from "@scandic-hotels/design-system/Modal"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
|
||||||
@@ -215,11 +216,11 @@ export default function Room({ booking, roomNr, user }: RoomProps) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={styles.imageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
<Image
|
{room?.images[0]?.src ? (
|
||||||
src={room?.images[0]?.imageSizes.small ?? ""}
|
<Image src={room.images[0].src} alt={roomName} fill />
|
||||||
alt={roomName}
|
) : (
|
||||||
fill
|
<ImageFallback />
|
||||||
/>
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.details}>
|
<div className={styles.details}>
|
||||||
<div className={styles.row}>
|
<div className={styles.row}>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import Image from "@scandic-hotels/design-system/Image"
|
import Image from "@scandic-hotels/design-system/Image"
|
||||||
|
import ImageFallback from "@scandic-hotels/design-system/ImageFallback"
|
||||||
|
|
||||||
import { useMyStayStore } from "@/stores/my-stay"
|
import { useMyStayStore } from "@/stores/my-stay"
|
||||||
|
|
||||||
@@ -19,13 +20,17 @@ export default function Img() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.imageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
|
{image?.src ? (
|
||||||
<Image
|
<Image
|
||||||
alt={roomName}
|
alt={roomName}
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
height={960}
|
height={960}
|
||||||
src={image?.imageSizes.small ?? ""}
|
src={image.src}
|
||||||
width={640}
|
width={640}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<ImageFallback height="640px" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,9 +168,9 @@ export default async function MyStay(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const imageSrc =
|
const imageSrc =
|
||||||
hotel.hotelContent.images.imageSizes.large ??
|
hotel.hotelContent.images.src ||
|
||||||
additionalData.gallery?.heroImages[0]?.imageSizes.large ??
|
additionalData.gallery?.heroImages[0]?.src ||
|
||||||
hotel.galleryImages[0]?.imageSizes.large
|
hotel.galleryImages[0]?.src
|
||||||
|
|
||||||
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
|
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
|
||||||
const promoUrl = new URL(`${baseUrl}/${lang}/`)
|
const promoUrl = new URL(`${baseUrl}/${lang}/`)
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ export default function MeetingRoomCard({ room }: MeetingRoomCardProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={styles.card}>
|
<article className={styles.card}>
|
||||||
{image?.imageSizes.small ? (
|
{image?.src ? (
|
||||||
<Image
|
<Image
|
||||||
src={image?.imageSizes.small}
|
src={image.src}
|
||||||
alt={image?.metaData.altText || image?.metaData.altText_En || ""}
|
alt={image.altText || image.altText_En || ""}
|
||||||
className={styles.image}
|
className={styles.image}
|
||||||
width={386}
|
width={386}
|
||||||
height={200}
|
height={200}
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import type { imageSchema } from "@scandic-hotels/trpc/routers/hotels/schemas/image"
|
|
||||||
import type { ProductTypeCheque } from "@scandic-hotels/trpc/types/availability"
|
import type { ProductTypeCheque } from "@scandic-hotels/trpc/types/availability"
|
||||||
import type { Amenities } from "@scandic-hotels/trpc/types/hotel"
|
import type { Amenities } from "@scandic-hotels/trpc/types/hotel"
|
||||||
import type { z } from "zod"
|
|
||||||
|
|
||||||
import type { Coordinates } from "@/types/components/maps/coordinates"
|
import type { Coordinates } from "@/types/components/maps/coordinates"
|
||||||
|
|
||||||
type ImageSizes = z.infer<typeof imageSchema>["imageSizes"]
|
|
||||||
type ImageMetaData = z.infer<typeof imageSchema>["metaData"]
|
|
||||||
|
|
||||||
export type HotelPin = {
|
export type HotelPin = {
|
||||||
bookingCode?: string | null
|
bookingCode?: string | null
|
||||||
name: string
|
name: string
|
||||||
@@ -20,8 +15,11 @@ export type HotelPin = {
|
|||||||
rateType: string | null
|
rateType: string | null
|
||||||
currency: string
|
currency: string
|
||||||
images: {
|
images: {
|
||||||
imageSizes: ImageSizes
|
src: string
|
||||||
metaData: ImageMetaData
|
altText: string
|
||||||
|
altText_En: string
|
||||||
|
title: string
|
||||||
|
title_En: string
|
||||||
}[]
|
}[]
|
||||||
amenities: Amenities
|
amenities: Amenities
|
||||||
ratings: number | null
|
ratings: number | null
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ export interface GalleryImage {
|
|||||||
src: string
|
src: string
|
||||||
alt: string
|
alt: string
|
||||||
caption?: string | null
|
caption?: string | null
|
||||||
smallSrc?: string | null
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,16 +124,16 @@ export function setFacilityCardGrids(
|
|||||||
// Can be a maximum 2 images per grid
|
// Can be a maximum 2 images per grid
|
||||||
const img: FacilityImage = {
|
const img: FacilityImage = {
|
||||||
backgroundImage: {
|
backgroundImage: {
|
||||||
url: image.imageSizes.large,
|
url: image.src,
|
||||||
title: image.metaData.title || image.metaData.title_En,
|
title: image.title || image.title_En,
|
||||||
meta: {
|
meta: {
|
||||||
alt: image.metaData.altText,
|
alt: image.altText,
|
||||||
caption: image.metaData.altText_En,
|
caption: image.altText_En,
|
||||||
},
|
},
|
||||||
id: image.imageSizes.large,
|
id: image.src,
|
||||||
},
|
},
|
||||||
theme: "image",
|
theme: "image",
|
||||||
id: image.imageSizes.large,
|
id: image.src,
|
||||||
}
|
}
|
||||||
return img
|
return img
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,14 +5,13 @@ import type { GalleryImage } from "@/types/components/imageGallery"
|
|||||||
|
|
||||||
function mapApiImageToGalleryImage(apiImage: ApiImage): GalleryImage {
|
function mapApiImageToGalleryImage(apiImage: ApiImage): GalleryImage {
|
||||||
return {
|
return {
|
||||||
src: apiImage.imageSizes.medium,
|
src: apiImage.src,
|
||||||
alt:
|
alt:
|
||||||
apiImage.metaData.altText ||
|
apiImage.altText ||
|
||||||
apiImage.metaData.altText_En ||
|
apiImage.altText_En ||
|
||||||
apiImage.metaData.title ||
|
apiImage.title ||
|
||||||
apiImage.metaData.title_En,
|
apiImage.title_En,
|
||||||
caption: apiImage.metaData.title || apiImage.metaData.title_En,
|
caption: apiImage.title || apiImage.title_En,
|
||||||
smallSrc: apiImage.imageSizes.small,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,8 +61,8 @@ export function generateHotelSchema(hotelData: HotelData) {
|
|||||||
if (image) {
|
if (image) {
|
||||||
jsonLd.image = {
|
jsonLd.image = {
|
||||||
"@type": "ImageObject",
|
"@type": "ImageObject",
|
||||||
url: image.imageSizes.small,
|
url: image.src,
|
||||||
caption: image.metaData.title || image.metaData.title_En,
|
caption: image.title || image.title_En,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,8 @@ export function getImage(data: RawMetadataSchema) {
|
|||||||
const restaurantImage = restaurantSubPage?.content?.images?.[0]
|
const restaurantImage = restaurantSubPage?.content?.images?.[0]
|
||||||
if (restaurantImage) {
|
if (restaurantImage) {
|
||||||
subpageImage = {
|
subpageImage = {
|
||||||
url: restaurantImage.imageSizes.small,
|
url: restaurantImage.src,
|
||||||
alt:
|
alt: restaurantImage.altText || restaurantImage.altText_En || "",
|
||||||
restaurantImage.metaData.altText ||
|
|
||||||
restaurantImage.metaData.altText_En ||
|
|
||||||
"",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,11 +35,8 @@ export function getImage(data: RawMetadataSchema) {
|
|||||||
data.additionalHotelData?.parkingImages?.heroImages[0]
|
data.additionalHotelData?.parkingImages?.heroImages[0]
|
||||||
if (parkingImage) {
|
if (parkingImage) {
|
||||||
subpageImage = {
|
subpageImage = {
|
||||||
url: parkingImage.imageSizes.small,
|
url: parkingImage.src,
|
||||||
alt:
|
alt: parkingImage.altText || parkingImage.altText_En || "",
|
||||||
parkingImage.metaData.altText ||
|
|
||||||
parkingImage.metaData.altText_En ||
|
|
||||||
"",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -52,11 +46,8 @@ export function getImage(data: RawMetadataSchema) {
|
|||||||
)?.content.images[0]
|
)?.content.images[0]
|
||||||
if (wellnessImage) {
|
if (wellnessImage) {
|
||||||
subpageImage = {
|
subpageImage = {
|
||||||
url: wellnessImage.imageSizes.small,
|
url: wellnessImage.src,
|
||||||
alt:
|
alt: wellnessImage.altText || wellnessImage.altText_En || "",
|
||||||
wellnessImage.metaData.altText ||
|
|
||||||
wellnessImage.metaData.altText_En ||
|
|
||||||
"",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -65,10 +56,10 @@ export function getImage(data: RawMetadataSchema) {
|
|||||||
data.additionalHotelData?.accessibility?.heroImages[0]
|
data.additionalHotelData?.accessibility?.heroImages[0]
|
||||||
if (accessibilityImage) {
|
if (accessibilityImage) {
|
||||||
subpageImage = {
|
subpageImage = {
|
||||||
url: accessibilityImage.imageSizes.small,
|
url: accessibilityImage.src,
|
||||||
alt:
|
alt:
|
||||||
accessibilityImage.metaData.altText ||
|
accessibilityImage.altText ||
|
||||||
accessibilityImage.metaData.altText_En ||
|
accessibilityImage.altText_En ||
|
||||||
"",
|
"",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,11 +69,8 @@ export function getImage(data: RawMetadataSchema) {
|
|||||||
data.additionalHotelData?.conferencesAndMeetings?.heroImages[0]
|
data.additionalHotelData?.conferencesAndMeetings?.heroImages[0]
|
||||||
if (meetingImage) {
|
if (meetingImage) {
|
||||||
subpageImage = {
|
subpageImage = {
|
||||||
url: meetingImage.imageSizes.small,
|
url: meetingImage.src,
|
||||||
alt:
|
alt: meetingImage.altText || meetingImage.altText_En || "",
|
||||||
meetingImage.metaData.altText ||
|
|
||||||
meetingImage.metaData.altText_En ||
|
|
||||||
"",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@@ -100,8 +88,8 @@ export function getImage(data: RawMetadataSchema) {
|
|||||||
data.additionalHotelData?.gallery?.smallerImages?.[0]
|
data.additionalHotelData?.gallery?.smallerImages?.[0]
|
||||||
if (hotelImage) {
|
if (hotelImage) {
|
||||||
return {
|
return {
|
||||||
url: hotelImage.imageSizes.small,
|
url: hotelImage.src,
|
||||||
alt: hotelImage.metaData.altText || undefined,
|
alt: hotelImage.altText || undefined,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
import { dt } from "@scandic-hotels/common/dt"
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
import Image from "@scandic-hotels/design-system/Image"
|
import Image from "@scandic-hotels/design-system/Image"
|
||||||
|
import ImageFallback from "@scandic-hotels/design-system/ImageFallback"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { getHotelRoom } from "@scandic-hotels/trpc/routers/booking/helpers"
|
import { getHotelRoom } from "@scandic-hotels/trpc/routers/booking/helpers"
|
||||||
|
|
||||||
@@ -114,16 +115,20 @@ export function Room({
|
|||||||
)}
|
)}
|
||||||
</header>
|
</header>
|
||||||
<div className={styles.booking}>
|
<div className={styles.booking}>
|
||||||
|
{img?.src ? (
|
||||||
<Image
|
<Image
|
||||||
alt={img?.metaData.altText ?? ""}
|
alt={img.altText || img.altText_En || ""}
|
||||||
className={styles.img}
|
className={styles.img}
|
||||||
focalPoint={{ x: 50, y: 50 }}
|
focalPoint={{ x: 50, y: 50 }}
|
||||||
height={204}
|
height={204}
|
||||||
src={img?.imageSizes.medium ?? ""}
|
src={img.src}
|
||||||
style={{ borderRadius: "var(--Corner-radius-md)" }}
|
style={{ borderRadius: "var(--Corner-radius-md)" }}
|
||||||
title={img?.metaData.title || img?.metaData.title_En || ""}
|
title={img.title || img.title_En || ""}
|
||||||
width={204}
|
width={204}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<ImageFallback height="204px" />
|
||||||
|
)}
|
||||||
<div className={styles.roomDetails}>
|
<div className={styles.roomDetails}>
|
||||||
<div className={styles.roomName}>
|
<div className={styles.roomName}>
|
||||||
<Typography variant="Title/Subtitle/md">
|
<Typography variant="Title/Subtitle/md">
|
||||||
|
|||||||
@@ -5,9 +5,7 @@ import type { z } from "zod"
|
|||||||
|
|
||||||
import type { HotelResponse } from "../SelectHotel/helpers"
|
import type { HotelResponse } from "../SelectHotel/helpers"
|
||||||
|
|
||||||
type ImageSizes = z.infer<typeof imageSchema>["imageSizes"]
|
type ApiImage = z.infer<typeof imageSchema>
|
||||||
type ImageMetaData = z.infer<typeof imageSchema>["metaData"]
|
|
||||||
|
|
||||||
interface Coordinates {
|
interface Coordinates {
|
||||||
lat: number
|
lat: number
|
||||||
lng: number
|
lng: number
|
||||||
@@ -24,10 +22,7 @@ export type HotelPin = {
|
|||||||
voucherPrice: number | null
|
voucherPrice: number | null
|
||||||
rateType: string | null
|
rateType: string | null
|
||||||
currency: string
|
currency: string
|
||||||
images: {
|
images: ApiImage[]
|
||||||
imageSizes: ImageSizes
|
|
||||||
metaData: ImageMetaData
|
|
||||||
}[]
|
|
||||||
amenities: Amenities
|
amenities: Amenities
|
||||||
ratings: number | null
|
ratings: number | null
|
||||||
operaId: string
|
operaId: string
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ export default function ListingHotelCardDialog({
|
|||||||
voucherPrice,
|
voucherPrice,
|
||||||
hasEnoughPoints,
|
hasEnoughPoints,
|
||||||
} = data
|
} = data
|
||||||
const firstImage = images[0]?.imageSizes?.small
|
const imageSrc = images[0]?.src
|
||||||
const altText = images[0]?.metaData?.altText
|
const altText = images[0]?.altText || images[0]?.altText_En
|
||||||
|
|
||||||
const notEnoughPointsLabel = intl.formatMessage({
|
const notEnoughPointsLabel = intl.formatMessage({
|
||||||
defaultMessage: "Not enough points",
|
defaultMessage: "Not enough points",
|
||||||
@@ -77,7 +77,7 @@ export default function ListingHotelCardDialog({
|
|||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<HotelCardDialogImage
|
<HotelCardDialogImage
|
||||||
firstImage={firstImage}
|
imageSrc={imageSrc}
|
||||||
altText={altText}
|
altText={altText}
|
||||||
rating={{ tripAdvisor: ratings }}
|
rating={{ tripAdvisor: ratings }}
|
||||||
imageError={imageError}
|
imageError={imageError}
|
||||||
|
|||||||
@@ -162,13 +162,12 @@ export function RoomSidePeekContent({ room }: RoomSidePeekContentProps) {
|
|||||||
|
|
||||||
function mapApiImagesToGalleryImages(apiImages: ApiImage[]) {
|
function mapApiImagesToGalleryImages(apiImages: ApiImage[]) {
|
||||||
return apiImages.map((apiImage) => ({
|
return apiImages.map((apiImage) => ({
|
||||||
src: apiImage.imageSizes.medium,
|
src: apiImage.src,
|
||||||
alt:
|
alt:
|
||||||
apiImage.metaData.altText ||
|
apiImage.altText ||
|
||||||
apiImage.metaData.altText_En ||
|
apiImage.altText_En ||
|
||||||
apiImage.metaData.title ||
|
apiImage.title ||
|
||||||
apiImage.metaData.title_En,
|
apiImage.title_En,
|
||||||
caption: apiImage.metaData.title || apiImage.metaData.title_En,
|
caption: apiImage.title || apiImage.title_En,
|
||||||
smallSrc: apiImage.imageSizes.small,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ export function SelectedRoomPanel({ roomIndex }: { roomIndex: number }) {
|
|||||||
isMainRoom ||
|
isMainRoom ||
|
||||||
(!isMainRoom && selectedRates.rates.slice(0, roomNr).every((room) => room))
|
(!isMainRoom && selectedRates.rates.slice(0, roomNr).every((room) => room))
|
||||||
|
|
||||||
|
const image = images?.[0]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.selectedRoomPanel}>
|
<div className={styles.selectedRoomPanel}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
@@ -64,16 +66,17 @@ export function SelectedRoomPanel({ roomIndex }: { roomIndex: number }) {
|
|||||||
<Body color="uiTextHighContrast">{selectedProductTitle}</Body>
|
<Body color="uiTextHighContrast">{selectedProductTitle}</Body>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.imageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
{images?.[0]?.imageSizes?.tiny ? (
|
{image?.src ? (
|
||||||
<Image
|
<Image
|
||||||
alt={
|
alt={
|
||||||
selectedRate.roomInfo.roomType ??
|
selectedRate.roomInfo.roomType ??
|
||||||
images[0].metaData?.altText ??
|
image.altText ??
|
||||||
|
image.altText_En ??
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
className={styles.img}
|
className={styles.img}
|
||||||
height={300}
|
height={300}
|
||||||
src={images[0].imageSizes.tiny}
|
src={image.src}
|
||||||
width={600}
|
width={600}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ import type { ApiImage } from "@scandic-hotels/trpc/types/hotel"
|
|||||||
export function mapApiImagesToGalleryImages(apiImages: ApiImage[]) {
|
export function mapApiImagesToGalleryImages(apiImages: ApiImage[]) {
|
||||||
return apiImages.map((apiImage) => {
|
return apiImages.map((apiImage) => {
|
||||||
return {
|
return {
|
||||||
src: apiImage.imageSizes.medium,
|
src: apiImage.src,
|
||||||
alt:
|
alt:
|
||||||
apiImage.metaData.altText ||
|
apiImage.altText ||
|
||||||
apiImage.metaData.altText_En ||
|
apiImage.altText_En ||
|
||||||
apiImage.metaData.title ||
|
apiImage.title ||
|
||||||
apiImage.metaData.title_En,
|
apiImage.title_En,
|
||||||
caption: apiImage.metaData.title || apiImage.metaData.title_En,
|
caption: apiImage.title || apiImage.title_En,
|
||||||
smallSrc: apiImage.imageSizes.small,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,8 @@ export type PromoProps = {
|
|||||||
text: string
|
text: string
|
||||||
title: string
|
title: string
|
||||||
image?: {
|
image?: {
|
||||||
imageSizes: {
|
src: string
|
||||||
large: string
|
|
||||||
medium: string
|
|
||||||
small: string
|
|
||||||
tiny: string
|
|
||||||
}
|
|
||||||
metaData: {
|
|
||||||
altText: string
|
altText: string
|
||||||
altText_En: string
|
altText_En: string
|
||||||
copyRight: string
|
|
||||||
title: string
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||||
import { HotelCard } from './index'
|
import { HotelCard } from './index'
|
||||||
|
|
||||||
import { fn } from 'storybook/test'
|
|
||||||
import { RateTypeEnum } from '@scandic-hotels/common/constants/rateType'
|
import { RateTypeEnum } from '@scandic-hotels/common/constants/rateType'
|
||||||
|
import { fn } from 'storybook/test'
|
||||||
import { Button } from '../Button'
|
import { Button } from '../Button'
|
||||||
import { MaterialIcon } from '../Icons/MaterialIcon'
|
import { MaterialIcon } from '../Icons/MaterialIcon'
|
||||||
|
|
||||||
@@ -70,7 +70,6 @@ export const Default: Story = {
|
|||||||
{
|
{
|
||||||
src: 'img/img2.jpg',
|
src: 'img/img2.jpg',
|
||||||
alt: 'Alt text',
|
alt: 'Alt text',
|
||||||
smallSrc: 'img/img2.jpg',
|
|
||||||
caption: 'Caption',
|
caption: 'Caption',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -97,3 +96,10 @@ export const MapListing: Story = {
|
|||||||
type: 'mapListing',
|
type: 'mapListing',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const WithoutImage: Story = {
|
||||||
|
args: {
|
||||||
|
...Default.args,
|
||||||
|
images: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import Image from '../../Image'
|
import Image from '../../Image'
|
||||||
|
|
||||||
import { hotelCardDialogImageVariants } from './variants'
|
|
||||||
import { TripAdvisorChip } from '../../TripAdvisorChip'
|
import { TripAdvisorChip } from '../../TripAdvisorChip'
|
||||||
|
import { hotelCardDialogImageVariants } from './variants'
|
||||||
|
|
||||||
import styles from './hotelCardDialogImage.module.css'
|
import ImageFallback from '../../ImageFallback'
|
||||||
|
|
||||||
export type HotelCardDialogImageProps = {
|
export type HotelCardDialogImageProps = {
|
||||||
firstImage?: string
|
imageSrc?: string
|
||||||
altText?: string
|
altText?: string
|
||||||
rating?: { tripAdvisor?: number | null }
|
rating?: { tripAdvisor?: number | null }
|
||||||
imageError: boolean
|
imageError: boolean
|
||||||
@@ -15,7 +15,7 @@ export type HotelCardDialogImageProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function HotelCardDialogImage({
|
export function HotelCardDialogImage({
|
||||||
firstImage,
|
imageSrc,
|
||||||
altText,
|
altText,
|
||||||
rating,
|
rating,
|
||||||
imageError,
|
imageError,
|
||||||
@@ -26,11 +26,11 @@ export function HotelCardDialogImage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames}>
|
<div className={classNames}>
|
||||||
{!firstImage || imageError ? (
|
{!imageSrc || imageError ? (
|
||||||
<div className={styles.imagePlaceholder} />
|
<ImageFallback />
|
||||||
) : (
|
) : (
|
||||||
<Image
|
<Image
|
||||||
src={firstImage}
|
src={imageSrc}
|
||||||
alt={altText || ''}
|
alt={altText || ''}
|
||||||
fill
|
fill
|
||||||
onError={() => setImageError(true)}
|
onError={() => setImageError(true)}
|
||||||
|
|||||||
@@ -14,14 +14,14 @@ import { OldDSButton as Button } from '../../../OldDSButton'
|
|||||||
import Subtitle from '../../../Subtitle'
|
import Subtitle from '../../../Subtitle'
|
||||||
import { Typography } from '../../../Typography'
|
import { Typography } from '../../../Typography'
|
||||||
|
|
||||||
import { NoPriceAvailableCard } from '../../NoPriceAvailableCard'
|
|
||||||
import { HotelCardDialogImage } from '../../HotelCardDialogImage'
|
import { HotelCardDialogImage } from '../../HotelCardDialogImage'
|
||||||
|
import { NoPriceAvailableCard } from '../../NoPriceAvailableCard'
|
||||||
|
|
||||||
import styles from './standaloneHotelCardDialog.module.css'
|
|
||||||
import { Lang } from '@scandic-hotels/common/constants/language'
|
import { Lang } from '@scandic-hotels/common/constants/language'
|
||||||
import { HotelPin } from '../../../Map/types'
|
|
||||||
import { FacilityToIcon } from '../../../FacilityToIcon'
|
import { FacilityToIcon } from '../../../FacilityToIcon'
|
||||||
|
import { HotelPin } from '../../../Map/types'
|
||||||
import { HotelPointsRow } from '../../HotelPointsRow'
|
import { HotelPointsRow } from '../../HotelPointsRow'
|
||||||
|
import styles from './standaloneHotelCardDialog.module.css'
|
||||||
|
|
||||||
interface StandaloneHotelCardProps {
|
interface StandaloneHotelCardProps {
|
||||||
data: HotelPin
|
data: HotelPin
|
||||||
@@ -75,7 +75,7 @@ export function StandaloneHotelCardDialog({
|
|||||||
<MaterialIcon icon="close" size={22} color="CurrentColor" />
|
<MaterialIcon icon="close" size={22} color="CurrentColor" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<HotelCardDialogImage
|
<HotelCardDialogImage
|
||||||
firstImage={image?.url}
|
imageSrc={image?.url}
|
||||||
altText={image?.alt}
|
altText={image?.alt}
|
||||||
rating={{ tripAdvisor: ratings?.tripAdvisor ?? null }}
|
rating={{ tripAdvisor: ratings?.tripAdvisor ?? null }}
|
||||||
imageError={imageError}
|
imageError={imageError}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||||
|
|
||||||
import { HotelInfoCard } from './index'
|
|
||||||
import { FacilityEnum } from '@scandic-hotels/common/constants/facilities'
|
|
||||||
import { AlertTypeEnum } from '@scandic-hotels/common/constants/alert'
|
import { AlertTypeEnum } from '@scandic-hotels/common/constants/alert'
|
||||||
import { Button } from '../Button'
|
import { FacilityEnum } from '@scandic-hotels/common/constants/facilities'
|
||||||
import { fn } from 'storybook/test'
|
import { fn } from 'storybook/test'
|
||||||
|
import { Button } from '../Button'
|
||||||
import { MaterialIcon } from '../Icons/MaterialIcon'
|
import { MaterialIcon } from '../Icons/MaterialIcon'
|
||||||
|
import { HotelInfoCard } from './index'
|
||||||
const meta: Meta<typeof HotelInfoCard> = {
|
const meta: Meta<typeof HotelInfoCard> = {
|
||||||
title: 'Components/HotelInfoCard',
|
title: 'Components/HotelInfoCard',
|
||||||
component: HotelInfoCard,
|
component: HotelInfoCard,
|
||||||
@@ -104,19 +104,16 @@ export const Default: Story = {
|
|||||||
{
|
{
|
||||||
src: './img/GrandHotelBudapest.png',
|
src: './img/GrandHotelBudapest.png',
|
||||||
alt: 'Grand Hotel Budapest',
|
alt: 'Grand Hotel Budapest',
|
||||||
smallSrc: './img/GrandHotelBudapest.png',
|
|
||||||
caption: 'Grand Hotel Budapest',
|
caption: 'Grand Hotel Budapest',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: './img/img1.png',
|
src: './img/img1.png',
|
||||||
alt: 'Image 1',
|
alt: 'Image 1',
|
||||||
smallSrc: './img/img1.png',
|
|
||||||
caption: 'Image 1',
|
caption: 'Image 1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
src: './img/img2.png',
|
src: './img/img2.png',
|
||||||
alt: 'Image 2',
|
alt: 'Image 2',
|
||||||
smallSrc: './img/img2.png',
|
|
||||||
caption: 'Image 2',
|
caption: 'Image 2',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import { useIntl } from 'react-intl'
|
|||||||
import { MaterialIcon } from '../Icons/MaterialIcon'
|
import { MaterialIcon } from '../Icons/MaterialIcon'
|
||||||
import Image from '../Image'
|
import Image from '../Image'
|
||||||
import ImageFallback from '../ImageFallback'
|
import ImageFallback from '../ImageFallback'
|
||||||
import { Typography } from '../Typography'
|
|
||||||
import Lightbox from '../Lightbox'
|
import Lightbox from '../Lightbox'
|
||||||
|
import { Typography } from '../Typography'
|
||||||
|
|
||||||
import styles from './imageGallery.module.css'
|
import styles from './imageGallery.module.css'
|
||||||
|
|
||||||
@@ -16,7 +16,6 @@ export interface GalleryImage {
|
|||||||
src: string
|
src: string
|
||||||
alt: string
|
alt: string
|
||||||
caption?: string | null
|
caption?: string | null
|
||||||
smallSrc?: string | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageGalleryProps = {
|
type ImageGalleryProps = {
|
||||||
@@ -50,7 +49,7 @@ function ImageGallery({
|
|||||||
}
|
}
|
||||||
: { height, width: height * 1.5 }
|
: { height, width: height * 1.5 }
|
||||||
|
|
||||||
if (!images || images.length === 0 || imageError) {
|
if (!images?.length || imageError) {
|
||||||
return <ImageFallback />
|
return <ImageFallback />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import { Typography } from '../../Typography'
|
|||||||
|
|
||||||
import Image from '../../Image'
|
import Image from '../../Image'
|
||||||
|
|
||||||
import styles from './gallery.module.css'
|
|
||||||
import { LightboxImage } from '..'
|
import { LightboxImage } from '..'
|
||||||
|
import styles from './gallery.module.css'
|
||||||
|
|
||||||
type GalleryProps = {
|
type GalleryProps = {
|
||||||
images: LightboxImage[]
|
images: LightboxImage[]
|
||||||
@@ -164,7 +164,7 @@ export default function Gallery({
|
|||||||
<AnimatePresence initial={false}>
|
<AnimatePresence initial={false}>
|
||||||
{getThumbImages().map((image, index) => (
|
{getThumbImages().map((image, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={image.smallSrc || image.src}
|
key={image.src}
|
||||||
className={styles.thumbnailContainer}
|
className={styles.thumbnailContainer}
|
||||||
initial={{ opacity: 0, x: 50 }}
|
initial={{ opacity: 0, x: 50 }}
|
||||||
animate={{ opacity: 1, x: 0 }}
|
animate={{ opacity: 1, x: 0 }}
|
||||||
@@ -179,7 +179,7 @@ export default function Gallery({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={image.smallSrc || image.src}
|
src={image.src}
|
||||||
alt={image.alt}
|
alt={image.alt}
|
||||||
fill
|
fill
|
||||||
sizes="200px"
|
sizes="200px"
|
||||||
@@ -196,7 +196,7 @@ export default function Gallery({
|
|||||||
<div className={styles.mobileGallery}>
|
<div className={styles.mobileGallery}>
|
||||||
{images.map((image, index) => (
|
{images.map((image, index) => (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={image.smallSrc || image.src}
|
key={image.src}
|
||||||
className={`${styles.thumbnailContainer} ${index % 3 === 0 ? styles.fullWidthImage : ''}`}
|
className={`${styles.thumbnailContainer} ${index % 3 === 0 ? styles.fullWidthImage : ''}`}
|
||||||
initial={{ opacity: 0, y: 20 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
@@ -211,7 +211,7 @@ export default function Gallery({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={image.smallSrc || image.src}
|
src={image.src}
|
||||||
alt={image.alt}
|
alt={image.alt}
|
||||||
fill
|
fill
|
||||||
sizes="100vw"
|
sizes="100vw"
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export type LightboxImage = {
|
|||||||
src: string
|
src: string
|
||||||
alt: string
|
alt: string
|
||||||
caption?: string | null
|
caption?: string | null
|
||||||
smallSrc?: string | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type LightboxProps = {
|
type LightboxProps = {
|
||||||
|
|||||||
@@ -526,7 +526,7 @@ export const ancillaryPackagesSchema = z
|
|||||||
id: item.id,
|
id: item.id,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.descriptions.html,
|
description: item.descriptions.html,
|
||||||
imageUrl: item.images[0]?.imageSizes.small || undefined,
|
imageUrl: item.images[0]?.src || undefined,
|
||||||
price: {
|
price: {
|
||||||
total: item.variants.ancillary.price.totalPrice,
|
total: item.variants.ancillary.price.totalPrice,
|
||||||
currency: item.variants.ancillary.price.currency,
|
currency: item.variants.ancillary.price.currency,
|
||||||
|
|||||||
@@ -2,46 +2,37 @@ import { z } from "zod"
|
|||||||
|
|
||||||
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
|
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
|
||||||
|
|
||||||
export const imageSizesSchema = z.object({
|
const DEFAULT_IMAGE_OBJ = {
|
||||||
large: nullableStringValidator,
|
altText: "Default image",
|
||||||
medium: nullableStringValidator,
|
altText_En: "Default image",
|
||||||
small: nullableStringValidator,
|
title: "Default image",
|
||||||
tiny: nullableStringValidator,
|
title_En: "Default image",
|
||||||
})
|
src: "https://placehold.co/1280x720",
|
||||||
|
}
|
||||||
|
|
||||||
export const imageMetaDataSchema = z.object({
|
export const imageSchema = z
|
||||||
|
.object({
|
||||||
|
imageSizes: z.object({
|
||||||
|
original: nullableStringValidator,
|
||||||
|
}),
|
||||||
|
metaData: z.object({
|
||||||
altText: nullableStringValidator,
|
altText: nullableStringValidator,
|
||||||
altText_En: nullableStringValidator,
|
altText_En: nullableStringValidator,
|
||||||
copyRight: nullableStringValidator,
|
copyRight: nullableStringValidator,
|
||||||
title: nullableStringValidator,
|
title: nullableStringValidator,
|
||||||
title_En: nullableStringValidator,
|
title_En: nullableStringValidator,
|
||||||
})
|
}),
|
||||||
|
|
||||||
const DEFAULT_IMAGE_OBJ = {
|
|
||||||
metaData: {
|
|
||||||
altText: "Default image",
|
|
||||||
altText_En: "Default image",
|
|
||||||
copyRight: "Default image",
|
|
||||||
title: "Default image",
|
|
||||||
title_En: "Default image",
|
|
||||||
},
|
|
||||||
imageSizes: {
|
|
||||||
tiny: "https://placehold.co/1280x720",
|
|
||||||
small: "https://placehold.co/1280x720",
|
|
||||||
medium: "https://placehold.co/1280x720",
|
|
||||||
large: "https://placehold.co/1280x720",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const imageSchema = z
|
|
||||||
.object({
|
|
||||||
imageSizes: imageSizesSchema,
|
|
||||||
metaData: imageMetaDataSchema,
|
|
||||||
})
|
})
|
||||||
.nullish()
|
.nullish()
|
||||||
.transform((val) => {
|
.transform((val) => {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
return DEFAULT_IMAGE_OBJ
|
return DEFAULT_IMAGE_OBJ
|
||||||
}
|
}
|
||||||
return val
|
return {
|
||||||
|
src: val.imageSizes.original,
|
||||||
|
altText: val.metaData.altText,
|
||||||
|
altText_En: val.metaData.altText_En,
|
||||||
|
title: val.metaData.title,
|
||||||
|
title_En: val.metaData.title_En,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
|||||||
import { BreakfastPackageEnum } from "../../../enums/breakfast"
|
import { BreakfastPackageEnum } from "../../../enums/breakfast"
|
||||||
import { PackageTypeEnum } from "../../../enums/packages"
|
import { PackageTypeEnum } from "../../../enums/packages"
|
||||||
import { RoomPackageCodeEnum } from "../../../enums/roomFilter"
|
import { RoomPackageCodeEnum } from "../../../enums/roomFilter"
|
||||||
import { imageSizesSchema } from "./image"
|
import { imageSchema } from "./image"
|
||||||
|
|
||||||
// TODO: Remove optional and default when the API change has been deployed
|
// TODO: Remove optional and default when the API change has been deployed
|
||||||
export const packagePriceSchema = z
|
export const packagePriceSchema = z
|
||||||
@@ -38,7 +38,7 @@ export const ancillaryContentSchema = z.object({
|
|||||||
}),
|
}),
|
||||||
title: z.string(),
|
title: z.string(),
|
||||||
descriptions: z.object({ html: z.string() }),
|
descriptions: z.object({ html: z.string() }),
|
||||||
images: z.array(z.object({ imageSizes: imageSizesSchema })),
|
images: z.array(imageSchema),
|
||||||
requiresDeliveryTime: z.boolean(),
|
requiresDeliveryTime: z.boolean(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user