summaryrefslogtreecommitdiff
path: root/lib/pq/pq-criteria/add-pq-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pq/pq-criteria/add-pq-dialog.tsx')
-rw-r--r--lib/pq/pq-criteria/add-pq-dialog.tsx145
1 files changed, 142 insertions, 3 deletions
diff --git a/lib/pq/pq-criteria/add-pq-dialog.tsx b/lib/pq/pq-criteria/add-pq-dialog.tsx
index 660eb360..1752f503 100644
--- a/lib/pq/pq-criteria/add-pq-dialog.tsx
+++ b/lib/pq/pq-criteria/add-pq-dialog.tsx
@@ -38,6 +38,10 @@ import {
import { useToast } from "@/hooks/use-toast"
import { createPqCriteria } from "../service"
+import { uploadPqCriteriaFileAction } from "@/lib/pq/service"
+import { Dropzone, DropzoneInput, DropzoneZone, DropzoneUploadIcon, DropzoneTitle, DropzoneDescription } from "@/components/ui/dropzone"
+import { FileList, FileListHeader, FileListInfo, FileListItem, FileListName, FileListDescription, FileListAction } from "@/components/ui/file-list"
+import { X, Loader2 } from "lucide-react"
// PQ 생성을 위한 Zod 스키마 정의
const createPqSchema = z.object({
@@ -48,7 +52,7 @@ const createPqSchema = z.object({
description: z.string().optional(),
remarks: z.string().optional(),
inputFormat: z.string().default("TEXT"),
-
+ type: z.string().optional(),
});
type CreatePqFormType = z.infer<typeof createPqSchema>;
@@ -74,6 +78,12 @@ const inputFormatOptions = [
{ value: "TEXT_FILE", label: "텍스트 + 파일" },
];
+const typeOptions = [
+ { value: "내자", label: "내자" },
+ { value: "외자", label: "외자" },
+ { value: "내외자", label: "내외자" },
+];
+
interface AddPqDialogProps {
pqListId: number;
}
@@ -81,6 +91,10 @@ interface AddPqDialogProps {
export function AddPqDialog({ pqListId }: AddPqDialogProps) {
const [open, setOpen] = React.useState(false)
const [isSubmitting, setIsSubmitting] = React.useState(false)
+ const [isUploading, setIsUploading] = React.useState(false)
+ const [uploadedFiles, setUploadedFiles] = React.useState<
+ { fileName: string; url: string; size?: number; originalFileName?: string }[]
+ >([])
const router = useRouter()
const { toast } = useToast()
@@ -95,7 +109,7 @@ export function AddPqDialog({ pqListId }: AddPqDialogProps) {
description: "",
remarks: "",
inputFormat: "TEXT",
-
+ type: "내외자",
},
})
const formState = form.formState
@@ -105,7 +119,10 @@ export function AddPqDialog({ pqListId }: AddPqDialogProps) {
setIsSubmitting(true)
// 서버 액션 호출
- const result = await createPqCriteria(pqListId, data)
+ const result = await createPqCriteria(pqListId, {
+ ...data,
+ attachments: uploadedFiles,
+ })
if (!result.success) {
toast({
@@ -124,6 +141,7 @@ export function AddPqDialog({ pqListId }: AddPqDialogProps) {
// 모달 닫고 폼 리셋
form.reset()
+ setUploadedFiles([])
setOpen(false)
// 페이지 새로고침
@@ -144,10 +162,34 @@ export function AddPqDialog({ pqListId }: AddPqDialogProps) {
function handleDialogOpenChange(nextOpen: boolean) {
if (!nextOpen) {
form.reset()
+ setUploadedFiles([])
}
setOpen(nextOpen)
}
+ const handleUpload = async (files: File[]) => {
+ try {
+ setIsUploading(true)
+ for (const file of files) {
+ const uploaded = await uploadPqCriteriaFileAction(file)
+ setUploadedFiles((prev) => [...prev, uploaded])
+ }
+ toast({
+ title: "업로드 완료",
+ description: "첨부파일이 업로드되었습니다.",
+ })
+ } catch (error) {
+ console.error(error)
+ toast({
+ title: "업로드 실패",
+ description: "첨부파일 업로드 중 오류가 발생했습니다.",
+ variant: "destructive",
+ })
+ } finally {
+ setIsUploading(false)
+ }
+ }
+
return (
<Dialog open={open} onOpenChange={handleDialogOpenChange}>
<DialogTrigger asChild>
@@ -253,6 +295,33 @@ export function AddPqDialog({ pqListId }: AddPqDialogProps) {
)}
/>
+ {/* Type 필드 */}
+ <FormField
+ control={form.control}
+ name="type"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>내/외자 구분</FormLabel>
+ <Select onValueChange={field.onChange} value={field.value}>
+ <FormControl>
+ <SelectTrigger>
+ <SelectValue placeholder="구분을 선택하세요" />
+ </SelectTrigger>
+ </FormControl>
+ <SelectContent>
+ {typeOptions.map((option) => (
+ <SelectItem key={option.value} value={option.value}>
+ {option.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <FormDescription>미선택 시 기본값은 내외자입니다.</FormDescription>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
{/* Input Format 필드 */}
<FormField
control={form.control}
@@ -279,6 +348,76 @@ export function AddPqDialog({ pqListId }: AddPqDialogProps) {
)}
/>
+ {/* 첨부 파일 업로드 */}
+ <div className="space-y-2">
+ <div className="flex items-center justify-between">
+ <FormLabel>첨부 파일</FormLabel>
+ {isUploading && (
+ <div className="flex items-center text-xs text-muted-foreground">
+ <Loader2 className="mr-1 h-3 w-3 animate-spin" /> 업로드 중...
+ </div>
+ )}
+ </div>
+ <Dropzone
+ maxSize={6e8}
+ onDropAccepted={(files) => handleUpload(files)}
+ onDropRejected={() =>
+ toast({
+ title: "업로드 실패",
+ description: "파일 크기/형식을 확인하세요.",
+ variant: "destructive",
+ })
+ }
+ disabled={isUploading}
+ >
+ {() => (
+ <FormItem>
+ <DropzoneZone className="flex justify-center h-28">
+ <FormControl>
+ <DropzoneInput />
+ </FormControl>
+ <div className="flex items-center gap-4">
+ <DropzoneUploadIcon />
+ <div className="grid gap-0.5">
+ <DropzoneTitle>파일을 드래그하거나 클릭하여 업로드</DropzoneTitle>
+ <DropzoneDescription>PDF, 이미지, 문서 (최대 600MB)</DropzoneDescription>
+ </div>
+ </div>
+ </DropzoneZone>
+ <FormDescription>기준 문서 첨부가 필요한 경우 업로드하세요.</FormDescription>
+ </FormItem>
+ )}
+ </Dropzone>
+
+ {uploadedFiles.length > 0 && (
+ <div className="space-y-2">
+ <p className="text-sm font-medium">첨부된 파일 ({uploadedFiles.length})</p>
+ <FileList>
+ {uploadedFiles.map((file, idx) => (
+ <FileListItem key={idx}>
+ <FileListHeader>
+ <FileListInfo>
+ <FileListName>{file.originalFileName || file.fileName}</FileListName>
+ {file.size && (
+ <FileListDescription>{`${file.size} bytes`}</FileListDescription>
+ )}
+ </FileListInfo>
+ <FileListAction
+ onClick={() =>
+ setUploadedFiles((prev) => prev.filter((_, i) => i !== idx))
+ }
+ >
+ <X className="h-4 w-4" />
+ <span className="sr-only">Remove</span>
+ </FileListAction>
+ </FileListHeader>
+ </FileListItem>
+ ))}
+ </FileList>
+ </div>
+ )}
+ </div>
+
{/* Description 필드 */}
<FormField
control={form.control}