feat: static my stays
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
export default function Default() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
import { stays } from "@/constants/routes/myPages"
|
||||||
|
|
||||||
|
import Breadcrumbs from "@/components/MyPages/Breadcrumbs"
|
||||||
|
|
||||||
|
import type { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function StaysBreadcrumbs({
|
||||||
|
params,
|
||||||
|
}: PageArgs<LangParams>) {
|
||||||
|
const href = stays[params.lang].replace(`/${params.lang}`, "")
|
||||||
|
const breadcrumbs = await serverClient().contentstack.breadcrumbs.get({
|
||||||
|
href,
|
||||||
|
locale: params.lang,
|
||||||
|
})
|
||||||
|
return <Breadcrumbs breadcrumbs={breadcrumbs} />
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import { serverClient } from "@/lib/trpc/server"
|
|||||||
import MaxWidth from "@/components/MaxWidth"
|
import MaxWidth from "@/components/MaxWidth"
|
||||||
import Overview from "@/components/MyPages/Blocks/Overview"
|
import Overview from "@/components/MyPages/Blocks/Overview"
|
||||||
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
|
import Shortcuts from "@/components/MyPages/Blocks/Shortcuts"
|
||||||
import UpcomingStays from "@/components/MyPages/Blocks/UpcomingStays"
|
import UpcomingStays from "@/components/MyPages/Blocks/Overview/UpcomingStays"
|
||||||
|
|
||||||
import styles from "./page.module.css"
|
import styles from "./page.module.css"
|
||||||
|
|
||||||
@@ -12,10 +12,11 @@ import type { LangParams, PageArgs } from "@/types/params"
|
|||||||
|
|
||||||
export default async function MyPageOverview({ params }: PageArgs<LangParams>) {
|
export default async function MyPageOverview({ params }: PageArgs<LangParams>) {
|
||||||
const user = await serverClient().user.get()
|
const user = await serverClient().user.get()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MaxWidth className={styles.blocks} tag="main">
|
<MaxWidth className={styles.blocks} tag="main">
|
||||||
<Overview user={user} />
|
<Overview user={user} />
|
||||||
<UpcomingStays lang={params.lang} stays={user.stays} />
|
<UpcomingStays lang={params.lang} />
|
||||||
<Shortcuts
|
<Shortcuts
|
||||||
shortcuts={user.shortcuts}
|
shortcuts={user.shortcuts}
|
||||||
subtitle={_("The community at your fingertips")}
|
subtitle={_("The community at your fingertips")}
|
||||||
|
|||||||
17
app/[lang]/(live)/(protected)/my-pages/stays/page.module.css
Normal file
17
app/[lang]/(live)/(protected)/my-pages/stays/page.module.css
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4.2rem;
|
||||||
|
padding-left: 2rem;
|
||||||
|
padding-right: 2rem;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 950px) {
|
||||||
|
.container {
|
||||||
|
gap: 10rem;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
15
app/[lang]/(live)/(protected)/my-pages/stays/page.tsx
Normal file
15
app/[lang]/(live)/(protected)/my-pages/stays/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import UpcomingStays from "@/components/MyPages/Blocks/Stays/Upcoming"
|
||||||
|
import PreviousStays from "@/components/MyPages/Blocks/Stays/Previous"
|
||||||
|
|
||||||
|
import styles from "./page.module.css"
|
||||||
|
|
||||||
|
import { LangParams, PageArgs } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function MyStays({ params }: PageArgs<LangParams>) {
|
||||||
|
return (
|
||||||
|
<main className={styles.container}>
|
||||||
|
<UpcomingStays lang={params.lang} />
|
||||||
|
<PreviousStays lang={params.lang} />
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
38
components/MyPages/Blocks/Overview/UpcomingStays/index.tsx
Normal file
38
components/MyPages/Blocks/Overview/UpcomingStays/index.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import Link from "@/components/TempDesignSystem/Link"
|
||||||
|
import Title from "@/components/MyPages/Title"
|
||||||
|
import StayCard from "../../Stays/StayCard"
|
||||||
|
import EmptyUpcomingStaysBlock from "../../Stays/EmptyUpcomingStays"
|
||||||
|
|
||||||
|
import styles from "./upcoming.module.css"
|
||||||
|
|
||||||
|
import type { LangParams } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function UpcomingStays({ lang }: LangParams) {
|
||||||
|
const stays = await serverClient().user.stays.upcoming({
|
||||||
|
perPage: 3,
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.container}>
|
||||||
|
<header className={styles.header}>
|
||||||
|
<Title level="h2" as="h5" uppercase>
|
||||||
|
Your upcoming stays
|
||||||
|
</Title>
|
||||||
|
<Link className={styles.link} href="#">
|
||||||
|
See all
|
||||||
|
</Link>
|
||||||
|
</header>
|
||||||
|
{stays.length ? (
|
||||||
|
<section className={styles.stays}>
|
||||||
|
{stays.map((stay) => (
|
||||||
|
<StayCard key={stay.uid} stay={stay} lang={lang} />
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
) : (
|
||||||
|
<EmptyUpcomingStaysBlock />
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
.container {
|
.container {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2.2rem;
|
gap: 2.2rem;
|
||||||
margin-right: -2rem;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
margin-right: -2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@@ -31,16 +31,15 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 950px) {
|
|
||||||
.stays {
|
|
||||||
padding-right: 2rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 950px) {
|
@media screen and (min-width: 950px) {
|
||||||
.container {
|
.container {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
@media screen and (max-width: 950px) {
|
||||||
|
.stays {
|
||||||
|
padding-right: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
color: var(--some-black-color, #111);
|
color: var(--some-black-color, #111);
|
||||||
@@ -14,7 +14,7 @@ export default function Shortcuts({
|
|||||||
return (
|
return (
|
||||||
<section className={styles.shortcuts}>
|
<section className={styles.shortcuts}>
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<Title level="h2" as="h4" uppercase>
|
<Title level="h2" as="h5" uppercase>
|
||||||
{title}
|
{title}
|
||||||
</Title>
|
</Title>
|
||||||
<p className={styles.subtitle}>{subtitle}</p>
|
<p className={styles.subtitle}>{subtitle}</p>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
.container {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 25rem;
|
||||||
|
background-color: var(--some-grey-color, #f2f2f2);
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
max-width: var(--max-width);
|
||||||
|
}
|
||||||
12
components/MyPages/Blocks/Stays/EmptyPreviousStays/index.tsx
Normal file
12
components/MyPages/Blocks/Stays/EmptyPreviousStays/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import Title from "@/components/MyPages/Title"
|
||||||
|
import styles from "./emptyPreviousStays.module.css"
|
||||||
|
|
||||||
|
export default function EmptyPreviousStaysBlock() {
|
||||||
|
return (
|
||||||
|
<section className={styles.container}>
|
||||||
|
<Title level="h3" as="h5" uppercase>
|
||||||
|
You have no previous stays.
|
||||||
|
</Title>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
.button {
|
||||||
|
background-color: var(--some-red-color, #ed2027);
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.redTitle {
|
||||||
|
color: var(--some-red-color, #ed2027);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 25rem;
|
||||||
|
gap: 2.5rem;
|
||||||
|
background-color: var(--some-grey-color, #f2f2f2);
|
||||||
|
border-radius: 0.8rem;
|
||||||
|
max-width: var(--max-width);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
padding: 0 2rem;
|
||||||
|
}
|
||||||
25
components/MyPages/Blocks/Stays/EmptyUpcomingStays/index.tsx
Normal file
25
components/MyPages/Blocks/Stays/EmptyUpcomingStays/index.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import Title from "@/components/MyPages/Title"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import styles from "./emptyUpcomingStays.module.css"
|
||||||
|
import Link from "next/link"
|
||||||
|
|
||||||
|
export default function EmptyUpcomingStaysBlock() {
|
||||||
|
return (
|
||||||
|
<section className={styles.container}>
|
||||||
|
<Title level="h3" as="h5" uppercase>
|
||||||
|
You have no upcoming stays.
|
||||||
|
<span className={styles.redTitle}> Where should you go next?</span>
|
||||||
|
</Title>
|
||||||
|
<Button
|
||||||
|
intent={"primary"}
|
||||||
|
className={styles.button}
|
||||||
|
asChild
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<Link className={styles.link} href={"#"} key={"getInspired"}>
|
||||||
|
Get inspired
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
13
components/MyPages/Blocks/Stays/Header/header.module.css
Normal file
13
components/MyPages/Blocks/Stays/Header/header.module.css
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.subtitle {
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
padding-bottom: 2.5rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 950px) {
|
||||||
|
.subtitle {
|
||||||
|
width: 60%;
|
||||||
|
padding-top: 2.5rem;
|
||||||
|
padding-bottom: 5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
components/MyPages/Blocks/Stays/Header/index.tsx
Normal file
18
components/MyPages/Blocks/Stays/Header/index.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import Title from "@/components/MyPages/Title"
|
||||||
|
|
||||||
|
import styles from "./header.module.css"
|
||||||
|
|
||||||
|
import { HeaderProps } from "@/types/components/myPages/stays/title"
|
||||||
|
|
||||||
|
export default function Header({ title, subtitle }: HeaderProps) {
|
||||||
|
return (
|
||||||
|
<header className={styles.header}>
|
||||||
|
<Title as="h3" weight="semiBold" uppercase>
|
||||||
|
{title}
|
||||||
|
</Title>
|
||||||
|
<Title as="h5" weight="regular" className={styles.subtitle}>
|
||||||
|
{subtitle}
|
||||||
|
</Title>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
||||||
28
components/MyPages/Blocks/Stays/Previous/index.tsx
Normal file
28
components/MyPages/Blocks/Stays/Previous/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import EmptyPreviousStaysBlock from "../EmptyPreviousStays"
|
||||||
|
import Header from "../Header"
|
||||||
|
import StayList from "../StayList"
|
||||||
|
|
||||||
|
import styles from "./previous.module.css"
|
||||||
|
|
||||||
|
import type { LangParams } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function PreviousStays({ lang }: LangParams) {
|
||||||
|
const stays = await serverClient().user.stays.previous()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.container}>
|
||||||
|
<Header
|
||||||
|
title="Your previous stays."
|
||||||
|
subtitle="Revisit your stays and rekindle those our moments together, with ease."
|
||||||
|
></Header>
|
||||||
|
|
||||||
|
{stays.length ? (
|
||||||
|
<StayList lang={lang} stays={stays} />
|
||||||
|
) : (
|
||||||
|
<EmptyPreviousStaysBlock />
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.container {
|
||||||
|
max-width: var(--max-width);
|
||||||
|
}
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
import { dt } from "@/lib/dt"
|
import { dt } from "@/lib/dt"
|
||||||
|
|
||||||
import Image from "@/components/Image"
|
import Image from "@/components/Image"
|
||||||
import Title from "@/components/MyPages/Title"
|
|
||||||
|
|
||||||
import styles from "./stay.module.css"
|
import styles from "./stay.module.css"
|
||||||
|
import Title from "@/components/MyPages/Title"
|
||||||
|
|
||||||
import type { LangParams } from "@/types/params"
|
import type { StayCardProps } from "@/types/components/myPages/stays/stayCard"
|
||||||
import type { StayProps } from "@/types/components/myPages/myPage/stays"
|
|
||||||
|
|
||||||
export default function Stay({
|
export default function StayCard({
|
||||||
dateArrive,
|
stay,
|
||||||
dateDepart,
|
|
||||||
guests,
|
|
||||||
hotel,
|
|
||||||
lang,
|
lang,
|
||||||
}: StayProps & LangParams) {
|
showDayCount = false,
|
||||||
|
}: StayCardProps) {
|
||||||
|
const { dateArrive, dateDepart, guests, hotel } = stay
|
||||||
|
|
||||||
const daysUntilArrival = dt(dateArrive).diff(dt(), "days")
|
const daysUntilArrival = dt(dateArrive).diff(dt(), "days")
|
||||||
const arrival = dt(dateArrive).locale(lang)
|
const arrival = dt(dateArrive).locale(lang)
|
||||||
const arrivalDate = arrival.format("DD MMM")
|
const arrivalDate = arrival.format("DD MMM")
|
||||||
@@ -22,12 +20,15 @@ export default function Stay({
|
|||||||
const depart = dt(dateDepart).locale(lang)
|
const depart = dt(dateDepart).locale(lang)
|
||||||
const departDate = depart.format("DD MMM YYYY")
|
const departDate = depart.format("DD MMM YYYY")
|
||||||
const departDateTime = depart.format("YYYY-MM-DD")
|
const departDateTime = depart.format("YYYY-MM-DD")
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<article className={styles.stay}>
|
<article className={styles.stay}>
|
||||||
<div className={styles.imageContainer}>
|
<div className={styles.imageContainer}>
|
||||||
<div className={styles.badge}>
|
{showDayCount ? (
|
||||||
<time className={styles.time}>In {daysUntilArrival} days</time>
|
<div className={styles.badge}>
|
||||||
</div>
|
<time className={styles.time}>In {daysUntilArrival} days</time>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<Image
|
<Image
|
||||||
alt="Placeholder image flower"
|
alt="Placeholder image flower"
|
||||||
height={73}
|
height={73}
|
||||||
@@ -36,7 +37,7 @@ export default function Stay({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<footer className={styles.footer}>
|
<footer className={styles.footer}>
|
||||||
<Title as="h5" level="h3" uppercase>
|
<Title as="h5" level="h3" uppercase className={styles.hotel}>
|
||||||
{hotel}
|
{hotel}
|
||||||
</Title>
|
</Title>
|
||||||
<section className={styles.container}>
|
<section className={styles.container}>
|
||||||
@@ -38,11 +38,19 @@
|
|||||||
border-left: 0.1rem solid var(--some-grey-color, #d9d9d9);
|
border-left: 0.1rem solid var(--some-grey-color, #d9d9d9);
|
||||||
border-right: 0.1rem solid var(--some-grey-color, #d9d9d9);
|
border-right: 0.1rem solid var(--some-grey-color, #d9d9d9);
|
||||||
border-radius: 0 0 0.8rem 0.8rem;
|
border-radius: 0 0 0.8rem 0.8rem;
|
||||||
display: flex;
|
display: inline-block;
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.8rem;
|
|
||||||
height: 9rem;
|
height: 9rem;
|
||||||
padding: 1.5rem 2rem;
|
padding: 1.5rem 2rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hotel {
|
||||||
|
overflow: hidden;
|
||||||
|
text-wrap: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
28
components/MyPages/Blocks/Stays/StayList/index.tsx
Normal file
28
components/MyPages/Blocks/Stays/StayList/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import StayCard from "../StayCard"
|
||||||
|
|
||||||
|
import styles from "./stayList.module.css"
|
||||||
|
|
||||||
|
import { StayListProps } from "@/types/components/myPages/stays/stayList"
|
||||||
|
|
||||||
|
export default function StayList({ lang, stays }: StayListProps) {
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<section className={styles.stays}>
|
||||||
|
{stays.map((stay) => (
|
||||||
|
<StayCard
|
||||||
|
key={stay.uid}
|
||||||
|
stay={stay}
|
||||||
|
lang={lang}
|
||||||
|
showDayCount={true}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</section>
|
||||||
|
<div className={styles.buttonContainer}>
|
||||||
|
<Button intent="primary" type="button">
|
||||||
|
Show more
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
32
components/MyPages/Blocks/Stays/StayList/stayList.module.css
Normal file
32
components/MyPages/Blocks/Stays/StayList/stayList.module.css
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.stays {
|
||||||
|
display: grid;
|
||||||
|
row-gap: 1.5rem;
|
||||||
|
column-gap: 2.2rem;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
|
||||||
|
/* Hide scrollbar IE and Edge */
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
/* Hide Scrollbar Firefox */
|
||||||
|
scrollbar-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide Scrollbar Chrome, Safari and Opera */
|
||||||
|
.stays::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 950px) {
|
||||||
|
.stays {
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(25rem, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonContainer {
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
components/MyPages/Blocks/Stays/Upcoming/index.tsx
Normal file
29
components/MyPages/Blocks/Stays/Upcoming/index.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
|
import EmptyUpcomingStaysBlock from "../EmptyUpcomingStays"
|
||||||
|
import Header from "../Header"
|
||||||
|
import StayList from "../StayList"
|
||||||
|
|
||||||
|
import styles from "./upcoming.module.css"
|
||||||
|
|
||||||
|
import type { LangParams } from "@/types/params"
|
||||||
|
|
||||||
|
export default async function UpcomingStays({ lang }: LangParams) {
|
||||||
|
const stays = await serverClient().user.stays.upcoming()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.container}>
|
||||||
|
<Header
|
||||||
|
title="Your upcoming stays."
|
||||||
|
subtitle="Excited about your next trip? So are we. Below are your upcoming stays
|
||||||
|
with us, complete with all the details you need to make each visit
|
||||||
|
perfect. Can't wait to welcome you back, friend!"
|
||||||
|
></Header>
|
||||||
|
{stays.length ? (
|
||||||
|
<StayList lang={lang} stays={stays} />
|
||||||
|
) : (
|
||||||
|
<EmptyUpcomingStaysBlock />
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.container {
|
||||||
|
max-width: var(--max-width);
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import Link from "next/link"
|
|
||||||
import Stay from "./Stay"
|
|
||||||
import Title from "@/components/MyPages/Title"
|
|
||||||
|
|
||||||
import styles from "./upcoming.module.css"
|
|
||||||
|
|
||||||
import type { LangParams } from "@/types/params"
|
|
||||||
import type { StaysProps } from "@/types/components/myPages/myPage/stays"
|
|
||||||
|
|
||||||
export default function UpcomingStays({
|
|
||||||
lang,
|
|
||||||
stays,
|
|
||||||
}: StaysProps & LangParams) {
|
|
||||||
return (
|
|
||||||
<section className={styles.container}>
|
|
||||||
<header className={styles.header}>
|
|
||||||
<Title level="h2" as="h4" uppercase>
|
|
||||||
Your upcoming stays
|
|
||||||
</Title>
|
|
||||||
<Link className={styles.link} href="#">
|
|
||||||
See all
|
|
||||||
</Link>
|
|
||||||
</header>
|
|
||||||
<section className={styles.stays}>
|
|
||||||
{stays.map((stay) => (
|
|
||||||
<Stay key={stay.hotel} {...stay} lang={lang} />
|
|
||||||
))}
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,11 @@
|
|||||||
import { benefits, myPages, overview, profile, profileEdit } from "./myPages"
|
import {
|
||||||
|
benefits,
|
||||||
|
myPages,
|
||||||
|
overview,
|
||||||
|
profile,
|
||||||
|
profileEdit,
|
||||||
|
stays,
|
||||||
|
} from "./myPages"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These are routes in code we know requires auth
|
* These are routes in code we know requires auth
|
||||||
@@ -11,4 +18,5 @@ export const authRequired = [
|
|||||||
...Object.values(overview),
|
...Object.values(overview),
|
||||||
...Object.values(profile),
|
...Object.values(profile),
|
||||||
...Object.values(profileEdit),
|
...Object.values(profileEdit),
|
||||||
|
...Object.values(stays),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -57,3 +57,13 @@ export const benefits = {
|
|||||||
no: `${myPages.no}/fordeler`,
|
no: `${myPages.no}/fordeler`,
|
||||||
sv: `${myPages.sv}/formaner`,
|
sv: `${myPages.sv}/formaner`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {import('@/types/routes').LangRoute} */
|
||||||
|
export const stays = {
|
||||||
|
da: `${myPages.da}/ophold`,
|
||||||
|
de: `${myPages.de}/aufenthalte`,
|
||||||
|
en: `${myPages.en}/stays`,
|
||||||
|
fi: `${myPages.fi}/oleskeluni`,
|
||||||
|
no: `${myPages.no}/opphold`,
|
||||||
|
sv: `${myPages.sv}/vistelser`,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import * as api from "@/lib/api"
|
import * as api from "@/lib/api"
|
||||||
import { benefits, extendedUser, nextLevelPerks } from "./temp"
|
import {
|
||||||
|
benefits,
|
||||||
|
extendedUser,
|
||||||
|
nextLevelPerks,
|
||||||
|
previousStays,
|
||||||
|
upcomingStays,
|
||||||
|
} from "./temp"
|
||||||
import {
|
import {
|
||||||
badRequestError,
|
badRequestError,
|
||||||
forbiddenError,
|
forbiddenError,
|
||||||
@@ -7,6 +13,8 @@ import {
|
|||||||
unauthorizedError,
|
unauthorizedError,
|
||||||
} from "@/server/errors/trpc"
|
} from "@/server/errors/trpc"
|
||||||
import { protectedProcedure, router } from "@/server/trpc"
|
import { protectedProcedure, router } from "@/server/trpc"
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
import { getUserSchema } from "./output"
|
import { getUserSchema } from "./output"
|
||||||
|
|
||||||
function fakingRequest<T>(payload: T): Promise<T> {
|
function fakingRequest<T>(payload: T): Promise<T> {
|
||||||
@@ -74,4 +82,33 @@ export const userQueryRouter = router({
|
|||||||
return await fakingRequest<typeof nextLevelPerks>(nextLevelPerks)
|
return await fakingRequest<typeof nextLevelPerks>(nextLevelPerks)
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
stays: router({
|
||||||
|
previous: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
perPage: z.number().min(0).default(6),
|
||||||
|
page: z.number().min(0).default(0),
|
||||||
|
})
|
||||||
|
.default({})
|
||||||
|
)
|
||||||
|
.query(async (opts) => {
|
||||||
|
const { perPage, page } = opts.input
|
||||||
|
return previousStays.slice(page * perPage, page * perPage + perPage)
|
||||||
|
}),
|
||||||
|
upcoming: protectedProcedure
|
||||||
|
.input(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
perPage: z.number().min(0).default(6),
|
||||||
|
page: z.number().min(0).default(0),
|
||||||
|
})
|
||||||
|
.default({})
|
||||||
|
)
|
||||||
|
.query(async (opts) => {
|
||||||
|
const { perPage, page } = opts.input
|
||||||
|
return upcomingStays.slice(page * perPage, page * perPage + perPage)
|
||||||
|
}),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -113,31 +113,170 @@ export const shortcuts = [
|
|||||||
// },
|
// },
|
||||||
]
|
]
|
||||||
|
|
||||||
export const stays = [
|
export const previousStays = [
|
||||||
{
|
{
|
||||||
|
uid: "0",
|
||||||
dateArrive: new Date("04 27 2024"),
|
dateArrive: new Date("04 27 2024"),
|
||||||
dateDepart: new Date("04 28 2024"),
|
dateDepart: new Date("04 28 2024"),
|
||||||
guests: 2,
|
guests: 2,
|
||||||
hotel: "Scandic Helsinki Hub",
|
hotel: "Scandic Helsinki Hub",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
uid: "1",
|
||||||
dateArrive: new Date("05 27 2024"),
|
dateArrive: new Date("05 27 2024"),
|
||||||
dateDepart: new Date("05 28 2024"),
|
dateDepart: new Date("05 28 2024"),
|
||||||
guests: 2,
|
guests: 2,
|
||||||
hotel: "Scandic Örebro Central",
|
hotel: "Scandic Örebro Central",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
uid: "2",
|
||||||
dateArrive: new Date("06 27 2024"),
|
dateArrive: new Date("06 27 2024"),
|
||||||
dateDepart: new Date("06 28 2024"),
|
dateDepart: new Date("06 28 2024"),
|
||||||
guests: 2,
|
guests: 2,
|
||||||
hotel: "Scandic Oslo City",
|
hotel: "Scandic Oslo City",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
uid: "3",
|
||||||
|
dateArrive: new Date("04 27 2024"),
|
||||||
|
dateDepart: new Date("04 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Lorem",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "4",
|
||||||
|
dateArrive: new Date("05 27 2024"),
|
||||||
|
dateDepart: new Date("05 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Ipsum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "5",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Dolor Sin Amet",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "6",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Anglais",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "7",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Park",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "8",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Klara",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "9",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Dolor A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "10",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic B",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "11",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic C",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "12",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic D",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "13",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic E",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "14",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic F",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "15",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic G",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const upcomingStays = [
|
||||||
|
{
|
||||||
|
uid: "0",
|
||||||
|
dateArrive: new Date("04 27 2024"),
|
||||||
|
dateDepart: new Date("04 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Helsinki Hub",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "1",
|
||||||
|
dateArrive: new Date("05 27 2024"),
|
||||||
|
dateDepart: new Date("05 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Örebro Central",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "2",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Oslo City",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "3",
|
||||||
|
dateArrive: new Date("04 27 2024"),
|
||||||
|
dateDepart: new Date("04 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Lorem",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "4",
|
||||||
|
dateArrive: new Date("05 27 2024"),
|
||||||
|
dateDepart: new Date("05 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Ipsum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uid: "5",
|
||||||
|
dateArrive: new Date("06 27 2024"),
|
||||||
|
dateDepart: new Date("06 28 2024"),
|
||||||
|
guests: 2,
|
||||||
|
hotel: "Scandic Dolor Sin Amet",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const extendedUser = {
|
export const extendedUser = {
|
||||||
journeys: challenges.journeys,
|
journeys: challenges.journeys,
|
||||||
nights: 14,
|
nights: 14,
|
||||||
shortcuts,
|
shortcuts,
|
||||||
stays,
|
upcomingStays,
|
||||||
victories: challenges.victories,
|
victories: challenges.victories,
|
||||||
}
|
}
|
||||||
|
|||||||
7
types/components/myPages/myPage/stay.ts
Normal file
7
types/components/myPages/myPage/stay.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export type Stay = {
|
||||||
|
uid: string
|
||||||
|
dateArrive: Date
|
||||||
|
dateDepart: Date
|
||||||
|
guests: number
|
||||||
|
hotel: string
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import type { Stay, User } from "@/types/user"
|
|
||||||
|
|
||||||
export type StaysProps = {
|
|
||||||
stays: User["stays"]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type StayProps = Stay
|
|
||||||
8
types/components/myPages/stays/stayCard.ts
Normal file
8
types/components/myPages/stays/stayCard.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Stay } from "../myPage/stay"
|
||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
|
||||||
|
export type StayCardProps = {
|
||||||
|
lang: Lang
|
||||||
|
showDayCount?: boolean
|
||||||
|
stay: Stay
|
||||||
|
}
|
||||||
7
types/components/myPages/stays/stayList.ts
Normal file
7
types/components/myPages/stays/stayList.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Lang } from "@/constants/languages"
|
||||||
|
import { Stay } from "../myPage/stay"
|
||||||
|
|
||||||
|
export type StayListProps = {
|
||||||
|
stays: Stay[]
|
||||||
|
lang: Lang
|
||||||
|
}
|
||||||
4
types/components/myPages/stays/title.ts
Normal file
4
types/components/myPages/stays/title.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export type HeaderProps = {
|
||||||
|
title: string
|
||||||
|
subtitle: string
|
||||||
|
}
|
||||||
@@ -12,13 +12,6 @@ type ShortcutLink = {
|
|||||||
title: string
|
title: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Stay = {
|
|
||||||
dateArrive: Date
|
|
||||||
dateDepart: Date
|
|
||||||
guests: number
|
|
||||||
hotel: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Victory = {
|
type Victory = {
|
||||||
tag: string
|
tag: string
|
||||||
title: string
|
title: string
|
||||||
@@ -33,6 +26,5 @@ export interface User extends z.infer<typeof getUserSchema> {
|
|||||||
journeys: Journey[]
|
journeys: Journey[]
|
||||||
nights: number
|
nights: number
|
||||||
shortcuts: ShortcutLink[]
|
shortcuts: ShortcutLink[]
|
||||||
stays: Stay[]
|
|
||||||
victories: Victory[]
|
victories: Victory[]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user