- 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.
224 lines
6.8 KiB
TypeScript
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>
|
|
);
|
|
}
|