diff --git a/public/svgs/Icon_checkbox_selected_on.svg b/public/svgs/Icon_checkbox_selected_on.svg new file mode 100644 index 00000000..69f4fe65 --- /dev/null +++ b/public/svgs/Icon_checkbox_selected_on.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/svgs/Icon_checkbox_unselected_on.svg b/public/svgs/Icon_checkbox_unselected_on.svg new file mode 100644 index 00000000..0bbb7e17 --- /dev/null +++ b/public/svgs/Icon_checkbox_unselected_on.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/svgs/icon_chevron_back.svg b/public/svgs/icon_chevron_back.svg new file mode 100644 index 00000000..0da9be5d --- /dev/null +++ b/public/svgs/icon_chevron_back.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/svgs/icon_toggle_off.svg b/public/svgs/icon_toggle_off.svg new file mode 100644 index 00000000..4a4197b0 --- /dev/null +++ b/public/svgs/icon_toggle_off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/svgs/icon_toggle_on.svg b/public/svgs/icon_toggle_on.svg new file mode 100644 index 00000000..c23c6c7d --- /dev/null +++ b/public/svgs/icon_toggle_on.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/svgs/IconCheckboxSelectedOn.tsx b/src/assets/svgs/IconCheckboxSelectedOn.tsx new file mode 100644 index 00000000..f026c681 --- /dev/null +++ b/src/assets/svgs/IconCheckboxSelectedOn.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; +import type { SVGProps } from "react"; +const SvgIconCheckboxSelectedOn = (props: SVGProps) => ( + + + + + +); +export default SvgIconCheckboxSelectedOn; diff --git a/src/assets/svgs/IconCheckboxUnselectedOn.tsx b/src/assets/svgs/IconCheckboxUnselectedOn.tsx new file mode 100644 index 00000000..746b0984 --- /dev/null +++ b/src/assets/svgs/IconCheckboxUnselectedOn.tsx @@ -0,0 +1,8 @@ +import * as React from "react"; +import type { SVGProps } from "react"; +const SvgIconCheckboxUnselectedOn = (props: SVGProps) => ( + + + +); +export default SvgIconCheckboxUnselectedOn; diff --git a/src/assets/svgs/IconChevronBack.tsx b/src/assets/svgs/IconChevronBack.tsx new file mode 100644 index 00000000..adc09dff --- /dev/null +++ b/src/assets/svgs/IconChevronBack.tsx @@ -0,0 +1,13 @@ +import * as React from "react"; +import type { SVGProps } from "react"; +const SvgIconChevronBack = (props: SVGProps) => ( + + + +); +export default SvgIconChevronBack; diff --git a/src/assets/svgs/IconToggleOff.tsx b/src/assets/svgs/IconToggleOff.tsx new file mode 100644 index 00000000..d584f216 --- /dev/null +++ b/src/assets/svgs/IconToggleOff.tsx @@ -0,0 +1,9 @@ +import * as React from "react"; +import type { SVGProps } from "react"; +const SvgIconToggleOff = (props: SVGProps) => ( + + + + +); +export default SvgIconToggleOff; diff --git a/src/assets/svgs/IconToggleOn.tsx b/src/assets/svgs/IconToggleOn.tsx new file mode 100644 index 00000000..1f6d2ca4 --- /dev/null +++ b/src/assets/svgs/IconToggleOn.tsx @@ -0,0 +1,9 @@ +import * as React from "react"; +import type { SVGProps } from "react"; +const SvgIconToggleOn = (props: SVGProps) => ( + + + + +); +export default SvgIconToggleOn; diff --git a/src/assets/svgs/index.tsx b/src/assets/svgs/index.tsx index a43d9502..fe2f3d96 100644 --- a/src/assets/svgs/index.tsx +++ b/src/assets/svgs/index.tsx @@ -8,6 +8,9 @@ export { default as IconArrowUp } from "./IconArrowUp"; export { default as IconBnk } from "./IconBnk"; export { default as IconCalendar } from "./IconCalendar"; export { default as IconCheck } from "./IconCheck"; +export { default as IconCheckboxSelectedOn } from "./IconCheckboxSelectedOn"; +export { default as IconCheckboxUnselectedOn } from "./IconCheckboxUnselectedOn"; +export { default as IconChevronBack } from "./IconChevronBack"; export { default as IconEyeOff } from "./IconEyeOff"; export { default as IconEyeOn } from "./IconEyeOn"; export { default as IconHanna } from "./IconHanna"; @@ -26,6 +29,8 @@ export { default as IconShinhyup } from "./IconShinhyup"; export { default as IconSoohyup } from "./IconSoohyup"; export { default as IconTextfiedlDelete } from "./IconTextfiedlDelete"; export { default as IconTime } from "./IconTime"; +export { default as IconToggleOff } from "./IconToggleOff"; +export { default as IconToggleOn } from "./IconToggleOn"; export { default as IconToss } from "./IconToss"; export { default as IconWoochaegook } from "./IconWoochaegook"; export { default as IconWoori } from "./IconWoori"; diff --git a/src/pages/MyRegisterdShow/MyRegisterdShow.tsx b/src/pages/MyRegisterdShow/MyRegisterdShow.tsx index 14da99c4..99dc86a7 100644 --- a/src/pages/MyRegisterdShow/MyRegisterdShow.tsx +++ b/src/pages/MyRegisterdShow/MyRegisterdShow.tsx @@ -5,7 +5,7 @@ import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import bannerNarrow from "../../assets/images/banner_narrow.png"; import * as S from "./MyRegisterdShow.styled"; -import RegisteredCard from "./components/RegisteredCard"; +import RegisteredCard from "./components/registeredcard/RegisteredCard"; import { MY_REGISTERED_SHOW, RegisteredObjProps } from "./constants/myRegisterShow"; const MyRegisterdShow = () => { diff --git a/src/pages/MyRegisterdShow/components/RegisteredCard.styled.ts b/src/pages/MyRegisterdShow/components/registeredcard/RegisteredCard.styled.ts similarity index 100% rename from src/pages/MyRegisterdShow/components/RegisteredCard.styled.ts rename to src/pages/MyRegisterdShow/components/registeredcard/RegisteredCard.styled.ts diff --git a/src/pages/MyRegisterdShow/components/RegisteredCard.tsx b/src/pages/MyRegisterdShow/components/registeredcard/RegisteredCard.tsx similarity index 93% rename from src/pages/MyRegisterdShow/components/RegisteredCard.tsx rename to src/pages/MyRegisterdShow/components/registeredcard/RegisteredCard.tsx index ac4655bc..a685957d 100644 --- a/src/pages/MyRegisterdShow/components/RegisteredCard.tsx +++ b/src/pages/MyRegisterdShow/components/registeredcard/RegisteredCard.tsx @@ -1,5 +1,5 @@ import Button from "@components/commons/button/Button"; -import { RegisteredObjProps } from "../constants/myRegisterShow"; +import { RegisteredObjProps } from "../../constants/myRegisterShow"; import * as S from "./RegisteredCard.styled"; const RegisteredCard = ({ title, period, genre, image }: Omit) => { diff --git a/src/pages/MyRegisterdShow/hooks/.gitkeep b/src/pages/MyRegisterdShow/hooks/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/pages/MyRegisterdShow/types/.gitkeep b/src/pages/MyRegisterdShow/types/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/pages/MyRegisterdShow/utils/.gitkeep b/src/pages/MyRegisterdShow/utils/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/src/pages/ticketholderlist/TicketHolderList.styled.ts b/src/pages/ticketholderlist/TicketHolderList.styled.ts new file mode 100644 index 00000000..e23be586 --- /dev/null +++ b/src/pages/ticketholderlist/TicketHolderList.styled.ts @@ -0,0 +1,71 @@ +import { IconToggleOff, IconToggleOn } from "@assets/svgs"; +import styled from "styled-components"; + +export const BodyWrapper = styled.div` + display: flex; + align-items: flex-start; + justify-content: center; + width: 37.4rem; + height: auto; + min-height: 60.8rem; /* 60.8rem(body의 높이) + 5.6rem(버튼의 높이) */ + margin-bottom: 10.4rem; + padding: 2.4rem; +`; + +export const BodyLayout = styled.section` + display: flex; + flex: 1 0 0; + flex-direction: column; + gap: 2.4rem; + align-items: flex-start; +`; + +export const LayoutHeaderBox = styled.div` + display: flex; + gap: 3.2rem; + align-items: flex-end; + align-self: stretch; +`; + +export const LayoutFilterBox = styled.div` + display: flex; + gap: 0.6rem; + align-items: center; +`; + +export const ToggleWrapper = styled.div` + display: flex; + gap: 0.4rem; + align-items: center; +`; + +export const ToggleText = styled.span` + color: ${({ theme }) => theme.colors.gray_400}; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; +`; + +export const ToggleOnIcon = styled(IconToggleOn)<{ $width: string; $height: string }>` + width: ${({ $width }) => $width}; + height: ${({ $height }) => $height}; +`; + +export const ToggleOffIcon = styled(IconToggleOff)<{ $width: string; $height: string }>` + width: ${({ $width }) => $width}; + height: ${({ $height }) => $height}; +`; + +export const FooterButtonWrapper = styled.div` + position: fixed; + bottom: 0; + left: 50%; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 37.4rem; + height: 10.4rem; + padding: 2.4rem; + + background-color: ${({ theme }) => theme.colors.gray_900}; + transform: translate(-50%, 0); +`; diff --git a/src/pages/ticketholderlist/TicketHolderList.tsx b/src/pages/ticketholderlist/TicketHolderList.tsx new file mode 100644 index 00000000..c72fbac4 --- /dev/null +++ b/src/pages/ticketholderlist/TicketHolderList.tsx @@ -0,0 +1,132 @@ +import Button from "@components/commons/button/Button"; +import { useEffect, useState } from "react"; +import Banner from "./components/banner/Banner"; +import ManagerCard from "./components/managercard/ManagerCard"; +import NarrowDropDown from "./components/narrowDropDown/NarrowDropDown"; +import eximg from "./constants/silkagel.png"; +import { BookingListProps, RESPONSE_TICKETHOLDER } from "./constants/ticketholderlist"; +import * as S from "./TicketHolderList.styled"; + +const TicketHolderList = () => { + const [reservedCount, setReservedCount] = useState(0); + //이거 판매 완료되었는지 여부에 따라서 렌더링하는거 다르게 할지 물어보기, 색깔도 어떻게 할 지 물어보기 + const [isOutdated, setIsOutdated] = useState(false); + const [detail, setDetail] = useState(false); + + // 0, undefined 일 때는 전체 렌더링 (필터링을 위한 state들) + const [schedule, setSchedule] = useState(0); //1,2,3 에 따라 필터링 + const [payment, setPayment] = useState(undefined); + const [responseData, setResponseData] = useState( + RESPONSE_TICKETHOLDER.data.bookingList + ); + + const handleToggleButton = () => { + setDetail((prop) => !prop); + }; + + const count = RESPONSE_TICKETHOLDER.data.totalScheduleCount; //나중에 api로 받아와서 반영해야함. state로 바꿀 필요 있을까? + + const filteredData = responseData.filter((obj) => { + const isScheduleMatched = + schedule === 0 || + (obj.scheduleNumber === "FIRST" && schedule === 1) || + (obj.scheduleNumber === "SECOND" && schedule === 2) || + (obj.scheduleNumber === "THIRD" && schedule === 3); + const isPaymentMatched = payment === undefined || obj.isPaymentCompleted === payment; + + return isScheduleMatched && isPaymentMatched; + }); + //도영이가 axios 사용하면 useEffect 필요없다고 했는데, 나중에 리팩토링 할 수도 있음. + useEffect(() => { + const totalCount = filteredData.reduce( + (totalSum, obj) => obj.purchaseTicketCount + totalSum, + 0 + ); + setReservedCount(totalCount); + //그리고 여기서 바로 다시 axios 요청 쏘는 로직 구성해두기 + }, [filteredData]); + //이해하기 어려울 것 같아 주석 남깁니다. 모든 회차, 입금 상태 2가지 필터를 사용하여 원하는 결과만 가져오는 형식입니다. + //schedule ===0 일 경우는 전체 회차, payment === undefined 일 경우는 전체 입금 여부(입금했든 안했든 렌더링)을 의미합니다. + + //상위 컴포넌트에서 받아온 set함수와 bookingId를 이용하여 현재 오브젝트(state)의 payment 상태를 바꾸도록 한다. + const handlePaymentToggle = (bookingId: number) => { + setResponseData((arr) => + arr.map((item) => + item.bookingId === bookingId + ? { ...item, isPaymentCompleted: !item.isPaymentCompleted } + : item + ) + ); + }; + return ( + <> + + + + + + {/*set 함수 직접 넘기는 거 안좋다고 했지만, 내부에서 감싸야 하므로 넘김 */} + + 모든 회차 + + + 입금 상태 + + + + {detail ? ( + <> + 자세히 + + + ) : ( + <> + 간략히 + + + )} + + + {filteredData.map((obj, index) => ( + handlePaymentToggle(obj.bookingId)} + bookername={obj.bookerName} + purchaseTicketeCount={obj.purchaseTicketCount} + scheduleNumber={obj.scheduleNumber} + bookerPhoneNumber={obj.bookerPhoneNumber} + createAt={obj.createdAt} + /> + ))} + + + + + + + + ); +}; + +export default TicketHolderList; diff --git a/src/pages/ticketholderlist/components/banner/Banner.styled.ts b/src/pages/ticketholderlist/components/banner/Banner.styled.ts new file mode 100644 index 00000000..bb682d5e --- /dev/null +++ b/src/pages/ticketholderlist/components/banner/Banner.styled.ts @@ -0,0 +1,45 @@ +import styled from "styled-components"; + +export const BannerWrapper = styled.div<{ $image: string }>` + width: 37.5rem; + height: 7.2rem; + + /* 사용자가 입력한 이미지 background 적당히 잘라서 사용하도록 */ + background-image: url(${({ $image }) => $image}); + background-repeat: no-repeat; + background-position: center; + background-size: cover; +`; + +export const BannerTextLayout = styled.div` + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 37.5rem; + height: 7.2rem; + + background: rgb(0 0 0 / 60%); +`; + +export const BannerTextBox = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + width: 32.7rem; +`; + +export const BannerTitleText = styled.span` + color: ${({ theme }) => theme.colors.white}; + ${({ theme }) => theme.fonts.heading4}; +`; + +export const BannerStateTextBox = styled.span` + color: ${({ theme }) => theme.colors.white}; + ${({ theme }) => theme.fonts["body1-normal-semi"]}; +`; + +export const CountTextSpan = styled.span` + color: ${({ theme }) => theme.colors.pink_400}; + ${({ theme }) => theme.fonts["body1-normal-semi"]}; +`; diff --git a/src/pages/ticketholderlist/components/banner/Banner.tsx b/src/pages/ticketholderlist/components/banner/Banner.tsx new file mode 100644 index 00000000..0189d61d --- /dev/null +++ b/src/pages/ticketholderlist/components/banner/Banner.tsx @@ -0,0 +1,34 @@ +import * as S from "./Banner.styled"; + +interface BannerProps { + image: string; + reservedCount: number; + isOutdated: boolean; +} + +const Banner = ({ image, reservedCount, isOutdated }: BannerProps) => { + return ( + + + + + 실리카겔 락앤롤 + {isOutdated ? ( + + 총 {reservedCount}매 + 판매 됨 + + ) : ( + + 총 {reservedCount}매 + 예매됨 + + )} + + + + + ); +}; + +export default Banner; diff --git a/src/pages/ticketholderlist/components/managercard/ManagerCard.styled.ts b/src/pages/ticketholderlist/components/managercard/ManagerCard.styled.ts new file mode 100644 index 00000000..d6fa26ec --- /dev/null +++ b/src/pages/ticketholderlist/components/managercard/ManagerCard.styled.ts @@ -0,0 +1,97 @@ +import { IconCheckboxSelectedOn, IconCheckboxUnselectedOn } from "@assets/svgs"; +import styled from "styled-components"; + +export const ManagerCardWrapper = styled.article<{ $isDetail: boolean }>` + display: flex; + flex-shrink: 0; + align-items: flex-start; + justify-content: center; + width: 32.6rem; + height: ${({ $isDetail }) => ($isDetail ? "14.6rem" : "7.4rem")}; +`; + +export const ManagerCardLayout = styled.div<{ $isDetail: boolean }>` + display: flex; + flex-direction: column; + flex-shrink: 0; + gap: 1.6rem; + align-items: flex-start; + width: 25.2rem; + height: ${({ $isDetail }) => ($isDetail ? "14.6rem" : "7.4rem")}; + padding: 1.6rem; + + background-color: ${({ theme }) => theme.colors.gray_800}; + border-right: 1px solid ${({ theme }) => theme.colors.black}; + border-radius: 6px; +`; + +export const ManagerCardBox = styled.div` + display: flex; + flex-direction: column; + gap: 0.4rem; + align-items: flex-start; + align-self: stretch; +`; + +export const ManagerCardTextBox = styled.div` + display: flex; + gap: 2.2rem; + align-items: center; + justify-content: space-between; +`; + +export const ManagerCardTextTitle = styled.span` + width: 3.7rem; + + color: ${({ theme }) => theme.colors.gray_400}; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; +`; + +export const ManagerCardTextContent = styled.span` + width: 17.3rem; + + color: ${({ theme }) => theme.colors.white}; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; +`; + +export const ManagerCardRadioLayout = styled.div<{ $isDetail: boolean; $isPaid: boolean }>` + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 7.4rem; + height: ${({ $isDetail }) => ($isDetail ? "14.6rem" : "7.4rem")}; + padding: 1.5rem 1.5rem 1.5rem 1.6rem; + + background-color: ${({ theme, $isPaid }) => + $isPaid ? theme.colors.pink_600 : theme.colors.gray_800}; + border-radius: 6px; +`; + +export const ManagerCardRadioBox = styled.div` + display: flex; + flex-direction: column; + flex-shrink: 0; + gap: 1rem; + align-items: center; + width: 4.3rem; + + cursor: pointer; +`; + +export const SelectedIcon = styled(IconCheckboxSelectedOn)` + width: 1.8rem; + height: 1.8rem; +`; + +export const UnselectedIcon = styled(IconCheckboxUnselectedOn)` + width: 1.8rem; + height: 1.8rem; +`; + +export const ManagerCardRadioText = styled.span` + color: ${({ theme }) => theme.colors.white}; + text-align: center; + + ${({ theme }) => theme.fonts["caption1-semi"]}; +`; diff --git a/src/pages/ticketholderlist/components/managercard/ManagerCard.tsx b/src/pages/ticketholderlist/components/managercard/ManagerCard.tsx new file mode 100644 index 00000000..2202aa82 --- /dev/null +++ b/src/pages/ticketholderlist/components/managercard/ManagerCard.tsx @@ -0,0 +1,82 @@ +import * as S from "./ManagerCard.styled"; + +const ManagerCard = ({ + bookingId, + isPaid, + isDetail, + setPaid, + bookername, + purchaseTicketeCount, + scheduleNumber, + bookerPhoneNumber, + createAt, +}: { + bookingId: number; + isPaid: boolean; + isDetail: boolean; + setPaid: () => void; + bookername: string; + purchaseTicketeCount: number; + scheduleNumber: string; + bookerPhoneNumber: string; + createAt: string; +}) => { + const date = createAt.split("T")[0]; + const formattedDate = date.replace(/-/g, "."); + const convertingNumber = (scheduleNumberrr: string) => { + switch (scheduleNumberrr) { + case "FIRST": + return 1; + break; + case "SECOND": + return 2; + break; + case "THIRD": + return 3; + break; + default: + console.log("error"); + } + }; + + return ( + + + + + 이름 + {bookername} + + + 매수 + {`${purchaseTicketeCount}매`} + + {isDetail && ( + <> + + 회차 + {`${convertingNumber(scheduleNumber)}회차`} + + + 연락처 + {bookerPhoneNumber} + + + 예매일 + {formattedDate} + + + )} + + + + + {isPaid ? : } + {isPaid ? "입금 완료" : "미입금"} + + + + ); +}; + +export default ManagerCard; diff --git a/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.styled.ts b/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.styled.ts new file mode 100644 index 00000000..dcd2970a --- /dev/null +++ b/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.styled.ts @@ -0,0 +1,107 @@ +import SvgIconChevronBack from "@assets/svgs/IconChevronBack"; +import styled, { css } from "styled-components"; + +export const DropdownWrapper = styled.div` + position: relative; +`; + +export const DropdownButton = styled.button<{ $isChoosed: boolean }>` + display: inline-flex; + flex-shrink: 0; + gap: 0.6rem; + align-items: center; + min-width: 9.3rem; + height: 4rem; + padding: 0.8rem 0.8rem 0.8rem 1.2rem; + + border: 1px solid + ${({ theme, $isChoosed }) => ($isChoosed ? theme.colors.gray_0 : theme.colors.gray_700)}; + border-radius: 0.4rem; +`; + +export const DropDownButtonContent = styled.div` + display: flex; + gap: 0.4rem; + align-items: center; + justify-content: space-between; + width: 100%; +`; + +export const ButtonContentSpan = styled.span` + color: ${({ theme }) => theme.colors.gray_0}; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; +`; + +export const SvgIcon = styled(SvgIconChevronBack)<{ $rotate: boolean }>` + width: 16px; + height: 16px; + + transform: rotate(180deg); + + ${(prop) => + prop.$rotate && + css` + transform: rotate(360deg); + `} +`; + +export const DropdownContentWrapper = styled.div<{ $show: boolean }>` + position: absolute; + top: 4.06rem; /* 버튼의 높이 3.26rem + 간격 0.8rem */ + z-index: 1; + display: ${(props) => (props.$show ? "flex" : "none")}; + flex-shrink: 0; + gap: 0.6rem; + align-items: center; + width: 9.2rem; + margin-top: 0.8rem; + padding: 0.8rem 0.8rem 0.8rem 1.2rem; + + background-color: ${({ theme }) => theme.colors.black}; + border: 1px solid ${({ theme }) => theme.colors.gray_400}; + border-radius: 0.4rem; +`; + +export const DropdownContentLayout = styled.div` + display: flex; + flex-direction: column; + gap: 0.6rem; + align-items: flex-start; + justify-content: center; +`; + +export const DropdownContentButton = styled.button<{ $isLast: boolean }>` + display: flex; + flex-direction: column; + gap: 0.8rem; + align-items: flex-start; + width: 6.8rem; + + /* 피그마에서는 겹치는 부분이 제대로 계산 안되서, 개발하면서 적당히 줄여둠 */ + padding: 0.2rem 0 0.6rem; + + ${({ $isLast }) => + !$isLast && + css` + border-bottom: 1px solid ${({ theme }) => theme.colors.gray_700}; + `} +`; + +export const DropdownContentText = styled.span<{ + $selected: boolean; +}>` + display: flex; + gap: 0.4rem; + align-items: center; + align-self: stretch; + justify-content: flex-start; + + color: ${({ theme }) => theme.colors.gray_0}; + ${({ theme }) => theme.fonts["body2-normal-medi"]}; + + ${({ $selected }) => + $selected && + css` + color: ${({ theme }) => theme.colors.pink_400}; + `} +`; diff --git a/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.tsx b/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.tsx new file mode 100644 index 00000000..6c3aedf5 --- /dev/null +++ b/src/pages/ticketholderlist/components/narrowDropDown/NarrowDropDown.tsx @@ -0,0 +1,138 @@ +import { ReactNode, useState } from "react"; +import * as S from "./NarrowDropDown.styled"; + +interface DropdownProps { + children: ReactNode; + totalScheduleCount: number; + schedule: number; + payment: boolean | undefined; + setSchedule?: (param: number) => void; + setPayment?: (param: boolean | undefined) => void; +} + +const NarrowDropDown = ({ + children, + totalScheduleCount, + schedule, + payment, + setSchedule, + setPayment, +}: DropdownProps) => { + const [showDropdown, setShowDropdown] = useState(false); + const [isOneChoosed, setIsOneChoosed] = useState(false); + + const handleToggle = () => { + setShowDropdown(!showDropdown); + }; + + const renderDropdownContent = (count: number, childrenNode: ReactNode) => { + const items = []; + const childrenString = childrenNode?.toString(); + if (childrenString === "모든 회차") { + const handleScheduleAll = () => { + setSchedule?.(0); + setIsOneChoosed(false); + setShowDropdown(false); + }; + items.push( + + 전체 + + ); + for (let i = 1; i <= count; i++) { + const handleSchedule = () => { + setSchedule?.(i); + setIsOneChoosed(true); + setShowDropdown(false); + }; + + items.push( + + {i}회차 + + ); + } + } else if (childrenString === "입금 상태") { + const handlePaymentUndefined = () => { + setPayment?.(undefined); + setIsOneChoosed(false); + setShowDropdown(false); + }; + + const handlePaymentFalse = () => { + setPayment?.(false); + setIsOneChoosed(true); + setShowDropdown(false); + }; + + const handlePaymentTrue = () => { + setPayment?.(true); + setIsOneChoosed(true); + setShowDropdown(false); + }; + + items.push( + + 전체 + + ); + + items.push( + + 미입금 + + ); + items.push( + + 입금완료 + + ); + } + + return items; + }; + + //공연별로 총 회차 값으로 넘겨주기로 함. + //각 공연별 회차는, DB 테이블 구조에 따라 enum값으로 넘겨주기로 함 + //필터링은 회차 번호(scheduleNumber : FIRST, SECOND, THIRD), 입금 완료 여부는 paymentStatus에 따라 나뉠거임 + // + + let changedChildren; + if (children === "입금 상태" && payment !== undefined) { + changedChildren = payment ? "입금완료" : "미입금"; + } else if (children === "모든 회차" && schedule !== 0) { + changedChildren = `${schedule}차`; + } + + return ( + + + + + {changedChildren === undefined ? children : changedChildren} + + + + + + + {renderDropdownContent(totalScheduleCount, children)} + + + + ); +}; + +export default NarrowDropDown; diff --git a/src/pages/ticketholderlist/constants/silkagel.png b/src/pages/ticketholderlist/constants/silkagel.png new file mode 100644 index 00000000..a5295d7b Binary files /dev/null and b/src/pages/ticketholderlist/constants/silkagel.png differ diff --git a/src/pages/ticketholderlist/constants/ticketholderlist.ts b/src/pages/ticketholderlist/constants/ticketholderlist.ts new file mode 100644 index 00000000..9f17fc35 --- /dev/null +++ b/src/pages/ticketholderlist/constants/ticketholderlist.ts @@ -0,0 +1,101 @@ +export interface BookingListProps { + bookingId: number; + bookerName: string; + bookerPhoneNumber: string; + scheduleId: number; + purchaseTicketCount: number; + createdAt: string; + isPaymentCompleted: boolean; + scheduleNumber: string; +} + +export interface TicketHolderListProps { + performanceTitle: string; + isBooking: boolean; + totalScheduleCount: number; + bookingList: BookingListProps[]; +} + +export interface TicketHolderListDataProps { + data: TicketHolderListProps; +} + +export const RESPONSE_TICKETHOLDER = { + data: { + performanceTitle: "비트밴드 정기공연", + isBooking: true, + totalScheduleCount: 3, + bookingList: [ + { + bookingId: 1, + bookerName: "황혜린", + bookerPhoneNumber: "010-1234-5678", + scheduleId: 2, //scheduleId는 테이블의 특성에 따라 존재하는 것 + purchaseTicketCount: 3, + createdAt: "2024-07-07T12:34:56.789Z", + isPaymentCompleted: true, + scheduleNumber: "SECOND", + }, + { + bookingId: 2, + bookerName: "이동훈", + bookerPhoneNumber: "010-1234-0000", + scheduleId: 1, + purchaseTicketCount: 2, + createdAt: "2024-07-08T12:34:56.789Z", + isPaymentCompleted: false, + scheduleNumber: "FIRST", + }, + { + bookingId: 3, + bookerName: "공준혁", + bookerPhoneNumber: "010-1234-9999", + scheduleId: 1, + purchaseTicketCount: 9, + createdAt: "2024-07-08T12:34:56.789Z", + isPaymentCompleted: true, + scheduleNumber: "THIRD", + }, + { + bookingId: 4, + bookerName: "정도영", + bookerPhoneNumber: "010-1234-0000", + scheduleId: 3, + purchaseTicketCount: 4, + createdAt: "2024-07-11T12:34:56.789Z", + isPaymentCompleted: false, + scheduleNumber: "THIRD", + }, + { + bookingId: 5, + bookerName: "김채현", + bookerPhoneNumber: "010-1234-0000", + scheduleId: 3, + purchaseTicketCount: 6, + createdAt: "2024-07-15T12:34:56.789Z", + isPaymentCompleted: false, + scheduleNumber: "THIRD", + }, + { + bookingId: 6, + bookerName: "윤신지", + bookerPhoneNumber: "010-1234-0000", + scheduleId: 3, + purchaseTicketCount: 2, + createdAt: "2024-07-03T12:34:56.789Z", + isPaymentCompleted: true, + scheduleNumber: "THIRD", + }, + { + bookingId: 7, + bookerName: "익명인", + bookerPhoneNumber: "010-1000-0000", + scheduleId: 1, + purchaseTicketCount: 2, + createdAt: "2024-07-08T12:34:56.789Z", + isPaymentCompleted: true, + scheduleNumber: "SECOND", + }, + ], + }, +}; diff --git a/src/routes/Router.tsx b/src/routes/Router.tsx index 40d9bf05..ebe30f46 100644 --- a/src/routes/Router.tsx +++ b/src/routes/Router.tsx @@ -10,6 +10,7 @@ import MyRegisterdShow from "@pages/MyRegisterdShow/MyRegisterdShow"; import NonMbLookup from "@pages/nonMbLookup/NonMbLookup"; import Register from "@pages/register/Register"; import TestPage from "@pages/test/TestPage"; +import TicketHolderList from "@pages/ticketholderlist/TicketHolderList"; import KakaoLoginTest from "@pages/KakaoLoginTest"; import KakaoLogin from "@pages/kakaoLogin/KakaoLogin"; import ViewBottomSheetTest from "@pages/ViewBottomSheetTest"; @@ -26,6 +27,7 @@ const router = createBrowserRouter([ { path: "register", element: }, { path: "gig/:performanceId", element: }, { path: "manage", element: }, + { path: "ticketholderlist", element: }, { path: "myregisteredshow", element: }, // ... other pages ], @@ -63,7 +65,7 @@ const router = createBrowserRouter([ // element: , // }, { path: "/testpage", element: }, - { path: "/myregisteredshow", element: }, + // ... ]); export default router;