1d49fc7ac7
- 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.
279 lines
8.1 KiB
TypeScript
279 lines
8.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { useUserManagement } from "@/hooks/useUserManagement";
|
|
import { UserToolbar } from "@/components/admin/UserToolbar";
|
|
import { UserTable } from "@/components/admin/UserTable";
|
|
import { Pagination } from "@/components/common/Pagination";
|
|
import { UserPasswordResetModal } from "@/components/admin/UserPasswordResetModal";
|
|
import { UserFormModal } from "@/components/admin/UserFormModal";
|
|
import { ScrollToTop } from "@/components/common/ScrollToTop";
|
|
import { usePageMultiLang } from "@/hooks/usePageMultiLang";
|
|
|
|
// 다국어 키 목록
|
|
const USER_MNG_KEYS = [
|
|
"user.mng.title",
|
|
"user.mng.description",
|
|
"error.occurred",
|
|
"table.sabun",
|
|
"table.company",
|
|
"table.dept",
|
|
"table.position",
|
|
"table.userId",
|
|
"table.userName",
|
|
"table.phone",
|
|
"table.email",
|
|
"table.regDate",
|
|
"table.status",
|
|
"table.dept.short",
|
|
"table.contact",
|
|
"table.empty",
|
|
"table.actions",
|
|
"action.edit.user",
|
|
"action.reset.password",
|
|
"action.view.history",
|
|
"toolbar.search.placeholder",
|
|
"toolbar.searching",
|
|
"toolbar.advanced.search",
|
|
"toolbar.advanced.search.title",
|
|
"toolbar.advanced.search.desc",
|
|
"toolbar.advanced.mode.warning",
|
|
"toolbar.advanced.reset",
|
|
"toolbar.total.count",
|
|
"toolbar.total.unit",
|
|
"toolbar.create.user",
|
|
"toolbar.search.company",
|
|
"toolbar.search.dept",
|
|
"toolbar.search.position",
|
|
"toolbar.search.userId",
|
|
"toolbar.search.userName",
|
|
"toolbar.search.tel",
|
|
"toolbar.search.email",
|
|
] as const;
|
|
|
|
// 한국어 기본값
|
|
const USER_MNG_DEFAULTS: Record<string, string> = {
|
|
"user.mng.title": "사용자 관리",
|
|
"user.mng.description": "시스템 사용자 계정 및 권한을 관리합니다",
|
|
"error.occurred": "오류가 발생했습니다",
|
|
"table.sabun": "사번",
|
|
"table.company": "회사",
|
|
"table.dept": "부서명",
|
|
"table.position": "직책",
|
|
"table.userId": "사용자 ID",
|
|
"table.userName": "사용자명",
|
|
"table.phone": "전화번호",
|
|
"table.email": "이메일",
|
|
"table.regDate": "등록일",
|
|
"table.status": "상태",
|
|
"table.dept.short": "부서",
|
|
"table.contact": "연락처",
|
|
"table.empty": "등록된 사용자가 없습니다.",
|
|
"table.actions": "작업",
|
|
"action.edit.user": "사용자 정보 수정",
|
|
"action.reset.password": "비밀번호 초기화",
|
|
"action.view.history": "변경이력 조회",
|
|
"toolbar.search.placeholder": "통합 검색...",
|
|
"toolbar.searching": "검색 중...",
|
|
"toolbar.advanced.search": "고급 검색",
|
|
"toolbar.advanced.search.title": "고급 검색 옵션",
|
|
"toolbar.advanced.search.desc": "각 필드별로 개별 검색 조건을 설정할 수 있습니다",
|
|
"toolbar.advanced.mode.warning":
|
|
"고급 검색 모드가 활성화되어 있습니다. 통합 검색을 사용하려면 고급 검색 조건을 초기화하세요.",
|
|
"toolbar.advanced.reset": "고급 검색 조건 초기화",
|
|
"toolbar.total.count": "총",
|
|
"toolbar.total.unit": "명",
|
|
"toolbar.create.user": "사용자 등록",
|
|
"toolbar.search.company": "회사명 검색",
|
|
"toolbar.search.dept": "부서명 검색",
|
|
"toolbar.search.position": "직책 검색",
|
|
"toolbar.search.userId": "사용자 ID 검색",
|
|
"toolbar.search.userName": "사용자명 검색",
|
|
"toolbar.search.tel": "전화번호/휴대폰 검색",
|
|
"toolbar.search.email": "이메일 검색",
|
|
};
|
|
|
|
/**
|
|
* 사용자관리 페이지
|
|
* URL: /admin/userMng
|
|
*
|
|
* shadcn/ui 스타일 가이드 적용
|
|
* - 원본 Spring + JSP 코드 패턴 기반 REST API 연동
|
|
* - 실제 데이터베이스와 연동되어 작동
|
|
*/
|
|
export default function UserMngPage() {
|
|
const { t } = usePageMultiLang({
|
|
keys: USER_MNG_KEYS,
|
|
defaults: USER_MNG_DEFAULTS,
|
|
menuCode: "user.management",
|
|
});
|
|
const {
|
|
// 데이터
|
|
users,
|
|
searchFilter,
|
|
isLoading,
|
|
isSearching,
|
|
error,
|
|
paginationInfo,
|
|
|
|
// 검색 기능
|
|
updateSearchFilter,
|
|
|
|
// 페이지네이션
|
|
handlePageChange,
|
|
handlePageSizeChange,
|
|
|
|
// 액션 핸들러
|
|
handleStatusToggle,
|
|
|
|
// 유틸리티
|
|
clearError,
|
|
refreshData,
|
|
} = useUserManagement();
|
|
|
|
// 비밀번호 초기화 모달 상태
|
|
const [passwordResetModal, setPasswordResetModal] = useState({
|
|
isOpen: false,
|
|
userId: null as string | null,
|
|
userName: null as string | null,
|
|
});
|
|
|
|
// 사용자 등록/수정 모달 상태
|
|
const [userFormModal, setUserFormModal] = useState({
|
|
isOpen: false,
|
|
editingUser: null as any | null,
|
|
});
|
|
|
|
// 사용자 등록 핸들러
|
|
const handleCreateUser = () => {
|
|
setUserFormModal({
|
|
isOpen: true,
|
|
editingUser: null,
|
|
});
|
|
};
|
|
|
|
// 사용자 수정 핸들러
|
|
const handleEditUser = (user: any) => {
|
|
setUserFormModal({
|
|
isOpen: true,
|
|
editingUser: user,
|
|
});
|
|
};
|
|
|
|
// 사용자 등록/수정 모달 닫기
|
|
const handleUserFormClose = () => {
|
|
setUserFormModal({
|
|
isOpen: false,
|
|
editingUser: null,
|
|
});
|
|
};
|
|
|
|
// 사용자 등록/수정 성공 핸들러
|
|
const handleUserFormSuccess = () => {
|
|
refreshData();
|
|
handleUserFormClose();
|
|
};
|
|
|
|
// 비밀번호 초기화 핸들러
|
|
const handlePasswordReset = (userId: string, userName: string) => {
|
|
setPasswordResetModal({
|
|
isOpen: true,
|
|
userId,
|
|
userName,
|
|
});
|
|
};
|
|
|
|
// 비밀번호 초기화 모달 닫기
|
|
const handlePasswordResetClose = () => {
|
|
setPasswordResetModal({
|
|
isOpen: false,
|
|
userId: null,
|
|
userName: null,
|
|
});
|
|
};
|
|
|
|
// 비밀번호 초기화 성공 핸들러
|
|
const handlePasswordResetSuccess = () => {
|
|
handlePasswordResetClose();
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-full flex-col bg-background">
|
|
{/* 상단 고정: 헤더 + 툴바 */}
|
|
<div className="shrink-0 space-y-6 p-6 pb-0">
|
|
<div className="space-y-2 border-b pb-4">
|
|
<h1 className="text-3xl font-bold tracking-tight">{t("user.mng.title")}</h1>
|
|
<p className="text-sm text-muted-foreground">{t("user.mng.description")}</p>
|
|
</div>
|
|
|
|
<UserToolbar
|
|
searchFilter={searchFilter}
|
|
totalCount={paginationInfo.totalItems}
|
|
isSearching={isSearching}
|
|
onSearchChange={updateSearchFilter}
|
|
onCreateClick={handleCreateUser}
|
|
t={t}
|
|
/>
|
|
|
|
{error && (
|
|
<div className="border-destructive/50 bg-destructive/10 rounded-lg border p-4">
|
|
<div className="flex items-center justify-between">
|
|
<p className="text-destructive text-sm font-semibold">{t("error.occurred")}</p>
|
|
<button
|
|
onClick={clearError}
|
|
className="text-destructive hover:text-destructive/80 transition-colors"
|
|
aria-label="에러 메시지 닫기"
|
|
>
|
|
✕
|
|
</button>
|
|
</div>
|
|
<p className="text-destructive/80 mt-1.5 text-sm">{error}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 중간: 테이블 (스크롤 영역) */}
|
|
<div className="min-h-0 flex-1 overflow-auto px-6 py-6">
|
|
<UserTable
|
|
users={users}
|
|
isLoading={isLoading}
|
|
paginationInfo={paginationInfo}
|
|
onStatusToggle={handleStatusToggle}
|
|
onPasswordReset={handlePasswordReset}
|
|
onEdit={handleEditUser}
|
|
t={t}
|
|
/>
|
|
</div>
|
|
|
|
{/* 하단 고정: 페이지네이션 */}
|
|
{!isLoading && users.length > 0 && (
|
|
<div className="shrink-0 border-t px-6 py-3">
|
|
<Pagination
|
|
paginationInfo={paginationInfo}
|
|
onPageChange={handlePageChange}
|
|
onPageSizeChange={handlePageSizeChange}
|
|
showPageSizeSelector={true}
|
|
pageSizeOptions={[10, 20, 50, 100]}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{/* 모달 */}
|
|
<UserFormModal
|
|
isOpen={userFormModal.isOpen}
|
|
onClose={handleUserFormClose}
|
|
onSuccess={handleUserFormSuccess}
|
|
editingUser={userFormModal.editingUser}
|
|
/>
|
|
<UserPasswordResetModal
|
|
isOpen={passwordResetModal.isOpen}
|
|
onClose={handlePasswordResetClose}
|
|
userId={passwordResetModal.userId}
|
|
userName={passwordResetModal.userName}
|
|
onSuccess={handlePasswordResetSuccess}
|
|
/>
|
|
<ScrollToTop />
|
|
</div>
|
|
);
|
|
}
|