diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/itb/table/create-rfq-dialog.tsx | 208 |
1 files changed, 68 insertions, 140 deletions
diff --git a/lib/itb/table/create-rfq-dialog.tsx b/lib/itb/table/create-rfq-dialog.tsx index 57a4b9d4..b292b647 100644 --- a/lib/itb/table/create-rfq-dialog.tsx +++ b/lib/itb/table/create-rfq-dialog.tsx @@ -19,38 +19,23 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "@/components/ui/command"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; -import { - FileText, - Package, - AlertCircle, +import { + FileText, + Package, + AlertCircle, CheckCircle, User, - ChevronsUpDown, - Check, Loader2, - Info + Info, + X } from "lucide-react"; import { toast } from "sonner"; -import { cn } from "@/lib/utils"; import type { PurchaseRequestView } from "@/db/schema"; import { approvePurchaseRequestsAndCreateRfqs } from "../service"; -import { getPUsersForFilter } from "@/lib/rfq-last/service"; import { useRouter } from "next/navigation"; +import { PurchaseGroupCodeSingleSelector, type PurchaseGroupCodeWithUser } from "@/components/common/selectors/purchase-group-code"; interface CreateRfqDialogProps { requests: PurchaseRequestView[]; @@ -66,50 +51,17 @@ export function CreateRfqDialog({ onSuccess, }: CreateRfqDialogProps) { const [isLoading, setIsLoading] = React.useState(false); - const [userPopoverOpen, setUserPopoverOpen] = React.useState(false); - const [users, setUsers] = React.useState<any[]>([]); - const [selectedUser, setSelectedUser] = React.useState<any>(null); - const [isLoadingUsers, setIsLoadingUsers] = React.useState(false); - const [userSearchTerm, setUserSearchTerm] = React.useState(""); + const [selectorOpen, setSelectorOpen] = React.useState(false); + const [selectedPurchaseGroupCode, setSelectedPurchaseGroupCode] = React.useState<PurchaseGroupCodeWithUser | undefined>(undefined); const router = useRouter(); - // 유저 목록 로드 - React.useEffect(() => { - const loadUsers = async () => { - setIsLoadingUsers(true); - try { - const userList = await getPUsersForFilter(); - setUsers(userList); - } catch (error) { - console.log("사용자 목록 로드 오류:", error); - toast.error("사용자 목록을 불러오는데 실패했습니다"); - } finally { - setIsLoadingUsers(false); - } - }; - - if (open) { - loadUsers(); - } - }, [open]); - - // 검색된 사용자 필터링 - const filteredUsers = React.useMemo(() => { - if (!userSearchTerm) return users; - - return users.filter(user => - user.name.toLowerCase().includes(userSearchTerm.toLowerCase()) || - user.userCode?.toLowerCase().includes(userSearchTerm.toLowerCase()) - ); - }, [users, userSearchTerm]); // 유효한 요청만 필터링 (이미 RFQ 생성된 것 제외) const validRequests = requests.filter(r => r.status !== "RFQ생성완료"); const invalidRequests = requests.filter(r => r.status === "RFQ생성완료"); - const handleSelectUser = (user: any) => { - setSelectedUser(user); - setUserPopoverOpen(false); + const handleSelectPurchaseGroupCode = (code: PurchaseGroupCodeWithUser) => { + setSelectedPurchaseGroupCode(code); }; const handleSubmit = async () => { @@ -124,9 +76,15 @@ export function CreateRfqDialog({ const requestIds = validRequests.map(r => r.id); const results = await approvePurchaseRequestsAndCreateRfqs( requestIds, - selectedUser?.id - ); - + selectedPurchaseGroupCode?.user?.id + ) as Array<{ + success?: boolean; + skipped?: boolean; + requestId: number; + error?: string; + message?: string; + }>; + const successCount = results.filter(r => r.success).length; const skipCount = results.filter(r => r.skipped).length; @@ -151,8 +109,7 @@ export function CreateRfqDialog({ const handleClose = () => { if (!isLoading) { - setSelectedUser(null); - setUserSearchTerm(""); + setSelectedPurchaseGroupCode(undefined); onOpenChange(false); } }; @@ -187,86 +144,57 @@ export function CreateRfqDialog({ <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} - > - {isLoadingUsers ? ( + <div className="flex gap-2"> + <Button + type="button" + variant="outline" + className="flex-1 justify-start h-10" + onClick={() => setSelectorOpen(true)} + > + <span className="flex items-center gap-2"> + <User className="h-4 w-4" /> + {selectedPurchaseGroupCode ? ( <> - <span>담당자 로딩 중...</span> - <Loader2 className="ml-2 h-4 w-4 animate-spin" /> + [{selectedPurchaseGroupCode.PURCHASE_GROUP_CODE}] {selectedPurchaseGroupCode.DISPLAY_NAME} + {selectedPurchaseGroupCode.user && ( + <span className="text-muted-foreground"> + ({selectedPurchaseGroupCode.user.name}) + </span> + )} </> ) : ( - <> - <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" /> - </> + <span className="text-muted-foreground"> + 구매 담당자를 선택하세요 (선택사항) + </span> )} + </span> + </Button> + {selectedPurchaseGroupCode && ( + <Button + type="button" + variant="outline" + size="icon" + onClick={() => setSelectedPurchaseGroupCode(undefined)} + > + <X className="h-4 w-4" /> </Button> - </PopoverTrigger> - <PopoverContent className="w-[400px] p-0"> - <Command> - <CommandInput - placeholder="이름 또는 코드로 검색..." - value={userSearchTerm} - onValueChange={setUserSearchTerm} - /> - <CommandList className="max-h-[300px]"> - <CommandEmpty>검색 결과가 없습니다</CommandEmpty> - <CommandGroup> - {filteredUsers.map((user) => ( - <CommandItem - key={user.id} - onSelect={() => handleSelectUser(user)} - className="flex items-center justify-between" - > - <span className="flex items-center gap-2"> - <User className="h-4 w-4" /> - {user.name} - {user.userCode && ( - <span className="text-muted-foreground text-sm"> - ({user.userCode}) - </span> - )} - </span> - <Check - className={cn( - "h-4 w-4", - selectedUser?.id === user.id ? "opacity-100" : "opacity-0" - )} - /> - </CommandItem> - ))} - </CommandGroup> - </CommandList> - </Command> - </PopoverContent> - </Popover> + )} + </div> <p className="text-xs text-muted-foreground"> 구매 담당자를 선택하지 않으면 나중에 지정할 수 있습니다 </p> </div> + {/* 구매그룹코드 선택 다이얼로그 */} + <PurchaseGroupCodeSingleSelector + open={selectorOpen} + onOpenChange={setSelectorOpen} + selectedCode={selectedPurchaseGroupCode} + onCodeSelect={handleSelectPurchaseGroupCode} + title="구매 담당자 선택" + description="구매 담당자를 선택하세요" + /> + {/* RFQ 생성 대상 목록 */} <div className="space-y-2"> <label className="text-sm font-medium"> @@ -298,22 +226,22 @@ export function CreateRfqDialog({ {request.requestCode} </TableCell> <TableCell className="max-w-[250px]"> - <div className="truncate" title={request.requestTitle}> + <div className="truncate" title={request.requestTitle || undefined}> {request.requestTitle} </div> </TableCell> <TableCell> - <div className="truncate" title={request.projectName}> + <div className="truncate" title={request.projectName || undefined}> {request.projectCode} </div> </TableCell> <TableCell> - <div className="truncate" title={request.packageName}> + <div className="truncate" title={request.packageName || undefined}> {request.packageNo} </div> </TableCell> <TableCell className="text-center"> - {request.itemCount > 0 && ( + {(request.itemCount ?? 0) > 0 && ( <Badge variant="secondary" className="gap-1"> <Package className="h-3 w-3" /> {request.itemCount} @@ -321,7 +249,7 @@ export function CreateRfqDialog({ )} </TableCell> <TableCell className="text-center"> - {request.attachmentCount > 0 && ( + {(request.attachmentCount ?? 0) > 0 && ( <Badge variant="secondary" className="gap-1"> <FileText className="h-3 w-3" /> {request.attachmentCount} @@ -342,7 +270,7 @@ export function CreateRfqDialog({ <AlertDescription> <ul className="list-disc list-inside space-y-1 text-sm"> <li>RFQ 생성 시 구매 요청의 첨부파일이 자동으로 이관됩니다</li> - <li>구매 요청 상태가 "RFQ생성완료"로 변경됩니다</li> + <li>구매 요청 상태가 "RFQ생성완료"로 변경됩니다</li> <li>각 구매 요청별로 개별 RFQ가 생성됩니다</li> </ul> </AlertDescription> |
