diff options
Diffstat (limited to 'lib/bidding/detail/table/bidding-detail-selection-reason-dialog.tsx')
| -rw-r--r-- | lib/bidding/detail/table/bidding-detail-selection-reason-dialog.tsx | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/lib/bidding/detail/table/bidding-detail-selection-reason-dialog.tsx b/lib/bidding/detail/table/bidding-detail-selection-reason-dialog.tsx new file mode 100644 index 00000000..0e7ca364 --- /dev/null +++ b/lib/bidding/detail/table/bidding-detail-selection-reason-dialog.tsx @@ -0,0 +1,167 @@ +'use client' + +import * as React from 'react' +import { Bidding } from '@/db/schema' +import { updateVendorSelectionReason } from '@/lib/bidding/detail/service' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { Button } from '@/components/ui/button' +import { Textarea } from '@/components/ui/textarea' +import { Label } from '@/components/ui/label' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { useToast } from '@/hooks/use-toast' +import { useTransition } from 'react' + +interface BiddingDetailSelectionReasonDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + bidding: Bidding + onSuccess: () => void +} + +export function BiddingDetailSelectionReasonDialog({ + open, + onOpenChange, + bidding, + onSuccess +}: BiddingDetailSelectionReasonDialogProps) { + const { toast } = useToast() + const [isPending, startTransition] = useTransition() + const [selectedCompanyId, setSelectedCompanyId] = React.useState<number | null>(null) + const [selectionReason, setSelectionReason] = React.useState('') + + // 낙찰된 업체 정보 조회 (실제로는 bidding_companies에서 isWinner가 true인 업체를 조회해야 함) + React.useEffect(() => { + if (open) { + // TODO: 실제로는 낙찰된 업체 정보를 조회하여 selectedCompanyId를 설정 + setSelectedCompanyId(null) + setSelectionReason('') + } + }, [open]) + + const handleSave = () => { + if (!selectedCompanyId) { + toast({ + title: '유효성 오류', + description: '선정된 업체를 선택해주세요.', + variant: 'destructive', + }) + return + } + + if (!selectionReason.trim()) { + toast({ + title: '유효성 오류', + description: '선정 사유를 입력해주세요.', + variant: 'destructive', + }) + return + } + + startTransition(async () => { + const result = await updateVendorSelectionReason( + bidding.id, + selectedCompanyId, + selectionReason, + 'current-user' // TODO: 실제 사용자 ID + ) + + if (result.success) { + toast({ + title: '성공', + description: result.message, + }) + onSuccess() + onOpenChange(false) + } else { + toast({ + title: '오류', + description: result.error, + variant: 'destructive', + }) + } + }) + } + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="sm:max-w-[600px]"> + <DialogHeader> + <DialogTitle>업체 선정 사유</DialogTitle> + <DialogDescription> + 입찰번호: {bidding.biddingNumber} - 낙찰 업체 선정 사유 입력 + </DialogDescription> + </DialogHeader> + + <div className="space-y-6"> + {/* 낙찰 정보 */} + <div className="space-y-4"> + <div className="grid grid-cols-2 gap-4"> + <div> + <Label htmlFor="biddingNumber">입찰번호</Label> + <div className="text-sm font-mono mt-1 p-2 bg-muted rounded"> + {bidding.biddingNumber} + </div> + </div> + <div> + <Label htmlFor="projectName">프로젝트명</Label> + <div className="text-sm mt-1 p-2 bg-muted rounded"> + {bidding.projectName || '-'} + </div> + </div> + </div> + </div> + + {/* 선정 업체 선택 */} + <div className="space-y-2"> + <Label htmlFor="selectedCompany">선정된 업체</Label> + <Select + value={selectedCompanyId?.toString() || ''} + onValueChange={(value) => setSelectedCompanyId(Number(value))} + > + <SelectTrigger> + <SelectValue placeholder="선정된 업체를 선택하세요" /> + </SelectTrigger> + <SelectContent> + {/* TODO: 실제로는 낙찰된 업체 목록을 조회하여 표시 */} + <SelectItem value="1">업체 A</SelectItem> + <SelectItem value="2">업체 B</SelectItem> + <SelectItem value="3">업체 C</SelectItem> + </SelectContent> + </Select> + </div> + + {/* 선정 사유 입력 */} + <div className="space-y-2"> + <Label htmlFor="selectionReason">선정 사유</Label> + <Textarea + id="selectionReason" + value={selectionReason} + onChange={(e) => setSelectionReason(e.target.value)} + placeholder="업체 선정 사유를 상세히 입력해주세요." + rows={6} + /> + <div className="text-sm text-muted-foreground"> + 선정 사유는 추후 검토 및 감사에 활용됩니다. 구체적인 선정 기준과 이유를 명확히 기재해주세요. + </div> + </div> + </div> + + <DialogFooter> + <Button variant="outline" onClick={() => onOpenChange(false)}> + 취소 + </Button> + <Button onClick={handleSave} disabled={isPending}> + 저장 + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + ) +} |
