0e895a90fa
백엔드: - V018 soft-delete (deleted_at 컬럼) + 휴지통/복구 흐름 - V019 미사용 컬럼 cleanup (V1 슬림 스코프) - DepartmentService.updateDepartment 에 parent_dept_code 사이클 가드 (자기 자신/자손을 부모로 지정 시도 차단) - DepartmentController, mapper 갱신 프론트: - 부서관리 페이지(deptMngList) UX 리디자인 - 트리 노드 ⋮ 컨텍스트 메뉴 (하위 추가, 다른 부서 아래로 이동, 정렬 4단계, 삭제) - 헤더 breadcrumb 으로 부서 위치 상시 표시 - 폼의 상위부서 row 제거 (트리 ⋮ 로 진입점 일원화) - 빈 상태 placeholder + X 닫기 동작 - 토글 버튼 토스 스타일 (아이콘 + 툴팁, 일정한 위치) - 부서유형 row 좁은 화면 가로 오버플로 fix - DepartmentPicker 신규 재사용 컴포넌트 (자손 자동 exclude, 사이클 차단) - 회사관리/프로비저닝 폼 개선 (Step1Basic, fields, CompanyTable, AdminPageRenderer) - companyList/[companyCode]/departments 구버전 페이지 삭제 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
137 lines
4.0 KiB
TypeScript
137 lines
4.0 KiB
TypeScript
import { Edit, Trash2, HardDrive, FileText, Users } from "lucide-react";
|
|
import { Company } from "@/types/company";
|
|
import { Button } from "@/components/ui/button";
|
|
import { ResponsiveDataView, RDVColumn, RDVCardField } from "@/components/common/ResponsiveDataView";
|
|
import { useRouter } from "next/navigation";
|
|
|
|
interface CompanyTableProps {
|
|
companies: Company[];
|
|
isLoading: boolean;
|
|
onEdit: (company: Company) => void;
|
|
onDelete: (company: Company) => void;
|
|
}
|
|
|
|
/**
|
|
* 회사 목록 테이블 컴포넌트
|
|
*/
|
|
export function CompanyTable({ companies, isLoading, onEdit, onDelete }: CompanyTableProps) {
|
|
const router = useRouter();
|
|
|
|
// 부서 관리 페이지로 이동 (legacy deptMngList 가 캐노니컬 페이지)
|
|
const handleManageDepartments = (_company: Company) => {
|
|
router.push(`/admin/userMng/deptMngList`);
|
|
};
|
|
|
|
// 디스크 사용량 포맷팅 함수
|
|
const formatDiskUsage = (company: Company) => {
|
|
if (!company.disk_usage) {
|
|
return (
|
|
<div className="text-muted-foreground flex items-center gap-1">
|
|
<HardDrive className="h-3 w-3" />
|
|
<span className="text-xs">정보 없음</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const { file_count: fileCount, total_size_mb: totalSizeMB } = company.disk_usage;
|
|
|
|
return (
|
|
<div className="flex flex-col gap-1">
|
|
<div className="flex items-center gap-1">
|
|
<FileText className="text-primary h-3 w-3" />
|
|
<span className="text-xs font-medium">{fileCount}개 파일</span>
|
|
</div>
|
|
<div className="flex items-center gap-1">
|
|
<HardDrive className="text-primary h-3 w-3" />
|
|
<span className="text-xs">{totalSizeMB.toFixed(1)} MB</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
// 데스크톱 테이블 컬럼 정의
|
|
const columns: RDVColumn<Company>[] = [
|
|
{
|
|
key: "company_code",
|
|
label: "회사코드",
|
|
width: "150px",
|
|
render: (value) => <span className="font-mono">{value}</span>,
|
|
},
|
|
{
|
|
key: "company_name",
|
|
label: "회사명",
|
|
render: (value) => <span className="font-medium">{value}</span>,
|
|
},
|
|
{
|
|
key: "writer",
|
|
label: "등록자",
|
|
width: "200px",
|
|
},
|
|
{
|
|
key: "diskUsage",
|
|
label: "디스크 사용량",
|
|
hideOnMobile: true,
|
|
render: (_value, row) => formatDiskUsage(row),
|
|
},
|
|
];
|
|
|
|
// 모바일 카드 필드 정의
|
|
const cardFields: RDVCardField<Company>[] = [
|
|
{
|
|
label: "작성자",
|
|
render: (company) => <span className="font-medium">{company.writer}</span>,
|
|
},
|
|
{
|
|
label: "디스크 사용량",
|
|
render: (company) => formatDiskUsage(company),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<ResponsiveDataView<Company>
|
|
data={companies}
|
|
columns={columns}
|
|
keyExtractor={(c) => c.regdate + c.company_code}
|
|
isLoading={isLoading}
|
|
emptyMessage="등록된 회사가 없습니다."
|
|
skeletonCount={10}
|
|
cardTitle={(c) => c.company_name}
|
|
cardSubtitle={(c) => <span className="font-mono">{c.company_code}</span>}
|
|
cardFields={cardFields}
|
|
actionsLabel="작업"
|
|
actionsWidth="180px"
|
|
renderActions={(company) => (
|
|
<>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => handleManageDepartments(company)}
|
|
className="h-8 w-8"
|
|
aria-label="부서관리"
|
|
>
|
|
<Users className="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => onEdit(company)}
|
|
className="h-8 w-8"
|
|
aria-label="수정"
|
|
>
|
|
<Edit className="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={() => onDelete(company)}
|
|
className="text-destructive hover:bg-destructive/10 hover:text-destructive h-8 w-8"
|
|
aria-label="삭제"
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</>
|
|
)}
|
|
/>
|
|
);
|
|
}
|