From 121a382b2d3c547f8facce73f57dbdbcf847d879 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Fri, 7 Nov 2025 06:53:06 +0000 Subject: (최겸) 구매 정보시스템 user vendorid 변경 기능 개발(신규) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../change-vendor/change-vendor-client.tsx | 424 +++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 app/[lng]/evcp/(evcp)/(system)/change-vendor/change-vendor-client.tsx (limited to 'app') diff --git a/app/[lng]/evcp/(evcp)/(system)/change-vendor/change-vendor-client.tsx b/app/[lng]/evcp/(evcp)/(system)/change-vendor/change-vendor-client.tsx new file mode 100644 index 00000000..47776bba --- /dev/null +++ b/app/[lng]/evcp/(evcp)/(system)/change-vendor/change-vendor-client.tsx @@ -0,0 +1,424 @@ +'use client'; + +import * as React from 'react'; +import { useState, useTransition } from 'react'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { Loader2, Search, User, Building2 } from 'lucide-react'; +import { searchUsers, getUserVendorInfo, getVendorList, changeVendor } from '@/lib/change-vendor/service'; +import { toast } from 'sonner'; + +interface User { + id: number; + name: string; + email: string; + companyId: number | null; +} + +interface UserVendorInfo { + userId: number; + userName: string; + userEmail: string; + currentVendorId: number | null; + currentVendorName: string | null; + currentVendorCode: string | null; +} + +interface Vendor { + id: number; + vendorName: string; + vendorCode: string | null; + status: string; +} + +export function ChangeVendorClient() { + const [isPending, startTransition] = useTransition(); + const [isSearchingUsers, setIsSearchingUsers] = React.useState(false); + const [isSearchingVendors, setIsSearchingVendors] = React.useState(false); + const [isLoadingVendorInfo, setIsLoadingVendorInfo] = React.useState(false); + + const [userSearchQuery, setUserSearchQuery] = useState(''); + const [users, setUsers] = useState([]); + const [selectedUserId, setSelectedUserId] = useState(null); + const [userVendorInfo, setUserVendorInfo] = useState(null); + + const [vendorSearchQuery, setVendorSearchQuery] = useState(''); + const [vendors, setVendors] = useState([]); + const [selectedVendorId, setSelectedVendorId] = useState(''); + + const [error, setError] = useState(null); + + // 유저 검색 + const handleSearchUsers = async () => { + if (isSearchingUsers) return; + + setIsSearchingUsers(true); + setError(null); + setSelectedUserId(null); + setUserVendorInfo(null); + setSelectedVendorId(''); + + try { + const result = await searchUsers(userSearchQuery); + + if (result.error) { + setError(result.error); + return; + } + + setUsers(result.data || []); + + if (result.data && result.data.length === 0) { + toast.info('검색 결과가 없습니다.'); + } + } catch (err) { + setError('유저 검색 중 오류가 발생했습니다.'); + console.error(err); + } finally { + setIsSearchingUsers(false); + } + }; + + // 유저 검색어 입력 시 엔터키 처리 + const handleUserSearchKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handleSearchUsers(); + } + }; + + // 유저 선택 시 해당 유저의 벤더 정보 조회 + const handleUserSelect = async (userId: number) => { + setSelectedUserId(userId); + setUserVendorInfo(null); + setSelectedVendorId(''); + setIsLoadingVendorInfo(true); + setError(null); + + try { + const result = await getUserVendorInfo(userId); + + if (result.error) { + setError(result.error); + return; + } + + setUserVendorInfo(result.data); + + // 벤더 목록도 함께 로드 + await loadVendors(); + } catch (err) { + setError('유저 정보를 불러오는 중 오류가 발생했습니다.'); + console.error(err); + } finally { + setIsLoadingVendorInfo(false); + } + }; + + // 벤더 목록 로드 + const loadVendors = async () => { + setIsSearchingVendors(true); + try { + const result = await getVendorList(vendorSearchQuery); + + if (result.error) { + setError(result.error); + return; + } + + setVendors(result.data || []); + } catch (err) { + setError('벤더 목록을 불러오는 중 오류가 발생했습니다.'); + console.error(err); + } finally { + setIsSearchingVendors(false); + } + }; + + // 벤더 검색 + const handleSearchVendors = async () => { + if (isSearchingVendors) return; + + setIsSearchingVendors(true); + setError(null); + setSelectedVendorId(''); + + try { + const result = await getVendorList(vendorSearchQuery); + + if (result.error) { + setError(result.error); + return; + } + + setVendors(result.data || []); + + if (result.data && result.data.length === 0) { + toast.info('검색 결과가 없습니다.'); + } + } catch (err) { + setError('벤더 검색 중 오류가 발생했습니다.'); + console.error(err); + } finally { + setIsSearchingVendors(false); + } + }; + + // 벤더 검색어 입력 시 엔터키 처리 + const handleVendorSearchKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handleSearchVendors(); + } + }; + + // 벤더 변경 처리 + const handleChangeVendor = () => { + if (!selectedUserId) { + toast.error('유저를 선택해주세요.'); + return; + } + + if (!selectedVendorId) { + toast.error('변경할 벤더를 선택해주세요.'); + return; + } + + const vendorId = Number(selectedVendorId); + + if (userVendorInfo?.currentVendorId === vendorId) { + toast.error('현재 선택된 벤더와 동일합니다.'); + return; + } + + startTransition(async () => { + try { + const result = await changeVendor(selectedUserId, vendorId); + + if (result.error || !result.success) { + toast.error(result.error || '벤더 변경에 실패했습니다.'); + return; + } + + toast.success(`"${result.data?.userName}"님의 벤더가 "${result.data?.vendorName}"로 변경되었습니다.`); + + // 유저 정보 새로고침 + await handleUserSelect(selectedUserId); + } catch (err) { + toast.error('벤더 변경 중 오류가 발생했습니다.'); + console.error(err); + } + }); + }; + + return ( +
+ {/* 1단계: 유저 검색 및 선택 */} + + + + + 유저 검색 및 선택 + + + 벤더를 변경할 유저를 검색하고 선택하세요. + + + + {/* 유저 검색 */} +
+
+ setUserSearchQuery(e.target.value)} + onKeyDown={handleUserSearchKeyDown} + disabled={isSearchingUsers} + /> +
+ +
+ + {/* 유저 선택 */} + {users.length > 0 && ( +
+ + +
+ )} + + {isLoadingVendorInfo && ( +
+ +
+ )} + + {/* 선택한 유저의 현재 벤더 정보 */} + {userVendorInfo && ( +
+
+
+ + +
+
+
{userVendorInfo.userName}
+
{userVendorInfo.userEmail}
+
+
+
+ + +
+
+ {userVendorInfo.currentVendorName || '벤더 정보 없음'} + {userVendorInfo.currentVendorCode && ( + + ({userVendorInfo.currentVendorCode}) + + )} +
+
+
+
+ )} +
+
+ + {/* 2단계: 벤더 검색 및 선택 (유저 선택 후에만 표시) */} + {selectedUserId && userVendorInfo && ( + + + + + 벤더 변경 + + + 변경할 벤더를 검색하고 선택하세요. + + + + {/* 벤더 검색 */} +
+
+ setVendorSearchQuery(e.target.value)} + onKeyDown={handleVendorSearchKeyDown} + disabled={isSearchingVendors} + /> +
+ +
+ + {/* 벤더 선택 */} + {vendors.length > 0 && ( +
+ + +
+ )} + + {error && ( + + {error} + + )} + + {/* 저장 버튼 */} + +
+
+ )} +
+ ); +} -- cgit v1.2.3