summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/itb/table/create-rfq-dialog.tsx208
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>구매 요청 상태가 &quot;RFQ생성완료&quot;로 변경됩니다</li>
<li>각 구매 요청별로 개별 RFQ가 생성됩니다</li>
</ul>
</AlertDescription>