summaryrefslogtreecommitdiff
path: root/components/bidding/manage/bidding-basic-info-editor.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-18 10:30:31 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-18 10:30:31 +0000
commitc4f5472b961afb237dc819f9dd3f42a7b8f71075 (patch)
treea1c0d00e46a005ff472bf1125e739bae73b0a53e /components/bidding/manage/bidding-basic-info-editor.tsx
parent1d1f6010704a1d655b3007887db0fe3ac866177a (diff)
(최겸) 구매 입찰 수정, 입찰초대 결재 등록, 재입찰, 차수증가, 폐찰, 유찰취소 로직 수정, readonly 추가 등
Diffstat (limited to 'components/bidding/manage/bidding-basic-info-editor.tsx')
-rw-r--r--components/bidding/manage/bidding-basic-info-editor.tsx136
1 files changed, 77 insertions, 59 deletions
diff --git a/components/bidding/manage/bidding-basic-info-editor.tsx b/components/bidding/manage/bidding-basic-info-editor.tsx
index e92c39a5..f0d56689 100644
--- a/components/bidding/manage/bidding-basic-info-editor.tsx
+++ b/components/bidding/manage/bidding-basic-info-editor.tsx
@@ -45,6 +45,16 @@ import type { ProcurementManagerWithUser } from '@/components/common/selectors/p
import { getBiddingDocuments, uploadBiddingDocument, deleteBiddingDocument } from '@/lib/bidding/detail/service'
import { downloadFile } from '@/lib/file-download'
+// Dropzone components
+import {
+ Dropzone,
+ DropzoneDescription,
+ DropzoneInput,
+ DropzoneTitle,
+ DropzoneUploadIcon,
+ DropzoneZone,
+} from "@/components/ui/dropzone"
+
// 입찰 기본 정보 에디터 컴포넌트
interface BiddingBasicInfo {
title?: string
@@ -78,6 +88,7 @@ interface BiddingBasicInfo {
interface BiddingBasicInfoEditorProps {
biddingId: number
+ readonly?: boolean
}
interface UploadedDocument {
@@ -95,7 +106,7 @@ interface UploadedDocument {
uploadedBy: string
}
-export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProps) {
+export function BiddingBasicInfoEditor({ biddingId, readonly = false }: BiddingBasicInfoEditorProps) {
const [isLoading, setIsLoading] = React.useState(true)
const [isSubmitting, setIsSubmitting] = React.useState(false)
const [isLoadingTemplate, setIsLoadingTemplate] = React.useState(false)
@@ -535,7 +546,7 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp
<FormItem>
<FormLabel>입찰명 <span className="text-red-500">*</span></FormLabel>
<FormControl>
- <Input placeholder="입찰명을 입력하세요" {...field} />
+ <Input placeholder="입찰명을 입력하세요" {...field} disabled={readonly} />
</FormControl>
<FormMessage />
</FormItem>
@@ -883,7 +894,7 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp
<FormItem>
<FormLabel>입찰개요</FormLabel>
<FormControl>
- <Textarea placeholder="입찰에 대한 설명을 입력하세요" rows={2} {...field} />
+ <Textarea placeholder="입찰에 대한 설명을 입력하세요" rows={2} {...field} readOnly={readonly} />
</FormControl>
<FormMessage />
</FormItem>
@@ -896,7 +907,7 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp
<FormItem>
<FormLabel>비고</FormLabel>
<FormControl>
- <Textarea placeholder="추가 사항이나 참고사항을 입력하세요" rows={3} {...field} />
+ <Textarea placeholder="추가 사항이나 참고사항을 입력하세요" rows={3} {...field} readOnly={readonly} />
</FormControl>
<FormMessage />
</FormItem>
@@ -1123,6 +1134,7 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp
}))
}}
rows={3}
+ readOnly={readonly}
/>
</div>
</div>
@@ -1138,7 +1150,7 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp
<TiptapEditor
content={field.value || noticeTemplate}
setContent={field.onChange}
- disabled={isLoadingTemplate}
+ disabled={isLoadingTemplate || readonly}
height="300px"
/>
</div>
@@ -1156,12 +1168,14 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp
</div>
{/* 액션 버튼 */}
- <div className="flex justify-end gap-4 pt-4">
- <Button type="submit" disabled={isSubmitting} className="flex items-center gap-2">
- {isSubmitting ? '저장 중...' : '저장'}
- <ChevronRight className="h-4 w-4" />
- </Button>
- </div>
+ {!readonly && (
+ <div className="flex justify-end gap-4 pt-4">
+ <Button type="submit" disabled={isSubmitting} className="flex items-center gap-2">
+ {isSubmitting ? '저장 중...' : '저장'}
+ <ChevronRight className="h-4 w-4" />
+ </Button>
+ </div>
+ )}
</form>
</Form>
</CardContent>
@@ -1175,33 +1189,35 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp
SHI용 첨부파일
</CardTitle>
<p className="text-sm text-muted-foreground">
- SHI에서 제공하는 문서나 파일을 업로드하세요
+ 내부 보관를 위해 필요한 문서나 파일을 업로드 하세요.
</p>
</CardHeader>
<CardContent className="space-y-4">
- <div className="border-2 border-dashed border-gray-300 rounded-lg p-6">
- <div className="text-center">
- <Upload className="h-12 w-12 text-gray-400 mx-auto mb-4" />
- <div className="space-y-2">
- <p className="text-sm text-gray-600">
- 파일을 드래그 앤 드롭하거나{' '}
- <label className="text-blue-600 hover:text-blue-500 cursor-pointer">
- <input
- type="file"
- multiple
- className="hidden"
- onChange={(e) => {
- const files = Array.from(e.target.files || [])
- setShiAttachmentFiles(prev => [...prev, ...files])
- }}
- accept=".pdf,.doc,.docx,.xls,.xlsx,.png,.jpg,.jpeg"
- />
- 찾아보세요
- </label>
- </p>
- </div>
- </div>
- </div>
+ <Dropzone
+ maxSize={6e8} // 600MB
+ onDropAccepted={(files) => {
+ const newFiles = Array.from(files)
+ setShiAttachmentFiles(prev => [...prev, ...newFiles])
+ }}
+ onDropRejected={() => {
+ toast({
+ title: "File upload rejected",
+ description: "Please check file size and type.",
+ variant: "destructive",
+ })
+ }}
+ >
+ {() => (
+ <DropzoneZone className="flex justify-center h-32">
+ <div className="flex items-center gap-6">
+ <DropzoneUploadIcon />
+ <div className="grid gap-0.5">
+ <DropzoneTitle>파일을 드래그하여 업로드</DropzoneTitle>
+ </div>
+ </div>
+ </DropzoneZone>
+ )}
+ </Dropzone>
{shiAttachmentFiles.length > 0 && (
<div className="space-y-2">
@@ -1307,33 +1323,35 @@ export function BiddingBasicInfoEditor({ biddingId }: BiddingBasicInfoEditorProp
협력업체용 첨부파일
</CardTitle>
<p className="text-sm text-muted-foreground">
- 협력업체에서 제공하는 문서나 파일을 업로드하세요
+ 협력사로 제공하는 문서나 파일을 업로드 하세요.
</p>
</CardHeader>
<CardContent className="space-y-4">
- <div className="border-2 border-dashed border-gray-300 rounded-lg p-6">
- <div className="text-center">
- <Upload className="h-12 w-12 text-gray-400 mx-auto mb-4" />
- <div className="space-y-2">
- <p className="text-sm text-gray-600">
- 파일을 드래그 앤 드롭하거나{' '}
- <label className="text-blue-600 hover:text-blue-500 cursor-pointer">
- <input
- type="file"
- multiple
- className="hidden"
- onChange={(e) => {
- const files = Array.from(e.target.files || [])
- setVendorAttachmentFiles(prev => [...prev, ...files])
- }}
- accept=".pdf,.doc,.docx,.xls,.xlsx,.png,.jpg,.jpeg"
- />
- 찾아보세요
- </label>
- </p>
- </div>
- </div>
- </div>
+ <Dropzone
+ maxSize={6e8} // 600MB
+ onDropAccepted={(files) => {
+ const newFiles = Array.from(files)
+ setVendorAttachmentFiles(prev => [...prev, ...newFiles])
+ }}
+ onDropRejected={() => {
+ toast({
+ title: "File upload rejected",
+ description: "Please check file size and type.",
+ variant: "destructive",
+ })
+ }}
+ >
+ {() => (
+ <DropzoneZone className="flex justify-center h-32">
+ <div className="flex items-center gap-6">
+ <DropzoneUploadIcon />
+ <div className="grid gap-0.5">
+ <DropzoneTitle>파일을 드래그하여 업로드</DropzoneTitle>
+ </div>
+ </div>
+ </DropzoneZone>
+ )}
+ </Dropzone>
{vendorAttachmentFiles.length > 0 && (
<div className="space-y-2">