Skip to content

Commit

Permalink
Merge pull request #97 from TEAM-BEAT/feat/#66/NarrowDropDown
Browse files Browse the repository at this point in the history
[Feat/#66] 예매자 리스트 페이지 구현 (+NarrowDropDown으로부터 시작)
  • Loading branch information
ocahs9 authored Jul 13, 2024
2 parents 6986d50 + 15ad97a commit 1196c67
Show file tree
Hide file tree
Showing 28 changed files with 896 additions and 3 deletions.
7 changes: 7 additions & 0 deletions public/svgs/Icon_checkbox_selected_on.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/svgs/Icon_checkbox_unselected_on.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/svgs/icon_chevron_back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/svgs/icon_toggle_off.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions public/svgs/icon_toggle_on.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions src/assets/svgs/IconCheckboxSelectedOn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from "react";
import type { SVGProps } from "react";
const SvgIconCheckboxSelectedOn = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 19 18" {...props}>
<rect width={17} height={17} x={1} y={0.5} fill="#E30964" rx={1.5} />
<rect width={17} height={17} x={1} y={0.5} stroke="#FF97C2" rx={1.5} />
<path
fill="#fff"
stroke="#fff"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={0.27}
d="M14.59 5.634a.585.585 0 0 0-.881-.77l-5.888 6.729-2.258-2.258a.585.585 0 1 0-.827.827l2.7 2.7a.585.585 0 0 0 .853-.028z"
/>
</svg>
);
export default SvgIconCheckboxSelectedOn;
8 changes: 8 additions & 0 deletions src/assets/svgs/IconCheckboxUnselectedOn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as React from "react";
import type { SVGProps } from "react";
const SvgIconCheckboxUnselectedOn = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 19 18" {...props}>
<rect width={17} height={17} x={1} y={0.5} stroke="#797979" rx={1.5} />
</svg>
);
export default SvgIconCheckboxUnselectedOn;
13 changes: 13 additions & 0 deletions src/assets/svgs/IconChevronBack.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as React from "react";
import type { SVGProps } from "react";
const SvgIconChevronBack = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16" {...props}>
<path
fill="#F4F4F4"
fillRule="evenodd"
d="M13.03 10.78a.75.75 0 0 1-1.06 0L8 6.81l-3.97 3.97a.75.75 0 1 1-1.06-1.06l4.5-4.5a.75.75 0 0 1 1.06 0l4.5 4.5a.75.75 0 0 1 0 1.06"
clipRule="evenodd"
/>
</svg>
);
export default SvgIconChevronBack;
9 changes: 9 additions & 0 deletions src/assets/svgs/IconToggleOff.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as React from "react";
import type { SVGProps } from "react";
const SvgIconToggleOff = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 56 33" {...props}>
<rect width={56} height={33} fill="#3E3E3E" rx={16.5} />
<circle cx={16.5} cy={16.5} r={13.5} fill="#fff" />
</svg>
);
export default SvgIconToggleOff;
9 changes: 9 additions & 0 deletions src/assets/svgs/IconToggleOn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as React from "react";
import type { SVGProps } from "react";
const SvgIconToggleOn = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 56 33" {...props}>
<rect width={56} height={33} fill="#E30964" rx={16.5} />
<circle cx={39.5} cy={16.5} r={13.5} fill="#fff" />
</svg>
);
export default SvgIconToggleOn;
5 changes: 5 additions & 0 deletions src/assets/svgs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion src/pages/MyRegisterdShow/MyRegisterdShow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<RegisteredObjProps, "id">) => {
Expand Down
Empty file.
Empty file.
Empty file.
71 changes: 71 additions & 0 deletions src/pages/ticketholderlist/TicketHolderList.styled.ts
Original file line number Diff line number Diff line change
@@ -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);
`;
132 changes: 132 additions & 0 deletions src/pages/ticketholderlist/TicketHolderList.tsx
Original file line number Diff line number Diff line change
@@ -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<boolean | undefined>(undefined);
const [responseData, setResponseData] = useState<BookingListProps[]>(
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 (
<>
<Banner image={eximg} reservedCount={reservedCount} isOutdated={isOutdated} />
<S.BodyWrapper>
<S.BodyLayout>
<S.LayoutHeaderBox>
<S.LayoutFilterBox>
{/*set 함수 직접 넘기는 거 안좋다고 했지만, 내부에서 감싸야 하므로 넘김 */}
<NarrowDropDown
schedule={schedule}
payment={payment}
totalScheduleCount={count}
setSchedule={setSchedule}
>
모든 회차
</NarrowDropDown>
<NarrowDropDown
schedule={schedule}
payment={payment}
totalScheduleCount={count}
setPayment={setPayment}
>
입금 상태
</NarrowDropDown>
</S.LayoutFilterBox>
<S.ToggleWrapper>
{detail ? (
<>
<S.ToggleText>자세히</S.ToggleText>
<S.ToggleOnIcon
onClick={handleToggleButton}
$width={"5.6rem"}
$height={"3.3rem"}
/>
</>
) : (
<>
<S.ToggleText>간략히</S.ToggleText>
<S.ToggleOffIcon
onClick={handleToggleButton}
$width={"5.6rem"}
$height={"3.3rem"}
/>
</>
)}
</S.ToggleWrapper>
</S.LayoutHeaderBox>
{filteredData.map((obj, index) => (
<ManagerCard
key={`managerCard-${index}`}
bookingId={obj.bookingId}
isPaid={obj.isPaymentCompleted}
isDetail={detail}
setPaid={() => handlePaymentToggle(obj.bookingId)}
bookername={obj.bookerName}
purchaseTicketeCount={obj.purchaseTicketCount}
scheduleNumber={obj.scheduleNumber}
bookerPhoneNumber={obj.bookerPhoneNumber}
createAt={obj.createdAt}
/>
))}

<S.FooterButtonWrapper>
<Button>변경내용 저장하기</Button>
</S.FooterButtonWrapper>
</S.BodyLayout>
</S.BodyWrapper>
</>
);
};

export default TicketHolderList;
45 changes: 45 additions & 0 deletions src/pages/ticketholderlist/components/banner/Banner.styled.ts
Original file line number Diff line number Diff line change
@@ -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"]};
`;
Loading

0 comments on commit 1196c67

Please sign in to comment.