File name
Commit message
Commit date
File name
Commit message
Commit date
09-03
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
'use client';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
import { FoxBadge, type FoxBadgeColor } from '@fox/core/components/fox-badge';
import { FoxButton } from '@fox/core/components/fox-button';
import { FoxButtonGroup } from '@fox/core/components/fox-button-group';
import {
FoxListContainer,
type FoxListColumn,
} from '@fox/core/components/fox-list-container';
import { FoxSelectText } from '@fox/core/components/fox-select-text';
import {
formatAdminRoleLabel,
formatOptionalValue,
type AdminMember,
} from '@/lib/domain/admin-member';
import {
ADMIN_MEMBERS_EXCEL_PATH,
ADMIN_MEMBER_PAGE_SIZE_OPTIONS,
ADMIN_MEMBER_SEARCH_FIELD_OPTIONS,
ADMIN_MEMBER_SORT_OPTIONS,
buildAdminMemberHref,
type AdminMemberPageSize,
type AdminMemberQuery,
type AdminMemberSearchField,
type AdminMemberSortOption,
} from '@/lib/domain/admin-member-query';
import { AdminMemberCreateModal } from './admin-member-create-modal';
import { AdminMemberRowActions } from './admin-member-row-actions';
interface AdminMemberListProps {
items: AdminMember[];
query: AdminMemberQuery;
currentPage: number;
totalPages: number;
totalCount: number;
currentAdminId: string;
}
const FILL_COLUMN_WIDTH = 277;
const BADGE_COLOR_BY_ROLE: Record<string, FoxBadgeColor> = {
ROLE_SYSTEM: 'accent',
ROLE_ADMIN: 'primary',
};
export function AdminMemberList({
items,
query,
currentPage,
totalPages,
totalCount,
currentAdminId,
}: AdminMemberListProps) {
const router = useRouter();
const [isCreateOpen, setIsCreateOpen] = useState(false);
function go(patch: Partial<AdminMemberQuery>) {
router.replace(buildAdminMemberHref(query, { ...patch, page: 1 }));
}
const offset = (currentPage - 1) * query.pageSize;
const columns: FoxListColumn<AdminMember>[] = [
{
key: 'no',
header: '번호',
width: 80,
render: (_row, index) => totalCount - offset - index,
},
{ key: 'name', header: '이름', width: FILL_COLUMN_WIDTH },
{ key: 'loginId', header: '아이디', width: FILL_COLUMN_WIDTH },
{
key: 'phoneNumber',
header: '휴대전화번호',
width: 200,
render: (row) => formatOptionalValue(row.phoneNumber),
},
{
key: 'email',
header: '이메일',
width: FILL_COLUMN_WIDTH,
render: (row) => formatOptionalValue(row.email),
},
{
key: 'role',
header: '역할',
width: 120,
render: (row) => (
<FoxBadge
type="pastel"
color={BADGE_COLOR_BY_ROLE[row.roleCode] ?? 'neutral'}
size="md"
>
{formatAdminRoleLabel(row.roleCode)}
</FoxBadge>
),
},
{
key: 'createdAt',
header: '생성일',
width: 160,
render: (row) => formatOptionalValue(row.createdAt),
},
{
key: 'actions',
header: '관리',
width: 160,
render: (row) => (
<AdminMemberRowActions
member={row}
isSelf={row.id === currentAdminId}
/>
),
},
];
return (
<>
<FoxListContainer<AdminMember>
title="관리자 회원 목록"
breadcrumb={[
{ label: '홈', href: '/' },
{ label: '회원관리' },
{ label: '관리자 회원' },
]}
caption="관리자 회원 목록"
columns={columns}
rows={items}
rowKey={(row) => row.id}
totalCount={totalCount}
emptyMessage="검색 결과가 없습니다. 다른 검색어로 다시 시도해 주세요."
sorts={
<>
<FoxSelectText
size="sm"
ariaLabel="정렬"
icon={<i className="fox-ico fox-ico-ArrowsDownUp" aria-hidden="true" />}
options={ADMIN_MEMBER_SORT_OPTIONS.map((option) => ({
value: option.value,
label: option.label,
}))}
value={query.sort}
onValueChange={(value) => go({ sort: value as AdminMemberSortOption })}
/>
<FoxSelectText
size="sm"
ariaLabel="페이지 크기"
options={ADMIN_MEMBER_PAGE_SIZE_OPTIONS.map((size) => ({
value: String(size),
label: `${size}개씩 보기`,
}))}
value={String(query.pageSize)}
onValueChange={(value) =>
go({ pageSize: Number(value) as AdminMemberPageSize })
}
/>
</>
}
searchFields={ADMIN_MEMBER_SEARCH_FIELD_OPTIONS.map((option) => ({
value: option.value,
label: option.label,
}))}
searchField={query.searchField}
keyword={query.keyword}
toolbarActions={
<FoxButtonGroup size="md">
<form action={ADMIN_MEMBERS_EXCEL_PATH} method="get">
<FoxButton
type="secondary"
size="md"
htmlType="submit"
leadingIcon={<i className="fox-ico fox-ico-DownloadSimple" aria-hidden="true" />}
label="엑셀 다운로드"
/>
</form>
<FoxButton
type="primary"
size="md"
leadingIcon={<i className="fox-ico fox-ico-Plus" aria-hidden="true" />}
label="신규 등록"
onAction={() => setIsCreateOpen(true)}
/>
</FoxButtonGroup>
}
page={currentPage}
totalPages={totalPages}
buildHref={(patch) =>
buildAdminMemberHref(query, { page: Number(patch.page ?? 1) })
}
onQueryChange={(patch) => {
if (patch.keyword !== undefined || patch.searchField !== undefined) {
go({
keyword: String(patch.keyword ?? ''),
searchField: (patch.searchField ??
query.searchField) as AdminMemberSearchField,
});
}
}}
/>
{isCreateOpen && (
<AdminMemberCreateModal onClose={() => setIsCreateOpen(false)} />
)}
</>
);
}