Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/#453] 예매자 관리 페이지 기능 구현 #454

Merged
merged 25 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3e6b279
chore: api schema 업데이트
sinji2102 Nov 25, 2024
815a405
feat: useDebounce 구현
sinji2102 Nov 25, 2024
e292be6
feat: 조회 API 연결
sinji2102 Nov 25, 2024
4c0bc54
Merge branch 'feat/#443/TicketHolderListPublishing' of https://github…
sinji2102 Nov 27, 2024
a471ec5
feat: 필터링 기능 칩-바텀시트 연결
sinji2102 Dec 1, 2024
3ef8329
feat: 예매자 조회 필터링 api 연결
sinji2102 Dec 1, 2024
2d6eb93
feat: 예매자 관리 상태에 따라 필터링
sinji2102 Dec 1, 2024
a9e1912
feat: 체크박스 이상하게 되던 오류 수정
sinji2102 Dec 2, 2024
3ecd641
feat: 입금 처리 api 연결
sinji2102 Dec 2, 2024
a9570ec
chore: develop pull 과정에서 생긴 충돌 해결
sinji2102 Dec 2, 2024
fd4db62
feat: 환불 처리 api 연결
sinji2102 Dec 2, 2024
cf84c44
feat: 취소 처리 api 연결
sinji2102 Dec 2, 2024
0ce87b0
feat: 환불 계좌 복사 기능 추가
sinji2102 Dec 2, 2024
a0be558
fix: react-query 관련 오류 수정
sinji2102 Dec 2, 2024
0ea1d50
feat: 예매자 검색 api 연결
sinji2102 Dec 2, 2024
7ac3d72
fix: 검색어가 1자일 때 생기는 오류 수정
sinji2102 Dec 2, 2024
4ea1411
feat: 예매자 없을 떄 화면 구현
sinji2102 Dec 2, 2024
776b6fe
fix: 타입 에러 수정
sinji2102 Dec 3, 2024
0c53546
design: 필터 적용 시 border white 적용
sinji2102 Dec 3, 2024
8307204
fix: api params 이름 변경
sinji2102 Dec 3, 2024
77a69ff
feat: csv 버튼 재연결
sinji2102 Dec 5, 2024
e678ce9
chore: develop pull 과정에서 생긴 충돌 해결
sinji2102 Dec 9, 2024
4525501
fix: #454 PR 코드리뷰 반영
sinji2102 Dec 9, 2024
eed8389
feat: 선택된 내역 없을 때 비활성화 구현
sinji2102 Dec 9, 2024
9d72603
design: 검색 바 내 누락된 아이콘 추가
sinji2102 Dec 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions public/svgs/icon_download.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
85 changes: 66 additions & 19 deletions src/apis/domains/tickets/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { components } from "@typings/api/schema";
import { ApiResponseType } from "@typings/commonType";
import { PatchFormDataProps } from "@typings/deleteBookerFormatProps";
import { AxiosResponse } from "axios";
import { convertingScheduleNumber } from "@constants/convertingScheduleNumber";

