Skip to content

Commit d1d1e81

Browse files
committed
Feat: Admin 회비 지불 여부 토글 기능 추가 #6
1 parent 6c91ae1 commit d1d1e81

File tree

2 files changed

+87
-20
lines changed

2 files changed

+87
-20
lines changed

src/app/admin/page.jsx

Lines changed: 74 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const columns = [
1515
{ name: 'NAME', uid: 'name' },
1616
{ name: 'MAJOR / ID', uid: 'major' },
1717
{ name: 'PAYMENT', uid: 'isPayed' },
18+
{ name: 'TOGGLE', uid: 'togglePay' },
1819
];
1920

2021
const statusColorMap = {
@@ -42,6 +43,7 @@ export default function Page() {
4243

4344
const rowsPerPage = 10; //한 페이지당 표시될 유저 수
4445

46+
//유저 데이터 조회
4547
const fetchUsers = useCallback(async () => {
4648
setLoading(true);
4749
setError('');
@@ -70,22 +72,69 @@ export default function Page() {
7072
}
7173
}, [apiClient, page, rowsPerPage, query]);
7274

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-
}, []);
84-
85-
const handleRowClick = (user) => {
75+
//회비 지불여부 체크박스
76+
const handleTogglePay = useCallback(
77+
async (userId, nextValue) => {
78+
const getItemId = (user) => user?.id;
79+
const prevUsers = currentUsers;
80+
81+
setCurrentUsers((prev) =>
82+
prev.map((u) => {
83+
if (getItemId(u) === userId) {
84+
const updated = { ...u, isPayed: nextValue };
85+
return updated;
86+
}
87+
return u;
88+
})
89+
);
90+
91+
try {
92+
await apiClient.patch(`/recruit/members/${userId}/payment`, { isPayed: nextValue });
93+
} catch (err) {
94+
// rollback
95+
setCurrentUsers(prevUsers);
96+
alert('결제 상태 변경에 실패했습니다. 다시 시도해주세요.');
97+
}
98+
},
99+
[apiClient, currentUsers]
100+
);
101+
102+
//테이블 요소
103+
const renderCell = useCallback(
104+
(user, columnKey) => {
105+
const normalizedUser = {
106+
...user,
107+
name: user?.name ?? '',
108+
major: user?.major ?? '',
109+
studentId: user?.studentId ?? '',
110+
isPayed: typeof user?.isPayed === 'boolean' ? user.isPayed : '',
111+
phoneNumber: user?.phoneNumber ?? '',
112+
id: user?.id ?? user?.member?.id,
113+
memberId: user?.member?.id ?? user?.id,
114+
};
115+
return <AdminTableCell user={normalizedUser} columnKey={columnKey} onTogglePay={handleTogglePay} />;
116+
},
117+
[handleTogglePay]
118+
);
119+
120+
//유저 상세 정보
121+
const handleRowClick = async (user) => {
86122
if (modalClosing.current) return; // 모달이 닫히는 중에는 클릭 무시
87-
setSelectedUser(user);
88-
setModalOpen(true);
123+
try {
124+
const memberId = user?.id;
125+
if (!memberId) {
126+
throw new Error('멤버 ID를 확인할 수 없습니다.');
127+
}
128+
const res = await apiClient.get(`/recruit/members/${memberId}`);
129+
const detail = res?.data?.data ?? null;
130+
if (!detail) {
131+
throw new Error('상세 정보를 불러오지 못했습니다.');
132+
}
133+
setSelectedUser(detail);
134+
setModalOpen(true);
135+
} catch (e) {
136+
alert('상세 정보를 불러오는 중 오류가 발생했습니다.');
137+
}
89138
};
90139

91140
const handleSearch = () => {
@@ -127,7 +176,11 @@ export default function Page() {
127176
>
128177
<TableHeader columns={columns}>
129178
{(column) => (
130-
<TableColumn key={column.uid} align={column.uid === 'actions' ? 'center' : 'start'}>
179+
<TableColumn
180+
key={column.uid}
181+
align={['actions', 'togglePay'].includes(column.uid) ? 'center' : 'start'}
182+
className={column.uid === 'togglePay' ? 'text-center' : ''}
183+
>
131184
{column.name}
132185
</TableColumn>
133186
)}
@@ -143,7 +196,11 @@ export default function Page() {
143196
key={item.member?.id ?? item.id}
144197
onClick={() => handleRowClick(item)}
145198
>
146-
{(columnKey) => <TableCell>{renderCell(item, columnKey)}</TableCell>}
199+
{(columnKey) => (
200+
<TableCell className={columnKey === 'togglePay' ? 'text-center' : ''}>
201+
{renderCell(item, columnKey)}
202+
</TableCell>
203+
)}
147204
</TableRow>
148205
)}
149206
</TableBody>

src/components/admin/AdminTableCell.jsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
"use client";
22

33
import React from 'react';
4-
import { User, Chip } from '@nextui-org/react';
4+
import { User, Chip, Checkbox } from '@nextui-org/react';
55

66
const statusColorMap = {
77
true: 'success',
88
false: 'danger',
99
};
1010

11-
export default function AdminTableCell({ user, columnKey }) {
12-
11+
export default function AdminTableCell({ user, columnKey, onTogglePay }) {
1312
const cellValue = user[columnKey];
1413

1514
switch (columnKey) {
@@ -39,6 +38,17 @@ export default function AdminTableCell({ user, columnKey }) {
3938
{user.isPayed ? '입금' : '미입금'}
4039
</Chip>
4140
);
41+
case 'togglePay':
42+
return (
43+
<div onClick={(e) => e.stopPropagation()} className='w-full flex justify-center items-center'>
44+
<div className='inline-flex justify-center items-center'>
45+
<Checkbox
46+
isSelected={!!user.isPayed}
47+
onValueChange={(checked) => onTogglePay?.(user.memberId ?? user.id, checked)}
48+
/>
49+
</div>
50+
</div>
51+
);
4252
default:
4353
return cellValue;
4454
}

0 commit comments

Comments
 (0)