- Updated the user list retrieval logic to ensure proper filtering based on company codes, enhancing security for user data access. - Implemented checks to restrict access to company management APIs, allowing only SUPER_ADMIN users to perform actions related to company data. - Adjusted the user interface to reflect access restrictions for non-SUPER_ADMIN users, providing clear feedback when access is denied. These changes strengthen the integrity of user management and ensure that sensitive company information is only accessible to authorized personnel.
200 lines
5.8 KiB
TypeScript
200 lines
5.8 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Shield, ShieldCheck, User as UserIcon, Users, Building2 } from "lucide-react";
|
|
import { ResponsiveDataView, RDVColumn, RDVCardField } from "@/components/common/ResponsiveDataView";
|
|
|
|
interface UserAuthTableProps {
|
|
users: any[];
|
|
isLoading: boolean;
|
|
isSuperAdmin?: boolean;
|
|
paginationInfo: {
|
|
currentPage: number;
|
|
pageSize: number;
|
|
totalItems: number;
|
|
totalPages: number;
|
|
};
|
|
onEditAuth: (user: any) => void;
|
|
onPageChange: (page: number) => void;
|
|
}
|
|
|
|
/**
|
|
* 사용자 권한 테이블 컴포넌트
|
|
*
|
|
* 사용자 목록과 권한 정보를 표시하고 권한 변경 기능 제공
|
|
*/
|
|
export function UserAuthTable({ users, isLoading, isSuperAdmin, paginationInfo, onEditAuth, onPageChange }: UserAuthTableProps) {
|
|
// 권한 레벨 표시
|
|
const getUserTypeInfo = (userType: string) => {
|
|
switch (userType) {
|
|
case "SUPER_ADMIN":
|
|
return {
|
|
label: "최고 관리자",
|
|
icon: <ShieldCheck className="h-3 w-3" />,
|
|
className: "bg-primary/20 text-primary border-primary/30",
|
|
};
|
|
case "COMPANY_ADMIN":
|
|
return {
|
|
label: "회사 관리자",
|
|
icon: <Building2 className="h-3 w-3" />,
|
|
className: "bg-primary/20 text-primary border-primary/30",
|
|
};
|
|
case "USER":
|
|
return {
|
|
label: "일반 사용자",
|
|
icon: <UserIcon className="h-3 w-3" />,
|
|
className: "bg-muted/50 text-muted-foreground border-border",
|
|
};
|
|
case "GUEST":
|
|
return {
|
|
label: "게스트",
|
|
icon: <Users className="h-3 w-3" />,
|
|
className: "bg-success/20 text-success border-success/30",
|
|
};
|
|
case "PARTNER":
|
|
return {
|
|
label: "협력업체",
|
|
icon: <Shield className="h-3 w-3" />,
|
|
className: "bg-warning/20 text-warning border-warning/30",
|
|
};
|
|
default:
|
|
return {
|
|
label: userType || "미지정",
|
|
icon: <UserIcon className="h-3 w-3" />,
|
|
className: "bg-muted/50 text-muted-foreground border-border",
|
|
};
|
|
}
|
|
};
|
|
|
|
// 행 번호 계산
|
|
const getRowNumber = (index: number) => {
|
|
return (paginationInfo.currentPage - 1) * paginationInfo.pageSize + index + 1;
|
|
};
|
|
|
|
// 데스크톱 테이블 컬럼 정의
|
|
const columns: RDVColumn<any>[] = [
|
|
{
|
|
key: "no",
|
|
label: "No",
|
|
width: "80px",
|
|
className: "text-center",
|
|
render: (_value, _row, index) => <span>{getRowNumber(index)}</span>,
|
|
},
|
|
{
|
|
key: "userId",
|
|
label: "사용자 ID",
|
|
render: (value) => <span className="font-mono">{value}</span>,
|
|
},
|
|
{
|
|
key: "userName",
|
|
label: "사용자명",
|
|
},
|
|
...(isSuperAdmin
|
|
? [
|
|
{
|
|
key: "companyName",
|
|
label: "회사",
|
|
hideOnMobile: true,
|
|
render: (_value: any, row: any) => <span>{row.companyName || row.companyCode}</span>,
|
|
} as RDVColumn<any>,
|
|
]
|
|
: []),
|
|
{
|
|
key: "deptName",
|
|
label: "부서",
|
|
hideOnMobile: true,
|
|
render: (value) => <span>{value || "-"}</span>,
|
|
},
|
|
{
|
|
key: "userType",
|
|
label: "현재 권한",
|
|
className: "text-center",
|
|
render: (_value, row) => {
|
|
const typeInfo = getUserTypeInfo(row.userType);
|
|
return (
|
|
<Badge variant="outline" className={`gap-1 ${typeInfo.className}`}>
|
|
{typeInfo.icon}
|
|
{typeInfo.label}
|
|
</Badge>
|
|
);
|
|
},
|
|
},
|
|
];
|
|
|
|
// 모바일 카드 필드 정의
|
|
const cardFields: RDVCardField<any>[] = [
|
|
...(isSuperAdmin
|
|
? [
|
|
{
|
|
label: "회사",
|
|
render: (user: any) => <span>{user.companyName || user.companyCode}</span>,
|
|
} as RDVCardField<any>,
|
|
]
|
|
: []),
|
|
{
|
|
label: "부서",
|
|
render: (user) => <span>{user.deptName || "-"}</span>,
|
|
},
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<ResponsiveDataView<any>
|
|
data={users}
|
|
columns={columns}
|
|
keyExtractor={(u) => u.userId}
|
|
isLoading={isLoading}
|
|
emptyMessage="등록된 사용자가 없습니다."
|
|
skeletonCount={10}
|
|
cardTitle={(u) => u.userName}
|
|
cardSubtitle={(u) => <span className="font-mono">{u.userId}</span>}
|
|
cardHeaderRight={(u) => {
|
|
const typeInfo = getUserTypeInfo(u.userType);
|
|
return (
|
|
<Badge variant="outline" className={`gap-1 ${typeInfo.className}`}>
|
|
{typeInfo.icon}
|
|
{typeInfo.label}
|
|
</Badge>
|
|
);
|
|
}}
|
|
cardFields={cardFields}
|
|
actionsLabel="액션"
|
|
actionsWidth="120px"
|
|
renderActions={(user) => (
|
|
<Button variant="outline" size="sm" onClick={() => onEditAuth(user)} className="h-8 gap-1 text-sm">
|
|
<Shield className="h-3 w-3" />
|
|
권한 변경
|
|
</Button>
|
|
)}
|
|
/>
|
|
|
|
{/* 페이지네이션 */}
|
|
{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>
|
|
);
|
|
}
|