From 8e70ba35379d21d89704f1095b7fd32bf286525d Mon Sep 17 00:00:00 2001
From: joonhoekim <26rote@gmail.com>
Date: Thu, 2 Oct 2025 14:08:33 +0900
Subject: (김준회) ITB 및 일반견적 선택시 구매담당자 선택을 구매그룹코드로
 처리하도록 변경, 오라클 연결 불가한 경우 하드코딩된 폴백데이터 제공처리
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 lib/rfq-last/table/rfq-assign-pic-dialog.tsx | 279 ++++++++-------------------
 1 file changed, 80 insertions(+), 199 deletions(-)

(limited to 'lib/rfq-last/table/rfq-assign-pic-dialog.tsx')

diff --git a/lib/rfq-last/table/rfq-assign-pic-dialog.tsx b/lib/rfq-last/table/rfq-assign-pic-dialog.tsx
index 94dde779..9ca34ccd 100644
--- a/lib/rfq-last/table/rfq-assign-pic-dialog.tsx
+++ b/lib/rfq-last/table/rfq-assign-pic-dialog.tsx
@@ -10,36 +10,15 @@ import {
   DialogTitle,
 } from "@/components/ui/dialog";
 import { Button } from "@/components/ui/button";
-import {
-  Command,
-  CommandEmpty,
-  CommandGroup,
-  CommandInput,
-  CommandItem,
-  CommandList,
-} from "@/components/ui/command";
-import {
-  Popover,
-  PopoverContent,
-  PopoverTrigger,
-} from "@/components/ui/popover";
-import { Check, ChevronsUpDown, Loader2, User, Users } from "lucide-react";
-import { cn } from "@/lib/utils";
+import { Loader2, Users } from "lucide-react";
 import { toast } from "sonner";
-import { getPUsersForFilter } from "@/lib/rfq-last/service";
 import { assignPicToRfqs } from "../service";
 import { Badge } from "@/components/ui/badge";
 import { Alert, AlertDescription } from "@/components/ui/alert";
