Merge branch 'develop' into feature/sw-561-design-fixes
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
.layout {
|
||||||
|
min-height: 100dvh;
|
||||||
|
background-color: var(--Scandic-Brand-Warm-White);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x3) var(--Spacing-x9);
|
||||||
|
grid-template-columns: 1fr 340px;
|
||||||
|
grid-template-rows: auto 1fr;
|
||||||
|
margin: var(--Spacing-x5) auto 0;
|
||||||
|
max-width: var(--max-width-navigation);
|
||||||
|
padding: var(--Spacing-x6) var(--Spacing-x2) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary {
|
||||||
|
align-self: flex-start;
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
grid-row: 1/-1;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { redirect } from "next/navigation"
|
||||||
|
|
||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import SelectedRoom from "@/components/HotelReservation/EnterDetails/SelectedRoom"
|
||||||
|
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
|
||||||
|
import Summary from "@/components/HotelReservation/SelectRate/Summary"
|
||||||
|
|
||||||
|
import styles from "./layout.module.css"
|
||||||
|
|
||||||
|
import type { LangParams, LayoutArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function StepLayout({
|
||||||
|
children,
|
||||||
|
params,
|
||||||
|
}: React.PropsWithChildren<LayoutArgs<LangParams>>) {
|
||||||
|
const hotel = await serverClient().hotel.hotelData.get({
|
||||||
|
hotelId: "811",
|
||||||
|
language: params.lang,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!hotel?.data) {
|
||||||
|
redirect(`/${params.lang}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className={styles.layout}>
|
||||||
|
<HotelSelectionHeader hotel={hotel.data.attributes} />
|
||||||
|
<div className={styles.content}>
|
||||||
|
<SelectedRoom />
|
||||||
|
{children}
|
||||||
|
<aside className={styles.summary}>
|
||||||
|
<Summary />
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
.page {
|
|
||||||
min-height: 100dvh;
|
|
||||||
padding-top: var(--Spacing-x6);
|
|
||||||
padding-left: var(--Spacing-x2);
|
|
||||||
padding-right: var(--Spacing-x2);
|
|
||||||
background-color: var(--Scandic-Brand-Warm-White);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
max-width: 1134px;
|
|
||||||
margin-top: var(--Spacing-x5);
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: var(--Spacing-x7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary {
|
|
||||||
max-width: 340px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
display: grid;
|
|
||||||
gap: var(--Spacing-x2);
|
|
||||||
}
|
|
||||||
@@ -9,15 +9,11 @@ import { trpc } from "@/lib/trpc/client"
|
|||||||
import BedType from "@/components/HotelReservation/EnterDetails/BedType"
|
import BedType from "@/components/HotelReservation/EnterDetails/BedType"
|
||||||
import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast"
|
import Breakfast from "@/components/HotelReservation/EnterDetails/Breakfast"
|
||||||
import Details from "@/components/HotelReservation/EnterDetails/Details"
|
import Details from "@/components/HotelReservation/EnterDetails/Details"
|
||||||
import HotelSelectionHeader from "@/components/HotelReservation/HotelSelectionHeader"
|
|
||||||
import Payment from "@/components/HotelReservation/SelectRate/Payment"
|
import Payment from "@/components/HotelReservation/SelectRate/Payment"
|
||||||
import SectionAccordion from "@/components/HotelReservation/SelectRate/SectionAccordion"
|
import SectionAccordion from "@/components/HotelReservation/SelectRate/SectionAccordion"
|
||||||
import Summary from "@/components/HotelReservation/SelectRate/Summary"
|
|
||||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||||
|
|
||||||
import styles from "./page.module.css"
|
import type { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
import { LangParams, PageArgs } from "@/types/params"
|
|
||||||
|
|
||||||
enum StepEnum {
|
enum StepEnum {
|
||||||
selectBed = "select-bed",
|
selectBed = "select-bed",
|
||||||
@@ -75,51 +71,43 @@ export default function StepPage({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className={styles.page}>
|
<section>
|
||||||
<HotelSelectionHeader hotel={hotel.data.attributes} />
|
<SectionAccordion
|
||||||
<div className={styles.content}>
|
header="Select bed"
|
||||||
<section className={styles.section}>
|
isCompleted={true}
|
||||||
<SectionAccordion
|
isOpen={activeStep === StepEnum.selectBed}
|
||||||
header="Select bed"
|
label={intl.formatMessage({ id: "Request bedtype" })}
|
||||||
isCompleted={true}
|
path="/select-bed"
|
||||||
isOpen={activeStep === StepEnum.selectBed}
|
>
|
||||||
label={intl.formatMessage({ id: "Request bedtype" })}
|
<BedType />
|
||||||
path="/select-bed"
|
</SectionAccordion>
|
||||||
>
|
<SectionAccordion
|
||||||
<BedType />
|
header="Food options"
|
||||||
</SectionAccordion>
|
isCompleted={true}
|
||||||
<SectionAccordion
|
isOpen={activeStep === StepEnum.breakfast}
|
||||||
header="Food options"
|
label={intl.formatMessage({ id: "Select breakfast options" })}
|
||||||
isCompleted={true}
|
path="/breakfast"
|
||||||
isOpen={activeStep === StepEnum.breakfast}
|
>
|
||||||
label={intl.formatMessage({ id: "Select breakfast options" })}
|
<Breakfast />
|
||||||
path="/breakfast"
|
</SectionAccordion>
|
||||||
>
|
<SectionAccordion
|
||||||
<Breakfast />
|
header="Details"
|
||||||
</SectionAccordion>
|
isCompleted={false}
|
||||||
<SectionAccordion
|
isOpen={activeStep === StepEnum.details}
|
||||||
header="Details"
|
label={intl.formatMessage({ id: "Enter your details" })}
|
||||||
isCompleted={false}
|
path="/details"
|
||||||
isOpen={activeStep === StepEnum.details}
|
>
|
||||||
label={intl.formatMessage({ id: "Enter your details" })}
|
<Details user={null} />
|
||||||
path="/details"
|
</SectionAccordion>
|
||||||
>
|
<SectionAccordion
|
||||||
<Details user={null} />
|
header="Payment"
|
||||||
</SectionAccordion>
|
isCompleted={false}
|
||||||
<SectionAccordion
|
isOpen={activeStep === StepEnum.payment}
|
||||||
header="Payment"
|
label={intl.formatMessage({ id: "Select payment method" })}
|
||||||
isCompleted={false}
|
path="/hotelreservation/select-bed"
|
||||||
isOpen={activeStep === StepEnum.payment}
|
>
|
||||||
label={intl.formatMessage({ id: "Select payment method" })}
|
<Payment hotel={hotel.data.attributes} />
|
||||||
path="/hotelreservation/select-bed"
|
</SectionAccordion>
|
||||||
>
|
</section>
|
||||||
<Payment hotel={hotel.data.attributes} />
|
|
||||||
</SectionAccordion>
|
|
||||||
</section>
|
|
||||||
<aside className={styles.summary}>
|
|
||||||
<Summary />
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { serverClient } from "@/lib/trpc/server"
|
|||||||
import tempHotelData from "@/server/routers/hotels/tempHotelData.json"
|
import tempHotelData from "@/server/routers/hotels/tempHotelData.json"
|
||||||
|
|
||||||
import RoomSelection from "@/components/HotelReservation/SelectRate/RoomSelection"
|
import RoomSelection from "@/components/HotelReservation/SelectRate/RoomSelection"
|
||||||
import { getIntl } from "@/i18n"
|
|
||||||
import { setLang } from "@/i18n/serverContext"
|
import { setLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
import styles from "./page.module.css"
|
import styles from "./page.module.css"
|
||||||
@@ -19,18 +18,15 @@ export default async function SelectRatePage({
|
|||||||
// TODO: Use real endpoint.
|
// TODO: Use real endpoint.
|
||||||
const hotel = tempHotelData.data.attributes
|
const hotel = tempHotelData.data.attributes
|
||||||
|
|
||||||
const rates = await serverClient().hotel.rates.get({
|
const roomConfigurations = await serverClient().hotel.availability.rooms({
|
||||||
// TODO: pass the correct hotel ID and all other parameters that should be included in the search
|
hotelId: parseInt(searchParams.hotel, 10),
|
||||||
hotelId: searchParams.hotel,
|
roomStayStartDate: "2024-11-02",
|
||||||
|
roomStayEndDate: "2024-11-03",
|
||||||
|
adults: 1,
|
||||||
})
|
})
|
||||||
|
if (!roomConfigurations) {
|
||||||
// const rates = await serverClient().hotel.availability.getForHotel({
|
return "No rooms found"
|
||||||
// hotelId: 811,
|
}
|
||||||
// roomStayStartDate: "2024-11-02",
|
|
||||||
// roomStayEndDate: "2024-11-03",
|
|
||||||
// adults: 1,
|
|
||||||
// })
|
|
||||||
const intl = await getIntl()
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -39,13 +35,7 @@ export default async function SelectRatePage({
|
|||||||
|
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.main}>
|
<div className={styles.main}>
|
||||||
<RoomSelection
|
<RoomSelection roomConfigurations={roomConfigurations} />
|
||||||
rates={rates}
|
|
||||||
// TODO: Get real value
|
|
||||||
nrOfNights={1}
|
|
||||||
// TODO: Get real value
|
|
||||||
nrOfAdults={1}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
107
components/Blocks/Table/index.tsx
Normal file
107
components/Blocks/Table/index.tsx
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import {
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
getPaginationRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
|
import SectionContainer from "@/components/Section/Container"
|
||||||
|
import SectionHeader from "@/components/Section/Header"
|
||||||
|
import ScrollWrapper from "@/components/TempDesignSystem/ScrollWrapper"
|
||||||
|
import ShowMoreButton from "@/components/TempDesignSystem/ShowMoreButton"
|
||||||
|
import Table from "@/components/TempDesignSystem/Table"
|
||||||
|
|
||||||
|
import styles from "./table.module.css"
|
||||||
|
|
||||||
|
import type { TableBlockProps } from "@/types/components/blocks/table"
|
||||||
|
|
||||||
|
export default function TableBlock({ data }: TableBlockProps) {
|
||||||
|
const { columns, rows, totalWidth, heading, preamble } = data
|
||||||
|
const initialPageSize = 5
|
||||||
|
const [pageSize, setPageSize] = useState(initialPageSize)
|
||||||
|
const showMoreVisible = rows.length > initialPageSize
|
||||||
|
const showLessVisible = pageSize >= rows.length
|
||||||
|
const columnDefs = columns.map((col) => ({
|
||||||
|
accessorKey: col.id,
|
||||||
|
header: col.header,
|
||||||
|
size: col.width,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const table = useReactTable({
|
||||||
|
columns: columnDefs,
|
||||||
|
data: rows,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
getPaginationRowModel: getPaginationRowModel(),
|
||||||
|
state: {
|
||||||
|
pagination: {
|
||||||
|
pageIndex: 0,
|
||||||
|
pageSize,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleShowMore() {
|
||||||
|
setPageSize(showLessVisible ? initialPageSize : rows.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionContainer>
|
||||||
|
{heading ? (
|
||||||
|
<SectionHeader preamble={data.preamble} title={heading} />
|
||||||
|
) : null}
|
||||||
|
<div className={styles.tableWrapper}>
|
||||||
|
<ScrollWrapper>
|
||||||
|
<Table
|
||||||
|
width={`${totalWidth}%`}
|
||||||
|
variant="content"
|
||||||
|
intent="striped"
|
||||||
|
layout="fixed"
|
||||||
|
borderRadius="none"
|
||||||
|
>
|
||||||
|
<Table.THead>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<Table.TR key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<Table.TH
|
||||||
|
key={header.id}
|
||||||
|
width={`${header.column.columnDef.size}%`}
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</Table.TH>
|
||||||
|
))}
|
||||||
|
</Table.TR>
|
||||||
|
))}
|
||||||
|
</Table.THead>
|
||||||
|
<Table.TBody>
|
||||||
|
{table.getRowModel().rows.map((row) => (
|
||||||
|
<Table.TR key={row.id}>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<Table.TD key={cell.id}>
|
||||||
|
{flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
|
)}
|
||||||
|
</Table.TD>
|
||||||
|
))}
|
||||||
|
</Table.TR>
|
||||||
|
))}
|
||||||
|
</Table.TBody>
|
||||||
|
</Table>
|
||||||
|
</ScrollWrapper>
|
||||||
|
{showMoreVisible ? (
|
||||||
|
<ShowMoreButton
|
||||||
|
loadMoreData={handleShowMore}
|
||||||
|
showLess={showLessVisible}
|
||||||
|
intent="table"
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</SectionContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
6
components/Blocks/Table/table.module.css
Normal file
6
components/Blocks/Table/table.module.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.tableWrapper {
|
||||||
|
display: grid;
|
||||||
|
border: 1px solid var(--Base-Border-Subtle);
|
||||||
|
border-radius: var(--Corner-radius-Small);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import TextCols from "@/components/Blocks/TextCols"
|
|||||||
import UspGrid from "@/components/Blocks/UspGrid"
|
import UspGrid from "@/components/Blocks/UspGrid"
|
||||||
import JsonToHtml from "@/components/JsonToHtml"
|
import JsonToHtml from "@/components/JsonToHtml"
|
||||||
|
|
||||||
|
import Table from "./Table"
|
||||||
|
|
||||||
import type { BlocksProps } from "@/types/components/blocks"
|
import type { BlocksProps } from "@/types/components/blocks"
|
||||||
import { BlocksEnums } from "@/types/enums/blocks"
|
import { BlocksEnums } from "@/types/enums/blocks"
|
||||||
|
|
||||||
@@ -47,6 +49,8 @@ export default function Blocks({ blocks }: BlocksProps) {
|
|||||||
title={block.shortcuts.title}
|
title={block.shortcuts.title}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
case BlocksEnums.block.Table:
|
||||||
|
return <Table data={block.table} />
|
||||||
case BlocksEnums.block.TextCols:
|
case BlocksEnums.block.TextCols:
|
||||||
return <TextCols text_cols={block.text_cols} />
|
return <TextCols text_cols={block.text_cols} />
|
||||||
case BlocksEnums.block.TextContent:
|
case BlocksEnums.block.TextContent:
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.05);
|
box-shadow: 0px 4px 24px 0px rgba(0, 0, 0, 0.05);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 10000;
|
z-index: 9;
|
||||||
background-color: var(--Base-Surface-Primary-light-Normal);
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mainContent {
|
.mainContent {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x4);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { FormProvider, useForm } from "react-hook-form"
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { KingBedIcon } from "@/components/Icons"
|
import { KingBedIcon } from "@/components/Icons"
|
||||||
import RadioCard from "@/components/TempDesignSystem/Form/Card/Radio"
|
import RadioCard from "@/components/TempDesignSystem/Form/ChoiceCard/Radio"
|
||||||
|
|
||||||
import { bedTypeSchema } from "./schema"
|
import { bedTypeSchema } from "./schema"
|
||||||
|
|
||||||
@@ -24,9 +24,7 @@ export default function BedType() {
|
|||||||
reValidateMode: "onChange",
|
reValidateMode: "onChange",
|
||||||
})
|
})
|
||||||
|
|
||||||
// @ts-expect-error - Types mismatch docs as this is
|
const text = intl.formatMessage<React.ReactNode>(
|
||||||
// a pattern that is allowed https://formatjs.io/docs/react-intl/api#usage
|
|
||||||
const text = intl.formatMessage(
|
|
||||||
{ id: "<b>Included</b> (based on availability)" },
|
{ id: "<b>Included</b> (based on availability)" },
|
||||||
{ b: (str) => <b>{str}</b> }
|
{ b: (str) => <b>{str}</b> }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { FormProvider, useForm } from "react-hook-form"
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { BreakfastIcon, NoBreakfastIcon } from "@/components/Icons"
|
import { BreakfastIcon, NoBreakfastIcon } from "@/components/Icons"
|
||||||
import RadioCard from "@/components/TempDesignSystem/Form/Card/Radio"
|
import RadioCard from "@/components/TempDesignSystem/Form/ChoiceCard/Radio"
|
||||||
|
|
||||||
import { breakfastSchema } from "./schema"
|
import { breakfastSchema } from "./schema"
|
||||||
|
|
||||||
@@ -31,9 +31,7 @@ export default function Breakfast() {
|
|||||||
Icon={BreakfastIcon}
|
Icon={BreakfastIcon}
|
||||||
id={breakfastEnum.BREAKFAST}
|
id={breakfastEnum.BREAKFAST}
|
||||||
name="breakfast"
|
name="breakfast"
|
||||||
// @ts-expect-error - Types mismatch docs as this is
|
subtitle={intl.formatMessage<React.ReactNode>(
|
||||||
// a pattern that is allowed https://formatjs.io/docs/react-intl/api#usage
|
|
||||||
subtitle={intl.formatMessage(
|
|
||||||
{ id: "<b>{amount} {currency}</b>/night per adult" },
|
{ id: "<b>{amount} {currency}</b>/night per adult" },
|
||||||
{
|
{
|
||||||
amount: "150",
|
amount: "150",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FormProvider, useForm } from "react-hook-form"
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
import CheckboxCard from "@/components/TempDesignSystem/Form/Card/Checkbox"
|
import CheckboxCard from "@/components/TempDesignSystem/Form/ChoiceCard/Checkbox"
|
||||||
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
|
import CountrySelect from "@/components/TempDesignSystem/Form/Country"
|
||||||
import Input from "@/components/TempDesignSystem/Form/Input"
|
import Input from "@/components/TempDesignSystem/Form/Input"
|
||||||
import Phone from "@/components/TempDesignSystem/Form/Phone"
|
import Phone from "@/components/TempDesignSystem/Form/Phone"
|
||||||
@@ -88,8 +88,9 @@ export default function Details({ user }: DetailsProps) {
|
|||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
{user ? null : (
|
{user ? null : (
|
||||||
<CheckboxCard
|
<CheckboxCard
|
||||||
|
highlightSubtitle
|
||||||
list={list}
|
list={list}
|
||||||
saving
|
name="join"
|
||||||
subtitle={intl.formatMessage(
|
subtitle={intl.formatMessage(
|
||||||
{
|
{
|
||||||
id: "{difference}{amount} {currency}",
|
id: "{difference}{amount} {currency}",
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { EditIcon, ImageIcon } from "@/components/Icons"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
|
import Footnote from "@/components/TempDesignSystem/Text/Footnote"
|
||||||
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
|
||||||
|
import styles from "./selectedRoom.module.css"
|
||||||
|
|
||||||
|
export default function SelectedRoom() {
|
||||||
|
const intl = useIntl()
|
||||||
|
return (
|
||||||
|
<article className={styles.container}>
|
||||||
|
<div className={styles.tempImage}>
|
||||||
|
<ImageIcon
|
||||||
|
color="baseButtonTertiaryOnFillNormal"
|
||||||
|
height={60}
|
||||||
|
width={60}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<div className={styles.textContainer}>
|
||||||
|
<Footnote
|
||||||
|
className={styles.label}
|
||||||
|
color="uiTextPlaceholder"
|
||||||
|
textTransform="uppercase"
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: "Your room" })}
|
||||||
|
</Footnote>
|
||||||
|
<div className={styles.text}>
|
||||||
|
{/**
|
||||||
|
* [TEMP]
|
||||||
|
* No translation on Subtitles as they will be derived
|
||||||
|
* from Room selection.
|
||||||
|
*/}
|
||||||
|
<Subtitle
|
||||||
|
className={styles.room}
|
||||||
|
color="uiTextHighContrast"
|
||||||
|
type="two"
|
||||||
|
>
|
||||||
|
Cozy cabin
|
||||||
|
</Subtitle>
|
||||||
|
<Subtitle
|
||||||
|
className={styles.invertFontWeight}
|
||||||
|
color="uiTextMediumContrast"
|
||||||
|
type="two"
|
||||||
|
>
|
||||||
|
Free rebooking
|
||||||
|
</Subtitle>
|
||||||
|
<Subtitle
|
||||||
|
className={styles.invertFontWeight}
|
||||||
|
color="uiTextMediumContrast"
|
||||||
|
type="two"
|
||||||
|
>
|
||||||
|
Pay now
|
||||||
|
</Subtitle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
intent="tertiary"
|
||||||
|
size="small"
|
||||||
|
theme="base"
|
||||||
|
variant="icon"
|
||||||
|
>
|
||||||
|
<Link href="#">
|
||||||
|
<EditIcon color="baseButtonTertiaryOnFillNormal" />
|
||||||
|
{intl.formatMessage({ id: "Modify" })}
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
.container {
|
||||||
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
|
border-radius: var(--Corner-radius-Large);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 144px 1fr;
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
|
padding: var(--Spacing-x2) var(--Spacing-x4) var(--Spacing-x2)
|
||||||
|
var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tempImage {
|
||||||
|
align-items: center;
|
||||||
|
background-color: lightgray;
|
||||||
|
border-radius: var(--Corner-radius-Medium);
|
||||||
|
display: flex;
|
||||||
|
height: auto;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
align-items: center;
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x3);
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.textContainer {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.invertFontWeight {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invertFontWeight:not(:last-of-type)::after,
|
||||||
|
.room::after {
|
||||||
|
color: var(--UI-Text-Medium-contrast);
|
||||||
|
content: "∙";
|
||||||
|
padding-left: var(--Spacing-x1);
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
.hotelSelectionHeader {
|
.hotelSelectionHeader {
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
background-color: var(--Base-Surface-Subtle-Normal);
|
background-color: var(--Base-Surface-Subtle-Normal);
|
||||||
padding: var(--Spacing-x3) var(--Spacing-x2);
|
padding: var(--Spacing-x3) var(--Spacing-x2);
|
||||||
justify-content: center;
|
}
|
||||||
|
|
||||||
|
.hotelSelectionHeaderWrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
gap: var(--Spacing-x3);
|
gap: var(--Spacing-x3);
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titleContainer {
|
.titleContainer {
|
||||||
@@ -33,9 +36,15 @@
|
|||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.hotelSelectionHeader {
|
.hotelSelectionHeader {
|
||||||
flex-direction: row;
|
|
||||||
padding: var(--Spacing-x4) var(--Spacing-x5);
|
padding: var(--Spacing-x4) var(--Spacing-x5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotelSelectionHeaderWrapper {
|
||||||
|
flex-direction: row;
|
||||||
gap: var(--Spacing-x6);
|
gap: var(--Spacing-x6);
|
||||||
|
max-width: var(--max-width-navigation);
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titleContainer > h1 {
|
.titleContainer > h1 {
|
||||||
|
|||||||
@@ -19,35 +19,35 @@ export default function HotelSelectionHeader({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<header className={styles.hotelSelectionHeader}>
|
<header className={styles.hotelSelectionHeader}>
|
||||||
<div className={styles.titleContainer}>
|
<div className={styles.hotelSelectionHeaderWrapper}>
|
||||||
<Title as="h3" level="h1">
|
<div className={styles.titleContainer}>
|
||||||
{hotel.name}
|
<Title as="h3" level="h1">
|
||||||
</Title>
|
{hotel.name}
|
||||||
<address className={styles.address}>
|
</Title>
|
||||||
<Caption color="textMediumContrast">
|
<address className={styles.address}>
|
||||||
{hotel.address.streetAddress}, {hotel.address.city}
|
<Caption color="textMediumContrast">
|
||||||
</Caption>
|
{hotel.address.streetAddress}, {hotel.address.city}
|
||||||
<div>
|
</Caption>
|
||||||
<Divider variant="vertical" color="subtle" />
|
<div>
|
||||||
</div>
|
<Divider variant="vertical" color="subtle" />
|
||||||
<Caption color="textMediumContrast">
|
</div>
|
||||||
{intl.formatMessage(
|
<Caption color="textMediumContrast">
|
||||||
{
|
{intl.formatMessage(
|
||||||
id: "Distance to city centre",
|
{ id: "Distance to city centre" },
|
||||||
},
|
{ number: hotel.location.distanceToCentre }
|
||||||
{ number: hotel.location.distanceToCentre }
|
)}
|
||||||
)}
|
</Caption>
|
||||||
</Caption>
|
</address>
|
||||||
</address>
|
</div>
|
||||||
</div>
|
<div className={styles.dividerContainer}>
|
||||||
<div className={styles.dividerContainer}>
|
<Divider variant="vertical" color="subtle" />
|
||||||
<Divider variant="vertical" color="subtle" />
|
</div>
|
||||||
</div>
|
<div className={styles.descriptionContainer}>
|
||||||
<div className={styles.descriptionContainer}>
|
<Body color="textHighContrast">
|
||||||
<Body color="textHighContrast">
|
{hotel.hotelContent.texts.descriptions.short}
|
||||||
{hotel.hotelContent.texts.descriptions.short}
|
</Body>
|
||||||
</Body>
|
<HotelDetailSidePeek />
|
||||||
<HotelDetailSidePeek />
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export default function Payment({ hotel }: PaymentProps) {
|
|||||||
name="payment-method"
|
name="payment-method"
|
||||||
id="card"
|
id="card"
|
||||||
value="card"
|
value="card"
|
||||||
checked={selectedPaymentMethod === "card"}
|
defaultChecked={selectedPaymentMethod === "card"}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="card">card</label>
|
<label htmlFor="card">card</label>
|
||||||
</button>
|
</button>
|
||||||
@@ -145,7 +145,7 @@ export default function Payment({ hotel }: PaymentProps) {
|
|||||||
name="payment-method"
|
name="payment-method"
|
||||||
id={paymentOption}
|
id={paymentOption}
|
||||||
value={paymentOption}
|
value={paymentOption}
|
||||||
checked={selectedPaymentMethod === paymentOption}
|
defaultChecked={selectedPaymentMethod === paymentOption}
|
||||||
/>
|
/>
|
||||||
<label htmlFor={paymentOption}>{paymentOption}</label>
|
<label htmlFor={paymentOption}>{paymentOption}</label>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -9,17 +9,31 @@ import styles from "./flexibilityOption.module.css"
|
|||||||
import { FlexibilityOptionProps } from "@/types/components/hotelReservation/selectRate/flexibilityOption"
|
import { FlexibilityOptionProps } from "@/types/components/hotelReservation/selectRate/flexibilityOption"
|
||||||
|
|
||||||
export default function FlexibilityOption({
|
export default function FlexibilityOption({
|
||||||
currency,
|
product,
|
||||||
standardPrice,
|
|
||||||
memberPrice,
|
|
||||||
name,
|
name,
|
||||||
value,
|
|
||||||
paymentTerm,
|
paymentTerm,
|
||||||
}: FlexibilityOptionProps) {
|
}: FlexibilityOptionProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
|
if (!product) {
|
||||||
|
// TODO: Implement empty state when this rate can't be booked
|
||||||
|
return <div>TBI: Rate not available</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const { productType } = product
|
||||||
|
const { public: publicPrice, member: memberPrice } = productType
|
||||||
|
const { localPrice: publicLocalPrice, requestedPrice: publicRequestedPrice } =
|
||||||
|
publicPrice
|
||||||
|
const { localPrice: memberLocalPrice, requestedPrice: memberRequestedPrice } =
|
||||||
|
memberPrice
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="flexibility" value={value} />
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="rateCode"
|
||||||
|
value={product.productType.public.rateCode}
|
||||||
|
/>
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<div className={styles.header}>
|
<div className={styles.header}>
|
||||||
<Body>{name}</Body>
|
<Body>{name}</Body>
|
||||||
@@ -29,15 +43,27 @@ export default function FlexibilityOption({
|
|||||||
<div>
|
<div>
|
||||||
<dt>{intl.formatMessage({ id: "Standard price" })}</dt>
|
<dt>{intl.formatMessage({ id: "Standard price" })}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{standardPrice} {currency}
|
{publicLocalPrice.pricePerNight} {publicLocalPrice.currency}/
|
||||||
|
{intl.formatMessage({ id: "night" })}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<dt>{intl.formatMessage({ id: "Member price" })}</dt>
|
<dt>{intl.formatMessage({ id: "Member price" })}</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{memberPrice} {currency}
|
{memberLocalPrice.pricePerNight} {memberLocalPrice.currency}/
|
||||||
|
{intl.formatMessage({ id: "night" })}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
{publicRequestedPrice && memberRequestedPrice && (
|
||||||
|
<div>
|
||||||
|
<dt>{intl.formatMessage({ id: "Approx." })}</dt>
|
||||||
|
<dd>
|
||||||
|
{publicRequestedPrice.pricePerNight}/
|
||||||
|
{memberRequestedPrice.pricePerNight}{" "}
|
||||||
|
{publicRequestedPrice.currency}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@@ -11,20 +11,54 @@ import styles from "./roomCard.module.css"
|
|||||||
import { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard"
|
import { RoomCardProps } from "@/types/components/hotelReservation/selectRate/roomCard"
|
||||||
|
|
||||||
export default function RoomCard({
|
export default function RoomCard({
|
||||||
room,
|
rateDefinitions,
|
||||||
nrOfAdults,
|
roomConfiguration,
|
||||||
nrOfNights,
|
|
||||||
breakfastIncluded,
|
|
||||||
}: RoomCardProps) {
|
}: RoomCardProps) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
|
||||||
|
const saveRate = rateDefinitions.find(
|
||||||
|
// TODO: Update string when API has decided
|
||||||
|
(rate) => rate.cancellationRule === "NonCancellable"
|
||||||
|
)
|
||||||
|
const changeRate = rateDefinitions.find(
|
||||||
|
// TODO: Update string when API has decided
|
||||||
|
(rate) => rate.cancellationRule === "Modifiable"
|
||||||
|
)
|
||||||
|
const flexRate = rateDefinitions.find(
|
||||||
|
// TODO: Update string when API has decided
|
||||||
|
(rate) => rate.cancellationRule === "CancellableBefore6PM"
|
||||||
|
)
|
||||||
|
|
||||||
|
const saveProduct = saveRate
|
||||||
|
? roomConfiguration.products.find(
|
||||||
|
(product) =>
|
||||||
|
product.productType.public.rateCode === saveRate.rateCode ||
|
||||||
|
product.productType.member.rateCode === saveRate.rateCode
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
const changeProduct = changeRate
|
||||||
|
? roomConfiguration.products.find(
|
||||||
|
(product) =>
|
||||||
|
product.productType.public.rateCode === changeRate.rateCode ||
|
||||||
|
product.productType.member.rateCode === changeRate.rateCode
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
const flexProduct = flexRate
|
||||||
|
? roomConfiguration.products.find(
|
||||||
|
(product) =>
|
||||||
|
product.productType.public.rateCode === flexRate.rateCode ||
|
||||||
|
product.productType.member.rateCode === flexRate.rateCode
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.card}>
|
<div className={styles.card}>
|
||||||
<div className={styles.cardBody}>
|
<div className={styles.cardBody}>
|
||||||
<div className={styles.specification}>
|
<div className={styles.specification}>
|
||||||
<Subtitle className={styles.name} type="two">
|
<Subtitle className={styles.name} type="two">
|
||||||
{room.name}
|
{roomConfiguration.roomType}
|
||||||
</Subtitle>
|
</Subtitle>
|
||||||
<Caption>{room.size}</Caption>
|
<Caption>Room size TBI</Caption>
|
||||||
<Button intent="text" type="button" size="small" theme="base">
|
<Button intent="text" type="button" size="small" theme="base">
|
||||||
{intl.formatMessage({ id: "See room details" })}
|
{intl.formatMessage({ id: "See room details" })}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -32,20 +66,15 @@ export default function RoomCard({
|
|||||||
{/*TODO: Handle pluralisation*/}
|
{/*TODO: Handle pluralisation*/}
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{
|
{
|
||||||
id: "Nr night, nr adult",
|
id: "Max {nrOfGuests} guests",
|
||||||
defaultMessage:
|
defaultMessage: "Max {nrOfGuests} guests",
|
||||||
"{nights, number} night, {adults, number} adult",
|
|
||||||
},
|
},
|
||||||
{ nights: nrOfNights, adults: nrOfAdults }
|
// TODO: Correct number
|
||||||
|
{ nrOfGuests: 2 }
|
||||||
)}
|
)}
|
||||||
{" | "}
|
{intl.formatMessage({
|
||||||
{breakfastIncluded
|
id: "Breakfast included",
|
||||||
? intl.formatMessage({
|
})}
|
||||||
id: "Breakfast included",
|
|
||||||
})
|
|
||||||
: intl.formatMessage({
|
|
||||||
id: "Breakfast excluded",
|
|
||||||
})}
|
|
||||||
</Caption>
|
</Caption>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -53,25 +82,19 @@ export default function RoomCard({
|
|||||||
name={intl.formatMessage({ id: "Non-refundable" })}
|
name={intl.formatMessage({ id: "Non-refundable" })}
|
||||||
value="non-refundable"
|
value="non-refundable"
|
||||||
paymentTerm={intl.formatMessage({ id: "Pay now" })}
|
paymentTerm={intl.formatMessage({ id: "Pay now" })}
|
||||||
standardPrice={room.prices.nonRefundable.standard}
|
product={saveProduct}
|
||||||
memberPrice={room.prices.nonRefundable.member}
|
|
||||||
currency={room.prices.currency}
|
|
||||||
/>
|
/>
|
||||||
<FlexibilityOption
|
<FlexibilityOption
|
||||||
name={intl.formatMessage({ id: "Free rebooking" })}
|
name={intl.formatMessage({ id: "Free rebooking" })}
|
||||||
value="free-rebooking"
|
value="free-rebooking"
|
||||||
paymentTerm={intl.formatMessage({ id: "Pay now" })}
|
paymentTerm={intl.formatMessage({ id: "Pay now" })}
|
||||||
standardPrice={room.prices.freeRebooking.standard}
|
product={changeProduct}
|
||||||
memberPrice={room.prices.freeRebooking.member}
|
|
||||||
currency={room.prices.currency}
|
|
||||||
/>
|
/>
|
||||||
<FlexibilityOption
|
<FlexibilityOption
|
||||||
name={intl.formatMessage({ id: "Free cancellation" })}
|
name={intl.formatMessage({ id: "Free cancellation" })}
|
||||||
value="free-cancellation"
|
value="free-cancellation"
|
||||||
paymentTerm={intl.formatMessage({ id: "Pay later" })}
|
paymentTerm={intl.formatMessage({ id: "Pay later" })}
|
||||||
standardPrice={room.prices.freeCancellation.standard}
|
product={flexProduct}
|
||||||
memberPrice={room.prices.freeCancellation.member}
|
|
||||||
currency={room.prices.currency}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -87,7 +110,8 @@ export default function RoomCard({
|
|||||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||||
<img
|
<img
|
||||||
alt={intl.formatMessage({ id: "A photo of the room" })}
|
alt={intl.formatMessage({ id: "A photo of the room" })}
|
||||||
src={room.imageSrc}
|
// TODO: Correct image URL
|
||||||
|
src="https://www.scandichotels.se/imageVault/publishedmedia/xnmqnmz6mz0uhuat0917/scandic-helsinki-hub-room-standard-KR-7.jpg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useRouter, useSearchParams } from "next/navigation"
|
import { useRouter, useSearchParams } from "next/navigation"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
|
||||||
import RoomCard from "./RoomCard"
|
import RoomCard from "./RoomCard"
|
||||||
|
|
||||||
@@ -8,12 +11,11 @@ import styles from "./roomSelection.module.css"
|
|||||||
import { RoomSelectionProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
|
import { RoomSelectionProps } from "@/types/components/hotelReservation/selectRate/roomSelection"
|
||||||
|
|
||||||
export default function RoomSelection({
|
export default function RoomSelection({
|
||||||
rates,
|
roomConfigurations,
|
||||||
nrOfNights,
|
|
||||||
nrOfAdults,
|
|
||||||
}: RoomSelectionProps) {
|
}: RoomSelectionProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@@ -25,31 +27,28 @@ export default function RoomSelection({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<ul className={styles.roomList}>
|
<form
|
||||||
{rates.map((room) => (
|
method="GET"
|
||||||
<li key={room.id}>
|
action={`select-bed?${searchParams}`}
|
||||||
<form
|
onSubmit={handleSubmit}
|
||||||
method="GET"
|
>
|
||||||
action={`select-bed?${searchParams}`}
|
<ul className={styles.roomList}>
|
||||||
onSubmit={handleSubmit}
|
{roomConfigurations.roomConfigurations.map((roomConfiguration) => (
|
||||||
>
|
<li key={roomConfiguration.roomType}>
|
||||||
<input
|
|
||||||
type="hidden"
|
|
||||||
name="roomClass"
|
|
||||||
value={room.id}
|
|
||||||
id={`room-${room.id}`}
|
|
||||||
/>
|
|
||||||
<RoomCard
|
<RoomCard
|
||||||
room={room}
|
rateDefinitions={roomConfigurations.rateDefinitions}
|
||||||
nrOfAdults={nrOfAdults}
|
roomConfiguration={roomConfiguration}
|
||||||
nrOfNights={nrOfNights}
|
|
||||||
breakfastIncluded={room.breakfastIncluded}
|
|
||||||
/>
|
/>
|
||||||
</form>
|
</li>
|
||||||
</li>
|
))}
|
||||||
))}
|
</ul>
|
||||||
</ul>
|
<div className={styles.summary}>
|
||||||
<div className={styles.summary}>This is summary</div>
|
This is summary
|
||||||
|
<Button type="submit" size="small" theme="primaryDark">
|
||||||
|
{intl.formatMessage({ id: "Choose room" })}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,6 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
background-color: white;
|
||||||
|
padding: var(--Spacing-x3) var(--Spacing-x7) var(--Spacing-x5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
.iconWrapper {
|
.iconWrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: var(--Spacing-x1);
|
top: var(--Spacing-x1);
|
||||||
z-index: 10;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.circle {
|
.circle {
|
||||||
|
|||||||
@@ -66,3 +66,8 @@
|
|||||||
.blue * {
|
.blue * {
|
||||||
fill: var(--UI-Input-Controls-Fill-Selected);
|
fill: var(--UI-Input-Controls-Fill-Selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.baseButtonTertiaryOnFillNormal,
|
||||||
|
.baseButtonTertiaryOnFillNormal * {
|
||||||
|
fill: var(--Base-Button-Tertiary-On-Fill-Normal);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,19 +5,20 @@ import styles from "./icon.module.css"
|
|||||||
const config = {
|
const config = {
|
||||||
variants: {
|
variants: {
|
||||||
color: {
|
color: {
|
||||||
|
baseButtonTertiaryOnFillNormal: styles.baseButtonTertiaryOnFillNormal,
|
||||||
baseIconLowContrast: styles.baseIconLowContrast,
|
baseIconLowContrast: styles.baseIconLowContrast,
|
||||||
black: styles.black,
|
black: styles.black,
|
||||||
|
blue: styles.blue,
|
||||||
burgundy: styles.burgundy,
|
burgundy: styles.burgundy,
|
||||||
|
green: styles.green,
|
||||||
grey80: styles.grey80,
|
grey80: styles.grey80,
|
||||||
pale: styles.pale,
|
pale: styles.pale,
|
||||||
peach80: styles.peach80,
|
peach80: styles.peach80,
|
||||||
primaryLightOnSurfaceAccent: styles.plosa,
|
primaryLightOnSurfaceAccent: styles.plosa,
|
||||||
red: styles.red,
|
red: styles.red,
|
||||||
green: styles.green,
|
|
||||||
white: styles.white,
|
white: styles.white,
|
||||||
uiTextHighContrast: styles.uiTextHighContrast,
|
uiTextHighContrast: styles.uiTextHighContrast,
|
||||||
uiTextMediumContrast: styles.uiTextMediumContrast,
|
uiTextMediumContrast: styles.uiTextMediumContrast,
|
||||||
blue: styles.blue,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ export default function FullView({
|
|||||||
className={styles.fullViewImage}
|
className={styles.fullViewImage}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={image.url}
|
|
||||||
alt={image.alt}
|
alt={image.alt}
|
||||||
layout="fill"
|
fill
|
||||||
objectFit="cover"
|
src={image.url}
|
||||||
|
style={{ objectFit: "cover" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className={styles.fullViewFooter}>
|
<div className={styles.fullViewFooter}>
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ a.default {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.btn.icon:is(.small, .medium, .large) {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--Spacing-x-half);
|
gap: var(--Spacing-x1);
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +76,8 @@ a.default {
|
|||||||
font-size: var(--typography-Caption-Bold-fontSize);
|
font-size: var(--typography-Caption-Bold-fontSize);
|
||||||
line-height: var(--typography-Caption-Bold-lineHeight);
|
line-height: var(--typography-Caption-Bold-lineHeight);
|
||||||
gap: var(--Spacing-x-quarter);
|
gap: var(--Spacing-x-quarter);
|
||||||
padding: calc(var(--Spacing-x1) + 2px) var(--Spacing-x2); /* Special case padding to adjust the missing border */
|
padding: calc(var(--Spacing-x1) + 2px) var(--Spacing-x2);
|
||||||
|
/* Special case padding to adjust the missing border */
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.small.secondary {
|
.btn.small.secondary {
|
||||||
@@ -85,7 +86,8 @@ a.default {
|
|||||||
|
|
||||||
.btn.medium {
|
.btn.medium {
|
||||||
gap: var(--Spacing-x-half);
|
gap: var(--Spacing-x-half);
|
||||||
padding: calc(var(--Spacing-x-one-and-half) + 2px) var(--Spacing-x2); /* Special case padding to adjust the missing border */
|
padding: calc(var(--Spacing-x-one-and-half) + 2px) var(--Spacing-x2);
|
||||||
|
/* Special case padding to adjust the missing border */
|
||||||
}
|
}
|
||||||
|
|
||||||
.medium.secondary {
|
.medium.secondary {
|
||||||
@@ -94,7 +96,8 @@ a.default {
|
|||||||
|
|
||||||
.btn.large {
|
.btn.large {
|
||||||
gap: var(--Spacing-x-half);
|
gap: var(--Spacing-x-half);
|
||||||
padding: calc(var(--Spacing-x2) + 2px) var(--Spacing-x3); /* Special case padding to adjust the missing border */
|
padding: calc(var(--Spacing-x2) + 2px) var(--Spacing-x3);
|
||||||
|
/* Special case padding to adjust the missing border */
|
||||||
}
|
}
|
||||||
|
|
||||||
.large.secondary {
|
.large.secondary {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Card from "."
|
import Card from "./_Card"
|
||||||
|
|
||||||
import type { CheckboxProps } from "./card"
|
import type { CheckboxProps } from "./_Card/card"
|
||||||
|
|
||||||
export default function CheckboxCard(props: CheckboxProps) {
|
export default function CheckboxCard(props: CheckboxProps) {
|
||||||
return <Card {...props} type="checkbox" />
|
return <Card {...props} type="checkbox" />
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Card from "."
|
import Card from "./_Card"
|
||||||
|
|
||||||
import type { RadioProps } from "./card"
|
import type { RadioProps } from "./_Card/card"
|
||||||
|
|
||||||
export default function RadioCard(props: RadioProps) {
|
export default function RadioCard(props: RadioProps) {
|
||||||
return <Card {...props} type="radio" />
|
return <Card {...props} type="radio" />
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
import type { IconProps } from "@/types/components/icon"
|
import type { IconProps } from "@/types/components/icon"
|
||||||
|
|
||||||
interface BaseCardProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
|
interface BaseCardProps
|
||||||
|
extends Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "title"> {
|
||||||
Icon?: (props: IconProps) => JSX.Element
|
Icon?: (props: IconProps) => JSX.Element
|
||||||
declined?: boolean
|
declined?: boolean
|
||||||
|
highlightSubtitle?: boolean
|
||||||
iconHeight?: number
|
iconHeight?: number
|
||||||
iconWidth?: number
|
iconWidth?: number
|
||||||
name?: string
|
name: string
|
||||||
saving?: boolean
|
subtitle?: React.ReactNode
|
||||||
subtitle?: string
|
title: React.ReactNode
|
||||||
title: string
|
|
||||||
type: "checkbox" | "radio"
|
type: "checkbox" | "radio"
|
||||||
value?: string
|
value?: string
|
||||||
}
|
}
|
||||||
@@ -22,7 +23,7 @@ interface ListCardProps extends BaseCardProps {
|
|||||||
|
|
||||||
interface TextCardProps extends BaseCardProps {
|
interface TextCardProps extends BaseCardProps {
|
||||||
list?: never
|
list?: never
|
||||||
text: string
|
text: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CardProps = ListCardProps | TextCardProps
|
export type CardProps = ListCardProps | TextCardProps
|
||||||
@@ -13,10 +13,10 @@ export default function Card({
|
|||||||
iconHeight = 32,
|
iconHeight = 32,
|
||||||
iconWidth = 32,
|
iconWidth = 32,
|
||||||
declined = false,
|
declined = false,
|
||||||
|
highlightSubtitle = false,
|
||||||
id,
|
id,
|
||||||
list,
|
list,
|
||||||
name = "join",
|
name,
|
||||||
saving = false,
|
|
||||||
subtitle,
|
subtitle,
|
||||||
text,
|
text,
|
||||||
title,
|
title,
|
||||||
@@ -31,7 +31,7 @@ export default function Card({
|
|||||||
{subtitle ? (
|
{subtitle ? (
|
||||||
<Caption
|
<Caption
|
||||||
className={styles.subtitle}
|
className={styles.subtitle}
|
||||||
color={saving ? "baseTextAccent" : "uiTextHighContrast"}
|
color={highlightSubtitle ? "baseTextAccent" : "uiTextHighContrast"}
|
||||||
textTransform="bold"
|
textTransform="bold"
|
||||||
>
|
>
|
||||||
{subtitle}
|
{subtitle}
|
||||||
36
components/TempDesignSystem/ScrollWrapper/index.tsx
Normal file
36
components/TempDesignSystem/ScrollWrapper/index.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useMemo } from "react"
|
||||||
|
|
||||||
|
import useScrollShadows from "@/hooks/useScrollShadows"
|
||||||
|
|
||||||
|
import styles from "./scrollWrapper.module.css"
|
||||||
|
|
||||||
|
import type { ScrollWrapperProps } from "./scrollWrapper"
|
||||||
|
|
||||||
|
export default function ScrollWrapper({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
}: ScrollWrapperProps) {
|
||||||
|
const { containerRef, showLeftShadow, showRightShadow } =
|
||||||
|
useScrollShadows<HTMLDivElement>()
|
||||||
|
|
||||||
|
const classNames = useMemo(() => {
|
||||||
|
const cls = [styles.scrollWrapper, className]
|
||||||
|
if (showLeftShadow) {
|
||||||
|
cls.push(styles.leftShadow)
|
||||||
|
}
|
||||||
|
if (showRightShadow) {
|
||||||
|
cls.push(styles.rightShadow)
|
||||||
|
}
|
||||||
|
return cls.join(" ")
|
||||||
|
}, [showLeftShadow, showRightShadow, className])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames}>
|
||||||
|
<div className={styles.content} ref={containerRef}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
.scrollWrapper {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollWrapper::before,
|
||||||
|
.scrollWrapper::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
transition: opacity 0.2s ease;
|
||||||
|
opacity: 0;
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollWrapper.leftShadow::before {
|
||||||
|
left: 0;
|
||||||
|
background: linear-gradient(to right, rgba(0, 0, 0, 0.3), transparent);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollWrapper.rightShadow::after {
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(to left, rgba(0, 0, 0, 0.3), transparent);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export interface ScrollWrapperProps
|
||||||
|
extends React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>> {}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
@@ -5,18 +5,29 @@ import { useIntl } from "react-intl"
|
|||||||
import { ChevronDownIcon } from "@/components/Icons"
|
import { ChevronDownIcon } from "@/components/Icons"
|
||||||
import Button from "@/components/TempDesignSystem/Button"
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
|
||||||
import styles from "./button.module.css"
|
import { showMoreButtonVariants } from "./variants"
|
||||||
|
|
||||||
import type { ShowMoreButtonParams } from "@/types/components/myPages/stays/button"
|
import styles from "./showMoreButton.module.css"
|
||||||
|
|
||||||
|
import type { ShowMoreButtonProps } from "./showMoreButton"
|
||||||
|
|
||||||
export default function ShowMoreButton({
|
export default function ShowMoreButton({
|
||||||
|
className,
|
||||||
|
intent,
|
||||||
disabled,
|
disabled,
|
||||||
|
showLess,
|
||||||
loadMoreData,
|
loadMoreData,
|
||||||
}: ShowMoreButtonParams) {
|
}: ShowMoreButtonProps) {
|
||||||
const { formatMessage } = useIntl()
|
const intl = useIntl()
|
||||||
|
const classNames = showMoreButtonVariants({
|
||||||
|
className,
|
||||||
|
intent,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={`${classNames} ${showLess ? styles.showLess : ""}`}>
|
||||||
<Button
|
<Button
|
||||||
|
className={styles.button}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={loadMoreData}
|
onClick={loadMoreData}
|
||||||
variant="icon"
|
variant="icon"
|
||||||
@@ -24,8 +35,8 @@ export default function ShowMoreButton({
|
|||||||
theme="base"
|
theme="base"
|
||||||
intent="text"
|
intent="text"
|
||||||
>
|
>
|
||||||
<ChevronDownIcon />
|
<ChevronDownIcon className={styles.icon} />
|
||||||
{formatMessage({ id: "Show more" })}
|
{intl.formatMessage({ id: showLess ? "Show less" : "Show more" })}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table {
|
||||||
|
display: grid;
|
||||||
|
justify-content: stretch;
|
||||||
|
border-top: 1px solid var(--Base-Border-Subtle);
|
||||||
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.table .button {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.showLess .icon {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
11
components/TempDesignSystem/ShowMoreButton/showMoreButton.ts
Normal file
11
components/TempDesignSystem/ShowMoreButton/showMoreButton.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { showMoreButtonVariants } from "./variants"
|
||||||
|
|
||||||
|
import type { VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
export interface ShowMoreButtonProps
|
||||||
|
extends React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>,
|
||||||
|
VariantProps<typeof showMoreButtonVariants> {
|
||||||
|
disabled?: boolean
|
||||||
|
showLess?: boolean
|
||||||
|
loadMoreData: () => void
|
||||||
|
}
|
||||||
11
components/TempDesignSystem/ShowMoreButton/variants.ts
Normal file
11
components/TempDesignSystem/ShowMoreButton/variants.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { cva } from "class-variance-authority"
|
||||||
|
|
||||||
|
import styles from "./showMoreButton.module.css"
|
||||||
|
|
||||||
|
export const showMoreButtonVariants = cva(styles.container, {
|
||||||
|
variants: {
|
||||||
|
intent: {
|
||||||
|
table: styles.table,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
import styles from "./table.module.css"
|
import styles from "./table.module.css"
|
||||||
|
|
||||||
function TH({ children }: React.PropsWithChildren) {
|
import type { THeadProps } from "./table"
|
||||||
return <th className={styles.th}>{children}</th>
|
|
||||||
|
function TH({ children, width = "auto", ...props }: THeadProps) {
|
||||||
|
return (
|
||||||
|
<th className={styles.th} style={{ width }} {...props}>
|
||||||
|
{children}
|
||||||
|
</th>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TH
|
export default TH
|
||||||
|
|||||||
@@ -3,11 +3,33 @@ import TD from "./TD"
|
|||||||
import TH from "./TH"
|
import TH from "./TH"
|
||||||
import THead from "./THead"
|
import THead from "./THead"
|
||||||
import TR from "./TR"
|
import TR from "./TR"
|
||||||
|
import { tableVariants } from "./variants"
|
||||||
|
|
||||||
import styles from "./table.module.css"
|
import type { TableProps } from "./table"
|
||||||
|
|
||||||
function Table({ children }: React.PropsWithChildren) {
|
function Table({
|
||||||
return <table className={styles.table}>{children}</table>
|
className,
|
||||||
|
intent,
|
||||||
|
borderRadius,
|
||||||
|
variant,
|
||||||
|
layout,
|
||||||
|
width = "100%",
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: TableProps) {
|
||||||
|
const classNames = tableVariants({
|
||||||
|
className,
|
||||||
|
borderRadius,
|
||||||
|
intent,
|
||||||
|
layout,
|
||||||
|
variant,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table className={classNames} style={{ width }} {...props}>
|
||||||
|
{children}
|
||||||
|
</table>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Table.THead = THead
|
Table.THead = THead
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
.table {
|
.table {
|
||||||
border-radius: var(--Corner-radius-Medium);
|
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thead {
|
.thead {
|
||||||
background-color: var(--Base-Background-Secondary-Normal, #f7e1d5);
|
color: var(--Base-Text-High-contrast);
|
||||||
|
background-color: var(--Base-Surface-Primary-dark-Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tbody {
|
.tbody {
|
||||||
background-color: var(--Base-Surface-Primary-light-Normal, #fff);
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
.tr:not(:last-of-type) {
|
.tr:not(:last-of-type) {
|
||||||
border-bottom: 1px solid var(--Primary-Light-On-Surface-Divider, #f0c1b6);
|
border-bottom: 1px solid var(--Base-Border-Subtle);
|
||||||
}
|
}
|
||||||
|
|
||||||
.th {
|
.th {
|
||||||
@@ -28,6 +28,35 @@
|
|||||||
padding: var(--Spacing-x2);
|
padding: var(--Spacing-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fixed {
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallRadius {
|
||||||
|
border-radius: var(--Corner-radius-Small);
|
||||||
|
}
|
||||||
|
.mediumRadius {
|
||||||
|
border-radius: var(--Corner-radius-Medium);
|
||||||
|
}
|
||||||
|
.largeRadius {
|
||||||
|
border-radius: var(--Corner-radius-Large);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content .thead {
|
||||||
|
background-color: var(--Base-Surface-Subtle-Hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content .tbody {
|
||||||
|
background-color: var(--Base-Background-Primary-Normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content.striped .tbody .tr:nth-child(odd) {
|
||||||
|
background-color: var(--Base-Surface-Subtle-Normal);
|
||||||
|
}
|
||||||
|
.content.striped .tbody .tr:nth-child(even) {
|
||||||
|
background-color: var(--Base-Background-Primary-Normal);
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
.th {
|
.th {
|
||||||
padding: var(--Spacing-x2) var(--Spacing-x3);
|
padding: var(--Spacing-x2) var(--Spacing-x3);
|
||||||
|
|||||||
14
components/TempDesignSystem/Table/table.ts
Normal file
14
components/TempDesignSystem/Table/table.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { tableVariants } from "./variants"
|
||||||
|
|
||||||
|
import type { VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
export interface TableProps
|
||||||
|
extends React.PropsWithChildren<React.HTMLAttributes<HTMLTableElement>>,
|
||||||
|
VariantProps<typeof tableVariants> {
|
||||||
|
width?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface THeadProps
|
||||||
|
extends React.PropsWithChildren<React.HTMLAttributes<HTMLTableCellElement>> {
|
||||||
|
width?: string
|
||||||
|
}
|
||||||
27
components/TempDesignSystem/Table/variants.ts
Normal file
27
components/TempDesignSystem/Table/variants.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { cva } from "class-variance-authority"
|
||||||
|
|
||||||
|
import styles from "./table.module.css"
|
||||||
|
|
||||||
|
export const tableVariants = cva(styles.table, {
|
||||||
|
variants: {
|
||||||
|
intent: {
|
||||||
|
light: styles.light,
|
||||||
|
striped: styles.striped,
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
content: styles.content,
|
||||||
|
},
|
||||||
|
borderRadius: {
|
||||||
|
none: "",
|
||||||
|
small: styles.smallRadius,
|
||||||
|
medium: styles.mediumRadius,
|
||||||
|
large: styles.largeRadius,
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
fixed: styles.fixed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
borderRadius: "medium",
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
0.3vw + 15px,
|
0.3vw + 15px,
|
||||||
var(--typography-Subtitle-1-Desktop-fontSize)
|
var(--typography-Subtitle-1-Desktop-fontSize)
|
||||||
);
|
);
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
letter-spacing: var(--typography-Subtitle-1-letterSpacing);
|
letter-spacing: var(--typography-Subtitle-1-letterSpacing);
|
||||||
line-height: var(--typography-Subtitle-1-lineHeight);
|
line-height: var(--typography-Subtitle-1-lineHeight);
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
0.3vw + 15px,
|
0.3vw + 15px,
|
||||||
var(--typography-Subtitle-2-Desktop-fontSize)
|
var(--typography-Subtitle-2-Desktop-fontSize)
|
||||||
);
|
);
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
letter-spacing: var(--typography-Subtitle-2-letterSpacing);
|
letter-spacing: var(--typography-Subtitle-2-letterSpacing);
|
||||||
line-height: var(--typography-Subtitle-2-lineHeight);
|
line-height: var(--typography-Subtitle-2-lineHeight);
|
||||||
}
|
}
|
||||||
@@ -62,3 +62,7 @@
|
|||||||
.uiTextHighContrast {
|
.uiTextHighContrast {
|
||||||
color: var(--UI-Text-High-contrast);
|
color: var(--UI-Text-High-contrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.uiTextMediumContrast {
|
||||||
|
color: var(--UI-Text-Medium-contrast);
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const config = {
|
|||||||
burgundy: styles.burgundy,
|
burgundy: styles.burgundy,
|
||||||
pale: styles.pale,
|
pale: styles.pale,
|
||||||
uiTextHighContrast: styles.uiTextHighContrast,
|
uiTextHighContrast: styles.uiTextHighContrast,
|
||||||
|
uiTextMediumContrast: styles.uiTextMediumContrast,
|
||||||
},
|
},
|
||||||
textAlign: {
|
textAlign: {
|
||||||
center: styles.center,
|
center: styles.center,
|
||||||
|
|||||||
34
hooks/useScrollShadows.ts
Normal file
34
hooks/useScrollShadows.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react"
|
||||||
|
|
||||||
|
export default function useScrollShadows<T extends HTMLElement>() {
|
||||||
|
const containerRef = useRef<T>(null)
|
||||||
|
const [showLeftShadow, setShowLeftShadow] = useState<boolean>(false)
|
||||||
|
const [showRightShadow, setShowRightShadow] = useState<boolean>(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleScroll = () => {
|
||||||
|
const container = containerRef.current
|
||||||
|
if (!container) return
|
||||||
|
|
||||||
|
setShowLeftShadow(container.scrollLeft > 0)
|
||||||
|
setShowRightShadow(
|
||||||
|
container.scrollLeft < container.scrollWidth - container.clientWidth
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = containerRef.current
|
||||||
|
if (container) {
|
||||||
|
container.addEventListener("scroll", handleScroll)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleScroll()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (container) {
|
||||||
|
container.removeEventListener("scroll", handleScroll)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return { containerRef, showLeftShadow, showRightShadow }
|
||||||
|
}
|
||||||
@@ -13,13 +13,12 @@
|
|||||||
"Already a friend?": "Allerede en ven?",
|
"Already a friend?": "Allerede en ven?",
|
||||||
"Amenities": "Faciliteter",
|
"Amenities": "Faciliteter",
|
||||||
"Amusement park": "Forlystelsespark",
|
"Amusement park": "Forlystelsespark",
|
||||||
"An error occurred when adding a credit card, please try again later.": "Der opstod en fejl under tilføjelse af et kreditkort. Prøv venligst igen senere.",
|
|
||||||
"An error occurred trying to manage your preferences, please try again later.": "Der opstod en fejl under forsøget på at administrere dine præferencer. Prøv venligst igen senere.",
|
"An error occurred trying to manage your preferences, please try again later.": "Der opstod en fejl under forsøget på at administrere dine præferencer. Prøv venligst igen senere.",
|
||||||
|
"An error occurred when adding a credit card, please try again later.": "Der opstod en fejl under tilføjelse af et kreditkort. Prøv venligst igen senere.",
|
||||||
"An error occurred when trying to update profile.": "Der opstod en fejl under forsøg på at opdatere profilen.",
|
"An error occurred when trying to update profile.": "Der opstod en fejl under forsøg på at opdatere profilen.",
|
||||||
"Any changes you've made will be lost.": "Alle ændringer, du har foretaget, går tabt.",
|
"Any changes you've made will be lost.": "Alle ændringer, du har foretaget, går tabt.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?",
|
||||||
"Arrival date": "Ankomstdato",
|
"Arrival date": "Ankomstdato",
|
||||||
"as of today": "pr. dags dato",
|
|
||||||
"As our": "Som vores {level}",
|
"As our": "Som vores {level}",
|
||||||
"As our Close Friend": "Som vores nære ven",
|
"As our Close Friend": "Som vores nære ven",
|
||||||
"At latest": "Senest",
|
"At latest": "Senest",
|
||||||
@@ -35,9 +34,9 @@
|
|||||||
"Breakfast buffet": "Morgenbuffet",
|
"Breakfast buffet": "Morgenbuffet",
|
||||||
"Breakfast excluded": "Morgenmad ikke inkluderet",
|
"Breakfast excluded": "Morgenmad ikke inkluderet",
|
||||||
"Breakfast included": "Morgenmad inkluderet",
|
"Breakfast included": "Morgenmad inkluderet",
|
||||||
|
"Breakfast restaurant": "Breakfast restaurant",
|
||||||
"Bus terminal": "Busstation",
|
"Bus terminal": "Busstation",
|
||||||
"Business": "Forretning",
|
"Business": "Forretning",
|
||||||
"Breakfast restaurant": "Breakfast restaurant",
|
|
||||||
"Cancel": "Afbestille",
|
"Cancel": "Afbestille",
|
||||||
"Check in": "Check ind",
|
"Check in": "Check ind",
|
||||||
"Check out": "Check ud",
|
"Check out": "Check ud",
|
||||||
@@ -83,9 +82,9 @@
|
|||||||
"Edit profile": "Rediger profil",
|
"Edit profile": "Rediger profil",
|
||||||
"Email": "E-mail",
|
"Email": "E-mail",
|
||||||
"Email address": "E-mailadresse",
|
"Email address": "E-mailadresse",
|
||||||
|
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
||||||
"Enter destination or hotel": "Indtast destination eller hotel",
|
"Enter destination or hotel": "Indtast destination eller hotel",
|
||||||
"Enter your details": "Indtast dine oplysninger",
|
"Enter your details": "Indtast dine oplysninger",
|
||||||
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Explore all levels and benefits": "Udforsk alle niveauer og fordele",
|
"Explore all levels and benefits": "Udforsk alle niveauer og fordele",
|
||||||
"Explore nearby": "Udforsk i nærheden",
|
"Explore nearby": "Udforsk i nærheden",
|
||||||
@@ -121,7 +120,6 @@
|
|||||||
"Join Scandic Friends": "Tilmeld dig Scandic Friends",
|
"Join Scandic Friends": "Tilmeld dig Scandic Friends",
|
||||||
"Join at no cost": "Tilmeld dig uden omkostninger",
|
"Join at no cost": "Tilmeld dig uden omkostninger",
|
||||||
"King bed": "Kingsize-seng",
|
"King bed": "Kingsize-seng",
|
||||||
"km to city center": "km til byens centrum",
|
|
||||||
"Language": "Sprog",
|
"Language": "Sprog",
|
||||||
"Lastname": "Efternavn",
|
"Lastname": "Efternavn",
|
||||||
"Latest searches": "Seneste søgninger",
|
"Latest searches": "Seneste søgninger",
|
||||||
@@ -209,11 +207,11 @@
|
|||||||
"Read more about the hotel": "Læs mere om hotellet",
|
"Read more about the hotel": "Læs mere om hotellet",
|
||||||
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
||||||
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
||||||
|
"Request bedtype": "Anmod om sengetype",
|
||||||
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
|
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
|
||||||
"Restaurant & Bar": "Restaurant & Bar",
|
"Restaurant & Bar": "Restaurant & Bar",
|
||||||
"Restaurants & Bars": "Restaurants & Bars",
|
"Restaurants & Bars": "Restaurants & Bars",
|
||||||
"Retype new password": "Gentag den nye adgangskode",
|
"Retype new password": "Gentag den nye adgangskode",
|
||||||
"Request bedtype": "Anmod om sengetype",
|
|
||||||
"Room & Terms": "Værelse & Vilkår",
|
"Room & Terms": "Værelse & Vilkår",
|
||||||
"Room facilities": "Værelsesfaciliteter",
|
"Room facilities": "Værelsesfaciliteter",
|
||||||
"Rooms": "Værelser",
|
"Rooms": "Værelser",
|
||||||
@@ -299,11 +297,12 @@
|
|||||||
"Your details": "Dine oplysninger",
|
"Your details": "Dine oplysninger",
|
||||||
"Your level": "Dit niveau",
|
"Your level": "Dit niveau",
|
||||||
"Your points to spend": "Dine brugbare point",
|
"Your points to spend": "Dine brugbare point",
|
||||||
|
"Your room": "Dit værelse",
|
||||||
"Zip code": "Postnummer",
|
"Zip code": "Postnummer",
|
||||||
"Zoo": "Zoo",
|
"Zoo": "Zoo",
|
||||||
"Zoom in": "Zoom ind",
|
"Zoom in": "Zoom ind",
|
||||||
"Zoom out": "Zoom ud",
|
"Zoom out": "Zoom ud",
|
||||||
"as of today": "fra idag",
|
"as of today": "pr. dags dato",
|
||||||
"booking.adults": "{totalAdults, plural, one {# voksen} other {# voksne}}",
|
"booking.adults": "{totalAdults, plural, one {# voksen} other {# voksne}}",
|
||||||
"booking.nights": "{totalNights, plural, one {# nat} other {# nætter}}",
|
"booking.nights": "{totalNights, plural, one {# nat} other {# nætter}}",
|
||||||
"booking.rooms": "{totalRooms, plural, one {# værelse} other {# værelser}}",
|
"booking.rooms": "{totalRooms, plural, one {# værelse} other {# værelser}}",
|
||||||
|
|||||||
@@ -207,11 +207,11 @@
|
|||||||
"Read more about the hotel": "Lesen Sie mehr über das Hotel",
|
"Read more about the hotel": "Lesen Sie mehr über das Hotel",
|
||||||
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
||||||
"Remove card from member profile": "Karte aus dem Mitgliedsprofil entfernen",
|
"Remove card from member profile": "Karte aus dem Mitgliedsprofil entfernen",
|
||||||
|
"Request bedtype": "Bettentyp anfragen",
|
||||||
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
|
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
|
||||||
"Restaurant & Bar": "Restaurant & Bar",
|
"Restaurant & Bar": "Restaurant & Bar",
|
||||||
"Restaurants & Bars": "Restaurants & Bars",
|
"Restaurants & Bars": "Restaurants & Bars",
|
||||||
"Retype new password": "Neues Passwort erneut eingeben",
|
"Retype new password": "Neues Passwort erneut eingeben",
|
||||||
"Request bedtype": "Bettentyp anfragen",
|
|
||||||
"Room & Terms": "Zimmer & Bedingungen",
|
"Room & Terms": "Zimmer & Bedingungen",
|
||||||
"Room facilities": "Zimmerausstattung",
|
"Room facilities": "Zimmerausstattung",
|
||||||
"Rooms": "Räume",
|
"Rooms": "Räume",
|
||||||
@@ -297,6 +297,7 @@
|
|||||||
"Your details": "Ihre Angaben",
|
"Your details": "Ihre Angaben",
|
||||||
"Your level": "Dein level",
|
"Your level": "Dein level",
|
||||||
"Your points to spend": "Meine Punkte",
|
"Your points to spend": "Meine Punkte",
|
||||||
|
"Your room": "Ihr Zimmer",
|
||||||
"Zip code": "PLZ",
|
"Zip code": "PLZ",
|
||||||
"Zoo": "Zoo",
|
"Zoo": "Zoo",
|
||||||
"Zoom in": "Vergrößern",
|
"Zoom in": "Vergrößern",
|
||||||
|
|||||||
@@ -120,7 +120,6 @@
|
|||||||
"Join Scandic Friends": "Join Scandic Friends",
|
"Join Scandic Friends": "Join Scandic Friends",
|
||||||
"Join at no cost": "Join at no cost",
|
"Join at no cost": "Join at no cost",
|
||||||
"King bed": "King bed",
|
"King bed": "King bed",
|
||||||
"km to city center": "km to city center",
|
|
||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
"Lastname": "Lastname",
|
"Lastname": "Lastname",
|
||||||
"Latest searches": "Latest searches",
|
"Latest searches": "Latest searches",
|
||||||
@@ -208,6 +207,7 @@
|
|||||||
"Read more about the hotel": "Read more about the hotel",
|
"Read more about the hotel": "Read more about the hotel",
|
||||||
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
||||||
"Remove card from member profile": "Remove card from member profile",
|
"Remove card from member profile": "Remove card from member profile",
|
||||||
|
"Request bedtype": "Request bedtype",
|
||||||
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
|
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
|
||||||
"Restaurant & Bar": "Restaurant & Bar",
|
"Restaurant & Bar": "Restaurant & Bar",
|
||||||
"Restaurants & Bars": "Restaurants & Bars",
|
"Restaurants & Bars": "Restaurants & Bars",
|
||||||
@@ -226,10 +226,12 @@
|
|||||||
"See room details": "See room details",
|
"See room details": "See room details",
|
||||||
"See rooms": "See rooms",
|
"See rooms": "See rooms",
|
||||||
"Select a country": "Select a country",
|
"Select a country": "Select a country",
|
||||||
|
"Select breakfast options": "Select breakfast options",
|
||||||
"Select country of residence": "Select country of residence",
|
"Select country of residence": "Select country of residence",
|
||||||
"Select date of birth": "Select date of birth",
|
"Select date of birth": "Select date of birth",
|
||||||
"Select dates": "Select dates",
|
"Select dates": "Select dates",
|
||||||
"Select language": "Select language",
|
"Select language": "Select language",
|
||||||
|
"Select payment method": "Select payment method",
|
||||||
"Select your language": "Select your language",
|
"Select your language": "Select your language",
|
||||||
"Shopping": "Shopping",
|
"Shopping": "Shopping",
|
||||||
"Shopping & Dining": "Shopping & Dining",
|
"Shopping & Dining": "Shopping & Dining",
|
||||||
@@ -295,6 +297,7 @@
|
|||||||
"Your details": "Your details",
|
"Your details": "Your details",
|
||||||
"Your level": "Your level",
|
"Your level": "Your level",
|
||||||
"Your points to spend": "Your points to spend",
|
"Your points to spend": "Your points to spend",
|
||||||
|
"Your room": "Your room",
|
||||||
"Zip code": "Zip code",
|
"Zip code": "Zip code",
|
||||||
"Zoo": "Zoo",
|
"Zoo": "Zoo",
|
||||||
"Zoom in": "Zoom in",
|
"Zoom in": "Zoom in",
|
||||||
@@ -315,9 +318,6 @@
|
|||||||
"number": "number",
|
"number": "number",
|
||||||
"or": "or",
|
"or": "or",
|
||||||
"points": "Points",
|
"points": "Points",
|
||||||
"Request bedtype": "Request bedtype",
|
|
||||||
"Select breakfast options": "Select breakfast options",
|
|
||||||
"Select payment method": "Select payment method",
|
|
||||||
"special character": "special character",
|
"special character": "special character",
|
||||||
"spendable points expiring by": "{points} spendable points expiring by {date}",
|
"spendable points expiring by": "{points} spendable points expiring by {date}",
|
||||||
"to": "to",
|
"to": "to",
|
||||||
|
|||||||
@@ -13,8 +13,8 @@
|
|||||||
"Already a friend?": "Oletko jo ystävä?",
|
"Already a friend?": "Oletko jo ystävä?",
|
||||||
"Amenities": "Mukavuudet",
|
"Amenities": "Mukavuudet",
|
||||||
"Amusement park": "Huvipuisto",
|
"Amusement park": "Huvipuisto",
|
||||||
"An error occurred when adding a credit card, please try again later.": "Luottokorttia lisättäessä tapahtui virhe. Yritä myöhemmin uudelleen.",
|
|
||||||
"An error occurred trying to manage your preferences, please try again later.": "Asetusten hallinnassa tapahtui virhe. Yritä myöhemmin uudelleen.",
|
"An error occurred trying to manage your preferences, please try again later.": "Asetusten hallinnassa tapahtui virhe. Yritä myöhemmin uudelleen.",
|
||||||
|
"An error occurred when adding a credit card, please try again later.": "Luottokorttia lisättäessä tapahtui virhe. Yritä myöhemmin uudelleen.",
|
||||||
"An error occurred when trying to update profile.": "Profiilia päivitettäessä tapahtui virhe.",
|
"An error occurred when trying to update profile.": "Profiilia päivitettäessä tapahtui virhe.",
|
||||||
"Any changes you've made will be lost.": "Kaikki tekemäsi muutokset menetetään.",
|
"Any changes you've made will be lost.": "Kaikki tekemäsi muutokset menetetään.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?",
|
||||||
@@ -34,9 +34,9 @@
|
|||||||
"Breakfast buffet": "Aamiaisbuffet",
|
"Breakfast buffet": "Aamiaisbuffet",
|
||||||
"Breakfast excluded": "Aamiainen ei sisälly",
|
"Breakfast excluded": "Aamiainen ei sisälly",
|
||||||
"Breakfast included": "Aamiainen sisältyy",
|
"Breakfast included": "Aamiainen sisältyy",
|
||||||
|
"Breakfast restaurant": "Breakfast restaurant",
|
||||||
"Bus terminal": "Bussiasema",
|
"Bus terminal": "Bussiasema",
|
||||||
"Business": "Business",
|
"Business": "Business",
|
||||||
"Breakfast restaurant": "Breakfast restaurant",
|
|
||||||
"Cancel": "Peruuttaa",
|
"Cancel": "Peruuttaa",
|
||||||
"Check in": "Sisäänkirjautuminen",
|
"Check in": "Sisäänkirjautuminen",
|
||||||
"Check out": "Uloskirjautuminen",
|
"Check out": "Uloskirjautuminen",
|
||||||
@@ -82,9 +82,9 @@
|
|||||||
"Edit profile": "Muokkaa profiilia",
|
"Edit profile": "Muokkaa profiilia",
|
||||||
"Email": "Sähköposti",
|
"Email": "Sähköposti",
|
||||||
"Email address": "Sähköpostiosoite",
|
"Email address": "Sähköpostiosoite",
|
||||||
|
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
||||||
"Enter destination or hotel": "Anna kohde tai hotelli",
|
"Enter destination or hotel": "Anna kohde tai hotelli",
|
||||||
"Enter your details": "Anna tietosi",
|
"Enter your details": "Anna tietosi",
|
||||||
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
|
"Explore all levels and benefits": "Tutustu kaikkiin tasoihin ja etuihin",
|
||||||
"Explore nearby": "Tutustu lähialueeseen",
|
"Explore nearby": "Tutustu lähialueeseen",
|
||||||
@@ -120,7 +120,6 @@
|
|||||||
"Join Scandic Friends": "Liity jäseneksi",
|
"Join Scandic Friends": "Liity jäseneksi",
|
||||||
"Join at no cost": "Liity maksutta",
|
"Join at no cost": "Liity maksutta",
|
||||||
"King bed": "King-vuode",
|
"King bed": "King-vuode",
|
||||||
"km to city center": "km keskustaan",
|
|
||||||
"Language": "Kieli",
|
"Language": "Kieli",
|
||||||
"Lastname": "Sukunimi",
|
"Lastname": "Sukunimi",
|
||||||
"Latest searches": "Viimeisimmät haut",
|
"Latest searches": "Viimeisimmät haut",
|
||||||
@@ -208,6 +207,7 @@
|
|||||||
"Read more about the hotel": "Lue lisää hotellista",
|
"Read more about the hotel": "Lue lisää hotellista",
|
||||||
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
||||||
"Remove card from member profile": "Poista kortti jäsenprofiilista",
|
"Remove card from member profile": "Poista kortti jäsenprofiilista",
|
||||||
|
"Request bedtype": "Pyydä sänkytyyppiä",
|
||||||
"Restaurant": "{count, plural, one {#Ravintola} other {#Restaurants}}",
|
"Restaurant": "{count, plural, one {#Ravintola} other {#Restaurants}}",
|
||||||
"Restaurant & Bar": "Ravintola & Baari",
|
"Restaurant & Bar": "Ravintola & Baari",
|
||||||
"Restaurants & Bars": "Restaurants & Bars",
|
"Restaurants & Bars": "Restaurants & Bars",
|
||||||
@@ -217,7 +217,6 @@
|
|||||||
"Rooms": "Huoneet",
|
"Rooms": "Huoneet",
|
||||||
"Rooms & Guests": "Huoneet & Vieraat",
|
"Rooms & Guests": "Huoneet & Vieraat",
|
||||||
"Rooms & Guestss": "Huoneet & Vieraat",
|
"Rooms & Guestss": "Huoneet & Vieraat",
|
||||||
"Request bedtype": "Pyydä sänkytyyppiä",
|
|
||||||
"Sauna and gym": "Sauna and gym",
|
"Sauna and gym": "Sauna and gym",
|
||||||
"Save": "Tallenna",
|
"Save": "Tallenna",
|
||||||
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
"Scandic Friends Mastercard": "Scandic Friends Mastercard",
|
||||||
@@ -299,6 +298,7 @@
|
|||||||
"Your details": "Tietosi",
|
"Your details": "Tietosi",
|
||||||
"Your level": "Tasosi",
|
"Your level": "Tasosi",
|
||||||
"Your points to spend": "Käytettävissä olevat pisteesi",
|
"Your points to spend": "Käytettävissä olevat pisteesi",
|
||||||
|
"Your room": "Sinun huoneesi",
|
||||||
"Zip code": "Postinumero",
|
"Zip code": "Postinumero",
|
||||||
"Zoo": "Eläintarha",
|
"Zoo": "Eläintarha",
|
||||||
"Zoom in": "Lähennä",
|
"Zoom in": "Lähennä",
|
||||||
|
|||||||
@@ -13,13 +13,12 @@
|
|||||||
"Already a friend?": "Allerede Friend?",
|
"Already a friend?": "Allerede Friend?",
|
||||||
"Amenities": "Fasiliteter",
|
"Amenities": "Fasiliteter",
|
||||||
"Amusement park": "Tivoli",
|
"Amusement park": "Tivoli",
|
||||||
"An error occurred when adding a credit card, please try again later.": "Det oppstod en feil ved å legge til et kredittkort. Prøv igjen senere.",
|
|
||||||
"An error occurred trying to manage your preferences, please try again later.": "Det oppstod en feil under forsøket på å administrere innstillingene dine. Prøv igjen senere.",
|
"An error occurred trying to manage your preferences, please try again later.": "Det oppstod en feil under forsøket på å administrere innstillingene dine. Prøv igjen senere.",
|
||||||
|
"An error occurred when adding a credit card, please try again later.": "Det oppstod en feil ved å legge til et kredittkort. Prøv igjen senere.",
|
||||||
"An error occurred when trying to update profile.": "Det oppstod en feil under forsøk på å oppdatere profilen.",
|
"An error occurred when trying to update profile.": "Det oppstod en feil under forsøk på å oppdatere profilen.",
|
||||||
"Any changes you've made will be lost.": "Eventuelle endringer du har gjort, går tapt.",
|
"Any changes you've made will be lost.": "Eventuelle endringer du har gjort, går tapt.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?",
|
||||||
"Arrival date": "Ankomstdato",
|
"Arrival date": "Ankomstdato",
|
||||||
"as of today": "per i dag",
|
|
||||||
"As our": "Som vår {level}",
|
"As our": "Som vår {level}",
|
||||||
"As our Close Friend": "Som vår nære venn",
|
"As our Close Friend": "Som vår nære venn",
|
||||||
"At latest": "Senest",
|
"At latest": "Senest",
|
||||||
@@ -82,16 +81,16 @@
|
|||||||
"Edit profile": "Rediger profil",
|
"Edit profile": "Rediger profil",
|
||||||
"Email": "E-post",
|
"Email": "E-post",
|
||||||
"Email address": "E-postadresse",
|
"Email address": "E-postadresse",
|
||||||
|
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
||||||
"Enter destination or hotel": "Skriv inn destinasjon eller hotell",
|
"Enter destination or hotel": "Skriv inn destinasjon eller hotell",
|
||||||
"Enter your details": "Skriv inn detaljene dine",
|
"Enter your details": "Skriv inn detaljene dine",
|
||||||
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
|
"Explore all levels and benefits": "Utforsk alle nivåer og fordeler",
|
||||||
"Explore nearby": "Utforsk i nærheten",
|
"Explore nearby": "Utforsk i nærheten",
|
||||||
"Extras to your booking": "Tilvalg til bestillingen din",
|
"Extras to your booking": "Tilvalg til bestillingen din",
|
||||||
|
"FAQ": "FAQ",
|
||||||
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
|
"Failed to delete credit card, please try again later.": "Kunne ikke slette kredittkortet, prøv igjen senere.",
|
||||||
"Fair": "Messe",
|
"Fair": "Messe",
|
||||||
"FAQ": "FAQ",
|
|
||||||
"Find booking": "Finn booking",
|
"Find booking": "Finn booking",
|
||||||
"Find hotels": "Finn hotell",
|
"Find hotels": "Finn hotell",
|
||||||
"Firstname": "Fornavn",
|
"Firstname": "Fornavn",
|
||||||
@@ -120,7 +119,6 @@
|
|||||||
"Join Scandic Friends": "Bli med i Scandic Friends",
|
"Join Scandic Friends": "Bli med i Scandic Friends",
|
||||||
"Join at no cost": "Bli med uten kostnad",
|
"Join at no cost": "Bli med uten kostnad",
|
||||||
"King bed": "King-size-seng",
|
"King bed": "King-size-seng",
|
||||||
"km to city center": "km til sentrum",
|
|
||||||
"Language": "Språk",
|
"Language": "Språk",
|
||||||
"Lastname": "Etternavn",
|
"Lastname": "Etternavn",
|
||||||
"Latest searches": "Siste søk",
|
"Latest searches": "Siste søk",
|
||||||
@@ -208,11 +206,11 @@
|
|||||||
"Read more about the hotel": "Les mer om hotellet",
|
"Read more about the hotel": "Les mer om hotellet",
|
||||||
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
||||||
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
"Remove card from member profile": "Fjern kortet fra medlemsprofilen",
|
||||||
|
"Request bedtype": "Be om sengetype",
|
||||||
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
|
"Restaurant": "{count, plural, one {#Restaurant} other {#Restaurants}}",
|
||||||
"Restaurant & Bar": "Restaurant & Bar",
|
"Restaurant & Bar": "Restaurant & Bar",
|
||||||
"Restaurants & Bars": "Restaurants & Bars",
|
"Restaurants & Bars": "Restaurants & Bars",
|
||||||
"Retype new password": "Skriv inn nytt passord på nytt",
|
"Retype new password": "Skriv inn nytt passord på nytt",
|
||||||
"Request bedtype": "Be om sengetype",
|
|
||||||
"Room & Terms": "Rom & Vilkår",
|
"Room & Terms": "Rom & Vilkår",
|
||||||
"Room facilities": "Romfasiliteter",
|
"Room facilities": "Romfasiliteter",
|
||||||
"Rooms": "Rom",
|
"Rooms": "Rom",
|
||||||
@@ -298,11 +296,12 @@
|
|||||||
"Your details": "Dine detaljer",
|
"Your details": "Dine detaljer",
|
||||||
"Your level": "Ditt nivå",
|
"Your level": "Ditt nivå",
|
||||||
"Your points to spend": "Dine brukbare poeng",
|
"Your points to spend": "Dine brukbare poeng",
|
||||||
|
"Your room": "Rommet ditt",
|
||||||
"Zip code": "Post kode",
|
"Zip code": "Post kode",
|
||||||
"Zoo": "Dyrehage",
|
"Zoo": "Dyrehage",
|
||||||
"Zoom in": "Zoom inn",
|
"Zoom in": "Zoom inn",
|
||||||
"Zoom out": "Zoom ut",
|
"Zoom out": "Zoom ut",
|
||||||
"as of today": "per idag",
|
"as of today": "per i dag",
|
||||||
"booking.adults": "{totalAdults, plural, one {# voksen} other {# voksne}}",
|
"booking.adults": "{totalAdults, plural, one {# voksen} other {# voksne}}",
|
||||||
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
|
"booking.nights": "{totalNights, plural, one {# natt} other {# netter}}",
|
||||||
"booking.rooms": "{totalRooms, plural, one {# rom} other {# rom}}",
|
"booking.rooms": "{totalRooms, plural, one {# rom} other {# rom}}",
|
||||||
|
|||||||
@@ -13,13 +13,12 @@
|
|||||||
"Already a friend?": "Är du redan en vän?",
|
"Already a friend?": "Är du redan en vän?",
|
||||||
"Amenities": "Bekvämligheter",
|
"Amenities": "Bekvämligheter",
|
||||||
"Amusement park": "Nöjespark",
|
"Amusement park": "Nöjespark",
|
||||||
"An error occurred when adding a credit card, please try again later.": "Ett fel uppstod när ett kreditkort lades till, försök igen senare.",
|
|
||||||
"An error occurred trying to manage your preferences, please try again later.": "Ett fel uppstod när du försökte hantera dina inställningar, försök igen senare.",
|
"An error occurred trying to manage your preferences, please try again later.": "Ett fel uppstod när du försökte hantera dina inställningar, försök igen senare.",
|
||||||
|
"An error occurred when adding a credit card, please try again later.": "Ett fel uppstod när ett kreditkort lades till, försök igen senare.",
|
||||||
"An error occurred when trying to update profile.": "Ett fel uppstod när du försökte uppdatera profilen.",
|
"An error occurred when trying to update profile.": "Ett fel uppstod när du försökte uppdatera profilen.",
|
||||||
"Any changes you've made will be lost.": "Alla ändringar du har gjort kommer att gå förlorade.",
|
"Any changes you've made will be lost.": "Alla ändringar du har gjort kommer att gå förlorade.",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?",
|
||||||
"Arrival date": "Ankomstdatum",
|
"Arrival date": "Ankomstdatum",
|
||||||
"as of today": "per idag",
|
|
||||||
"As our": "Som vår {level}",
|
"As our": "Som vår {level}",
|
||||||
"As our Close Friend": "Som vår nära vän",
|
"As our Close Friend": "Som vår nära vän",
|
||||||
"At latest": "Senast",
|
"At latest": "Senast",
|
||||||
@@ -35,9 +34,9 @@
|
|||||||
"Breakfast buffet": "Frukostbuffé",
|
"Breakfast buffet": "Frukostbuffé",
|
||||||
"Breakfast excluded": "Frukost ingår ej",
|
"Breakfast excluded": "Frukost ingår ej",
|
||||||
"Breakfast included": "Frukost ingår",
|
"Breakfast included": "Frukost ingår",
|
||||||
|
"Breakfast restaurant": "Breakfast restaurant",
|
||||||
"Bus terminal": "Bussterminal",
|
"Bus terminal": "Bussterminal",
|
||||||
"Business": "Business",
|
"Business": "Business",
|
||||||
"Breakfast restaurant": "Breakfast restaurant",
|
|
||||||
"Cancel": "Avbryt",
|
"Cancel": "Avbryt",
|
||||||
"Check in": "Checka in",
|
"Check in": "Checka in",
|
||||||
"Check out": "Checka ut",
|
"Check out": "Checka ut",
|
||||||
@@ -83,9 +82,9 @@
|
|||||||
"Edit profile": "Redigera profil",
|
"Edit profile": "Redigera profil",
|
||||||
"Email": "E-post",
|
"Email": "E-post",
|
||||||
"Email address": "E-postadress",
|
"Email address": "E-postadress",
|
||||||
|
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
||||||
"Enter destination or hotel": "Ange destination eller hotell",
|
"Enter destination or hotel": "Ange destination eller hotell",
|
||||||
"Enter your details": "Ange dina uppgifter",
|
"Enter your details": "Ange dina uppgifter",
|
||||||
"Enjoy relaxed restaurant experiences": "Enjoy relaxed restaurant experiences",
|
|
||||||
"Events that make an impression": "Events that make an impression",
|
"Events that make an impression": "Events that make an impression",
|
||||||
"Explore all levels and benefits": "Utforska alla nivåer och fördelar",
|
"Explore all levels and benefits": "Utforska alla nivåer och fördelar",
|
||||||
"Explore nearby": "Utforska i närheten",
|
"Explore nearby": "Utforska i närheten",
|
||||||
@@ -118,11 +117,9 @@
|
|||||||
"How it works": "Hur det fungerar",
|
"How it works": "Hur det fungerar",
|
||||||
"Image gallery": "Bildgalleri",
|
"Image gallery": "Bildgalleri",
|
||||||
"It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Det gick inte att hantera dina kommunikationsinställningar just nu, försök igen senare eller kontakta supporten om problemet kvarstår.",
|
"It is not posible to manage your communication preferences right now, please try again later or contact support if the problem persists.": "Det gick inte att hantera dina kommunikationsinställningar just nu, försök igen senare eller kontakta supporten om problemet kvarstår.",
|
||||||
|
|
||||||
"Join Scandic Friends": "Gå med i Scandic Friends",
|
"Join Scandic Friends": "Gå med i Scandic Friends",
|
||||||
"Join at no cost": "Gå med utan kostnad",
|
"Join at no cost": "Gå med utan kostnad",
|
||||||
"King bed": "King size-säng",
|
"King bed": "King size-säng",
|
||||||
"km to city center": "km till stadens centrum",
|
|
||||||
"Language": "Språk",
|
"Language": "Språk",
|
||||||
"Lastname": "Efternamn",
|
"Lastname": "Efternamn",
|
||||||
"Latest searches": "Senaste sökningarna",
|
"Latest searches": "Senaste sökningarna",
|
||||||
@@ -210,11 +207,11 @@
|
|||||||
"Read more about the hotel": "Läs mer om hotellet",
|
"Read more about the hotel": "Läs mer om hotellet",
|
||||||
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
"Read more about wellness & exercise": "Read more about wellness & exercise",
|
||||||
"Remove card from member profile": "Ta bort kortet från medlemsprofilen",
|
"Remove card from member profile": "Ta bort kortet från medlemsprofilen",
|
||||||
|
"Request bedtype": "Request bedtype",
|
||||||
"Restaurant": "{count, plural, one {#Restaurang} other {#Restauranger}}",
|
"Restaurant": "{count, plural, one {#Restaurang} other {#Restauranger}}",
|
||||||
"Restaurant & Bar": "Restaurang & Bar",
|
"Restaurant & Bar": "Restaurang & Bar",
|
||||||
"Restaurants & Bars": "Restaurants & Bars",
|
"Restaurants & Bars": "Restaurants & Bars",
|
||||||
"Retype new password": "Upprepa nytt lösenord",
|
"Retype new password": "Upprepa nytt lösenord",
|
||||||
"Request bedtype": "Request bedtype",
|
|
||||||
"Room & Terms": "Rum & Villkor",
|
"Room & Terms": "Rum & Villkor",
|
||||||
"Room facilities": "Rumfaciliteter",
|
"Room facilities": "Rumfaciliteter",
|
||||||
"Rooms": "Rum",
|
"Rooms": "Rum",
|
||||||
@@ -266,7 +263,6 @@
|
|||||||
"Tripadvisor reviews": "{rating} ({count} recensioner på Tripadvisor)",
|
"Tripadvisor reviews": "{rating} ({count} recensioner på Tripadvisor)",
|
||||||
"Type of bed": "Sängtyp",
|
"Type of bed": "Sängtyp",
|
||||||
"Type of room": "Rumstyp",
|
"Type of room": "Rumstyp",
|
||||||
"uppercase letter": "stor bokstav",
|
|
||||||
"Use bonus cheque": "Använd bonuscheck",
|
"Use bonus cheque": "Använd bonuscheck",
|
||||||
"Use code/voucher": "Använd kod/voucher",
|
"Use code/voucher": "Använd kod/voucher",
|
||||||
"User information": "Användarinformation",
|
"User information": "Användarinformation",
|
||||||
@@ -301,6 +297,7 @@
|
|||||||
"Your details": "Dina uppgifter",
|
"Your details": "Dina uppgifter",
|
||||||
"Your level": "Din nivå",
|
"Your level": "Din nivå",
|
||||||
"Your points to spend": "Dina spenderbara poäng",
|
"Your points to spend": "Dina spenderbara poäng",
|
||||||
|
"Your room": "Ditt rum",
|
||||||
"Zip code": "Postnummer",
|
"Zip code": "Postnummer",
|
||||||
"Zoo": "Djurpark",
|
"Zoo": "Djurpark",
|
||||||
"Zoom in": "Zooma in",
|
"Zoom in": "Zooma in",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export namespace endpoints {
|
|||||||
}
|
}
|
||||||
export const enum v1 {
|
export const enum v1 {
|
||||||
hotelsAvailability = "availability/v1/availabilities/city",
|
hotelsAvailability = "availability/v1/availabilities/city",
|
||||||
|
roomsAvailability = "availability/v1/availabilities/hotel",
|
||||||
profile = "profile/v1/Profile",
|
profile = "profile/v1/Profile",
|
||||||
booking = "booking/v1/Bookings",
|
booking = "booking/v1/Bookings",
|
||||||
creditCards = `${profile}/creditCards`,
|
creditCards = `${profile}/creditCards`,
|
||||||
|
|||||||
9
lib/graphql/Fragments/Blocks/Table.graphql
Normal file
9
lib/graphql/Fragments/Blocks/Table.graphql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
fragment Table_ContentPage on ContentPageBlocksTable {
|
||||||
|
__typename
|
||||||
|
table {
|
||||||
|
heading
|
||||||
|
preamble
|
||||||
|
column_widths
|
||||||
|
table
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#import "../../Fragments/Blocks/Content.graphql"
|
#import "../../Fragments/Blocks/Content.graphql"
|
||||||
#import "../../Fragments/Blocks/DynamicContent.graphql"
|
#import "../../Fragments/Blocks/DynamicContent.graphql"
|
||||||
#import "../../Fragments/Blocks/Shortcuts.graphql"
|
#import "../../Fragments/Blocks/Shortcuts.graphql"
|
||||||
|
#import "../../Fragments/Blocks/Table.graphql"
|
||||||
#import "../../Fragments/Blocks/TextCols.graphql"
|
#import "../../Fragments/Blocks/TextCols.graphql"
|
||||||
#import "../../Fragments/Blocks/UspGrid.graphql"
|
#import "../../Fragments/Blocks/UspGrid.graphql"
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ query GetContentPage($locale: String!, $uid: String!) {
|
|||||||
...Content_ContentPage
|
...Content_ContentPage
|
||||||
...DynamicContent_ContentPage
|
...DynamicContent_ContentPage
|
||||||
...Shortcuts_ContentPage
|
...Shortcuts_ContentPage
|
||||||
|
...Table_ContentPage
|
||||||
...TextCols_ContentPage
|
...TextCols_ContentPage
|
||||||
...UspGrid_ContentPage
|
...UspGrid_ContentPage
|
||||||
}
|
}
|
||||||
|
|||||||
32
package-lock.json
generated
32
package-lock.json
generated
@@ -23,6 +23,7 @@
|
|||||||
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.9",
|
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.9",
|
||||||
"@t3-oss/env-nextjs": "^0.9.2",
|
"@t3-oss/env-nextjs": "^0.9.2",
|
||||||
"@tanstack/react-query": "^5.28.6",
|
"@tanstack/react-query": "^5.28.6",
|
||||||
|
"@tanstack/react-table": "^8.20.5",
|
||||||
"@trpc/client": "^11.0.0-rc.467",
|
"@trpc/client": "^11.0.0-rc.467",
|
||||||
"@trpc/react-query": "^11.0.0-rc.467",
|
"@trpc/react-query": "^11.0.0-rc.467",
|
||||||
"@trpc/server": "^11.0.0-rc.467",
|
"@trpc/server": "^11.0.0-rc.467",
|
||||||
@@ -5971,6 +5972,37 @@
|
|||||||
"react": "^18.0.0"
|
"react": "^18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/react-table": {
|
||||||
|
"version": "8.20.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.20.5.tgz",
|
||||||
|
"integrity": "sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/table-core": "8.20.5"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8",
|
||||||
|
"react-dom": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/table-core": {
|
||||||
|
"version": "8.20.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.20.5.tgz",
|
||||||
|
"integrity": "sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@testing-library/dom": {
|
"node_modules/@testing-library/dom": {
|
||||||
"version": "10.1.0",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.1.0.tgz",
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.9",
|
"@scandic-hotels/design-system": "git+https://x-token-auth:$DESIGN_SYSTEM_ACCESS_TOKEN@bitbucket.org/scandic-swap/design-system.git#v0.1.0-rc.9",
|
||||||
"@t3-oss/env-nextjs": "^0.9.2",
|
"@t3-oss/env-nextjs": "^0.9.2",
|
||||||
"@tanstack/react-query": "^5.28.6",
|
"@tanstack/react-query": "^5.28.6",
|
||||||
|
"@tanstack/react-table": "^8.20.5",
|
||||||
"@trpc/client": "^11.0.0-rc.467",
|
"@trpc/client": "^11.0.0-rc.467",
|
||||||
"@trpc/react-query": "^11.0.0-rc.467",
|
"@trpc/react-query": "^11.0.0-rc.467",
|
||||||
"@trpc/server": "^11.0.0-rc.467",
|
"@trpc/server": "^11.0.0-rc.467",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
shortcutsRefsSchema,
|
shortcutsRefsSchema,
|
||||||
shortcutsSchema,
|
shortcutsSchema,
|
||||||
} from "../schemas/blocks/shortcuts"
|
} from "../schemas/blocks/shortcuts"
|
||||||
|
import { tableSchema } from "../schemas/blocks/table"
|
||||||
import { textColsRefsSchema, textColsSchema } from "../schemas/blocks/textCols"
|
import { textColsRefsSchema, textColsSchema } from "../schemas/blocks/textCols"
|
||||||
import { uspGridRefsSchema, uspGridSchema } from "../schemas/blocks/uspGrid"
|
import { uspGridRefsSchema, uspGridSchema } from "../schemas/blocks/uspGrid"
|
||||||
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
|
import { tempImageVaultAssetSchema } from "../schemas/imageVault"
|
||||||
@@ -75,11 +76,18 @@ export const contentPageUspGrid = z
|
|||||||
})
|
})
|
||||||
.merge(uspGridSchema)
|
.merge(uspGridSchema)
|
||||||
|
|
||||||
|
export const contentPageTable = z
|
||||||
|
.object({
|
||||||
|
__typename: z.literal(ContentPageEnum.ContentStack.blocks.Table),
|
||||||
|
})
|
||||||
|
.merge(tableSchema)
|
||||||
|
|
||||||
export const blocksSchema = z.discriminatedUnion("__typename", [
|
export const blocksSchema = z.discriminatedUnion("__typename", [
|
||||||
contentPageCards,
|
contentPageCards,
|
||||||
contentPageContent,
|
contentPageContent,
|
||||||
contentPageDynamicContent,
|
contentPageDynamicContent,
|
||||||
contentPageShortcuts,
|
contentPageShortcuts,
|
||||||
|
contentPageTable,
|
||||||
contentPageTextCols,
|
contentPageTextCols,
|
||||||
contentPageUspGrid,
|
contentPageUspGrid,
|
||||||
])
|
])
|
||||||
|
|||||||
58
server/routers/contentstack/schemas/blocks/table.ts
Normal file
58
server/routers/contentstack/schemas/blocks/table.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { BlocksEnums } from "@/types/enums/blocks"
|
||||||
|
|
||||||
|
export const tableSchema = z.object({
|
||||||
|
typename: z
|
||||||
|
.literal(BlocksEnums.block.Table)
|
||||||
|
.optional()
|
||||||
|
.default(BlocksEnums.block.Table),
|
||||||
|
table: z
|
||||||
|
.object({
|
||||||
|
heading: z.string().optional(),
|
||||||
|
preamble: z.string().optional().default(""),
|
||||||
|
column_widths: z.array(z.number()),
|
||||||
|
table: z.object({
|
||||||
|
tableState: z.object({
|
||||||
|
columns: z.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
label: z.string().default(""),
|
||||||
|
accessor: z.string(),
|
||||||
|
dataType: z.string(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
data: z.array(z.object({}).catchall(z.string())),
|
||||||
|
skipReset: z.boolean(),
|
||||||
|
tableActionEnabled: z.boolean(),
|
||||||
|
headerRowAdded: z.boolean(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.transform((data) => {
|
||||||
|
const totalWidth = data.column_widths.reduce(
|
||||||
|
(acc, width) => acc + width,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
const columns = data.table.tableState.columns.map((col, idx) => ({
|
||||||
|
id: col.id,
|
||||||
|
header: col.label || "",
|
||||||
|
width: data.column_widths[idx] || 0,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const rows = data.table.tableState.data.map((rowData) =>
|
||||||
|
columns.reduce<Record<string, string>>((transformedRow, column) => {
|
||||||
|
transformedRow[column.id] = rowData[column.id] || ""
|
||||||
|
return transformedRow
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
heading: data.heading,
|
||||||
|
preamble: data.preamble,
|
||||||
|
columns,
|
||||||
|
rows,
|
||||||
|
totalWidth,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
@@ -17,6 +17,17 @@ export const getHotelsAvailabilityInputSchema = z.object({
|
|||||||
attachedProfileId: z.string().optional().default(""),
|
attachedProfileId: z.string().optional().default(""),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const getRoomsAvailabilityInputSchema = z.object({
|
||||||
|
hotelId: z.number(),
|
||||||
|
roomStayStartDate: z.string(),
|
||||||
|
roomStayEndDate: z.string(),
|
||||||
|
adults: z.number(),
|
||||||
|
children: z.number().optional().default(0),
|
||||||
|
promotionCode: z.string().optional(),
|
||||||
|
reservationProfileType: z.string().optional().default(""),
|
||||||
|
attachedProfileId: z.string().optional().default(""),
|
||||||
|
})
|
||||||
|
|
||||||
export const getRatesInputSchema = z.object({
|
export const getRatesInputSchema = z.object({
|
||||||
hotelId: z.string(),
|
hotelId: z.string(),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -572,6 +572,86 @@ export type HotelsAvailability = z.infer<typeof hotelsAvailabilitySchema>
|
|||||||
export type HotelsAvailabilityPrices =
|
export type HotelsAvailabilityPrices =
|
||||||
HotelsAvailability["data"][number]["attributes"]["bestPricePerNight"]
|
HotelsAvailability["data"][number]["attributes"]["bestPricePerNight"]
|
||||||
|
|
||||||
|
const productSchema = z.object({
|
||||||
|
productType: z.object({
|
||||||
|
public: z.object({
|
||||||
|
rateCode: z.string(),
|
||||||
|
rateType: z.string().optional(),
|
||||||
|
localPrice: z.object({
|
||||||
|
pricePerNight: z.string(),
|
||||||
|
pricePerStay: z.string(),
|
||||||
|
currency: z.string(),
|
||||||
|
}),
|
||||||
|
requestedPrice: z
|
||||||
|
.object({
|
||||||
|
pricePerNight: z.string(),
|
||||||
|
pricePerStay: z.string(),
|
||||||
|
currency: z.string(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
}),
|
||||||
|
member: z.object({
|
||||||
|
rateCode: z.string(),
|
||||||
|
rateType: z.string().optional(),
|
||||||
|
localPrice: z.object({
|
||||||
|
pricePerNight: z.string(),
|
||||||
|
pricePerStay: z.string(),
|
||||||
|
currency: z.string(),
|
||||||
|
}),
|
||||||
|
requestedPrice: z
|
||||||
|
.object({
|
||||||
|
pricePerNight: z.string(),
|
||||||
|
pricePerStay: z.string(),
|
||||||
|
currency: z.string(),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
const roomConfigurationSchema = z.object({
|
||||||
|
status: z.string(),
|
||||||
|
bedType: z.string(),
|
||||||
|
roomType: z.string(),
|
||||||
|
roomsLeft: z.number(),
|
||||||
|
features: z.array(z.object({ inventory: z.number(), code: z.string() })),
|
||||||
|
products: z.array(productSchema),
|
||||||
|
})
|
||||||
|
|
||||||
|
const rateDefinitionSchema = z.object({
|
||||||
|
title: z.string(),
|
||||||
|
breakfastIncluded: z.boolean(),
|
||||||
|
rateType: z.string().optional(),
|
||||||
|
rateCode: z.string(),
|
||||||
|
generalTerms: z.array(z.string()),
|
||||||
|
cancellationRule: z.string(),
|
||||||
|
cancellationText: z.string(),
|
||||||
|
mustBeGuaranteed: z.boolean(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const roomsAvailabilitySchema = z
|
||||||
|
.object({
|
||||||
|
data: z.object({
|
||||||
|
attributes: z.object({
|
||||||
|
checkInDate: z.string(),
|
||||||
|
checkOutDate: z.string(),
|
||||||
|
occupancy: occupancySchema.optional(),
|
||||||
|
hotelId: z.number(),
|
||||||
|
roomConfigurations: z.array(roomConfigurationSchema),
|
||||||
|
rateDefinitions: z.array(rateDefinitionSchema),
|
||||||
|
}),
|
||||||
|
relationships: linksSchema.optional(),
|
||||||
|
type: z.string().optional(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.transform((o) => o.data.attributes)
|
||||||
|
|
||||||
|
export const getRoomsAvailabilitySchema = roomsAvailabilitySchema
|
||||||
|
export type RoomsAvailability = z.infer<typeof roomsAvailabilitySchema>
|
||||||
|
export type RoomConfiguration = z.infer<typeof roomConfigurationSchema>
|
||||||
|
export type Product = z.infer<typeof productSchema>
|
||||||
|
export type RateDefinition = z.infer<typeof rateDefinitionSchema>
|
||||||
|
|
||||||
const flexibilityPrice = z.object({
|
const flexibilityPrice = z.object({
|
||||||
standard: z.number(),
|
standard: z.number(),
|
||||||
member: z.number(),
|
member: z.number(),
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ import {
|
|||||||
getHotelsAvailabilityInputSchema,
|
getHotelsAvailabilityInputSchema,
|
||||||
getlHotelDataInputSchema,
|
getlHotelDataInputSchema,
|
||||||
getRatesInputSchema,
|
getRatesInputSchema,
|
||||||
|
getRoomsAvailabilityInputSchema,
|
||||||
} from "./input"
|
} from "./input"
|
||||||
import {
|
import {
|
||||||
getHotelDataSchema,
|
getHotelDataSchema,
|
||||||
getHotelsAvailabilitySchema,
|
getHotelsAvailabilitySchema,
|
||||||
getRatesSchema,
|
getRatesSchema,
|
||||||
|
getRoomsAvailabilitySchema,
|
||||||
roomSchema,
|
roomSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import tempRatesData from "./tempRatesData.json"
|
import tempRatesData from "./tempRatesData.json"
|
||||||
@@ -61,6 +63,16 @@ const hotelsAvailabilityFailCounter = meter.createCounter(
|
|||||||
"trpc.hotel.availability.hotels-fail"
|
"trpc.hotel.availability.hotels-fail"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const roomsAvailabilityCounter = meter.createCounter(
|
||||||
|
"trpc.hotel.availability.rooms"
|
||||||
|
)
|
||||||
|
const roomsAvailabilitySuccessCounter = meter.createCounter(
|
||||||
|
"trpc.hotel.availability.rooms-success"
|
||||||
|
)
|
||||||
|
const roomsAvailabilityFailCounter = meter.createCounter(
|
||||||
|
"trpc.hotel.availability.rooms-fail"
|
||||||
|
)
|
||||||
|
|
||||||
async function getContentstackData(
|
async function getContentstackData(
|
||||||
locale: string,
|
locale: string,
|
||||||
uid: string | null | undefined
|
uid: string | null | undefined
|
||||||
@@ -376,6 +388,123 @@ export const hotelQueryRouter = router({
|
|||||||
.flatMap((hotels) => hotels.attributes),
|
.flatMap((hotels) => hotels.attributes),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
rooms: hotelServiceProcedure
|
||||||
|
.input(getRoomsAvailabilityInputSchema)
|
||||||
|
.query(async ({ input, ctx }) => {
|
||||||
|
const {
|
||||||
|
hotelId,
|
||||||
|
roomStayStartDate,
|
||||||
|
roomStayEndDate,
|
||||||
|
adults,
|
||||||
|
children,
|
||||||
|
promotionCode,
|
||||||
|
reservationProfileType,
|
||||||
|
attachedProfileId,
|
||||||
|
} = input
|
||||||
|
|
||||||
|
const params: Record<string, string | number | undefined> = {
|
||||||
|
roomStayStartDate,
|
||||||
|
roomStayEndDate,
|
||||||
|
adults,
|
||||||
|
children,
|
||||||
|
promotionCode,
|
||||||
|
reservationProfileType,
|
||||||
|
attachedProfileId,
|
||||||
|
}
|
||||||
|
|
||||||
|
roomsAvailabilityCounter.add(1, {
|
||||||
|
hotelId,
|
||||||
|
roomStayStartDate,
|
||||||
|
roomStayEndDate,
|
||||||
|
adults,
|
||||||
|
children,
|
||||||
|
promotionCode,
|
||||||
|
reservationProfileType,
|
||||||
|
})
|
||||||
|
console.info(
|
||||||
|
"api.hotels.roomsAvailability start",
|
||||||
|
JSON.stringify({ query: { hotelId, params } })
|
||||||
|
)
|
||||||
|
const apiResponse = await api.get(
|
||||||
|
`${api.endpoints.v1.roomsAvailability}/${hotelId}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${ctx.serviceToken}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
params
|
||||||
|
)
|
||||||
|
if (!apiResponse.ok) {
|
||||||
|
const text = await apiResponse.text()
|
||||||
|
roomsAvailabilityFailCounter.add(1, {
|
||||||
|
hotelId,
|
||||||
|
roomStayStartDate,
|
||||||
|
roomStayEndDate,
|
||||||
|
adults,
|
||||||
|
children,
|
||||||
|
promotionCode,
|
||||||
|
reservationProfileType,
|
||||||
|
error_type: "http_error",
|
||||||
|
error: JSON.stringify({
|
||||||
|
status: apiResponse.status,
|
||||||
|
statusText: apiResponse.statusText,
|
||||||
|
text,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
console.error(
|
||||||
|
"api.hotels.roomsAvailability error",
|
||||||
|
JSON.stringify({
|
||||||
|
query: { hotelId, params },
|
||||||
|
error: {
|
||||||
|
status: apiResponse.status,
|
||||||
|
statusText: apiResponse.statusText,
|
||||||
|
text,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const apiJson = await apiResponse.json()
|
||||||
|
const validateAvailabilityData =
|
||||||
|
getRoomsAvailabilitySchema.safeParse(apiJson)
|
||||||
|
if (!validateAvailabilityData.success) {
|
||||||
|
roomsAvailabilityFailCounter.add(1, {
|
||||||
|
hotelId,
|
||||||
|
roomStayStartDate,
|
||||||
|
roomStayEndDate,
|
||||||
|
adults,
|
||||||
|
children,
|
||||||
|
promotionCode,
|
||||||
|
reservationProfileType,
|
||||||
|
error_type: "validation_error",
|
||||||
|
error: JSON.stringify(validateAvailabilityData.error),
|
||||||
|
})
|
||||||
|
console.error(
|
||||||
|
"api.hotels.roomsAvailability validation error",
|
||||||
|
JSON.stringify({
|
||||||
|
query: { hotelId, params },
|
||||||
|
error: validateAvailabilityData.error,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
throw badRequestError()
|
||||||
|
}
|
||||||
|
roomsAvailabilitySuccessCounter.add(1, {
|
||||||
|
hotelId,
|
||||||
|
roomStayStartDate,
|
||||||
|
roomStayEndDate,
|
||||||
|
adults,
|
||||||
|
children,
|
||||||
|
promotionCode,
|
||||||
|
reservationProfileType,
|
||||||
|
})
|
||||||
|
console.info(
|
||||||
|
"api.hotels.roomsAvailability success",
|
||||||
|
JSON.stringify({
|
||||||
|
query: { hotelId, params: params },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return validateAvailabilityData.data
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
rates: router({
|
rates: router({
|
||||||
get: publicProcedure
|
get: publicProcedure
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ export const getStaysSchema = z.object({
|
|||||||
limit: z.number(),
|
limit: z.number(),
|
||||||
totalCount: z.number(),
|
totalCount: z.number(),
|
||||||
})
|
})
|
||||||
|
.optional()
|
||||||
.nullable(),
|
.nullable(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
5
types/components/blocks/table.ts
Normal file
5
types/components/blocks/table.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import type { TableData } from "@/types/trpc/routers/contentstack/blocks"
|
||||||
|
|
||||||
|
export interface TableBlockProps {
|
||||||
|
data: TableData
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import { Product, RateDefinition } from "@/server/routers/hotels/output"
|
||||||
|
|
||||||
export type FlexibilityOptionProps = {
|
export type FlexibilityOptionProps = {
|
||||||
|
product: Product | undefined
|
||||||
name: string
|
name: string
|
||||||
value: string
|
value: string
|
||||||
paymentTerm: string
|
paymentTerm: string
|
||||||
standardPrice: number
|
|
||||||
memberPrice: number
|
|
||||||
currency: string
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { Rate } from "@/server/routers/hotels/output"
|
import {
|
||||||
|
RateDefinition,
|
||||||
|
RoomConfiguration,
|
||||||
|
} from "@/server/routers/hotels/output"
|
||||||
|
|
||||||
export type RoomCardProps = {
|
export type RoomCardProps = {
|
||||||
room: Rate
|
roomConfiguration: RoomConfiguration
|
||||||
nrOfNights: number
|
rateDefinitions: RateDefinition[]
|
||||||
nrOfAdults: number
|
|
||||||
breakfastIncluded: boolean
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { Rate } from "@/server/routers/hotels/output"
|
import { RoomsAvailability } from "@/server/routers/hotels/output"
|
||||||
|
|
||||||
export interface RoomSelectionProps {
|
export interface RoomSelectionProps {
|
||||||
rates: Rate[]
|
roomConfigurations: RoomsAvailability
|
||||||
nrOfAdults: number
|
|
||||||
nrOfNights: number
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export namespace BlocksEnums {
|
|||||||
Content = "Content",
|
Content = "Content",
|
||||||
DynamicContent = "DynamicContent",
|
DynamicContent = "DynamicContent",
|
||||||
Shortcuts = "Shortcuts",
|
Shortcuts = "Shortcuts",
|
||||||
|
Table = "Table",
|
||||||
TextCols = "TextCols",
|
TextCols = "TextCols",
|
||||||
TextContent = "TextContent",
|
TextContent = "TextContent",
|
||||||
UspGrid = "UspGrid",
|
UspGrid = "UspGrid",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export namespace ContentPageEnum {
|
|||||||
Shortcuts = "ContentPageBlocksShortcuts",
|
Shortcuts = "ContentPageBlocksShortcuts",
|
||||||
TextCols = "ContentPageBlocksTextCols",
|
TextCols = "ContentPageBlocksTextCols",
|
||||||
UspGrid = "ContentPageBlocksUspGrid",
|
UspGrid = "ContentPageBlocksUspGrid",
|
||||||
|
Table = "ContentPageBlocksTable",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum sidebar {
|
export const enum sidebar {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { cardsGridSchema } from "@/server/routers/contentstack/schemas/blocks/ca
|
|||||||
import { contentSchema } from "@/server/routers/contentstack/schemas/blocks/content"
|
import { contentSchema } from "@/server/routers/contentstack/schemas/blocks/content"
|
||||||
import { dynamicContentSchema } from "@/server/routers/contentstack/schemas/blocks/dynamicContent"
|
import { dynamicContentSchema } from "@/server/routers/contentstack/schemas/blocks/dynamicContent"
|
||||||
import { shortcutsSchema } from "@/server/routers/contentstack/schemas/blocks/shortcuts"
|
import { shortcutsSchema } from "@/server/routers/contentstack/schemas/blocks/shortcuts"
|
||||||
|
import { tableSchema } from "@/server/routers/contentstack/schemas/blocks/table"
|
||||||
import { textColsSchema } from "@/server/routers/contentstack/schemas/blocks/textCols"
|
import { textColsSchema } from "@/server/routers/contentstack/schemas/blocks/textCols"
|
||||||
import { uspGridSchema } from "@/server/routers/contentstack/schemas/blocks/uspGrid"
|
import { uspGridSchema } from "@/server/routers/contentstack/schemas/blocks/uspGrid"
|
||||||
|
|
||||||
@@ -12,5 +13,7 @@ export interface Content extends z.output<typeof contentSchema> {}
|
|||||||
export interface DynamicContent extends z.output<typeof dynamicContentSchema> {}
|
export interface DynamicContent extends z.output<typeof dynamicContentSchema> {}
|
||||||
export interface Shortcuts extends z.output<typeof shortcutsSchema> {}
|
export interface Shortcuts extends z.output<typeof shortcutsSchema> {}
|
||||||
export type Shortcut = Shortcuts["shortcuts"]
|
export type Shortcut = Shortcuts["shortcuts"]
|
||||||
|
export interface TableBlock extends z.output<typeof tableSchema> {}
|
||||||
|
export type TableData = TableBlock["table"]
|
||||||
export interface TextCols extends z.output<typeof textColsSchema> {}
|
export interface TextCols extends z.output<typeof textColsSchema> {}
|
||||||
export interface UspGrid extends z.output<typeof uspGridSchema> {}
|
export interface UspGrid extends z.output<typeof uspGridSchema> {}
|
||||||
|
|||||||
Reference in New Issue
Block a user