Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
104 changes: 104 additions & 0 deletions apps/nowait-user/src/constants/departments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 학과 정보 매핑
export const DEPARTMENTS: { [key: number]: string } = {
1: "자유전공",
2: "한국학전공",
3: "경영학부",
4: "금융빅데이터학부",
5: "미디어커뮤니케이션학과",
6: "관광경영학과",
7: "경제학과",
8: "의료산업경영학과",
9: "응용통계학과",
10: "사회복지학과",
11: "유아교육학과",
12: "심리학과",
13: "한국어문학과",
14: "영미어문학과",
15: "동양어문학과",
16: "유럽어문학과",
17: "법학과",
18: "경찰행정학과",
19: "행정학과",
20: "경찰학연계전공",
21: "도시계획학전공",
22: "조경학전공",
23: "실내건축학전공",
24: "건축학전공",
25: "건축공학전공",
26: "설비•소방공학과",
27: "화공생명공학과",
28: "기계공학전공",
29: "산업공학전공",
30: "스마트팩토리전공",
31: "토목환경공학과",
32: "신소재공학과",
33: "배터리공학과",
34: "식품생명공학과",
35: "식품영양학과",
36: "바이오나노학과",
37: "생명과학과",
38: "물리학과",
39: "화학과",
40: "AI•소프트웨어학부",
41: "컴퓨터공학전공",
42: "스마트보안전공",
43: "전자공학전공",
44: "차세대반도체설계전공",
45: "전기공학과",
46: "스마트시티학과",
47: "의공학과",
48: "에너지IT학과",
49: "클라우드공학과",
50: "한의예과",
51: "한의학과",
52: "회화•조소전공",
53: "시각디자인전공",
54: "패션디자인전공",
55: "산업디자인전공",
56: "성악전공",
57: "기악전공",
58: "작곡전공",
59: "체육전공",
60: "태권도전공",
61: "연기예술학과",
62: "바이오의료기기학과",
63: "게임•영상학과",
64: "반도체•디스플레이학과",
65: "반도체설계학과",
66: "미래자동차학과",
67: "IT융합대학",
68: "가천리버럴아츠칼리지",
69: "경영대학",
70: "공과대학",
71: "미래산업대학",
72: "바이오나노대학",
73: "법과대학",
74: "사회과학대학",
75: "예술•체육대학",
76: "인문대학",
77: "창업대학",
78: "한의과대학",
82: "반도체대학",
83: "반도체공학전공",
};

// departmentId를 학과명으로 변환하는 함수
export const getDepartmentName = (departmentId: number): string => {
return DEPARTMENTS[departmentId] || "알 수 없음";
};

// 모든 학과 목록을 반환하는 함수
export const getAllDepartments = () => {
return Object.entries(DEPARTMENTS).map(([id, name]) => ({
id: parseInt(id),
name,
}));
};

// 학과명으로 ID를 찾는 함수
export const getDepartmentId = (departmentName: string): number | null => {
const entry = Object.entries(DEPARTMENTS).find(
([_, name]) => name === departmentName
);
return entry ? parseInt(entry[0]) : null;
};
129 changes: 93 additions & 36 deletions apps/nowait-user/src/hooks/useInfiniteStores.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,119 @@
import { useEffect } from "react";
import { useInfiniteQuery } from "@tanstack/react-query";
import axios from "axios";

// Store 데이터 타입 정의
// 실제 서버 API 응답 타입 (StoreCard에서 직접 사용)
interface Store {
id: number;
storeName: string;
department: string;
status: "open" | "closed";
waitingCount: number;
imageUrl?: string;
storeId: number;
departmentId: number;
name: string;
location: string;
description: string;
images: string[];
isActive: boolean;
deleted: boolean;
createdAt: string;
waitingCount?: number; // 대기인원 API 연동 시 추가 예정
}

// 가상의 주점 데이터를 가져오는 함수
const fetchStores = async ({ pageParam = 1 }): Promise<Store[]> => {
// 로딩 시뮬레이션
await new Promise((resolve) => setTimeout(resolve, 300));
// 실제 서버 응답 구조
interface ServerResponse {
success: boolean;
response: {
hasNext: boolean;
storeReadDtos: Store[];
};
}

const stores: Store[] = Array.from({ length: 20 }, (_, index) => {
const globalIndex = (pageParam - 1) * 20 + index;
return {
id: globalIndex + 1,
storeName: `주점${globalIndex + 1}`,
department: globalIndex % 2 === 0 ? "컴퓨터공학과" : "경영학과",
status: globalIndex % 3 === 0 ? "closed" : "open",
waitingCount: Math.floor(Math.random() * 10) + 1,
};
});
// 서버에서 주점 데이터를 가져오는 함수
const fetchStores = async ({ pageParam = 0 }): Promise<Store[]> => {
try {
const SERVER_URI = import.meta.env.VITE_SERVER_URI;
const response = await axios.get<ServerResponse>(
`${SERVER_URI}/admin/stores/all-stores`,
{
params: {
page: pageParam,
size: 20,
},
}
);

console.log("서버 응답 전체:", response.data);

// 서버 응답 구조에 맞게 데이터 추출
if (response.data.success && response.data.response?.storeReadDtos) {
const storeArray = response.data.response.storeReadDtos;
console.log("추출된 주점 배열:", storeArray);

// 삭제된 주점 필터링하고 실제 서버 데이터 그대로 반환
const stores: Store[] = storeArray
.filter((store) => store && !store.deleted)
.map((store) => ({
...store,
waitingCount: undefined, // 대기인원 API 연동 시 추가 예정
}));

return stores;
console.log("필터링된 주점 데이터:", stores);
return stores;
} else {
console.warn(
"서버 응답이 성공하지 못했거나 데이터가 없습니다:",
response.data
);
return [];
}
} catch (error) {
console.error("주점 데이터 로딩 실패:", error);
if (axios.isAxiosError(error)) {
console.error("응답 상태:", error.response?.status);
console.error("응답 데이터:", error.response?.data);
}
return [];
}
};

export const useInfiniteStores = () => {
// 무한 스크롤을 위한 useInfiniteQuery
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery({
queryKey: ["stores"],
queryFn: fetchStores,
initialPageParam: 1,
getNextPageParam: (_lastPage, allPages) =>
allPages.length < 10 ? allPages.length + 1 : undefined,
});
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
error,
isLoading,
} = useInfiniteQuery({
queryKey: ["stores"],
queryFn: fetchStores,
initialPageParam: 0,
getNextPageParam: (lastPage, allPages) => {
// 서버에서 hasNext를 제공하지만, 일단 기존 로직 유지
// 더 이상 데이터가 없으면 undefined 반환
if (lastPage.length < 20) {
return undefined;
}
return allPages.length;
},
retry: 3, // 실패 시 3번 재시도
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
});

// 모든 페이지의 stores를 하나의 배열로 합치기
const stores = data?.pages.flat() ?? [];

// 초기 로딩 시 자동으로 두 번째 페이지도 로딩
// 에러 로깅
useEffect(() => {
if (stores.length === 20 && hasNextPage && !isFetchingNextPage) {
console.log("초기 로딩: 두 번째 페이지 자동 로딩");
fetchNextPage();
if (error) {
console.error("주점 데이터 로딩 에러:", error);
}
}, [stores.length, hasNextPage, isFetchingNextPage, fetchNextPage]);
}, [error]);

return {
stores,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
error,
};
};

Expand Down
Loading