-
-interface User {
-  id: number;
-  name: string | null;
-  userCode: string | null;
-  deptName: string | null;
-  isAbsent: boolean | null;
-  isDeletedOnNonSap: boolean | null;
-  email?: string;
-}
+import { 
+  PurchaseGroupCodeSingleSelector,
+  PurchaseGroupCodeWithUser 
+} from "@/components/common/selectors/purchase-group-code";
 
 interface RfqAssignPicDialogProps {
   open: boolean;
@@ -56,12 +35,9 @@ export function RfqAssignPicDialog({
   selectedRfqCodes,
   onSuccess,
 }: RfqAssignPicDialogProps) {
-  const [users, setUsers] = React.useState<User[]>([] as User[]);
-  const [isLoadingUsers, setIsLoadingUsers] = React.useState(false);
   const [isAssigning, setIsAssigning] = React.useState(false);
-  const [selectedUser, setSelectedUser] = React.useState<User | null>(null);
-  const [userPopoverOpen, setUserPopoverOpen] = React.useState(false);
-  const [userSearchTerm, setUserSearchTerm] = React.useState("");
+  const [selectedCode, setSelectedCode] = React.useState<PurchaseGroupCodeWithUser | undefined>(undefined);
+  const [selectorOpen, setSelectorOpen] = React.useState(false);
 
   // ITB만 필터링 (rfqCode가 "I"로 시작하는 것)
   const itbCodes = React.useMemo(() => {
@@ -72,50 +48,36 @@ export function RfqAssignPicDialog({
     return selectedRfqIds.filter((id, index) => selectedRfqCodes[index]?.startsWith("I"));
   }, [selectedRfqIds, selectedRfqCodes]);
 
-  // 유저 목록 로드
+  // 다이얼로그 열릴 때 초기화
   React.useEffect(() => {
-    const loadUsers = async () => {
-      setIsLoadingUsers(true);
-      try {
-        const userList = await getPUsersForFilter();
-        setUsers(userList as User[]);
-      } catch (error) {
-        console.log("사용자 목록 로드 오류:", error);
-        toast.error("사용자 목록을 불러오는데 실패했습니다");
-      } finally {
-        setIsLoadingUsers(false);
-      }
-    };
-
     if (open) {
-      loadUsers();
-      // 다이얼로그 열릴 때 초기화
-      setSelectedUser(null);
-      setUserSearchTerm("");
+      setSelectedCode(undefined);
     }
   }, [open]);
 
-  // 유저 검색
-  const filteredUsers = React.useMemo(() => {
-    if (!userSearchTerm || !userSearchTerm.trim()) return users;
-
-    const searchTerm = userSearchTerm.trim();
-    return users.filter(
-      (user) =>
-        (user.name && user.name.includes(searchTerm)) ||
-        (user.userCode && user.userCode.toLowerCase().includes(searchTerm.toLowerCase())) ||
-        (user.deptName && user.deptName.includes(searchTerm))
-    );
-  }, [users, userSearchTerm]);
-
-  const handleSelectUser = (user: User) => {
-    setSelectedUser(user);
-    setUserPopoverOpen(false);
+  const handleCodeSelect = (code: PurchaseGroupCodeWithUser) => {
+    setSelectedCode(code);
+    
+    // 유저 정보가 없는 경우 toast로 알림
+    if (!code.user) {
+      toast.warning(
+        `해당 구매그룹코드(${code.PURCHASE_GROUP_CODE})의 사번 정보의 유저가 없습니다`,
+        {
+          description: `사번: ${code.EMPLOYEE_NUMBER}`,
+          duration: 5000,
+        }
+      );
+    }
   };
 
   const handleAssign = async () => {
-    if (!selectedUser) {
-      toast.error("담당자를 선택해주세요");
+    if (!selectedCode) {
+      toast.error("구매그룹코드를 선택해주세요");
+      return;
+    }
+
+    if (!selectedCode.user) {
+      toast.error("선택한 구매그룹코드에 연결된 사용자가 없습니다");
       return;
     }
 
@@ -128,7 +90,7 @@ export function RfqAssignPicDialog({
     try {
       const result = await assignPicToRfqs({
         rfqIds: itbIds,
-        picUserId: selectedUser.id,
+        picUserId: selectedCode.user.id,
       });
 
       if (result.success) {
@@ -196,144 +158,63 @@ export function RfqAssignPicDialog({
             )}
           </div>
 
-          {/* 담당자 선택 */}
+          {/* 구매 담당자 선택 (구매그룹코드) */}
           <div className="space-y-2">
-            <label className="text-sm font-medium">구매 담당자</label>
-            <Popover open={userPopoverOpen} onOpenChange={setUserPopoverOpen}>
-              <PopoverTrigger asChild>
-                <Button
-                  type="button"
-                  variant="outline"
-                  className="w-full justify-between h-10"
-                  disabled={isLoadingUsers || itbCodes.length === 0}
-                >
-                  {isLoadingUsers ? (
-                    <>
-                      <span>담당자 로딩 중...</span>
-                      <Loader2 className="ml-2 h-4 w-4 animate-spin" />
-                    </>
-                  ) : (
-                    <>
-                      <span className="flex items-center gap-2">
-                        <User className="h-4 w-4" />
-                        {selectedUser ? (
-                          <>
-                            {selectedUser.name}
-                            {selectedUser.userCode && (
-                              <span className="text-muted-foreground">
-                                ({selectedUser.userCode})
-                              </span>
-                            )}
-                          </>
-                        ) : (
-                          <span className="text-muted-foreground">
-                            구매 담당자를 선택하세요
-                          </span>
-                        )}
-                      </span>
-                      <ChevronsUpDown className="h-4 w-4 opacity-50" />
-                    </>
+            <label className="text-sm font-medium">구매 담당자 (구매그룹코드)</label>
+            <Button
+              type="button"
+              variant="outline"
+              className="w-full justify-start h-auto min-h-[40px]"
+              disabled={itbCodes.length === 0}
+              onClick={() => setSelectorOpen(true)}
+            >
+              {selectedCode ? (
+                <div className="flex flex-col items-start gap-1 w-full">
+                  <div className="flex items-center gap-2">
+                    <Badge variant="secondary" className="font-mono">
+                      {selectedCode.PURCHASE_GROUP_CODE}
+                    </Badge>
+                    <span>{selectedCode.DISPLAY_NAME}</span>
+                  </div>
+                  {selectedCode.user && (
+                    <div className="text-xs text-muted-foreground">
+                      사용자: {selectedCode.user.name} ({selectedCode.user.email})
+                    </div>
                   )}
-                </Button>
-              </PopoverTrigger>
-              <PopoverContent className="w-[460px] p-0">
-                <Command shouldFilter={false}>
-                  <CommandInput
-                    placeholder="이름, 구매그룹코드 또는 부서로 검색..."
-                    value={userSearchTerm}
-                    onValueChange={setUserSearchTerm}
-                  />
-                  <CommandList className="max-h-[300px]">
-                    <CommandEmpty>검색 결과가 없습니다</CommandEmpty>
-                    <CommandGroup
-                      className="overflow-y-auto"
-                      onWheel={(e) => {
-                        // 마우스 휠 스크롤이 제대로 작동하도록 이벤트 전파 허용
-                        e.stopPropagation();
-                      }}
-                    >
-                      {filteredUsers.map((user) => (
-                        <CommandItem
-                          key={user.id}
-                          value={`${user.name || ''} ${user.userCode || ''} ${user.deptName || ''}`}
-                          onSelect={() => handleSelectUser(user)}
-                          className="flex items-center justify-between"
-                        >
-                          <div className="flex flex-col gap-1">
-                            <div className="flex items-center gap-2">
-                              <User className="h-4 w-4" />
-                              <span>{user.name || '이름 없음'}</span>
-                              {user.userCode && (
-                                <span className="text-muted-foreground text-sm">
-                                  ({user.userCode})
-                                </span>
-                              )}
-                              {(user.isAbsent || user.isDeletedOnNonSap) && (
-                                <div className="flex gap-1">
-                                  {user.isAbsent && (
-                                    <Badge variant="outline" className="text-xs px-1 py-0">
-                                      휴직
-                                    </Badge>
-                                  )}
-                                  {user.isDeletedOnNonSap && (
-                                    <Badge variant="outline" className="text-xs px-1 py-0">
-                                      퇴직
-                                    </Badge>
-                                  )}
-                                </div>
-                              )}
-                            </div>
-                            {user.deptName && (
-                              <span className="text-xs text-muted-foreground">
-                                {user.deptName}
-                              </span>
-                            )}
-                          </div>
-                          <Check
-                            className={cn(
-                              "h-4 w-4",
-                              selectedUser?.id === user.id
-                                ? "opacity-100"
-                                : "opacity-0"
-                            )}
-                          />
-                        </CommandItem>
-                      ))}
-                    </CommandGroup>
-                  </CommandList>
-                </Command>
-              </PopoverContent>
-            </Popover>
-            {selectedUser && (
-              <div className="text-xs text-muted-foreground">
-                <div className="flex items-center gap-2">
-                  <p>
-                    선택한 담당자: {selectedUser.name || '이름 없음'}
-                    {selectedUser.userCode && ` (${selectedUser.userCode})`}
-                  </p>
-                  {(selectedUser.isAbsent || selectedUser.isDeletedOnNonSap) && (
-                    <div className="flex gap-1">
-                      {selectedUser.isAbsent && (
-                        <Badge variant="outline" className="text-xs px-1 py-0 border-orange-300 text-orange-700 bg-orange-50">
-                          휴직
-                        </Badge>
-                      )}
-                      {selectedUser.isDeletedOnNonSap && (
-                        <Badge variant="outline" className="text-xs px-1 py-0 border-red-300 text-red-700 bg-red-50">
-                          퇴직
-                        </Badge>
-                      )}
+                  {!selectedCode.user && (
+                    <div className="text-xs text-orange-600">
+                      ⚠️ 연결된 사용자가 없습니다
                     </div>
                   )}
                 </div>
-                {selectedUser.deptName && (
-                  <p>{selectedUser.deptName}</p>
-                )}
-              </div>
+              ) : (
+                <span className="text-muted-foreground">
+                  구매그룹코드를 선택하세요
+                </span>
+              )}
+            </Button>
+            
+            {selectedCode && !selectedCode.user && (
+              <Alert className="border-orange-200 bg-orange-50">
+                <AlertDescription className="text-orange-800 text-xs">
+                  선택한 구매그룹코드에 연결된 사용자가 없습니다. 다른 구매그룹코드를 선택해주세요.
+                </AlertDescription>
+              </Alert>
             )}
           </div>
         </div>
 
+        {/* 구매그룹코드 선택 다이얼로그 */}
+        <PurchaseGroupCodeSingleSelector
+          open={selectorOpen}
+          onOpenChange={setSelectorOpen}
+          selectedCode={selectedCode}
+          onCodeSelect={handleCodeSelect}
+          title="구매 담당자 선택"
+          description="ITB에 지정할 구매 담당자의 구매그룹코드를 선택하세요"
+          showConfirmButtons={false}
+        />
+
         <DialogFooter>
           <Button
             type="button"
@@ -346,7 +227,7 @@ export function RfqAssignPicDialog({
           <Button
             type="submit"
             onClick={handleAssign}
-            disabled={!selectedUser || itbCodes.length === 0 || isAssigning}
+            disabled={!selectedCode || !selectedCode.user || itbCodes.length === 0 || isAssigning}
           >
             {isAssigning ? (
               <>
@@ -361,4 +242,4 @@ export function RfqAssignPicDialog({
       </DialogContent>
     </Dialog>
   );
-}
\ No newline at end of file
+}
-- 
cgit v1.2.3

