ERP-node/frontend/components/admin/UserAuthTable.tsx

255 lines
9.9 KiB
TypeScript
Raw Normal View History

2025-10-27 16:40:59 +09:00
"use client";
import React from "react";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Shield, ShieldCheck, User as UserIcon, Users, Building2 } from "lucide-react";
interface UserAuthTableProps {
users: any[];
isLoading: boolean;
paginationInfo: {
currentPage: number;
pageSize: number;
totalItems: number;
totalPages: number;
};
onEditAuth: (user: any) => void;
onPageChange: (page: number) => void;
}
/**
*
*
*
*/
export function UserAuthTable({ users, isLoading, paginationInfo, onEditAuth, onPageChange }: UserAuthTableProps) {
// 권한 레벨 표시
const getUserTypeInfo = (userType: string) => {
switch (userType) {
case "SUPER_ADMIN":
return {
label: "최고 관리자",
icon: <ShieldCheck className="h-3 w-3" />,
className: "bg-purple-100 text-purple-800 border-purple-300",
};
case "COMPANY_ADMIN":
return {
label: "회사 관리자",
icon: <Building2 className="h-3 w-3" />,
className: "bg-blue-100 text-blue-800 border-blue-300",
};
case "USER":
return {
label: "일반 사용자",
icon: <UserIcon className="h-3 w-3" />,
className: "bg-gray-100 text-gray-800 border-gray-300",
};
case "GUEST":
return {
label: "게스트",
icon: <Users className="h-3 w-3" />,
className: "bg-green-100 text-green-800 border-green-300",
};
case "PARTNER":
return {
label: "협력업체",
icon: <Shield className="h-3 w-3" />,
className: "bg-orange-100 text-orange-800 border-orange-300",
};
default:
return {
label: userType || "미지정",
icon: <UserIcon className="h-3 w-3" />,
className: "bg-gray-100 text-gray-800 border-gray-300",
};
}
};
// 행 번호 계산
const getRowNumber = (index: number) => {
return (paginationInfo.currentPage - 1) * paginationInfo.pageSize + index + 1;
};
// 로딩 스켈레톤
if (isLoading) {
return (
<div className="bg-card hidden rounded-lg border shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 border-b">
<TableHead className="h-12 w-[80px] text-center text-sm font-semibold">No</TableHead>
<TableHead className="h-12 text-sm font-semibold"> ID</TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
<TableHead className="h-12 text-center text-sm font-semibold"> </TableHead>
<TableHead className="h-12 w-[120px] text-center text-sm font-semibold"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{Array.from({ length: 10 }).map((_, index) => (
<TableRow key={index} className="border-b">
<TableCell className="h-16">
<div className="bg-muted h-4 animate-pulse rounded"></div>
</TableCell>
<TableCell className="h-16">
<div className="bg-muted h-4 animate-pulse rounded"></div>
</TableCell>
<TableCell className="h-16">
<div className="bg-muted h-4 animate-pulse rounded"></div>
</TableCell>
<TableCell className="h-16">
<div className="bg-muted h-4 animate-pulse rounded"></div>
</TableCell>
<TableCell className="h-16">
<div className="bg-muted h-4 animate-pulse rounded"></div>
</TableCell>
<TableCell className="h-16">
<div className="bg-muted mx-auto h-6 w-24 animate-pulse rounded-full"></div>
</TableCell>
<TableCell className="h-16">
<div className="bg-muted mx-auto h-8 w-20 animate-pulse rounded"></div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}
// 빈 상태
if (users.length === 0) {
return (
<div className="bg-card flex h-64 flex-col items-center justify-center rounded-lg border shadow-sm">
<p className="text-muted-foreground text-sm"> .</p>
</div>
);
}
// 실제 데이터 렌더링
return (
<div className="space-y-4">
{/* 데스크톱 테이블 */}
<div className="bg-card hidden rounded-lg border shadow-sm lg:block">
<Table>
<TableHeader>
<TableRow className="bg-muted/50 hover:bg-muted/50 border-b">
<TableHead className="h-12 w-[80px] text-center text-sm font-semibold">No</TableHead>
<TableHead className="h-12 text-sm font-semibold"> ID</TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
<TableHead className="h-12 text-sm font-semibold"></TableHead>
<TableHead className="h-12 text-center text-sm font-semibold"> </TableHead>
<TableHead className="h-12 w-[120px] text-center text-sm font-semibold"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{users.map((user, index) => {
const typeInfo = getUserTypeInfo(user.userType);
return (
<TableRow key={user.userId} className="hover:bg-muted/50 border-b transition-colors">
<TableCell className="h-16 text-center text-sm">{getRowNumber(index)}</TableCell>
<TableCell className="h-16 font-mono text-sm">{user.userId}</TableCell>
<TableCell className="h-16 text-sm">{user.userName}</TableCell>
<TableCell className="h-16 text-sm">{user.companyName || user.companyCode}</TableCell>
<TableCell className="h-16 text-sm">{user.deptName || "-"}</TableCell>
<TableCell className="h-16 text-center">
<Badge variant="outline" className={`gap-1 ${typeInfo.className}`}>
{typeInfo.icon}
{typeInfo.label}
</Badge>
</TableCell>
<TableCell className="h-16 text-center">
<Button variant="outline" size="sm" onClick={() => onEditAuth(user)} className="h-8 gap-1 text-sm">
<Shield className="h-3 w-3" />
</Button>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
{/* 모바일 카드 뷰 */}
<div className="grid gap-4 sm:grid-cols-2 lg:hidden">
{users.map((user, index) => {
const typeInfo = getUserTypeInfo(user.userType);
return (
<div
key={user.userId}
className="bg-card hover:bg-muted/50 rounded-lg border p-4 shadow-sm transition-colors"
>
{/* 헤더 */}
<div className="mb-4 flex items-start justify-between">
<div className="flex-1">
<h3 className="text-base font-semibold">{user.userName}</h3>
<p className="text-muted-foreground mt-1 font-mono text-sm">{user.userId}</p>
</div>
<Badge variant="outline" className={`gap-1 ${typeInfo.className}`}>
{typeInfo.icon}
{typeInfo.label}
</Badge>
</div>
{/* 정보 */}
<div className="space-y-2 border-t pt-4">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground"></span>
<span className="font-medium">{user.companyName || user.companyCode}</span>
</div>
<div className="flex justify-between text-sm">
<span className="text-muted-foreground"></span>
<span className="font-medium">{user.deptName || "-"}</span>
</div>
</div>
{/* 액션 */}
<div className="mt-4 border-t pt-4">
<Button
variant="outline"
size="sm"
onClick={() => onEditAuth(user)}
className="h-9 w-full gap-2 text-sm"
>
<Shield className="h-4 w-4" />
</Button>
</div>
</div>
);
})}
</div>
{/* 페이지네이션 */}
{paginationInfo.totalPages > 1 && (
<div className="flex items-center justify-center gap-2">
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(paginationInfo.currentPage - 1)}
disabled={paginationInfo.currentPage === 1}
>
</Button>
<span className="text-muted-foreground text-sm">
{paginationInfo.currentPage} / {paginationInfo.totalPages}
</span>
<Button
variant="outline"
size="sm"
onClick={() => onPageChange(paginationInfo.currentPage + 1)}
disabled={paginationInfo.currentPage === paginationInfo.totalPages}
>
</Button>
</div>
)}
</div>
);
}