Skip to content

Commit 009095c

Browse files
committed
Refactor: Admin 페이지에서 API 호출 및 데이터 처리 로직 개선, 검색 기능 활성화 #6
1 parent 9b53775 commit 009095c

File tree

3 files changed

+80
-30
lines changed

3 files changed

+80
-30
lines changed

src/app/admin/page.jsx

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import AdminTableCell from '@/components/admin/AdminTableCell';
99
import AdminTableTopContent from '@/components/admin/AdminTableTopContent';
1010
import AdminTableBottomContent from '@/components/admin/AdminTableBottomContent';
1111

12-
import { users } from '@/mock/users';
12+
import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
1313

1414
const columns = [
1515
{ name: 'NAME', uid: 'name' },
1616
{ name: 'MAJOR / ID', uid: 'major' },
17-
{ name: 'PAYMENT', uid: 'status' },
17+
{ name: 'PAYMENT', uid: 'isPayed' },
1818
];
1919

2020
const statusColorMap = {
@@ -24,31 +24,63 @@ const statusColorMap = {
2424

2525
export default function Page() {
2626
const router = useRouter();
27+
const { apiClient } = useAuthenticatedApi();
2728

28-
// 추후 users 를 사용해 데이터를 받아오고, totalUsers를 사용해 페이지네이션 만들 예정
29-
const [page, setPage] = React.useState(1); // 현재 페이지 상태 (추후 페이지 상태에 따라 api 통신으로 데이터 불러오기)
29+
const [page, setPage] = React.useState(1);
3030

3131
const [modalOpen, setModalOpen] = React.useState(false); // 모달 열림 상태
3232
const modalClosing = useRef(false); // 모달이 닫히는 상태를 추적
3333

3434
const [selectedUser, setSelectedUser] = React.useState(null); // 선택된 사용자 데이터
3535
const [searchValue, setSearchValue] = React.useState(''); // 검색 입력 상태
36+
const [query, setQuery] = React.useState(''); // API 호출시 검색 내용
37+
const [loading, setLoading] = React.useState(false);
38+
const [error, setError] = React.useState('');
39+
const [currentUsers, setCurrentUsers] = React.useState([]);
40+
const [totalUsers, setTotalUsers] = React.useState(0);
41+
const [totalPages, setTotalPages] = React.useState(0);
3642

3743
const rowsPerPage = 10; //한 페이지당 표시될 유저 수
38-
const totalUsers = 110; //총 유저 수 (총 페이지 표시를 위함)
3944

40-
// 현재 페이지 데이터 계산 (임시)
41-
const currentUsers = React.useMemo(() => {
42-
const startIndex = (page - 1) * rowsPerPage;
43-
const endIndex = startIndex + rowsPerPage;
44-
return users.slice(startIndex, endIndex);
45-
}, [page, rowsPerPage]);
46-
47-
const totalPages = React.useMemo(() => Math.ceil(totalUsers / rowsPerPage), [totalUsers, rowsPerPage]);
48-
49-
const renderCell = useCallback((user, columnKey) =>
50-
<AdminTableCell user={user} columnKey={columnKey} />
51-
, []);
45+
const fetchUsers = useCallback(async () => {
46+
setLoading(true);
47+
setError('');
48+
try {
49+
const params = {
50+
page: page - 1,
51+
size: rowsPerPage,
52+
sort: 'createdAt',
53+
dir: 'DESC',
54+
question: query || undefined,
55+
};
56+
const res = await apiClient.get('/recruit/members', { params });
57+
const list = Array.isArray(res?.data?.data) ? res.data.data : [];
58+
const total = res?.data?.meta?.totalElements ?? list.length;
59+
const computedTotalPages = Math.max(1, Math.ceil(total / rowsPerPage));
60+
61+
setCurrentUsers(list);
62+
setTotalUsers(total);
63+
setTotalPages(computedTotalPages);
64+
} catch (err) {
65+
setError(String(err?.message || 'failed to load users'));
66+
setCurrentUsers([]);
67+
setTotalUsers(0);
68+
} finally {
69+
setLoading(false);
70+
}
71+
}, [apiClient, page, rowsPerPage, query]);
72+
73+
const renderCell = useCallback((user, columnKey) => {
74+
const normalizedUser = {
75+
...user,
76+
name: user?.name ?? '',
77+
major: user?.major ?? '',
78+
studentId: user?.studentId ?? '',
79+
isPayed: typeof user?.isPayed === 'boolean' ? user.isPayed : '',
80+
phoneNumber: user?.phoneNumber ?? '',
81+
};
82+
return <AdminTableCell user={normalizedUser} columnKey={columnKey} />;
83+
}, []);
5284

5385
const handleRowClick = (user) => {
5486
if (modalClosing.current) return; // 모달이 닫히는 중에는 클릭 무시
@@ -57,8 +89,8 @@ export default function Page() {
5789
};
5890

5991
const handleSearch = () => {
60-
//추후 api 연결 함수로 변경 예정
61-
console.log(searchValue); //임시
92+
setPage(1);
93+
setQuery((searchValue || '').trim());
6294
};
6395

6496
const handleCloseModal = () => {
@@ -69,6 +101,17 @@ export default function Page() {
69101
}, 300);
70102
};
71103

104+
useEffect(() => {
105+
if (searchValue === '' && query !== '') {
106+
setPage(1);
107+
setQuery('');
108+
}
109+
}, [searchValue, query]);
110+
111+
useEffect(() => {
112+
fetchUsers();
113+
}, [fetchUsers]);
114+
72115
return (
73116
<>
74117
<div>
@@ -89,11 +132,15 @@ export default function Page() {
89132
</TableColumn>
90133
)}
91134
</TableHeader>
92-
<TableBody items={currentUsers}>
135+
<TableBody
136+
items={currentUsers}
137+
isLoading={loading}
138+
emptyContent={loading ? '불러오는 중...' : '데이터가 없습니다.'}
139+
>
93140
{(item) => (
94141
<TableRow
95142
className='hover:bg-[#35353b99] cursor-pointer'
96-
key={item.member.id}
143+
key={item.member?.id ?? item.id}
97144
onClick={() => handleRowClick(item)}
98145
>
99146
{(columnKey) => <TableCell>{renderCell(item, columnKey)}</TableCell>}

src/components/admin/AdminTableCell.jsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ const statusColorMap = {
99
};
1010

1111
export default function AdminTableCell({ user, columnKey }) {
12-
const cellValue = user.member[columnKey];
12+
13+
const cellValue = user[columnKey];
1314

1415
switch (columnKey) {
1516
case 'name':
@@ -19,23 +20,23 @@ export default function AdminTableCell({ user, columnKey }) {
1920
avatarProps={{
2021
className: 'w-0 h-0 overflow-hidden',
2122
}}
22-
description={user.member.email}
23+
description={user.phoneNumber}
2324
name={cellValue}
2425
>
25-
{user.member.email}
26+
{user.phoneNumber}
2627
</User>
2728
);
2829
case 'major':
2930
return (
3031
<div className='flex flex-col'>
31-
<p className='text-white text-bold text-sm capitalize'>{user.member.majors.main}</p>
32-
<p className='text-bold text-sm capitalize text-default-400'>{user.member.studentId}</p>
32+
<p className='text-white text-bold text-sm capitalize'>{user.major}</p>
33+
<p className='text-bold text-sm capitalize text-default-400'>{user.studentId}</p>
3334
</div>
3435
);
35-
case 'status':
36+
case 'isPayed':
3637
return (
37-
<Chip className='capitalize' color={statusColorMap[user.member.isPayed]} size='sm' variant='flat'>
38-
{user.member.isPayed ? '입금' : '미입금'}
38+
<Chip className='capitalize' color={statusColorMap[user.isPayed]} size='sm' variant='flat'>
39+
{user.isPayed ? '입금' : '미입금'}
3940
</Chip>
4041
);
4142
default:

src/components/admin/UserDetailsModal.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export default function UserDetailsModal({ user, isOpen, onClose, preventClose }
1919

2020
if (!isOpen) return null;
2121

22+
const member = user?.member ?? user ?? {};
23+
2224
const rowStyle = 'border border-[#5b5b6699]';
2325
const cellStyle = 'p-3 font-bold text-gray-300';
2426
const valueStyle = 'p-3';
@@ -28,7 +30,7 @@ export default function UserDetailsModal({ user, isOpen, onClose, preventClose }
2830
<div className='fixed inset-0 flex items-center justify-center p-4 z-50 bg-black/50'>
2931
<div className='max-w-[600px] w-full max-h-[90vh] mobile:max-h-[80vh] bg-[#27272A] rounded-lg shadow-md p-6 overflow-y-auto'>
3032
<div className='text-lg font-bold text-center text-white mb-4'>User Details</div>
31-
33+
3234
{/* User Basic Info */}
3335
<table className='w-full border-collapse border text-white mb-6'>
3436
<tbody>

0 commit comments

Comments
 (0)