Skip to content

Commit c461f8d

Browse files
authored
Merge pull request #195 from CSE-Shaco/develop
fix(user-admin): 렌더링 오류 500 에러 해결
2 parents a25bfbb + 16de800 commit c461f8d

File tree

1 file changed

+119
-111
lines changed

1 file changed

+119
-111
lines changed

src/app/admin/member-manager/page.jsx

Lines changed: 119 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -288,117 +288,125 @@ export default function AdminUsersPage() {
288288
</TableColumn>)}
289289
</TableHeader>
290290

291-
<TableBody items={rows} isLoading={loading} loadingContent={<Spinner label="불러오는 중..."/>}
292-
emptyContent={err || '데이터가 없습니다.'}>
293-
{(user) => (<TableRow key={user.id} className="hover:bg-[#35353b99]">
294-
{/* NAME */}
295-
<TableCell>
296-
<span className="font-medium mr-2">{user.name}</span>
297-
<Chip size="sm" variant="flat" color={roleColor(user.userRole)}>
298-
{user.userRole}
299-
</Chip>
300-
</TableCell>
301-
302-
{/* MAJOR */}
303-
<TableCell>{user.major}</TableCell>
304-
305-
{/* STUDENT ID */}
306-
<TableCell>{user.studentId}</TableCell>
307-
308-
{/* EMAIL */}
309-
<TableCell>
310-
<span className="text-sm">{user.email}</span>
311-
</TableCell>
312-
313-
{/* ROLE */}
314-
<TableCell>
315-
<div className="flex items-center gap-3">
316-
<Select
317-
aria-label="역할 수정"
318-
selectedKeys={new Set([user.userRole || ''])}
319-
onSelectionChange={(keys) => {
320-
const nextRole = String(Array.from(keys)[0] || user.userRole);
321-
if (nextRole !== user.userRole) {
322-
const ok = confirm(`역할을 '${user.userRole}' → '${nextRole}' 로 변경할까요?`);
323-
if (ok) void patchSmart({user, nextRole, nextTeam: user.team ?? null});
324-
}
325-
}}
326-
size="sm"
327-
className="min-w-[140px]"
328-
classNames={{
329-
trigger: 'bg-zinc-900 text-white border border-zinc-700 data-[hover=true]:bg-zinc-800',
330-
value: 'text-white',
331-
popoverContent: 'bg-zinc-900 border border-zinc-700',
332-
listbox: 'text-white',
333-
selectorIcon: 'text-zinc-400',
334-
}}
335-
itemClasses={{
336-
base: 'rounded-md data-[hover=true]:bg-zinc-800 data-[focus=true]:bg-zinc-800',
337-
title: 'text-white',
338-
}}
339-
>
340-
{ROLE_OPTIONS.map((r) => (<SelectItem key={r} value={r}>
341-
{r}
342-
</SelectItem>))}
343-
</Select>
344-
</div>
345-
</TableCell>
346-
347-
{/* TEAM */}
348-
<TableCell>
349-
<Select
350-
aria-label="팀 수정"
351-
selectedKeys={new Set([user.team || ''])}
352-
onSelectionChange={(keys) => {
353-
const k = String(Array.from(keys)[0] ?? '');
354-
const nextTeam = k === '' ? null : k; // 서버에는 enum name로 전송
355-
if ((nextTeam ?? null) !== (user.team ?? null)) {
356-
const old = user.team ? TEAM_LABEL[user.team] || user.team : '(없음)';
357-
const neu = nextTeam ? TEAM_LABEL[nextTeam] || nextTeam : '(없음)';
358-
const ok = confirm(`팀을 '${old}' → '${neu}' 로 변경할까요?`);
359-
if (ok) void patchSmart({user, nextRole: user.userRole, nextTeam});
360-
}
361-
}}
362-
size="sm"
363-
className="min-w-[160px]"
364-
classNames={{
365-
trigger: 'bg-zinc-900 text-white border border-zinc-700 data-[hover=true]:bg-zinc-800',
366-
value: 'text-white',
367-
popoverContent: 'bg-zinc-900 border border-zinc-700',
368-
listbox: 'text-white',
369-
selectorIcon: 'text-zinc-400',
370-
}}
371-
itemClasses={{
372-
base: 'rounded-md data-[hover=true]:bg-zinc-800 data-[focus=true]:bg-zinc-800',
373-
title: 'text-white',
374-
}}
375-
>
376-
<SelectItem key="" value="">
377-
(없음)
378-
</SelectItem>
379-
{TEAM_ENUM_VALUES.map((t) => (<SelectItem key={t} value={t}>
380-
{TEAM_LABEL[t]}
381-
</SelectItem>))}
382-
</Select>
383-
</TableCell>
384-
385-
{/* ACTIONS (Delete) */}
386-
{canRenderDelete ? (<TableCell>
387-
<div className="flex justify-end">
388-
{user.id !== me?.id ? (<Button
389-
size="sm"
390-
color="danger"
391-
variant="flat"
392-
onClick={(e) => {
393-
e.stopPropagation();
394-
void handleDelete(user);
395-
}}
396-
>
397-
삭제
398-
</Button>) : null}
399-
</div>
400-
</TableCell>) : null}
401-
</TableRow>)}
291+
<TableBody
292+
items={rows}
293+
isLoading={loading}
294+
loadingContent={<Spinner label="불러오는 중..."/>}
295+
emptyContent={err || '데이터가 없습니다.'}
296+
>
297+
{(user) => {
298+
const cells = [// NAME
299+
(<TableCell key="name">
300+
<span className="font-medium mr-2">{user.name}</span>
301+
<Chip size="sm" variant="flat" color={roleColor(user.userRole)}>
302+
{user.userRole}
303+
</Chip>
304+
</TableCell>),
305+
306+
// MAJOR
307+
<TableCell key="major">{user.major}</TableCell>,
308+
309+
// STUDENT ID
310+
<TableCell key="studentId">{user.studentId}</TableCell>,
311+
312+
// EMAIL
313+
(<TableCell key="email">
314+
<span className="text-sm">{user.email}</span>
315+
</TableCell>),
316+
317+
// ROLE
318+
(<TableCell key="role">
319+
<div className="flex items-center gap-3">
320+
<Select
321+
aria-label="역할 수정"
322+
selectedKeys={new Set([user.userRole || ''])}
323+
onSelectionChange={(keys) => {
324+
const nextRole = String(Array.from(keys)[0] || user.userRole);
325+
if (nextRole !== user.userRole) {
326+
const ok = confirm(`역할을 '${user.userRole}' → '${nextRole}' 로 변경할까요?`);
327+
if (ok) void patchSmart({user, nextRole, nextTeam: user.team ?? null});
328+
}
329+
}}
330+
size="sm"
331+
className="min-w-[140px]"
332+
classNames={{
333+
trigger: 'bg-zinc-900 text-white border border-zinc-700 data-[hover=true]:bg-zinc-800',
334+
value: 'text-white',
335+
popoverContent: 'bg-zinc-900 border border-zinc-700',
336+
listbox: 'text-white',
337+
selectorIcon: 'text-zinc-400',
338+
}}
339+
itemClasses={{
340+
base: 'rounded-md data-[hover=true]:bg-zinc-800 data-[focus=true]:bg-zinc-800',
341+
title: 'text-white',
342+
}}
343+
>
344+
{ROLE_OPTIONS.map((r) => (<SelectItem key={r} value={r}>
345+
{r}
346+
</SelectItem>))}
347+
</Select>
348+
</div>
349+
</TableCell>),
350+
351+
// TEAM
352+
(<TableCell key="team">
353+
<Select
354+
aria-label="팀 수정"
355+
selectedKeys={new Set([user.team || ''])}
356+
onSelectionChange={(keys) => {
357+
const k = String(Array.from(keys)[0] ?? '');
358+
const nextTeam = k === '' ? null : k; // 서버에는 enum name 전송
359+
if ((nextTeam ?? null) !== (user.team ?? null)) {
360+
const old = user.team ? TEAM_LABEL[user.team] || user.team : '(없음)';
361+
const neu = nextTeam ? TEAM_LABEL[nextTeam] || nextTeam : '(없음)';
362+
const ok = confirm(`팀을 '${old}' → '${neu}' 로 변경할까요?`);
363+
if (ok) void patchSmart({user, nextRole: user.userRole, nextTeam});
364+
}
365+
}}
366+
size="sm"
367+
className="min-w-[160px]"
368+
classNames={{
369+
trigger: 'bg-zinc-900 text-white border border-zinc-700 data-[hover=true]:bg-zinc-800',
370+
value: 'text-white',
371+
popoverContent: 'bg-zinc-900 border border-zinc-700',
372+
listbox: 'text-white',
373+
selectorIcon: 'text-zinc-400',
374+
}}
375+
itemClasses={{
376+
base: 'rounded-md data-[hover=true]:bg-zinc-800 data-[focus=true]:bg-zinc-800',
377+
title: 'text-white',
378+
}}
379+
>
380+
<SelectItem key="" value="">
381+
(없음)
382+
</SelectItem>
383+
{TEAM_ENUM_VALUES.map((t) => (<SelectItem key={t} value={t}>
384+
{TEAM_LABEL[t]}
385+
</SelectItem>))}
386+
</Select>
387+
</TableCell>),
388+
389+
// ACTIONS (조건부)
390+
canRenderDelete && (<TableCell key="actions">
391+
<div className="flex justify-end">
392+
{user.id !== me?.id ? (<Button
393+
size="sm"
394+
color="danger"
395+
variant="flat"
396+
onClick={(e) => {
397+
e.stopPropagation();
398+
void handleDelete(user);
399+
}}
400+
>
401+
삭제
402+
</Button>) : null}
403+
</div>
404+
</TableCell>),].filter(Boolean);
405+
406+
return (<TableRow key={user.id} className="hover:bg-[#35353b99]">
407+
{cells}
408+
</TableRow>);
409+
}}
402410
</TableBody>
403411
</Table>
404412
</div>);

0 commit comments

Comments
 (0)