o2o-negosium-original/negodata/front/src/features/members/components/MemberTable.tsx
Mina Choi c62b66e35b [feat] negodata: 소유권 게이팅 + 견적 낙찰 + 작성자명 표시 + 전화입력 + 카드 엑셀
- 소유권 게이팅(common/authz): 변경 액션 본인∪OWNER, 협력사 삭제 OWNER 전용
- 견적 수동 낙찰(award) + 작성자명(creatorName) 표시 + 전화번호 입력 컴포넌트 + 카드 엑셀 업로드
- supplier_type 은 이번 커밋 미변경(다음 커밋에서 코드부터 정리 예정)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:18:33 +09:00

105 lines
3.1 KiB
TypeScript

import { Badge } from '@/components/ui/badge';
import { DataTable } from '@/components/ui/data-table';
import { TablePagination } from '@/components/ui/table-pagination';
import { USER_ROLE_LABEL } from '@/lib/enumLabels';
import { formatPhoneKR } from '@/lib/phone';
import { UserStatus, USER_STATUS_LABEL, type Member } from '../types';
type MemberTableProps = {
data: Member[];
onRowClick: (member: Member) => void;
page: number;
totalPages: number;
totalCount: number;
pageSize: number;
onPageChange: (page: number) => void;
};
const statusBadgeClass = (status: number) =>
status === UserStatus.ACTIVE
? 'bg-emerald-50 text-emerald-700 dark:bg-emerald-950/20 dark:text-emerald-400 border border-emerald-200'
: 'bg-zinc-100 text-zinc-500 border border-zinc-300';
const fmtDate = (v?: string | null) => (v ? new Date(v).toLocaleDateString('sv-SE') : '-');
export function MemberTable({
data,
onRowClick,
page,
totalPages,
totalCount,
pageSize,
onPageChange,
}: MemberTableProps) {
return (
<DataTable
data={data}
rowKey={(m) => m.user_id}
onRowClick={onRowClick}
empty="등록된 회사 계정이 없습니다."
footer={
<TablePagination
page={page}
totalPages={totalPages}
totalCount={totalCount}
pageSize={pageSize}
onPageChange={onPageChange}
label="회사 계정"
unit="명"
/>
}
columns={[
{
header: '로그인 ID',
align: 'left',
headClassName: 'w-1/5',
cell: (m) => <span className="font-mono font-bold text-sm text-foreground">{m.id}</span>,
},
{
header: '이름',
align: 'left',
cell: (m) => <span className="font-semibold text-foreground">{m.name || '-'}</span>,
},
{
header: '이메일 / 연락처',
align: 'left',
mobileBlock: true,
cell: (m) => (
<div className="space-y-0.5 font-mono text-xs">
<div className="text-foreground">{m.email || '-'}</div>
<div className="text-[10px] text-muted-foreground">{m.contact_number ? formatPhoneKR(m.contact_number) : '-'}</div>
</div>
),
},
{
header: '권한',
align: 'center',
cell: (m) => (
<Badge variant="outline" className="px-2 py-0.5 text-[10px] font-bold rounded-full">
{USER_ROLE_LABEL[m.role]}
</Badge>
),
},
{
header: '상태',
align: 'center',
cell: (m) => (
<Badge
variant="outline"
className={`px-2 py-0.5 text-[10px] font-bold rounded-full ${statusBadgeClass(m.status)}`}
>
{USER_STATUS_LABEL[m.status]}
</Badge>
),
},
{
header: '최근 접속',
align: 'center',
cellClassName: 'font-mono text-[10px] text-muted-foreground',
cell: (m) => fmtDate(m.last_accessed_at),
},
]}
/>
);
}