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([]); // 검색 필터 상태 const [searchFilter, setSearchFilter] = useState({}); // 통합 검색어 디바운싱 (500ms 지연) const debouncedSearchValue = useDebounce(searchFilter.searchValue || "", 500); // 고급 검색 필드들 디바운싱 const debouncedSabun = useDebounce(searchFilter.search_sabun || "", 500); const debouncedCompanyName = useDebounce(searchFilter.search_companyName || "", 500); const debouncedDeptName = useDebounce(searchFilter.search_deptName || "", 500); const debouncedPositionName = useDebounce(searchFilter.search_positionName || "", 500); const debouncedUserId = useDebounce(searchFilter.search_userId || "", 500); const debouncedUserName = useDebounce(searchFilter.search_userName || "", 500); const debouncedTel = useDebounce(searchFilter.search_tel || "", 500); const debouncedEmail = useDebounce(searchFilter.search_email || "", 500); // 디바운싱된 검색 필터 (useMemo로 최적화) const debouncedSearchFilter = useMemo( () => ({ // 통합 검색 searchValue: debouncedSearchValue, // 고급 검색 search_sabun: debouncedSabun, search_companyName: debouncedCompanyName, search_deptName: debouncedDeptName, search_positionName: debouncedPositionName, search_userId: debouncedUserId, search_userName: debouncedUserName, search_tel: debouncedTel, search_email: debouncedEmail, // 하위 호환성 searchType: searchFilter.searchType || "all", }), [ debouncedSearchValue, debouncedSabun, debouncedCompanyName, debouncedDeptName, debouncedPositionName, debouncedUserId, debouncedUserName, debouncedTel, debouncedEmail, searchFilter.searchType, ], ); // 검색 중인지 확인 (모든 검색 필드를 고려) const isSearching = useMemo(() => { return ( (searchFilter.searchValue || "") !== debouncedSearchValue || (searchFilter.search_sabun || "") !== debouncedSabun || (searchFilter.search_companyName || "") !== debouncedCompanyName || (searchFilter.search_deptName || "") !== debouncedDeptName || (searchFilter.search_positionName || "") !== debouncedPositionName || (searchFilter.search_userId || "") !== debouncedUserId || (searchFilter.search_userName || "") !== debouncedUserName || (searchFilter.search_tel || "") !== debouncedTel || (searchFilter.search_email || "") !== debouncedEmail ); }, [ searchFilter.searchValue, debouncedSearchValue, searchFilter.search_sabun, debouncedSabun, searchFilter.search_companyName, debouncedCompanyName, searchFilter.search_deptName, debouncedDeptName, searchFilter.search_positionName, debouncedPositionName, searchFilter.search_userId, debouncedUserId, searchFilter.search_userName, debouncedUserName, searchFilter.search_tel, debouncedTel, searchFilter.search_email, debouncedEmail, ]); // 로딩 및 에러 상태 const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); // 페이지네이션 상태 const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(20); const [totalItems, setTotalItems] = useState(0); // 사용자 목록 로드 (새로운 통합 검색 방식) const loadUsers = useCallback( async (filter?: UserSearchFilter) => { setIsLoading(true); setError(null); try { // 검색 파라미터 구성 const searchParams: Record = { page: currentPage, countPerPage: pageSize, }; // 검색 조건 추가 if (filter) { // 통합 검색 (우선순위 최고) if (filter.searchValue && filter.searchValue.trim()) { searchParams.search = filter.searchValue.trim(); } // 고급 검색 (개별 필드별) if (filter.search_sabun && filter.search_sabun.trim()) { searchParams.search_sabun = filter.search_sabun.trim(); } if (filter.search_companyName && filter.search_companyName.trim()) { searchParams.search_companyName = filter.search_companyName.trim(); } if (filter.search_deptName && filter.search_deptName.trim()) { searchParams.search_deptName = filter.search_deptName.trim(); } if (filter.search_positionName && filter.search_positionName.trim()) { searchParams.search_positionName = filter.search_positionName.trim(); } if (filter.search_userId && filter.search_userId.trim()) { searchParams.search_userId = filter.search_userId.trim(); } if (filter.search_userName && filter.search_userName.trim()) { searchParams.search_userName = filter.search_userName.trim(); } if (filter.search_tel && filter.search_tel.trim()) { searchParams.search_tel = filter.search_tel.trim(); } if (filter.search_email && filter.search_email.trim()) { searchParams.search_email = filter.search_email.trim(); } // 하위 호환성: 기존 searchType/searchValue 방식 지원 if (!filter.searchValue && filter.searchType && filter.searchType !== "all" && searchParams.searchValue) { // 기존 방식 변환은 일단 제거 (통합 검색과 고급 검색만 지원) } } 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); } } else { setUsers([]); setTotalItems(0); } } catch (err) { setError(err instanceof Error ? err.message : "사용자 목록 조회에 실패했습니다."); setUsers([]); setTotalItems(0); } finally { setIsLoading(false); } }, [currentPage, pageSize], ); // 초기 로드 useEffect(() => { loadUsers(); }, [loadUsers]); // 디바운싱된 검색 조건이 변경될 때마다 API 호출 useEffect(() => { loadUsers(debouncedSearchFilter); }, [ debouncedSearchFilter.searchValue, debouncedSearchFilter.search_sabun, debouncedSearchFilter.search_companyName, debouncedSearchFilter.search_deptName, debouncedSearchFilter.search_positionName, debouncedSearchFilter.search_userId, debouncedSearchFilter.search_userName, debouncedSearchFilter.search_tel, debouncedSearchFilter.search_email, loadUsers, ]); // 검색 필터 업데이트 const updateSearchFilter = useCallback((newFilter: Partial) => { setSearchFilter((prev) => ({ ...prev, ...newFilter })); // 검색 조건이 변경될 때마다 첫 페이지로 이동 const hasSearchChange = !!( newFilter.searchValue !== undefined || newFilter.search_sabun !== undefined || newFilter.search_companyName !== undefined || newFilter.search_deptName !== undefined || newFilter.search_positionName !== undefined || newFilter.search_userId !== undefined || newFilter.search_userName !== undefined || newFilter.search_tel !== undefined || newFilter.search_email !== undefined || newFilter.searchType !== undefined ); if (hasSearchChange) { 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.userName} (${user.userId}) → ${newStatus}`); // 백엔드 API 호출 const response = await userAPI.updateStatus(user.userId, 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.userId === user.userId ? { ...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, }; };