Files
vexplor_dev/frontend/components/admin/UserAuthTable.tsx
kjs 1d49fc7ac7 Implement multi-language support in user management and system management pages
- Integrated multi-language functionality across various user management components, including user list, roles list, and user authorization pages, enhancing accessibility for diverse users.
- Updated UI elements to utilize translation keys, ensuring that all text is dynamically translated based on user preferences.
- Improved error handling messages to be localized, providing a better user experience in case of issues.

These changes significantly enhance the usability and internationalization of the user management features, making the application more inclusive.
2026-04-01 15:57:12 +09:00

224 lines
6.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";
// 컴포넌트 내부 기본 텍스트 (t prop이 없을 때 사용)
const USER_AUTH_TABLE_DEFAULTS: Record<string, string> = {
"role.super_admin": "최고 관리자",
"role.company_admin": "회사 관리자",
"role.user": "일반 사용자",
"role.guest": "게스트",
"role.partner": "협력업체",
"role.unassigned": "미지정",
"table.userId": "사용자 ID",
"table.userName": "사용자명",
"table.company": "회사",
"table.dept": "부서",
"table.currentAuth": "현재 권한",
"table.empty": "등록된 사용자가 없습니다.",
"table.actions": "액션",
"action.change.auth": "권한 변경",
"pagination.prev": "이전",
"pagination.next": "다음",
};
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;
t?: (key: string, params?: Record<string, string | number>) => string;
}
/**
* 사용자 권한 테이블 컴포넌트
*
* 사용자 목록과 권한 정보를 표시하고 권한 변경 기능 제공
*/
export function UserAuthTable({ users, isLoading, isSuperAdmin, paginationInfo, onEditAuth, onPageChange, t }: UserAuthTableProps) {
// 다국어 래퍼 (t prop이 없으면 기본 텍스트 사용)
const _t = t || ((key: string) => USER_AUTH_TABLE_DEFAULTS[key] || key);
// 권한 레벨 표시
const getUserTypeInfo = (userType: string) => {
switch (userType) {
case "SUPER_ADMIN":
return {
label: _t("role.super_admin"),
icon: <ShieldCheck className="h-3 w-3" />,
className: "bg-primary/20 text-primary border-primary/30",
};
case "COMPANY_ADMIN":
return {
label: _t("role.company_admin"),
icon: <Building2 className="h-3 w-3" />,
className: "bg-primary/20 text-primary border-primary/30",
};
case "USER":
return {
label: _t("role.user"),
icon: <UserIcon className="h-3 w-3" />,
className: "bg-muted/50 text-muted-foreground border-border",
};
case "GUEST":
return {
label: _t("role.guest"),
icon: <Users className="h-3 w-3" />,
className: "bg-success/20 text-success border-success/30",
};
case "PARTNER":
return {
label: _t("role.partner"),
icon: <Shield className="h-3 w-3" />,
className: "bg-warning/20 text-warning border-warning/30",
};
default:
return {
label: userType || _t("role.unassigned"),
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: _t("table.userId"),
render: (value) => <span className="font-mono">{value}</span>,
},
{
key: "userName",
label: _t("table.userName"),
},
...(isSuperAdmin
? [
{
key: "companyName",
label: _t("table.company"),
hideOnMobile: true,
render: (_value: any, row: any) => <span>{row.companyName || row.companyCode}</span>,
} as RDVColumn<any>,
]
: []),
{
key: "deptName",
label: _t("table.dept"),
hideOnMobile: true,
render: (value) => <span>{value || "-"}</span>,
},
{
key: "userType",
label: _t("table.currentAuth"),
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: _t("table.company"),
render: (user: any) => <span>{user.companyName || user.companyCode}</span>,
} as RDVCardField<any>,
]
: []),
{
label: _t("table.dept"),
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={_t("table.empty")}
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={_t("table.actions")}
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" />
{_t("action.change.auth")}
</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}
>
{_t("pagination.prev")}
</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}
>
{_t("pagination.next")}
</Button>
</div>
)}
</div>
);
}