Skip to content

Commit de030ea

Browse files
authored
Merge pull request #160 from GDGoCINHA/develop
Feat: core-recruit 페이지 추가
2 parents 8d37662 + d2bfa92 commit de030ea

File tree

8 files changed

+1476
-9
lines changed

8 files changed

+1476
-9
lines changed

package-lock.json

Lines changed: 389 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"react-dom": "^18.2.0",
2929
"react-error-boundary": "^6.0.0",
3030
"react-icons": "^5.4.0",
31+
"recharts": "^3.1.2",
3132
"type-hangul": "^0.2.4"
3233
},
3334
"devDependencies": {

src/app/admin/page.jsx

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import UserDetailsModal from '@/components/admin/UserDetailsModal';
88
import AdminTableCell from '@/components/admin/AdminTableCell';
99
import AdminTableTopContent from '@/components/admin/AdminTableTopContent';
1010
import AdminTableBottomContent from '@/components/admin/AdminTableBottomContent';
11+
import AdminDashboard from '@/components/admin/AdminDashboard';
1112

1213
import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
1314

@@ -36,6 +37,12 @@ export default function Page() {
3637
const [totalUsers, setTotalUsers] = React.useState(0);
3738
const [totalPages, setTotalPages] = React.useState(0);
3839

40+
// 대시보드 통계용 전체 멤버 데이터
41+
const [statsUsers, setStatsUsers] = React.useState([]);
42+
const [statsTotal, setStatsTotal] = React.useState(0);
43+
const [statsLoading, setStatsLoading] = React.useState(false);
44+
const [statsError, setStatsError] = React.useState('');
45+
3946
const rowsPerPage = 10; //한 페이지당 표시될 유저 수
4047

4148
//유저 데이터 조회
@@ -67,6 +74,45 @@ export default function Page() {
6774
}
6875
}, [apiClient, page, rowsPerPage, query]);
6976

77+
// 통계용 전체 멤버 데이터 조회 (페이지네이션 병렬 수집)
78+
const fetchAllUsersForStats = useCallback(async () => {
79+
setStatsLoading(true);
80+
setStatsError('');
81+
try {
82+
const pageSize = 200;
83+
const baseParams = { size: pageSize, sort: 'createdAt', dir: 'DESC' };
84+
85+
const firstRes = await apiClient.get('/recruit/members', { params: { ...baseParams, page: 0 } });
86+
const firstList = Array.isArray(firstRes?.data?.data) ? firstRes.data.data : [];
87+
const total = firstRes?.data?.meta?.totalElements ?? firstList.length;
88+
const totalPagesForStats = Math.max(1, Math.ceil(total / pageSize));
89+
90+
let all = firstList;
91+
if (totalPagesForStats > 1) {
92+
const promises = [];
93+
for (let p = 1; p < totalPagesForStats; p++) {
94+
promises.push(apiClient.get('/recruit/members', { params: { ...baseParams, page: p } }));
95+
}
96+
const results = await Promise.allSettled(promises);
97+
results.forEach((res) => {
98+
if (res.status === 'fulfilled') {
99+
const list = Array.isArray(res.value?.data?.data) ? res.value.data.data : [];
100+
all = all.concat(list);
101+
}
102+
});
103+
}
104+
105+
setStatsUsers(all);
106+
setStatsTotal(total);
107+
} catch (e) {
108+
setStatsUsers([]);
109+
setStatsTotal(0);
110+
setStatsError(String(e?.message || 'failed to load stats'));
111+
} finally {
112+
setStatsLoading(false);
113+
}
114+
}, [apiClient]);
115+
70116
//회비 지불여부 체크박스
71117
const handleTogglePay = useCallback(
72118
async (userId, nextValue) => {
@@ -159,11 +205,15 @@ export default function Page() {
159205
fetchUsers();
160206
}, [fetchUsers]);
161207

208+
useEffect(() => {
209+
fetchAllUsersForStats();
210+
}, [fetchAllUsersForStats]);
211+
162212
return (
163213
<>
164214
<div>
165215
<Table
166-
className='dark py-[30px] px-[96px] mobile:px-[10px]'
216+
className='dark text-white py-[30px] px-[96px] mobile:px-[10px]'
167217
aria-label='Example table with custom cells'
168218
bottomContent={
169219
<div className='relative'>
@@ -197,7 +247,7 @@ export default function Page() {
197247
>
198248
{(item) => (
199249
<TableRow
200-
className='hover:bg-[#35353b99] cursor-pointer'
250+
className='hover:bg-[#35353b99] cursor-pointer text-white'
201251
key={item.member?.id ?? item.id}
202252
onClick={() => handleRowClick(item)}
203253
>
@@ -212,6 +262,15 @@ export default function Page() {
212262
</Table>
213263

214264
<UserDetailsModal user={selectedUser} isOpen={modalOpen} onClose={handleCloseModal} preventClose />
265+
266+
{/* 대시보드 */}
267+
{/* {statsError ? (
268+
<div className='px-[96px] mobile:px-[10px] text-red-400 py-4'>대시보드 로드 실패: {statsError}</div>
269+
) : statsLoading ? (
270+
<div className='px-[96px] mobile:px-[10px] text-white py-4'>대시보드 불러오는 중...</div>
271+
) : (
272+
<AdminDashboard members={statsUsers} totalCount={statsTotal} />
273+
)} */}
215274
</div>
216275
</>
217276
);

src/app/core-recruit/layout.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Suspense } from "react";
2+
import Loader from '@/components/ui/common/Loader.jsx';
3+
4+
export const metadata = {
5+
title: "Core Recruit",
6+
description: "GDGoC INHA Core Member Recruitment Form",
7+
};
8+
9+
export default function CoreRecruitLayout({ children }) {
10+
return (
11+
<Suspense fallback={ <Loader /> }>
12+
{children}
13+
</Suspense>
14+
);
15+
}
16+
17+

0 commit comments

Comments
 (0)