Merged in feat/SW-1676-modify-contact-details-my-stay-anonymous (pull request #1468)

Feat/SW-1676 modify contact details my stay anonymous

* feat(SW-1676): Modify guest details step 1

* feat(SW-1676) Integration to api to update guest details

* feat(SW-1676) Reuse of old modal

* feat(SW-1676) updated modify guest

* feat(SW-1676) cleanup

* feat(SW-1676) updated myStayReturnRoute to sessionStorage


Approved-by: Niclas Edenvin
This commit is contained in:
Pontus Dreij
2025-03-07 13:41:25 +00:00
parent 2c7d72c540
commit 2509794d0c
33 changed files with 528 additions and 251 deletions

View File

@@ -30,14 +30,14 @@ import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmat
interface ActionPanelProps {
booking: BookingConfirmation["booking"]
hotel: Hotel
showCancelButton: boolean
showCancelStayButton: boolean
onCancelClick: () => void
}
export default function ActionPanel({
booking,
hotel,
showCancelButton,
showCancelStayButton,
onCancelClick,
}: ActionPanelProps) {
const intl = useIntl()
@@ -67,7 +67,7 @@ export default function ActionPanel({
<div className={styles.menu}>
<Button
variant="icon"
onClick={onCancelClick}
onClick={() => {}}
intent="text"
className={styles.button}
>
@@ -76,7 +76,7 @@ export default function ActionPanel({
</Button>
<Button
variant="icon"
onClick={onCancelClick}
onClick={() => {}}
intent="text"
className={styles.button}
>
@@ -91,14 +91,14 @@ export default function ActionPanel({
/>
<Button
variant="icon"
onClick={onCancelClick}
onClick={() => {}}
intent="text"
className={styles.button}
>
{intl.formatMessage({ id: "Download invoice" })}
<DownloadIcon width={24} height={24} color="burgundy" />
</Button>
{showCancelButton && (
{showCancelStayButton && (
<Button
variant="icon"
onClick={onCancelClick}
@@ -119,7 +119,7 @@ export default function ActionPanel({
{booking.confirmationNumber}
</Subtitle>
</div>
<div className={styles.hotel}>
<div>
<Body color="uiTextHighContrast" textAlign="right">
{hotel.name}
</Body>

View File

@@ -1,67 +0,0 @@
import { CloseLargeIcon } from "@/components/Icons"
import Button from "@/components/TempDesignSystem/Button"
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
import styles from "./modalContent.module.css"
import type { ReactNode } from "react"
interface ModalContentProps {
title: string
content: ReactNode
primaryAction: {
label: string
onClick: () => void
intent?: "primary" | "secondary" | "text"
isLoading?: boolean
disabled?: boolean
} | null
secondaryAction: {
label: string
onClick: () => void
intent?: "primary" | "secondary" | "text"
} | null
onClose: () => void
}
export function ModalContent({
title,
content,
primaryAction,
secondaryAction,
onClose,
}: ModalContentProps) {
return (
<>
<header className={styles.header}>
<Subtitle color="uiTextHighContrast">{title}</Subtitle>
<button onClick={onClose} type="button" className={styles.close}>
<CloseLargeIcon color="uiTextMediumContrast" />
</button>
</header>
<div className={styles.content}>{content}</div>
<footer className={styles.footer}>
{secondaryAction && (
<Button
theme="base"
intent={secondaryAction.intent ?? "text"}
color="burgundy"
onClick={secondaryAction.onClick}
>
{secondaryAction.label}
</Button>
)}
{primaryAction && (
<Button
theme="base"
intent={primaryAction.intent ?? "secondary"}
onClick={primaryAction.onClick}
disabled={primaryAction.isLoading || primaryAction.disabled}
>
{primaryAction.label}
</Button>
)}
</footer>
</>
)
}

View File

@@ -1,36 +0,0 @@
.content {
width: 640px;
max-width: 100%;
display: flex;
flex-direction: column;
gap: var(--Spacing-x3);
padding: var(--Spacing-x1) var(--Spacing-x3) var(--Spacing-x4);
max-height: 70vh;
overflow-y: auto;
}
.header {
position: relative;
padding: var(--Spacing-x3) var(--Spacing-x3) 0;
}
.footer {
display: flex;
justify-content: space-between;
width: 100%;
border-top: 1px solid var(--Base-Border-Subtle);
padding: var(--Spacing-x3);
}
.close {
background: none;
border: none;
cursor: pointer;
position: absolute;
display: flex;
align-items: center;
padding: 0;
justify-content: center;
top: 20px;
right: 20px;
}

View File

@@ -1,24 +1,17 @@
"use client"
import { motion } from "framer-motion"
import { useEffect, useState } from "react"
import { Dialog, Modal, ModalOverlay } from "react-aria-components"
import { useState } from "react"
import { useIntl } from "react-intl"
import { BookingStatusEnum } from "@/constants/booking"
import { ChevronDownIcon } from "@/components/Icons"
import {
type AnimationState,
AnimationStateEnum,
} from "@/components/Modal/modal"
import { slideFromTop } from "@/components/Modal/motionVariants"
import Modal from "@/components/Modal"
import Button from "@/components/TempDesignSystem/Button"
import CancelStay from "../CancelStay"
import ActionPanel from "./ActionPanel"
import styles from "./modifyModal.module.css"
import type { Hotel } from "@/types/hotel"
import type { BookingConfirmation } from "@/types/trpc/routers/booking/confirmation"
@@ -38,39 +31,14 @@ export default function ManageStay({
bookingStatus,
}: ManageStayProps) {
const [isOpen, setIsOpen] = useState(false)
const [animation, setAnimation] = useState<AnimationState>(
AnimationStateEnum.visible
)
const [activeView, setActiveView] = useState<ActiveView>("actionPanel")
const intl = useIntl()
const MotionOverlay = motion(ModalOverlay)
const MotionModal = motion(Modal)
const showCancelButton =
const showCancelStayButton =
bookingStatus !== BookingStatusEnum.Cancelled && booking.isCancelable
useEffect(() => {
if (typeof isOpen === "boolean") {
setAnimation(
isOpen ? AnimationStateEnum.visible : AnimationStateEnum.hidden
)
}
if (isOpen === undefined) {
setAnimation(AnimationStateEnum.unmounted)
}
}, [isOpen])
function modalStateHandler(newAnimationState: AnimationState) {
setAnimation((currentAnimationState) =>
newAnimationState === AnimationStateEnum.hidden &&
currentAnimationState === AnimationStateEnum.hidden
? AnimationStateEnum.unmounted
: currentAnimationState
)
}
function handleClose() {
setIsOpen(false)
setActiveView("actionPanel")
@@ -99,7 +67,7 @@ export default function ManageStay({
booking={booking}
hotel={hotel}
onCancelClick={() => setActiveView("cancelStay")}
showCancelButton={showCancelButton}
showCancelStayButton={showCancelStayButton}
/>
)
}
@@ -111,28 +79,9 @@ export default function ManageStay({
{intl.formatMessage({ id: "Manage stay" })}
<ChevronDownIcon width={24} height={24} color="burgundy" />
</Button>
<MotionOverlay
isOpen={isOpen}
className={styles.overlay}
initial={"hidden"}
onAnimationComplete={modalStateHandler}
onOpenChange={handleClose}
isDismissable
>
<MotionModal
className={styles.modal}
initial={"hidden"}
animate={animation}
variants={slideFromTop}
>
<Dialog
className={styles.dialog}
aria-label={intl.formatMessage({ id: "Dialog" })}
>
{renderContent()}
</Dialog>
</MotionModal>
</MotionOverlay>
<Modal isOpen={isOpen} onToggle={handleClose} withActions hideHeader>
{renderContent()}
</Modal>
</>
)
}

View File

@@ -1,62 +0,0 @@
.overlay {
background: rgba(0, 0, 0, 0.5);
height: var(--visual-viewport-height);
position: fixed;
top: 0;
left: 0;
width: 100vw;
z-index: var(--default-modal-overlay-z-index);
}
.modal {
background-color: var(--Base-Surface-Primary-light-Normal);
border-radius: var(--Corner-radius-Medium) var(--Corner-radius-Medium) 0 0;
box-shadow: var(--modal-box-shadow);
width: 100%;
position: absolute;
left: 0;
bottom: 0;
z-index: var(--default-modal-z-index);
}
.dialog {
display: flex;
flex-direction: column;
/* For removing focus outline when modal opens first time */
outline: 0 none;
/* for supporting animations within content */
position: relative;
overflow: hidden;
}
.close {
background: none;
border: none;
cursor: pointer;
position: absolute;
right: var(--Spacing-x2);
width: var(--button-dimension);
height: var(--button-dimension);
display: flex;
align-items: center;
padding: 0;
justify-content: center;
}
@media screen and (min-width: 768px) {
.overlay {
display: flex;
justify-content: center;
align-items: center;
}
.modal {
left: auto;
bottom: auto;
width: auto;
border-radius: var(--Corner-radius-Medium);
max-width: var(--max-width-page);
}
}