ERP-node/frontend/hooks/useUserManagement.ts

254 lines
8.8 KiB
TypeScript
Raw Normal View History

2025-08-21 09:41:46 +09:00
import { useState, useCallback, useEffect, useMemo } from "react";
import { User, UserSearchFilter } from "@/types/user";
import { userAPI } from "@/lib/api/user"; // 백엔드 연동 활성화
import { PaginationInfo } from "@/components/common/Pagination";
import { useDebounce } from "./useDebounce";
// import { MOCK_USERS } from "@/constants/user"; // 목 데이터 비활성화
/**
*
*/
export const useUserManagement = () => {
// 사용자 목록 상태
const [users, setUsers] = useState<User[]>([]);
// 검색 필터 상태
const [searchFilter, setSearchFilter] = useState<UserSearchFilter>({});
// 검색어만 디바운싱 (500ms 지연) - searchType은 즉시 반영
const debouncedSearchValue = useDebounce(searchFilter.searchValue || "", 500);
// 디바운싱된 검색 필터 (useMemo로 최적화)
const debouncedSearchFilter = useMemo(
() => ({
searchValue: debouncedSearchValue,
searchType: searchFilter.searchType || "all",
}),
[debouncedSearchValue, searchFilter.searchType],
);
// 검색 중인지 확인 (검색어만 체크)
const isSearching = (searchFilter.searchValue || "") !== debouncedSearchValue;
// 로딩 및 에러 상태
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 페이지네이션 상태
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
const [totalItems, setTotalItems] = useState(0);
// 사용자 목록 로드 (특정 검색 조건으로 호출)
const loadUsers = useCallback(
async (searchValue?: string, searchType?: string) => {
setIsLoading(true);
setError(null);
try {
// 백엔드 API 호출
// 검색 파라미터 구성 (단순 검색 방식)
const searchParams: Record<string, string | number | undefined> = {
page: currentPage,
countPerPage: pageSize,
};
// 검색어가 있고 searchType이 'all'이 아닐 때만 검색 파라미터 추가
if (searchValue && searchValue.trim() && searchType && searchType !== "all") {
const trimmedValue = searchValue.trim();
// 각 검색 타입별로 해당하는 백엔드 파라미터 매핑 (백엔드 MyBatis와 정확히 일치)
switch (searchType) {
case "sabun":
searchParams.search_sabun = trimmedValue;
break;
case "company_name":
searchParams.search_companyName = trimmedValue; // MyBatis: search_companyName
break;
case "dept_name":
searchParams.search_deptName = trimmedValue; // MyBatis: search_deptName
break;
case "position_name":
searchParams.search_positionName = trimmedValue; // MyBatis: search_positionName
break;
case "user_id":
searchParams.search_userId = trimmedValue; // MyBatis: search_userId
break;
case "user_name":
searchParams.search_userName = trimmedValue; // MyBatis: search_userName
break;
case "tel":
searchParams.search_tel = trimmedValue; // MyBatis: search_tel
break;
case "email":
searchParams.search_email = trimmedValue; // MyBatis: search_email
break;
default:
searchParams.search_userName = trimmedValue; // 기본값
}
}
console.log("🔍 검색 파라미터:", searchParams);
const response = await userAPI.getList(searchParams);
// 백엔드 응답 구조에 맞게 처리 { success, data, total }
if (response && response.success && response.data) {
// 새로운 API 응답 구조: { success, data: { users, pagination } }
if (response.data.users && Array.isArray(response.data.users)) {
setUsers(response.data.users);
setTotalItems(response.data.pagination?.totalCount || response.data.users.length);
} else if (Array.isArray(response.data)) {
// 기존 구조: { success, data: User[] }
setUsers(response.data);
setTotalItems(response.total || response.data.length);
} else {
setUsers([]);
setTotalItems(0);
}
2025-08-21 09:41:46 +09:00
} else {
setUsers([]);
setTotalItems(0);
}
} catch (err) {
setError(err instanceof Error ? err.message : "사용자 목록 조회에 실패했습니다.");
setUsers([]);
setTotalItems(0);
} finally {
setIsLoading(false);
}
},
[currentPage, pageSize],
);
// 초기 로드
useEffect(() => {
loadUsers();
}, [loadUsers]);
// 검색어 변경 시에만 API 호출 (검색어가 있고 'all'이 아닐 때)
useEffect(() => {
if (
debouncedSearchFilter.searchValue &&
debouncedSearchFilter.searchValue.trim() &&
debouncedSearchFilter.searchType !== "all"
) {
loadUsers(debouncedSearchFilter.searchValue, debouncedSearchFilter.searchType);
}
}, [debouncedSearchFilter.searchValue, loadUsers]);
// '전체' 선택 시에만 즉시 반영
useEffect(() => {
if (searchFilter.searchType === "all" && !searchFilter.searchValue) {
loadUsers(); // 전체 목록 로드 (검색 조건 없음)
}
}, [searchFilter.searchType, loadUsers]);
// 검색 필터 업데이트
const updateSearchFilter = useCallback((newFilter: Partial<UserSearchFilter>) => {
setSearchFilter((prev) => ({ ...prev, ...newFilter }));
// searchType이 변경되거나 searchValue가 변경될 때만 첫 페이지로 이동
if (newFilter.searchType !== undefined || newFilter.searchValue !== undefined) {
setCurrentPage(1);
}
}, []);
// 페이지 변경 핸들러
const handlePageChange = useCallback((page: number) => {
setCurrentPage(page);
}, []);
// 페이지 크기 변경 핸들러
const handlePageSizeChange = useCallback((newPageSize: number) => {
setPageSize(newPageSize);
setCurrentPage(1); // 페이지 크기 변경 시 첫 페이지로 이동
}, []);
// 페이지네이션 정보 계산
const paginationInfo: PaginationInfo = {
currentPage,
totalPages: Math.ceil(totalItems / pageSize),
totalItems,
itemsPerPage: pageSize,
startItem: totalItems > 0 ? (currentPage - 1) * pageSize + 1 : 0,
endItem: Math.min(currentPage * pageSize, totalItems),
};
// 사용자 편집 기능 제거됨
// 사용자 삭제 기능 제거됨
// 사용자 상태 토글 핸들러
const handleStatusToggle = useCallback(async (user: User, newStatus: string) => {
try {
console.log(`🎛️ 상태 변경: ${user.user_name} (${user.user_id}) → ${newStatus}`);
// 백엔드 API 호출
const response = await userAPI.updateStatus(user.user_id, newStatus);
// 백엔드 응답 구조: { result: boolean, msg: string }
if (response && typeof response === "object" && "result" in response) {
const apiResponse = response as unknown as { result: boolean; msg: string };
if (apiResponse.result) {
console.log("✅ 상태 변경 성공:", apiResponse.msg);
// 전체 목록 새로고침 대신 개별 사용자 상태만 업데이트
setUsers((prevUsers) => prevUsers.map((u) => (u.user_id === user.user_id ? { ...u, status: newStatus } : u)));
} else {
console.error("❌ 상태 변경 실패:", apiResponse.msg);
alert(apiResponse.msg || "상태 변경에 실패했습니다.");
}
} else {
console.error("❌ 예상치 못한 응답 형식:", response);
alert("상태 변경 중 오류가 발생했습니다.");
}
} catch (error) {
console.error("상태 변경 오류:", error);
alert("상태 변경 중 오류가 발생했습니다.");
}
}, []);
// 데이터 새로고침 (비밀번호 초기화 후 목록 갱신용)
const refreshData = useCallback(() => {
loadUsers(debouncedSearchFilter.searchValue, debouncedSearchFilter.searchType);
}, [loadUsers, debouncedSearchFilter.searchValue, debouncedSearchFilter.searchType]);
// 사용자 등록 핸들러
const handleCreate = useCallback(() => {
console.log("📝 사용자 등록");
alert("사용자 등록 기능은 추후 구현 예정입니다.");
}, []);
// 에러 상태 초기화
const clearError = useCallback(() => {
setError(null);
}, []);
return {
// 데이터
users,
searchFilter,
isLoading,
isSearching,
error,
paginationInfo,
// 검색 기능
updateSearchFilter,
// 페이지네이션
handlePageChange,
handlePageSizeChange,
// 액션 핸들러
handleStatusToggle,
handleCreate,
// 유틸리티
loadUsers,
refreshData,
clearError,
};
};