diff options
Diffstat (limited to 'lib/pq/pq-criteria/update-pq-sheet.tsx')
| -rw-r--r-- | lib/pq/pq-criteria/update-pq-sheet.tsx | 148 |
1 files changed, 147 insertions, 1 deletions
diff --git a/lib/pq/pq-criteria/update-pq-sheet.tsx b/lib/pq/pq-criteria/update-pq-sheet.tsx index fb298e9b..6aeb689f 100644 --- a/lib/pq/pq-criteria/update-pq-sheet.tsx +++ b/lib/pq/pq-criteria/update-pq-sheet.tsx @@ -41,6 +41,10 @@ import { Textarea } from "@/components/ui/textarea" import { updatePqCriteria } from "../service"
import { groupOptions } from "./add-pq-dialog"
import { Checkbox } from "@/components/ui/checkbox"
+import { uploadPqCriteriaFileAction, getPqCriteriaAttachments } 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 updatePqSchema = z.object({
@@ -52,6 +56,7 @@ const updatePqSchema = z.object({ inputFormat: z.string().default("TEXT"),
subGroupName: z.string().optional(),
+ type: z.string().optional(),
});
type UpdatePqSchema = z.infer<typeof updatePqSchema>;
@@ -67,6 +72,12 @@ const inputFormatOptions = [ { value: "TEXT_FILE", label: "텍스트 + 파일" }
];
+const typeOptions = [
+ { value: "내자", label: "내자" },
+ { value: "외자", label: "외자" },
+ { value: "내외자", label: "내외자" },
+];
+
interface UpdatePqSheetProps
extends React.ComponentPropsWithRef<typeof Sheet> {
pq: {
@@ -79,11 +90,16 @@ interface UpdatePqSheetProps inputFormat: string;
subGroupName: string | null;
+ type?: string | null;
} | null
}
export function UpdatePqSheet({ pq, ...props }: UpdatePqSheetProps) {
const [isUpdatePending, startUpdateTransition] = React.useTransition()
+ const [isUploading, setIsUploading] = React.useState(false)
+ const [attachments, setAttachments] = React.useState<
+ { fileName: string; url: string; size?: number; originalFileName?: string }[]
+ >([])
const router = useRouter()
const form = useForm<UpdatePqSchema>({
@@ -97,6 +113,7 @@ export function UpdatePqSheet({ pq, ...props }: UpdatePqSheetProps) { inputFormat: pq?.inputFormat ?? "TEXT",
subGroupName: pq?.subGroupName ?? "",
+ type: pq?.type ?? "내외자",
},
})
@@ -112,15 +129,51 @@ export function UpdatePqSheet({ pq, ...props }: UpdatePqSheetProps) { inputFormat: pq.inputFormat ?? "TEXT",
subGroupName: pq.subGroupName ?? "",
+ type: pq.type ?? "내외자",
});
+
+ // 기존 첨부 로드
+ getPqCriteriaAttachments(pq.id).then((res) => {
+ if (res.success && res.data) {
+ setAttachments(
+ res.data.map((a) => ({
+ fileName: a.fileName,
+ url: a.filePath,
+ size: a.fileSize ?? undefined,
+ originalFileName: a.originalFileName || a.fileName,
+ }))
+ )
+ } else {
+ setAttachments([])
+ }
+ })
}
}, [pq, form]);
+ const handleUpload = async (files: File[]) => {
+ try {
+ setIsUploading(true)
+ for (const file of files) {
+ const uploaded = await uploadPqCriteriaFileAction(file)
+ setAttachments((prev) => [...prev, uploaded])
+ }
+ toast.success("첨부파일이 업로드되었습니다")
+ } catch (error) {
+ console.error(error)
+ toast.error("첨부파일 업로드에 실패했습니다")
+ } finally {
+ setIsUploading(false)
+ }
+ }
+
function onSubmit(input: UpdatePqSchema) {
startUpdateTransition(async () => {
if (!pq) return
- const result = await updatePqCriteria(pq.id, input)
+ const result = await updatePqCriteria(pq.id, {
+ ...input,
+ attachments,
+ })
if (!result.success) {
toast.error(result.message || "PQ 항목 수정에 실패했습니다")
@@ -231,6 +284,33 @@ export function UpdatePqSheet({ pq, ...props }: UpdatePqSheetProps) { )}
/>
+ {/* 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}
@@ -257,6 +337,72 @@ export function UpdatePqSheet({ pq, ...props }: UpdatePqSheetProps) { )}
/>
+ {/* 첨부 파일 업로드 */}
+ <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.error("파일 크기/형식을 확인하세요.")
+ }
+ disabled={isUploading}
+ >
+ {() => (
+ <FormItem>
+ <DropzoneZone className="flex justify-center h-24">
+ <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>
+
+ {attachments.length > 0 && (
+ <div className="space-y-2">
+ <p className="text-sm font-medium">첨부된 파일 ({attachments.length})</p>
+ <FileList>
+ {attachments.map((file, idx) => (
+ <FileListItem key={idx}>
+ <FileListHeader>
+ <FileListInfo>
+ <FileListName>{file.originalFileName || file.fileName}</FileListName>
+ {file.size && (
+ <FileListDescription>{`${file.size} bytes`}</FileListDescription>
+ )}
+ </FileListInfo>
+ <FileListAction
+ onClick={() =>
+ setAttachments((prev) => prev.filter((_, i) => i !== idx))
+ }
+ >
+ <X className="h-4 w-4" />
+ <span className="sr-only">Remove</span>
+ </FileListAction>
+ </FileListHeader>
+ </FileListItem>
+ ))}
+ </FileList>
+ </div>
+ )}
+ </div>
+
{/* Required 체크박스 */}
|
