summaryrefslogtreecommitdiff
path: root/components/additional-info
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-04-28 02:13:30 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-04-28 02:13:30 +0000
commitef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch)
tree345251a3ed0f4429716fa5edaa31024d8f4cb560 /components/additional-info
parent9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff)
~20250428 작업사항
Diffstat (limited to 'components/additional-info')
-rw-r--r--components/additional-info/join-form.tsx130
1 files changed, 89 insertions, 41 deletions
diff --git a/components/additional-info/join-form.tsx b/components/additional-info/join-form.tsx
index 2cd385c3..4a9a3379 100644
--- a/components/additional-info/join-form.tsx
+++ b/components/additional-info/join-form.tsx
@@ -41,7 +41,7 @@ import { cn } from "@/lib/utils"
import { useTranslation } from "@/i18n/client"
import { getVendorDetailById, downloadVendorAttachments, updateVendorInfo } from "@/lib/vendors/service"
-import { updateVendorSchema, type UpdateVendorInfoSchema } from "@/lib/vendors/validations"
+import { updateVendorSchema, updateVendorSchemaWithConditions, type UpdateVendorInfoSchema } from "@/lib/vendors/validations"
import {
Select,
SelectContent,
@@ -104,6 +104,14 @@ const creditRatingScaleMap: Record<string, string[]> = {
SCI: ["AAA", "AA+", "AA", "AA-", "A+", "A", "A-", "BBB+", "BBB-", "B"],
}
+const cashFlowRatingScaleMap: Record<string, string[]> = {
+ NICE: ["우수", "양호", "보통", "미흡", "불량"],
+ KIS: ["A+", "A", "B+", "B", "C", "D"],
+ KED: ["1등급", "2등급", "3등급", "4등급", "5등급"],
+ SCI: ["Level 1", "Level 2", "Level 3", "Level 4"],
+}
+
+
const MAX_FILE_SIZE = 3e9
// 파일 타입 정의
@@ -124,7 +132,7 @@ export function InfoForm() {
const companyId = session?.user?.companyId || "17"
- // 벤더 데이터 상태
+ // 협력업체 데이터 상태
const [vendor, setVendor] = React.useState<any>(null)
const [isLoading, setIsLoading] = React.useState(true)
const [isSubmitting, setIsSubmitting] = React.useState(false)
@@ -137,10 +145,11 @@ export function InfoForm() {
const [selectedFiles, setSelectedFiles] = React.useState<File[]>([])
const [creditRatingFile, setCreditRatingFile] = React.useState<File[]>([])
const [cashFlowRatingFile, setCashFlowRatingFile] = React.useState<File[]>([])
+ const [isDownloading, setIsDownloading] = React.useState(false);
// React Hook Form
const form = useForm<UpdateVendorInfoSchema>({
- resolver: zodResolver(updateVendorSchema),
+ resolver: zodResolver(updateVendorSchemaWithConditions),
defaultValues: {
vendorName: "",
taxId: "",
@@ -181,21 +190,21 @@ export function InfoForm() {
name: "contacts",
})
- // 벤더 정보 가져오기
+ // 협력업체 정보 가져오기
React.useEffect(() => {
async function fetchVendorData() {
if (!companyId) return
try {
setIsLoading(true)
- // 벤더 상세 정보 가져오기 (view 사용)
+ // 협력업체 상세 정보 가져오기 (view 사용)
const vendorData = await getVendorDetailById(Number(companyId))
if (!vendorData) {
toast({
variant: "destructive",
title: "오류",
- description: "벤더 정보를 찾을 수 없습니다.",
+ description: "협력업체 정보를 찾을 수 없습니다.",
})
return
}
@@ -258,7 +267,7 @@ export function InfoForm() {
toast({
variant: "destructive",
title: "데이터 로드 오류",
- description: "벤더 정보를 불러오는 중 오류가 발생했습니다.",
+ description: "협력업체 정보를 불러오는 중 오류가 발생했습니다.",
})
} finally {
setIsLoading(false)
@@ -268,43 +277,82 @@ export function InfoForm() {
fetchVendorData()
}, [companyId, form, replaceContacts])
- // 파일 다운로드 처리
- const handleDownloadFile = async (fileId: number) => {
+ const handleDownloadFile = async (file: AttachmentFile) => {
try {
- const downloadInfo = await downloadVendorAttachments(Number(companyId), fileId)
-
- if (downloadInfo && downloadInfo.url) {
- // 브라우저에서 다운로드 링크 열기
- window.open(downloadInfo.url, '_blank')
- }
- } catch (error) {
- console.error("Error downloading file:", error)
+ setIsDownloading(true);
+
+ // 파일이 객체인지 ID인지 확인하고 처리
+ const fileId = typeof file === 'object' ? file.id : file;
+ const fileName = typeof file === 'object' ? file.fileName : `file-${fileId}`;
+
+ // 다운로드 링크 생성 (URL 인코딩 적용)
+ const downloadUrl = `/api/vendors/attachments/download?id=${fileId}&vendorId=${Number(companyId)}`;
+
+ // a 태그를 사용한 다운로드
+ const downloadLink = document.createElement('a');
+ downloadLink.href = downloadUrl;
+ downloadLink.download = fileName;
+ downloadLink.target = '_blank'; // 추가: 새 탭에서 열도록 설정 (일부 브라우저에서 더 안정적)
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+
+ // 정리 (메모리 누수 방지)
+ setTimeout(() => {
+ document.body.removeChild(downloadLink);
+ }, 100);
+
toast({
- variant: "destructive",
- title: "다운로드 오류",
- description: "파일 다운로드 중 오류가 발생했습니다.",
- })
- }
- }
-
- // 모든 첨부파일 다운로드
- const handleDownloadAllFiles = async () => {
- try {
- const downloadInfo = await downloadVendorAttachments(Number(companyId))
-
- if (downloadInfo && downloadInfo.url) {
- window.open(downloadInfo.url, '_blank')
- }
+ title: "다운로드 시작",
+ description: "파일 다운로드가 시작되었습니다.",
+ });
} catch (error) {
- console.error("Error downloading files:", error)
+ console.error("Error downloading file:", error);
toast({
variant: "destructive",
title: "다운로드 오류",
description: "파일 다운로드 중 오류가 발생했습니다.",
- })
+ });
+ } finally {
+ setIsDownloading(false);
}
+ };
+
+ // 전체 파일 다운로드 함수
+const handleDownloadAllFiles = async () => {
+ try {
+ setIsDownloading(true);
+
+ // 다운로드 URL 생성
+ const downloadUrl = `/api/vendors/attachments/download-all?vendorId=${Number(companyId)}`;
+
+ // a 태그를 사용한 다운로드
+ const downloadLink = document.createElement('a');
+ downloadLink.href = downloadUrl;
+ downloadLink.download = `vendor-${companyId}-files.zip`;
+ downloadLink.target = '_blank';
+ document.body.appendChild(downloadLink);
+ downloadLink.click();
+
+ // 정리
+ setTimeout(() => {
+ document.body.removeChild(downloadLink);
+ }, 100);
+
+ toast({
+ title: "다운로드 시작",
+ description: "전체 파일 다운로드가 시작되었습니다.",
+ });
+ } catch (error) {
+ console.error("Error downloading files:", error);
+ toast({
+ variant: "destructive",
+ title: "다운로드 오류",
+ description: "파일 다운로드 중 오류가 발생했습니다.",
+ });
+ } finally {
+ setIsDownloading(false);
}
-
+};
// Dropzone handlers
const handleDropAccepted = (acceptedFiles: File[]) => {
const newFiles = [...selectedFiles, ...acceptedFiles]
@@ -476,7 +524,7 @@ export function InfoForm() {
return (
<div className="container py-10 flex justify-center items-center">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
- <span className="ml-2">벤더 정보를 불러오는 중입니다...</span>
+ <span className="ml-2">협력업체 정보를 불러오는 중입니다...</span>
</div>
)
}
@@ -543,7 +591,7 @@ export function InfoForm() {
</FileListDescription>
</FileListInfo>
<div className="flex items-center space-x-2">
- <FileListAction onClick={() => handleDownloadFile(file.id)}>
+ <FileListAction onClick={() => handleDownloadFile(file)}>
<Download className="h-4 w-4" />
</FileListAction>
<FileListAction onClick={() => handleDeleteExistingFile(file.id)}>
@@ -574,7 +622,7 @@ export function InfoForm() {
</FileListDescription>
</FileListInfo>
<div className="flex items-center space-x-2">
- <FileListAction onClick={() => handleDownloadFile(file.id)}>
+ <FileListAction onClick={() => handleDownloadFile(file)}>
<Download className="h-4 w-4" />
</FileListAction>
<FileListAction onClick={() => handleDeleteExistingFile(file.id)}>
@@ -605,7 +653,7 @@ export function InfoForm() {
</FileListDescription>
</FileListInfo>
<div className="flex items-center space-x-2">
- <FileListAction onClick={() => handleDownloadFile(file.id)}>
+ <FileListAction onClick={() => handleDownloadFile(file)}>
<Download className="h-4 w-4" />
</FileListAction>
<FileListAction onClick={() => handleDeleteExistingFile(file.id)}>
@@ -1095,8 +1143,8 @@ export function InfoForm() {
render={({ field }) => {
const selectedAgency = form.watch("creditAgency")
const ratingScale =
- creditRatingScaleMap[
- selectedAgency as keyof typeof creditRatingScaleMap
+ cashFlowRatingScaleMap[
+ selectedAgency as keyof typeof cashFlowRatingScaleMap
] || []
return (
<FormItem>