diff options
Diffstat (limited to 'lib/general-contracts/main/create-general-contract-dialog.tsx')
| -rw-r--r-- | lib/general-contracts/main/create-general-contract-dialog.tsx | 156 |
1 files changed, 99 insertions, 57 deletions
diff --git a/lib/general-contracts/main/create-general-contract-dialog.tsx b/lib/general-contracts/main/create-general-contract-dialog.tsx index 2c3fc8bc..168b8cbc 100644 --- a/lib/general-contracts/main/create-general-contract-dialog.tsx +++ b/lib/general-contracts/main/create-general-contract-dialog.tsx @@ -20,11 +20,12 @@ import { Textarea } from "@/components/ui/textarea" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Calendar } from "@/components/ui/calendar"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
-import { CalendarIcon } from "lucide-react"
+import { CalendarIcon, Check, ChevronsUpDown } from "lucide-react"
+import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "@/components/ui/command"
import { format } from "date-fns"
import { ko } from "date-fns/locale"
import { cn } from "@/lib/utils"
-import { createContract, getVendors, getProjects } from "@/lib/general-contracts/service"
+import { createContract, getVendors } from "@/lib/general-contracts/service"
import {
GENERAL_CONTRACT_CATEGORIES,
GENERAL_CONTRACT_TYPES,
@@ -39,7 +40,6 @@ interface CreateContractForm { type: string
executionMethod: string
vendorId: number | null
- projectId: number | null
startDate: Date | undefined
endDate: Date | undefined
validityEndDate: Date | undefined
@@ -52,7 +52,8 @@ export function CreateGeneralContractDialog() { const [open, setOpen] = React.useState(false)
const [isLoading, setIsLoading] = React.useState(false)
const [vendors, setVendors] = React.useState<Array<{ id: number; vendorName: string; vendorCode: string | null }>>([])
- const [projects, setProjects] = React.useState<Array<{ id: number; code: string; name: string; type: string }>>([])
+ const [vendorSearchTerm, setVendorSearchTerm] = React.useState("")
+ const [vendorOpen, setVendorOpen] = React.useState(false)
const [form, setForm] = React.useState<CreateContractForm>({
contractNumber: '',
@@ -61,7 +62,6 @@ export function CreateGeneralContractDialog() { type: '',
executionMethod: '',
vendorId: null,
- projectId: null,
startDate: undefined,
endDate: undefined,
validityEndDate: undefined,
@@ -70,36 +70,54 @@ export function CreateGeneralContractDialog() { // 업체 목록 조회
React.useEffect(() => {
- const fetchVendors = async () => {
+ const fetchData = async () => {
try {
const vendorList = await getVendors()
+ console.log('vendorList', vendorList)
setVendors(vendorList)
} catch (error) {
- console.error('Error fetching vendors:', error)
+ console.error('데이터 조회 오류:', error)
+ toast.error('데이터를 불러오는데 실패했습니다')
+ setVendors([])
}
}
- fetchVendors()
+ fetchData()
}, [])
- // 프로젝트 목록 조회
- React.useEffect(() => {
- const fetchProjects = async () => {
- try {
- const projectList = await getProjects()
- console.log(projectList)
- setProjects(projectList)
- } catch (error) {
- console.error('Error fetching projects:', error)
- }
- }
- fetchProjects()
- }, [])
+ // 협력업체 검색 필터링
+ const filteredVendors = React.useMemo(() => {
+ if (!vendorSearchTerm.trim()) return vendors
+ const lowerSearch = vendorSearchTerm.toLowerCase()
+ return vendors.filter(
+ vendor =>
+ vendor.vendorName.toLowerCase().includes(lowerSearch) ||
+ (vendor.vendorCode && vendor.vendorCode.toLowerCase().includes(lowerSearch))
+ )
+ }, [vendors, vendorSearchTerm])
const handleSubmit = async () => {
// 필수 필드 검증
- if (!form.name || !form.category || !form.type || !form.executionMethod ||
- !form.vendorId || !form.startDate || !form.endDate) {
- toast.error("필수 항목을 모두 입력해주세요.")
+ const validationErrors: string[] = []
+
+ if (!form.name) validationErrors.push('계약명')
+ if (!form.category) validationErrors.push('계약구분')
+ if (!form.type) validationErrors.push('계약종류')
+ if (!form.executionMethod) validationErrors.push('체결방식')
+ if (!form.vendorId) validationErrors.push('협력업체')
+
+ // AD, LO, OF 계약이 아닌 경우에만 계약기간 필수값 체크
+ if (!['AD', 'LO', 'OF'].includes(form.type)) {
+ if (!form.startDate) validationErrors.push('계약시작일')
+ if (!form.endDate) validationErrors.push('계약종료일')
+ }
+
+ // LO 계약인 경우 계약체결유효기간 필수값 체크
+ if (form.type === 'LO' && !form.validityEndDate) {
+ validationErrors.push('유효기간')
+ }
+
+ if (validationErrors.length > 0) {
+ toast.error(`다음 필수 항목을 입력해주세요: ${validationErrors.join(', ')}`)
return
}
@@ -116,7 +134,6 @@ export function CreateGeneralContractDialog() { category: form.category,
type: form.type,
executionMethod: form.executionMethod,
- projectId: form.projectId,
contractSourceType: 'manual',
vendorId: form.vendorId!,
startDate: form.startDate!.toISOString().split('T')[0],
@@ -152,7 +169,6 @@ export function CreateGeneralContractDialog() { type: '',
executionMethod: '',
vendorId: null,
- projectId: null,
startDate: undefined,
endDate: undefined,
validityEndDate: undefined,
@@ -231,15 +247,14 @@ export function CreateGeneralContractDialog() { 'AL': '연간운송계약',
'OS': '외주용역계약',
'OW': '도급계약',
- 'IS': '검사계약',
'LO': 'LOI',
'FA': 'FA',
'SC': '납품합의계약',
'OF': '클레임상계계약',
'AW': '사전작업합의',
'AD': '사전납품합의',
- 'AM': '설계계약',
- 'SC_SELL': '폐기물매각계약'
+ 'SG': '임치(물품보관)계약',
+ 'SR': '폐기물매각계약'
}
return (
<SelectItem key={type} value={type}>
@@ -269,35 +284,62 @@ export function CreateGeneralContractDialog() { </div>
<div className="grid gap-2">
- <Label htmlFor="project">프로젝트</Label>
- <Select value={form.projectId?.toString()} onValueChange={(value) => setForm(prev => ({ ...prev, projectId: parseInt(value) }))}>
- <SelectTrigger>
- <SelectValue placeholder="프로젝트 선택 (선택사항)" />
- </SelectTrigger>
- <SelectContent>
- {projects.map((project) => (
- <SelectItem key={project.id} value={project.id.toString()}>
- {project.name} ({project.code})
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
- </div>
-
- <div className="grid gap-2">
<Label htmlFor="vendor">협력업체 *</Label>
- <Select value={form.vendorId?.toString()} onValueChange={(value) => setForm(prev => ({ ...prev, vendorId: parseInt(value) }))}>
- <SelectTrigger>
- <SelectValue placeholder="협력업체 선택" />
- </SelectTrigger>
- <SelectContent>
- {vendors.map((vendor) => (
- <SelectItem key={vendor.id} value={vendor.id.toString()}>
- {vendor.vendorName} ({vendor.vendorCode})
- </SelectItem>
- ))}
- </SelectContent>
- </Select>
+ <Popover open={vendorOpen} onOpenChange={setVendorOpen}>
+ <PopoverTrigger asChild>
+ <Button
+ variant="outline"
+ role="combobox"
+ aria-expanded={vendorOpen}
+ className="w-full justify-between"
+ >
+ {form.vendorId ? (
+ (() => {
+ const selected = vendors.find(v => v.id === form.vendorId)
+ return selected ? `${selected.vendorName} (${selected.vendorCode || ''})` : "협력업체 선택"
+ })()
+ ) : (
+ "협력업체 선택"
+ )}
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
+ </Button>
+ </PopoverTrigger>
+ <PopoverContent className="w-[400px] p-0">
+ <Command>
+ <CommandInput
+ placeholder="협력업체명/코드 검색..."
+ onValueChange={setVendorSearchTerm}
+ />
+ <CommandList className="max-h-[300px]">
+ <CommandEmpty>검색 결과가 없습니다</CommandEmpty>
+ <CommandGroup>
+ {filteredVendors.map((vendor) => (
+ <CommandItem
+ key={vendor.id}
+ value={`${vendor.vendorName} ${vendor.vendorCode || ''}`}
+ onSelect={() => {
+ setForm(prev => ({ ...prev, vendorId: vendor.id }))
+ setVendorOpen(false)
+ setVendorSearchTerm("")
+ }}
+ >
+ <Check
+ className={cn(
+ "mr-2 h-4 w-4",
+ form.vendorId === vendor.id ? "opacity-100" : "opacity-0"
+ )}
+ />
+ <span className="font-medium">{vendor.vendorName}</span>
+ {vendor.vendorCode && (
+ <span className="ml-2 text-gray-500">({vendor.vendorCode})</span>
+ )}
+ </CommandItem>
+ ))}
+ </CommandGroup>
+ </CommandList>
+ </Command>
+ </PopoverContent>
+ </Popover>
</div>
<div className="grid grid-cols-3 gap-4">
|
