Skip to content

Commit 90ed795

Browse files
authored
Merge pull request TEAM-BEAT#228 from TEAM-BEAT/feat/TEAM-BEAT#215/PresignedUrl
[Feat/TEAM-BEAT#215] presigned url 및 공연 등록 API 추가
2 parents 79a13ed + 0015a17 commit 90ed795

File tree

21 files changed

+1804
-1497
lines changed

21 files changed

+1804
-1497
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"lottie-react": "^2.4.0",
4040
"postcss": "^8.4.38",
4141
"postcss-scss": "^4.0.9",
42+
"qs": "^6.12.3",
4243
"react": "^18.3.1",
4344
"react-dom": "^18.3.1",
4445
"react-lottie-player": "^2.0.0",
@@ -65,6 +66,7 @@
6566
"@storybook/react-vite": "^8.1.11",
6667
"@storybook/test": "^8.1.11",
6768
"@types/prettier-linter-helpers": "^1",
69+
"@types/qs": "^6",
6870
"@types/react": "^18.3.3",
6971
"@types/react-dom": "^18.3.0",
7072
"@types/shelljs": "^0",

src/apis/domains/files/api.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { get } from "@apis/index";
2+
import axios, { AxiosResponse } from "axios";
3+
import qs from "qs";
4+
5+
interface ImageInterface {
6+
[key: string]: string;
7+
}
8+
9+
export interface PresignedResponse {
10+
poster: ImageInterface;
11+
cast: ImageInterface;
12+
staff: ImageInterface;
13+
}
14+
15+
export interface GetPresignedUrlParams {
16+
posterImage: string;
17+
castImages: string[];
18+
staffImages: string[];
19+
}
20+
21+
export const getPresignedUrl = async (
22+
params: GetPresignedUrlParams
23+
): Promise<PresignedResponse | null> => {
24+
try {
25+
const paramsWithEmptyArrays = {
26+
...params,
27+
castImages: params.castImages.length === 0 ? [""] : params.castImages,
28+
staffImages: params.staffImages.length === 0 ? [""] : params.staffImages,
29+
};
30+
31+
const response: AxiosResponse<PresignedResponse> = await get("/files/presigned-url", {
32+
params: paramsWithEmptyArrays,
33+
paramsSerializer: (params) => {
34+
const queryString = qs.stringify(params, { arrayFormat: "repeat" });
35+
36+
const modifiedQueryString = queryString
37+
.replace(/castImages=%5B%5D/g, "castImages")
38+
.replace(/staffImages=%5B%5D/g, "staffImages");
39+
return modifiedQueryString;
40+
},
41+
});
42+
43+
return response.data;
44+
} catch (error) {
45+
console.error("error", error);
46+
return null;
47+
}
48+
};
49+
50+
export interface PutImageUploadParams {
51+
url: string;
52+
file: File;
53+
}
54+
55+
export const putS3ImageUpload = async ({ url, file }: PutImageUploadParams) => {
56+
try {
57+
const response = await axios.put(url, file, {
58+
headers: {
59+
"Content-Type": file.type,
60+
},
61+
});
62+
63+
return response;
64+
} catch (err) {
65+
console.error(err);
66+
}
67+
return null;
68+
};

src/apis/domains/files/queries.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { useMutation, useQuery } from "@tanstack/react-query";
2+
import {
3+
getPresignedUrl,
4+
GetPresignedUrlParams,
5+
PresignedResponse,
6+
PutImageUploadParams,
7+
putS3ImageUpload,
8+
} from "./api";
9+
10+
const QUERY_KEY = {
11+
PRESIGNED_URL: "presignedURL",
12+
};
13+
14+
export const useGetPresignedUrl = (params: GetPresignedUrlParams) => {
15+
return useQuery<PresignedResponse>({
16+
queryKey: [QUERY_KEY.PRESIGNED_URL],
17+
queryFn: () => getPresignedUrl(params),
18+
enabled: false,
19+
});
20+
};
21+
22+
export const usePutS3Upload = () => {
23+
return useMutation({
24+
mutationFn: ({ url, file }: PutImageUploadParams) => putS3ImageUpload({ url, file }),
25+
});
26+
};

src/apis/domains/home/queries.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { useQuery } from "@tanstack/react-query";
22
import { getAllScheduleList } from "./api";
33

4-
const QUERY_KEY = {
4+
export const HOME_QUERY_KEY = {
55
LIST: "list",
66
};
77

88
// 2. 쿼리 작성
99
export const useGetAllScheduleList = () => {
1010
return useQuery({
11-
queryKey: [QUERY_KEY.LIST],
11+
queryKey: [HOME_QUERY_KEY.LIST],
1212
queryFn: () => getAllScheduleList(), // API 요청 함수
1313
staleTime: 1000 * 60 * 60,
1414
gcTime: 1000 * 60 * 60 * 24,

src/apis/domains/performance/api.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { get } from "@apis/index";
1+
import { get, post } from "@apis/index";
22
import { components } from "@typings/api/schema";
33
import { ApiResponseType } from "@typings/commonType";
44
import axios, { AxiosResponse } from "axios";
@@ -62,3 +62,17 @@ export const getScheduleAvailable = async (
6262
return -1;
6363
}
6464
};
65+
66+
export type PerformanceResponse = components["schemas"]["PerformanceResponse"];
67+
68+
export const postPerformance = async (formData): Promise<PerformanceResponse | number> => {
69+
try {
70+
const response = await post("/performances", formData);
71+
72+
return response.data;
73+
} catch (error) {
74+
console.error("error", error);
75+
76+
return null;
77+
}
78+
};

src/apis/domains/performance/queries.ts

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
import { useQuery } from "@tanstack/react-query";
2-
import { getBookingPerformanceDetail, getPerformanceDetail, getScheduleAvailable } from "./api";
1+
import { SHOW_TYPE_KEY } from "@pages/gig/constants";
2+
import { QueryClient, useMutation, useQuery } from "@tanstack/react-query";
3+
import { Dayjs } from "dayjs";
4+
import { useNavigate } from "react-router-dom";
5+
import { HOME_QUERY_KEY } from "../home/queries";
6+
import {
7+
getBookingPerformanceDetail,
8+
getPerformanceDetail,
9+
getScheduleAvailable,
10+
postPerformance,
11+
} from "./api";
312

413
export const QUERY_KEY = {
514
DETAIL: "detail",
@@ -31,3 +40,67 @@ export const useGetScheduleAvailable = (scheduleId: number, purchaseTicketCount:
3140
enabled: false,
3241
});
3342
};
43+
44+
interface PerformanceFormData {
45+
posterImage: string;
46+
castList: {
47+
castPhoto: string;
48+
castName: string;
49+
castRole: string;
50+
}[];
51+
staffList: {
52+
staffPhoto: string;
53+
staffName: string;
54+
staffRole: string;
55+
}[];
56+
performanceTitle: string;
57+
genre: SHOW_TYPE_KEY;
58+
runningTime: number | null;
59+
performanceDescription: string;
60+
performanceAttentionNote: string;
61+
bankName: string;
62+
accountNumber: string;
63+
accountHolder: string;
64+
performanceTeamName: string;
65+
performanceVenue: string;
66+
performanceContact: string;
67+
performancePeriod: string;
68+
ticketPrice: number | null;
69+
totalScheduleCount: number;
70+
scheduleList: {
71+
performanceDate: Dayjs | Date | null | string;
72+
totalTicketCount: number | null;
73+
scheduleNumber: string;
74+
}[];
75+
}
76+
77+
interface PerformanceResponse {
78+
status: number;
79+
data: {
80+
performanceId: number;
81+
};
82+
}
83+
84+
const isPerformanceResponse = (res: any): res is PerformanceResponse => {
85+
return res && typeof res === "object" && "status" in res && "data" in res;
86+
};
87+
88+
export const usePostPerformance = () => {
89+
const queryClient = new QueryClient();
90+
const navigate = useNavigate();
91+
92+
return useMutation({
93+
mutationFn: (formData: PerformanceFormData) => postPerformance(formData),
94+
onSuccess: (res) => {
95+
queryClient.invalidateQueries({ queryKey: [HOME_QUERY_KEY.LIST] });
96+
97+
if (isPerformanceResponse(res) && res.status === 201) {
98+
navigate("/register-complete", {
99+
state: { performanceId: res.data.performanceId },
100+
});
101+
} else {
102+
console.error("Unexpected response type", res);
103+
}
104+
},
105+
});
106+
};

src/apis/index.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
import axios from "axios";
22

3+
const getAccessToken = (): string | null => {
4+
const user = localStorage.getItem("user");
5+
if (user) {
6+
try {
7+
const userObj = JSON.parse(user);
8+
return userObj.accessToken || "";
9+
} catch (error) {
10+
console.error("Failed to parse user from localStorage", error);
11+
return "";
12+
}
13+
}
14+
return "";
15+
};
16+
317
export const instance = axios.create({
418
baseURL: import.meta.env.VITE_API_BASE_URL,
519
withCredentials: true,
620

721
headers: {
8-
Authorization: `Bearer ${localStorage.getItem("accessToken") || ""}`,
22+
Authorization: `Bearer ${getAccessToken()}`,
923
},
1024
});
1125

src/components/commons/label/Label.styled.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const CountLabel = styled(Subtract)`
1515
export const CountDueDate = styled.div`
1616
position: absolute;
1717
width: 3.6rem;
18-
margin: 1.3rem 0 0 0.6rem;
18+
margin: 1.3rem 0 0 ;
1919
2020
color: ${({ theme }) => theme.colors.pink_400};
2121

src/constants/bankList.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
export const BANK_LIST = [
2020
{
2121
id: 1,
22-
name: "NH_NONHYUP",
22+
name: "NH_NONGHYUP",
2323
nameKr: "NH농협",
2424
bankImg: IconNonghyup,
2525
},

src/hooks/useTokenRefresher.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import { instance } from "@apis/index";
22
import { useEffect } from "react";
33
import { useNavigate } from "react-router-dom";
4+
import useModal from "./useModal";
45

6+
// TODO: 추후 수정
57
export default function TokenRefresher() {
68
const navigate = useNavigate();
9+
const { openAlert } = useModal();
710

811
useEffect(() => {
912
const interceptor = instance.interceptors.response.use(
1013
// 성공적인 응답 처리
14+
1115
(response) => {
12-
console.log("Starting Request", response);
16+
// console.log("Starting Request", response);
1317
return response;
1418
},
1519
async (error) => {
@@ -56,7 +60,8 @@ export default function TokenRefresher() {
5660
else {
5761
localStorage.clear();
5862
navigate("/main");
59-
alert("토큰이 만료되어 자동으로 로그아웃 되었습니다.");
63+
64+
openAlert({ title: "토큰이 만료되어 자동으로 로그아웃 되었습니다." });
6065
}
6166
} else if (status === 400 || status === 404 || status === 409) {
6267
console.log("hi3");

0 commit comments

Comments
 (0)