- 소유권 게이팅(common/authz): 변경 액션 본인∪OWNER, 협력사 삭제 OWNER 전용 - 견적 수동 낙찰(award) + 작성자명(creatorName) 표시 + 전화번호 입력 컴포넌트 + 카드 엑셀 업로드 - supplier_type 은 이번 커밋 미변경(다음 커밋에서 코드부터 정리 예정) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
105 lines
3.1 KiB
TypeScript
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),
|
|
},
|
|
]}
|
|
/>
|
|
);
|
|
}
|