diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-17 10:41:29 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-17 10:41:29 +0000 |
| commit | c8beed57d9fb10c02b8951cd4267017984ca5beb (patch) | |
| tree | fb4fe9988eda149fee59ffdb337ab7ec3d4c3122 /lib/general-contracts/detail | |
| parent | 10cb50753ccf318024c4394282f9e8d968dcd1a5 (diff) | |
(최겸) 구매 일반계약 프로젝트id추가, 선적지, 하역지 연동, numbering 수정
Diffstat (limited to 'lib/general-contracts/detail')
| -rw-r--r-- | lib/general-contracts/detail/general-contract-approval-request-dialog.tsx | 2 | ||||
| -rw-r--r-- | lib/general-contracts/detail/general-contract-basic-info.tsx | 241 |
2 files changed, 190 insertions, 53 deletions
diff --git a/lib/general-contracts/detail/general-contract-approval-request-dialog.tsx b/lib/general-contracts/detail/general-contract-approval-request-dialog.tsx index e4aa022a..f05fe9ef 100644 --- a/lib/general-contracts/detail/general-contract-approval-request-dialog.tsx +++ b/lib/general-contracts/detail/general-contract-approval-request-dialog.tsx @@ -863,7 +863,7 @@ export function ContractApprovalRequestDialog({ <span className="font-medium">선적지:</span> {String(contractSummary?.basicInfo?.shippingLocation || '')}
</div>
<div>
- <span className="font-medium">도착지:</span> {String(contractSummary?.basicInfo?.dischargeLocation || '')}
+ <span className="font-medium">하역지:</span> {String(contractSummary?.basicInfo?.dischargeLocation || '')}
</div>
<div>
<span className="font-medium">계약납기:</span> {String(contractSummary?.basicInfo?.contractDeliveryDate || '')}
diff --git a/lib/general-contracts/detail/general-contract-basic-info.tsx b/lib/general-contracts/detail/general-contract-basic-info.tsx index ac1315bb..882ed8b2 100644 --- a/lib/general-contracts/detail/general-contract-basic-info.tsx +++ b/lib/general-contracts/detail/general-contract-basic-info.tsx @@ -14,6 +14,7 @@ import { toast } from 'sonner' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { GeneralContract } from '@/db/schema'
import { ContractDocuments } from './general-contract-documents'
+import { getPaymentTermsForSelection, getIncotermsForSelection, getPlaceOfShippingForSelection, getPlaceOfDestinationForSelection } from '@/lib/procurement-select/service'
interface ContractBasicInfoProps {
contractId: number
@@ -27,6 +28,13 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { // 독립적인 상태 관리
const [paymentDeliveryPercent, setPaymentDeliveryPercent] = useState('')
+
+ // Procurement 데이터 상태들
+ const [paymentTermsOptions, setPaymentTermsOptions] = useState<Array<{code: string, description: string}>>([])
+ const [incotermsOptions, setIncotermsOptions] = useState<Array<{code: string, description: string}>>([])
+ const [shippingPlaces, setShippingPlaces] = useState<Array<{code: string, description: string}>>([])
+ const [destinationPlaces, setDestinationPlaces] = useState<Array<{code: string, description: string}>>([])
+ const [procurementLoading, setProcurementLoading] = useState(false)
const [formData, setFormData] = useState({
specificationType: '',
@@ -169,6 +177,67 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { loadContract()
}
}, [contractId])
+
+ // Procurement 데이터 로드 함수들
+ const loadPaymentTerms = React.useCallback(async () => {
+ setProcurementLoading(true);
+ try {
+ const data = await getPaymentTermsForSelection();
+ setPaymentTermsOptions(data);
+ } catch (error) {
+ console.error("Failed to load payment terms:", error);
+ toast.error("결제조건 목록을 불러오는데 실패했습니다.");
+ } finally {
+ setProcurementLoading(false);
+ }
+ }, []);
+
+ const loadIncoterms = React.useCallback(async () => {
+ setProcurementLoading(true);
+ try {
+ const data = await getIncotermsForSelection();
+ setIncotermsOptions(data);
+ } catch (error) {
+ console.error("Failed to load incoterms:", error);
+ toast.error("운송조건 목록을 불러오는데 실패했습니다.");
+ } finally {
+ setProcurementLoading(false);
+ }
+ }, []);
+
+ const loadShippingPlaces = React.useCallback(async () => {
+ setProcurementLoading(true);
+ try {
+ const data = await getPlaceOfShippingForSelection();
+ setShippingPlaces(data);
+ } catch (error) {
+ console.error("Failed to load shipping places:", error);
+ toast.error("선적지 목록을 불러오는데 실패했습니다.");
+ } finally {
+ setProcurementLoading(false);
+ }
+ }, []);
+
+ const loadDestinationPlaces = React.useCallback(async () => {
+ setProcurementLoading(true);
+ try {
+ const data = await getPlaceOfDestinationForSelection();
+ setDestinationPlaces(data);
+ } catch (error) {
+ console.error("Failed to load destination places:", error);
+ toast.error("하역지 목록을 불러오는데 실패했습니다.");
+ } finally {
+ setProcurementLoading(false);
+ }
+ }, []);
+
+ // 컴포넌트 마운트 시 procurement 데이터 로드
+ React.useEffect(() => {
+ loadPaymentTerms();
+ loadIncoterms();
+ loadShippingPlaces();
+ loadDestinationPlaces();
+ }, [loadPaymentTerms, loadIncoterms, loadShippingPlaces, loadDestinationPlaces]);
const handleSaveContractInfo = async () => {
if (!userId) {
toast.error('사용자 정보를 찾을 수 없습니다.')
@@ -518,15 +587,16 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <Label htmlFor="apBond" className="text-sm">AP Bond & Performance Bond</Label>
<Input
type="number"
+ min="0"
placeholder="%"
className="w-16"
value={formData.paymentBeforeDelivery.apBondPercent || ''}
- onChange={(e) => setFormData(prev => ({
- ...prev,
- paymentBeforeDelivery: {
- ...prev.paymentBeforeDelivery,
- apBondPercent: e.target.value
- }
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ apBondPercent: e.target.value
+ }
}))}
disabled={!formData.paymentBeforeDelivery.apBond}
/>
@@ -548,15 +618,16 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <Label htmlFor="drawingSubmission" className="text-sm">도면제출</Label>
<Input
type="number"
+ min="0"
placeholder="%"
className="w-16"
value={formData.paymentBeforeDelivery.drawingSubmissionPercent || ''}
- onChange={(e) => setFormData(prev => ({
- ...prev,
- paymentBeforeDelivery: {
- ...prev.paymentBeforeDelivery,
- drawingSubmissionPercent: e.target.value
- }
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ drawingSubmissionPercent: e.target.value
+ }
}))}
disabled={!formData.paymentBeforeDelivery.drawingSubmission}
/>
@@ -578,15 +649,16 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <Label htmlFor="materialPurchase" className="text-sm">소재구매 문서</Label>
<Input
type="number"
+ min="0"
placeholder="%"
className="w-16"
value={formData.paymentBeforeDelivery.materialPurchasePercent || ''}
- onChange={(e) => setFormData(prev => ({
- ...prev,
- paymentBeforeDelivery: {
- ...prev.paymentBeforeDelivery,
- materialPurchasePercent: e.target.value
- }
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentBeforeDelivery: {
+ ...prev.paymentBeforeDelivery,
+ materialPurchasePercent: e.target.value
+ }
}))}
disabled={!formData.paymentBeforeDelivery.materialPurchase}
/>
@@ -618,6 +690,7 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <div className="flex items-center gap-2 mt-2">
<Input
type="number"
+ min="0"
value={paymentDeliveryPercent}
onChange={(e) => setPaymentDeliveryPercent(e.target.value)}
placeholder="퍼센트"
@@ -654,15 +727,16 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <Label htmlFor="commissioning" className="text-sm">Commissioning 완료</Label>
<Input
type="number"
+ min="0"
placeholder="%"
className="w-16"
value={formData.paymentAfterDelivery.commissioningPercent || ''}
- onChange={(e) => setFormData(prev => ({
- ...prev,
- paymentAfterDelivery: {
- ...prev.paymentAfterDelivery,
- commissioningPercent: e.target.value
- }
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentAfterDelivery: {
+ ...prev.paymentAfterDelivery,
+ commissioningPercent: e.target.value
+ }
}))}
disabled={!formData.paymentAfterDelivery.commissioning}
/>
@@ -684,15 +758,16 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <Label htmlFor="finalDocument" className="text-sm">최종문서 승인</Label>
<Input
type="number"
+ min="0"
placeholder="%"
className="w-16"
value={formData.paymentAfterDelivery.finalDocumentPercent || ''}
- onChange={(e) => setFormData(prev => ({
- ...prev,
- paymentAfterDelivery: {
- ...prev.paymentAfterDelivery,
- finalDocumentPercent: e.target.value
- }
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ paymentAfterDelivery: {
+ ...prev.paymentAfterDelivery,
+ finalDocumentPercent: e.target.value
+ }
}))}
disabled={!formData.paymentAfterDelivery.finalDocument}
/>
@@ -736,13 +811,27 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <div className="space-y-3">
<div className="space-y-2">
<Label htmlFor="paymentTerm">지불조건 <span className="text-red-600">*</span></Label>
- <Input
- type="text"
+ <Select
value={formData.paymentTerm}
- onChange={(e) => setFormData(prev => ({ ...prev, paymentTerm: e.target.value }))}
- placeholder="지불조건을 입력하세요"
- className={errors.paymentTerm ? 'border-red-500' : ''}
- />
+ onValueChange={(value) => setFormData(prev => ({ ...prev, paymentTerm: value }))}
+ >
+ <SelectTrigger className={errors.paymentTerm ? 'border-red-500' : ''}>
+ <SelectValue placeholder="지불조건을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {paymentTermsOptions.length > 0 ? (
+ paymentTermsOptions.map((option) => (
+ <SelectItem key={option.code} value={option.code}>
+ {option.code} {option.description && `(${option.description})`}
+ </SelectItem>
+ ))
+ ) : (
+ <SelectItem value="loading" disabled>
+ 데이터를 불러오는 중...
+ </SelectItem>
+ )}
+ </SelectContent>
+ </Select>
{errors.paymentTerm && (
<p className="text-sm text-red-600">지불조건은 필수값입니다.</p>
)}
@@ -781,12 +870,13 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <Label htmlFor="liquidatedDamages" className="text-sm">지체상금</Label>
<Input
type="number"
+ min="0"
placeholder="%"
className="w-16"
value={formData.liquidatedDamagesPercent || ''}
- onChange={(e) => setFormData(prev => ({
- ...prev,
- liquidatedDamagesPercent: e.target.value
+ onChange={(e) => setFormData(prev => ({
+ ...prev,
+ liquidatedDamagesPercent: e.target.value
}))}
disabled={!formData.liquidatedDamages}
/>
@@ -823,12 +913,27 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <div className="space-y-3">
<div className="space-y-2">
<Label htmlFor="deliveryTerm">인도조건</Label>
- <Input
- type="text"
+ <Select
value={formData.deliveryTerm}
- onChange={(e) => setFormData(prev => ({ ...prev, deliveryTerm: e.target.value }))}
- placeholder="인도조건을 입력하세요"
- />
+ onValueChange={(value) => setFormData(prev => ({ ...prev, deliveryTerm: value }))}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder="인도조건을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {incotermsOptions.length > 0 ? (
+ incotermsOptions.map((option) => (
+ <SelectItem key={option.code} value={option.code}>
+ {option.code} {option.description && `(${option.description})`}
+ </SelectItem>
+ ))
+ ) : (
+ <SelectItem value="loading" disabled>
+ 데이터를 불러오는 중...
+ </SelectItem>
+ )}
+ </SelectContent>
+ </Select>
</div>
</div>
</div>
@@ -838,11 +943,27 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <div className="space-y-3">
<div className="space-y-2">
<Label htmlFor="shippingLocation">선적지</Label>
- <Input
+ <Select
value={formData.shippingLocation}
- onChange={(e) => setFormData(prev => ({ ...prev, shippingLocation: e.target.value }))}
- placeholder="선적지를 입력하세요"
- />
+ onValueChange={(value) => setFormData(prev => ({ ...prev, shippingLocation: value }))}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder="선적지를 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {shippingPlaces.length > 0 ? (
+ shippingPlaces.map((place) => (
+ <SelectItem key={place.code} value={place.code}>
+ {place.code} {place.description && `(${place.description})`}
+ </SelectItem>
+ ))
+ ) : (
+ <SelectItem value="loading" disabled>
+ 데이터를 불러오는 중...
+ </SelectItem>
+ )}
+ </SelectContent>
+ </Select>
</div>
</div>
</div>
@@ -852,11 +973,27 @@ export function ContractBasicInfo({ contractId }: ContractBasicInfoProps) { <div className="space-y-3">
<div className="space-y-2">
<Label htmlFor="dischargeLocation">하역지</Label>
- <Input
+ <Select
value={formData.dischargeLocation}
- onChange={(e) => setFormData(prev => ({ ...prev, dischargeLocation: e.target.value }))}
- placeholder="하역지를 입력하세요"
- />
+ onValueChange={(value) => setFormData(prev => ({ ...prev, dischargeLocation: value }))}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder="하역지를 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ {destinationPlaces.length > 0 ? (
+ destinationPlaces.map((place) => (
+ <SelectItem key={place.code} value={place.code}>
+ {place.code} {place.description && `(${place.description})`}
+ </SelectItem>
+ ))
+ ) : (
+ <SelectItem value="loading" disabled>
+ 데이터를 불러오는 중...
+ </SelectItem>
+ )}
+ </SelectContent>
+ </Select>
</div>
</div>
</div>
|
