Skip to content

Commit

Permalink
Merge pull request #103 from newnivers/feat/#96
Browse files Browse the repository at this point in the history
feat(domain/detail): 예약 모달 UI 작업
  • Loading branch information
JSH-data authored Jan 18, 2024
2 parents d5ab674 + 4806364 commit 534a00d
Show file tree
Hide file tree
Showing 11 changed files with 571 additions and 21 deletions.
3 changes: 1 addition & 2 deletions components/common/button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ const ButtonText = styled(Text)<{ sort: ButtonSort }>`
const { colors } = theme;
return css`
font-weight: 700;
line-height: 24px;
${theme.typoToken.subhead02}
letter-spacing: 0.5px;
color: ${sort === "primary" ? colors.white : colors.primary_01};
`;
Expand Down
2 changes: 2 additions & 0 deletions components/common/modal/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from "./modal.default";
export * from "./modal.portal";
export * from "./modal.confirm";
export * from "./modal.reservation";
102 changes: 102 additions & 0 deletions components/common/modal/modal.confirm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { useEffect, type ReactNode } from "react";
import Image from "next/image";
import styled, { css } from "styled-components";
import Button from "@/components/common/button";
import { SpacerSkleton } from "@/components/common/spacer";
import Typography from "@/components/common/text/Typography";

interface ConfirmModalProps {
isShow: boolean;
onClose: () => void;
onConfirm: () => void;
title: string;
children: ReactNode;
}

export function ConfirmModal({
isShow,
onClose,
onConfirm,
title,
children,
}: ConfirmModalProps) {
useEffect(() => {
if (isShow) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}

return () => {
document.body.style.overflow = "";
};
}, [isShow]);

if (!isShow) {
return null;
}

return (
<Backdrop>
<Content onClick={(e) => e.stopPropagation()}>
<SpacerSkleton justify="flex-end">
<button onClick={onClose}>
<Image
src="/icon/default-close.svg"
width={24}
height={24}
alt="default-close"
/>
</button>
</SpacerSkleton>
<ModalTitle typo="subhead02">{title}</ModalTitle>
<Typography typo="body03">{children}</Typography>
<ConfirmButton onClick={onConfirm}>확인</ConfirmButton>
</Content>
</Backdrop>
);
}

const ModalTitle = styled(Typography)`
margin-top: 0.5rem;
margin-bottom: 1.4rem;
`;

const Backdrop = styled.div`
${({ theme }) => {
const { colors } = theme;
return css`
z-index: 9999;
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background-color: ${colors.secondary["black-50"]};
`;
}}
`;

const ConfirmButton = styled(Button)`
margin-top: 2rem;
`;

const Content = styled.div`
${({ theme }) => {
const { colors } = theme;
return css`
display: flex;
flex-direction: column;
padding: 1.5rem 2rem;
border-radius: 12px;
background-color: ${colors.secondary.white};
box-shadow: 0 5px 15px ${colors.secondary["black-30"]};
text-align: center;
`;
}}
`;
7 changes: 6 additions & 1 deletion components/domains/detail/RowInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import dayjs from "dayjs";
import styled from "styled-components";
import Typography from "@/components/common/text/Typography";

Expand All @@ -12,6 +13,8 @@ export interface RowInfoData {
}

export default function RowInfo({ infoData }: { infoData: RowInfoData }) {
console.log(dayjs(infoData?.startDate).format("YYYY-MM-DD"));

return (
<Container>
<KeyWrapper>
Expand All @@ -24,7 +27,9 @@ export default function RowInfo({ infoData }: { infoData: RowInfoData }) {
</KeyWrapper>
<TextWrapper>
<Text typo="body02">{infoData.place}</Text>
<Text typo="body02">{`${infoData.startDate}~${infoData.endDate}`}</Text>
<Text typo="body02">{`${dayjs(infoData?.startDate).format(
"YYYY-MM-DD"
)}~${dayjs(infoData?.endDate).format("YYYY-MM-DD")}`}</Text>
<Text typo="body02">{`만 ${infoData.ageLimit}세 이상`}</Text>
<Text typo="body02">{`총 ${infoData.runningTime}분`}</Text>
<Text typo="body02">{`${infoData.interMission}분`}</Text>
Expand Down
34 changes: 22 additions & 12 deletions components/domains/detail/calendar/DaySchedules.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
import type { Dispatch, SetStateAction } from "react";
import format from "date-fns/format";
import dayjs from "dayjs";
import styled from "styled-components";
import Typography from "@/components/common/text/Typography";
import type { Schedule } from "./type";

type DaySchedulesProps = {
schedules: Schedule[];
onClickSchedule: Dispatch<SetStateAction<number | null>>;
selectedDate: Date | null;
clickedId: number | null;
};

export default function DaySchedules({
schedules,
selectedDate,
onClickSchedule,
clickedId,
}: DaySchedulesProps) {
return (
<GridContainer>
{schedules.map((schedule, index) => {
return (
<AvailableSchedule key={schedule.id}>
<Typography typo="body02">
{`${index + 1}회 | ${format(
new Date(schedule.startAt),
"HH:mm"
)}`}
</Typography>
</AvailableSchedule>
);
})}
{schedules
.filter((schedule) => {
return (
dayjs(schedule.startAt).startOf("day").valueOf() ===
selectedDate?.valueOf()
);
})
.map((schedule, index) => {
return (
<AvailableSchedule key={schedule.id}>
<Typography typo="body02">
{`${index + 1}회 | ${format(
new Date(schedule.startAt),
"HH:mm"
)}`}
</Typography>
</AvailableSchedule>
);
})}
</GridContainer>
);
}
Expand Down
64 changes: 58 additions & 6 deletions components/domains/detail/calendar/ReservationCalendar.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,101 @@
import { useState } from "react";
import styled, { css } from "styled-components";
import styled from "styled-components";
import Button from "@/components/common/button";
import { CustomCalendar } from "@/components/common/calendar";
import { ConfirmModal } from "@/components/common/modal";
import Typography from "@/components/common/text/Typography";
import CustomHeader from "@/components/domains/detail/calendar/CustomHeader";
import DaySchedules from "@/components/domains/detail/calendar/DaySchedules";
import ReservedSeat from "@/components/domains/detail/calendar/ResearvedSeat";
import type { ReservationCalendarProps } from "@/components/domains/detail/calendar/type";
import type {
ReservationCalendarProps,
Schedule,
} from "@/components/domains/detail/calendar/type";
import { ReservationModal } from "@/components/domains/detail/reservation/ReservationModal";
import ReservationNotice from "@/components/domains/detail/reservation/ReservationNotice";

type ReserveModalCase = "NOTICE" | "COMPLETION" | "RESERVATION" | null;

const convertScheduleData = (schedules: Schedule[]) => {
return schedules.map((schedule) => {
return new Date(schedule.startAt);
});
};

export default function ReservationCalendar({
schedules,
schedules = [],
}: ReservationCalendarProps) {
// TODO: Link Calendar Function

const [date, setDate] = useState<Date | null>(null);
const [clickedScheduleId, setClickedScheduleId] = useState<number | null>(
null
);
const [availableSeatCount, setAvailableSeatCount] = useState<null | number>(
null
);
const [modalStatus, setModalStatus] =
useState<ReserveModalCase>("RESERVATION");

const onChangeDate = (date: Date | null) => {
setDate(date);
};

const closeModal = () => {
setModalStatus(null);
};

return (
<Container>
<Wrapper>
<Header typo="subhead03">날짜/시간 선택</Header>
<CustomCalendar
includeDates={convertScheduleData(schedules)}
renderCustomHeader={(headerProps) => (
<CustomHeader headerProps={headerProps} />
)}
selected={date}
onChangeDate={onChangeDate}
/>
<DaySchedules
selectedDate={date}
schedules={schedules}
onClickSchedule={setClickedScheduleId}
clickedId={clickedScheduleId}
/>
<ReservedSeat seatCount={100} />
</Wrapper>
<ReserveButton>예매하기</ReserveButton>
<ReserveButton
onClick={() => {
setModalStatus("NOTICE");
}}
>
예매하기
</ReserveButton>
<ConfirmModal
isShow={modalStatus === "NOTICE"}
title={"예매 공지 확인"}
onClose={closeModal}
onConfirm={() => {
setModalStatus("RESERVATION");
}}
>
<ReservationNotice />
</ConfirmModal>
<ConfirmModal
isShow={modalStatus === "COMPLETION"}
title={"결재 완료"}
onClose={closeModal}
onConfirm={closeModal}
>
티켓 구매가 완료되었습니다.
<br /> 구매하신 티켓은 MY PAGE에서 확인 가능합니다.
</ConfirmModal>
<ReservationModal
isShow={modalStatus === "RESERVATION"}
onReserve={() => {
setModalStatus("COMPLETION");
}}
onClose={closeModal}
/>
</Container>
);
}
Expand Down
Loading

0 comments on commit 534a00d

Please sign in to comment.