// 예매자 목록 조회 API (GET)
export interface getTicketReq {
Expand All @@ -15,11 +16,42 @@ export interface getTicketReq {
type TicketRetrieveResponse = components["schemas"]["TicketRetrieveResponse"];

export const getTicketRetrieve = async (
formData: getTicketReq
formData: getTicketReq,
filterList
): Promise<TicketRetrieveResponse | null> => {
try {
const params = new URLSearchParams();
filterList.scheduleNumber.map((item) =>
params.append("scheduleNumbers", convertingScheduleNumber(item))
);
filterList.bookingStatus.map((item) => params.append("bookingStatuses", item));

const response: AxiosResponse<ApiResponseType<TicketRetrieveResponse>> = await get(
`tickets/${formData.performanceId}?${params.toString()}`
);

return response.data.data;
} catch (error) {
console.log("error", error);
return null;
}
};

export const getTicketRetrieveSearch = async (
formData: getTicketReq,
searchWord,
filterList
): Promise<TicketRetrieveResponse | null> => {
try {
const params = new URLSearchParams();
params.append("searchWord", searchWord);
filterList.scheduleNumber.map((item) =>
params.append("scheduleNumbers", convertingScheduleNumber(item))
);
filterList.bookingStatus.map((item) => params.append("bookingStatuses", item));

const response: AxiosResponse<ApiResponseType<TicketRetrieveResponse>> = await get(
`tickets/${formData.performanceId}`
`tickets/search/${formData.performanceId}?${params.toString()}`
);

return response.data.data;
Expand All @@ -29,17 +61,11 @@ export const getTicketRetrieve = async (
}
};

type SuccessResponseVoid = components["schemas"]["SuccessResponseVoid"];

// 예매자 입급 여부 수정 API (PUT)
/*
export interface putTicketReq {
performanceId: number;
performanceTitle: string;
totalScheduleCoun: number;
bookingList: BookingListProps[];
}
*/

export type TicketUpdateRequest = components["schemas"]["TicketUpdateRequest"];
type SuccessResponseVoid = components["schemas"]["SuccessResponseVoid"];

//async 함수는 항상 promise로 감싸서 값을 리턴한다.
//즉, 비동기 함수의 값을 사용하려면 추후 await이나 then 을 사용해야 한다던데..
Expand All @@ -49,7 +75,7 @@ export const putTicketUpdate = async (
): Promise<SuccessResponseVoid | null> => {
try {
const response: AxiosResponse<ApiResponseType<SuccessResponseVoid>> = await put(
"tickets",
"tickets/update",
formData
);

Expand All @@ -60,18 +86,39 @@ export const putTicketUpdate = async (
}
};

// 예매자 취소 API (PATCH)
//이거 타입 잘못되었을 수도..? bookingList 가 number를 담은 배열로 되어 있는데, 실제로는 아니었음
//export type TicketDeleteRequest = components["schemas"]["TicketDeleteRequest"];
// 예매자 환불처리 (PUT)

export type TicketRefundRequest = components["schemas"]["TicketRefundRequest"];

export const patchTicketCancel = async (
formData: PatchFormDataProps
export const putTicketRefund = async (
formData: TicketRefundRequest
): Promise<SuccessResponseVoid | null> => {
try {
const response: AxiosResponse<ApiResponseType<SuccessResponseVoid>> = await patch(
"tickets",
const response: AxiosResponse<ApiResponseType<SuccessResponseVoid>> = await put(
"tickets/refund",
formData
);

return response.data.data;
} catch (error) {
console.log("error", error);
return null;
}
};

// 예매자 삭제 (PUT)

export type TicketDeleteRequest = components["schemas"]["TicketDeleteRequest"];

export const putTicketDelete = async (
formData: TicketDeleteRequest
): Promise<SuccessResponseVoid | null> => {
try {
const response: AxiosResponse<ApiResponseType<SuccessResponseVoid>> = await put(
"tickets/delete",
formData
);

return response.data.data;
} catch (error) {
console.log("error", error);
Expand Down
45 changes: 34 additions & 11 deletions src/apis/domains/tickets/queries.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { QueryClient, useMutation, useQuery } from "@tanstack/react-query";
import { PatchFormDataProps } from "@typings/deleteBookerFormatProps";
import {
getTicketReq,
getTicketRetrieve,
patchTicketCancel,
getTicketRetrieveSearch,
putTicketDelete,
putTicketRefund,
putTicketUpdate,
TicketDeleteRequest,
TicketRefundRequest,
TicketUpdateRequest,
} from "./api";

Expand All @@ -13,19 +16,30 @@ const QUERY_KEY = {
SELLER_BOOKING_LIST: "sellerBookingList",
};

export const useTicketRetrive = (formData: getTicketReq) => {
export const useTicketRetrive = (formData: getTicketReq, filterList) => {
return useQuery({
queryKey: [QUERY_KEY.SELLER_BOOKING_LIST],
queryFn: () => getTicketRetrieve(formData),
// staleTime: 1000 * 60 * 60,
queryFn: () => getTicketRetrieve(formData, filterList),
staleTime: 1000 * 60 * 60,
gcTime: 1000 * 60 * 60 * 24,
});
};

// 예매자 목록 검색 API (GET)를 위한 쿼리 작성

export const useTicketRetriveSearch = (formData: getTicketReq, searchWord, filterList) => {
return useQuery({
queryKey: [QUERY_KEY.SELLER_BOOKING_LIST],
queryFn: () => getTicketRetrieveSearch(formData, searchWord, filterList),
staleTime: 1000 * 60 * 60,
gcTime: 1000 * 60 * 60 * 24,
});
};

const queryClient = new QueryClient();

// 예매자 입급 여부 수정 API (PUT)를 위한 쿼리 작성
export const useTicketUpdate = () => {
const queryClient = new QueryClient();

return useMutation({
mutationFn: (formData: TicketUpdateRequest) => putTicketUpdate(formData),
onSuccess: (res) => {
Expand All @@ -35,12 +49,21 @@ export const useTicketUpdate = () => {
});
};

// 예매자를 취소하는 API (PATCH)를 위한 쿼리 작성
export const useTicketPatch = () => {
const queryClient = new QueryClient();
// 예매자 환불 여부 수정 API (PUT)를 위한 쿼리 작성
export const useTicketRefund = () => {
return useMutation({
mutationFn: (formData: TicketRefundRequest) => putTicketRefund(formData),
onSuccess: (res) => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.SELLER_BOOKING_LIST] });
queryClient.refetchQueries({ queryKey: [QUERY_KEY.SELLER_BOOKING_LIST] });
},
});
};

// 예매자 삭제 여부 수정 API (PUT)를 위한 쿼리 작성
export const useTicketDelete = () => {
return useMutation({
mutationFn: (formData: PatchFormDataProps) => patchTicketCancel(formData),
mutationFn: (formData: TicketDeleteRequest) => putTicketDelete(formData),
onSuccess: (res) => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEY.SELLER_BOOKING_LIST] });
queryClient.refetchQueries({ queryKey: [QUERY_KEY.SELLER_BOOKING_LIST] });
Expand Down
11 changes: 11 additions & 0 deletions src/assets/svgs/IconDownload.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as React from "react";
import type { SVGProps } from "react";
const SvgIconDownload = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20" {...props}>
<path
fill="#FB247F"
d="M9.999 13.333 5.832 9.166l1.167-1.208 2.166 2.167V3.333h1.667v6.792l2.167-2.167 1.166 1.208zm-5 3.333q-.687 0-1.177-.489A1.6 1.6 0 0 1 3.332 15v-2.5h1.667V15h10v-2.5h1.666V15q0 .687-.489 1.177a1.6 1.6 0 0 1-1.177.49z"
/>
</svg>
);
export default SvgIconDownload;
2 changes: 1 addition & 1 deletion src/assets/svgs/IconSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { SVGProps } from "react";
const SvgIconSearch = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" {...props}>
<path
fill="#F4F4F4"
fill="current"
fillRule="evenodd"
d="M6.964 6.964a5 5 0 1 1 7.072 7.071 5 5 0 0 1-7.072-7.07m-1.06-1.06a6.5 6.5 0 0 0 8.631 9.692l3.934 3.934a.75.75 0 1 0 1.06-1.06l-3.934-3.934a6.5 6.5 0 0 0-9.691-8.632"
clipRule="evenodd"
Expand Down
70 changes: 0 additions & 70 deletions src/assets/svgs/index.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/assets/svgs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export { default as IconCheckboxSelectedOn } from "./IconCheckboxSelectedOn";
export { default as IconCheckboxUnselectedOn } from "./IconCheckboxUnselectedOn";
export { default as IconChecked } from "./IconChecked";
export { default as IconChevronBack } from "./IconChevronBack";
export { default as IconDownload } from "./IconDownload";
export { default as IconEmpty } from "./IconEmpty";
export { default as IconEyeOff } from "./IconEyeOff";
export { default as IconEyeOn } from "./IconEyeOn";
Expand Down Expand Up @@ -70,4 +71,4 @@ export { default as NotFoundAsset } from "./NotFoundAsset";
export { default as SelectionControlCheckboxSelectedOff } from "./SelectionControlCheckboxSelectedOff";
export { default as Subtract } from "./Subtract";
export { default as Switch } from "./Switch";
export { default as Union } from "./Union";
export { default as Union } from "./Union";
18 changes: 16 additions & 2 deletions src/components/commons/navigation/Navigation.styled.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { IcHamburgar, IconArrowLeft, IconArrowRight, IconLogo, IconXButton } from "@assets/svgs";
import {
IcHamburgar,
IconArrowLeft,
IconArrowRight,
IconLogo,
IconXButton,
IconDownload,
} from "@assets/svgs";
import styled from "styled-components";

export const NavigationWrapper = styled.div`
Expand Down Expand Up @@ -38,6 +45,13 @@ export const HamburgarButton = styled(IcHamburgar)`
cursor: pointer;
`;

export const DownloadButton = styled(IconDownload)`
width: 2rem;
height: 2rem;

cursor: pointer;
`;

export const NavigationLeftButton = styled(IconArrowLeft)`
box-sizing: content-box;
width: 2.4rem;
Expand Down Expand Up @@ -67,7 +81,7 @@ export const NavigationXButton = styled(IconXButton)`

// TODO: 뷰에 띄워보니 padding이 없어 스타일링 이상함 디자이너 분께 물어보기
export const SubTextButton = styled.button`
width: 3.2rem;
display: flex;
margin: 0 0.4rem 0 0;

${({ theme }) => theme.fonts["body1-normal-semi"]};
Expand Down
13 changes: 13 additions & 0 deletions src/components/commons/navigation/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,19 @@ const Navigation = () => {
);
}

if (headerStyle === NAVIGATION_STATE.ICON_TITLE_DOWNLOAD) {
return (
<S.NavigationWrapper>
<S.NavigationLeftButton onClick={leftOnClick} />
<S.NavigationTitle>{title}</S.NavigationTitle>
<S.SubTextButton onClick={rightOnClick}>
{subText}
<S.DownloadButton />
</S.SubTextButton>
</S.NavigationWrapper>
);
}

if (!headerStyle) {
return null;
}
Expand Down
20 changes: 20 additions & 0 deletions src/constants/convertingBookingStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type PaymentType =
| "CHECKING_PAYMENT"
| "BOOKING_CONFIRMED"
| "BOOKING_CANCELLED"
| "REFUND_REQUESTED";

export const convertingBookingStatus = (_bookingStatus: PaymentType): string => {
switch (_bookingStatus) {
case "CHECKING_PAYMENT":
return "미입금";
case "BOOKING_CONFIRMED":
return "입금 완료";
case "BOOKING_CANCELLED":
return "취소 완료";
case "REFUND_REQUESTED":
return "환불 요청";
default:
Comment on lines +15 to +17
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3) 아 제가 사용할때는 "취소 요청"인데 여기서는 "환불 요청"으로 사용되고 있네요!! 이거 하나로 통일하는게 편하겠죠?? 기디 선생님들한테 통일해달라고 부탁드리는거 어떻게 생각하세요?

throw new Error("알 수 없는 상태입니다.");
}
};
Loading
Loading