From c72d0897f7b37843109c86f61d97eba05ba3ca0d Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 13 Jun 2025 07:08:01 +0000
Subject: (대표님) 20250613 16시 08분 b-rfq, document 등
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../table/bulk-upload-dialog.tsx                   | 884 +++++++++++----------
 1 file changed, 445 insertions(+), 439 deletions(-)

(limited to 'lib/vendor-document-list/table/bulk-upload-dialog.tsx')

diff --git a/lib/vendor-document-list/table/bulk-upload-dialog.tsx b/lib/vendor-document-list/table/bulk-upload-dialog.tsx
index b7021985..b28d3b3d 100644
--- a/lib/vendor-document-list/table/bulk-upload-dialog.tsx
+++ b/lib/vendor-document-list/table/bulk-upload-dialog.tsx
@@ -666,8 +666,9 @@ const canProceedToUpload = matchResult && matchResult.matched.length > 0 && matc
 
 return (
   <Dialog open={open} onOpenChange={handleDialogClose}>
-    <DialogContent className="sm:max-w-6xl max-h-[90vh] overflow-y-auto">
-      <DialogHeader>
+    <DialogContent className="sm:max-w-6xl max-h-[90vh] flex flex-col" style={{maxWidth:900}}>
+      {/* 고정 헤더 */}
+      <DialogHeader className="flex-shrink-0">
         <DialogTitle className="flex items-center gap-2">
           <Files className="w-5 h-5" />
           일괄 업로드
@@ -686,476 +687,481 @@ return (
         </div>
       </DialogHeader>
 
-      {/* 단계별 진행 상태 */}
-      <div className="flex items-center gap-2 mb-4">
-        {[
-          { key: 'template', label: '템플릿' },
-          { key: 'files', label: '파일 업로드' },
-          { key: 'review', label: '검토' },
-          { key: 'upload', label: '업로드' },
-        ].map((step, index) => (
-          <React.Fragment key={step.key}>
-            <div className={`flex items-center gap-1 px-2 py-1 rounded text-xs ${
-              currentStep === step.key ? 'bg-primary text-primary-foreground' :
-              ['template', 'files', 'review'].indexOf(currentStep) > ['template', 'files', 'review'].indexOf(step.key) ? 'bg-green-100 text-green-700' :
-              'bg-gray-100 text-gray-500'
-            }`}>
-              {step.label}
-            </div>
-            {index < 3 && <div className="w-2 h-px bg-gray-300" />}
-          </React.Fragment>
-        ))}
-      </div>
+      {/* 스크롤 가능한 메인 컨텐츠 영역 */}
+      <div className="flex-1 overflow-y-auto px-1">
+        {/* 단계별 진행 상태 */}
+        <div className="flex items-center gap-2 mb-4">
+          {[
+            { key: 'template', label: '템플릿' },
+            { key: 'files', label: '파일 업로드' },
+            { key: 'review', label: '검토' },
+            { key: 'upload', label: '업로드' },
+          ].map((step, index) => (
+            <React.Fragment key={step.key}>
+              <div className={`flex items-center gap-1 px-2 py-1 rounded text-xs ${
+                currentStep === step.key ? 'bg-primary text-primary-foreground' :
+                ['template', 'files', 'review'].indexOf(currentStep) > ['template', 'files', 'review'].indexOf(step.key) ? 'bg-green-100 text-green-700' :
+                'bg-gray-100 text-gray-500'
+              }`}>
+                {step.label}
+              </div>
+              {index < 3 && <div className="w-2 h-px bg-gray-300" />}
+            </React.Fragment>
+          ))}
+        </div>
 
-      <Form {...form}>
-        <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
-          
-          {/* 1단계: 템플릿 다운로드 및 업로드 */}
-          {currentStep === 'template' && (
-            <div className="space-y-4">
-              <Card>
-                <CardHeader>
-                  <CardTitle className="text-lg flex items-center gap-2">
-                    <Download className="w-4 h-4" />
-                    1단계: 템플릿 다운로드
-                  </CardTitle>
-                </CardHeader>
-                <CardContent className="space-y-4">
-                  <p className="text-sm text-gray-600">
-                    현재 문서 목록을 기반으로 업로드 템플릿을 생성합니다. 
-                    마지막 "fileNames" 칼럼에 업로드할 파일명을 ';'로 구분하여 입력하세요.
-                  </p>
-                  <Button type="button" onClick={exportTemplate} className="gap-2">
-                    <Download className="w-4 h-4" />
-                    템플릿 다운로드 ({documents.length}개 문서)
-                  </Button>
-                </CardContent>
-              </Card>
-
-              <Card>
-                <CardHeader>
-                  <CardTitle className="text-lg flex items-center gap-2">
-                    <Upload className="w-4 h-4" />
-                    작성된 템플릿 업로드
-                  </CardTitle>
-                </CardHeader>
-                <CardContent>
-                  <Dropzone
-                    maxSize={10e6} // 10MB
-                    multiple={false}
-                    accept={{ 
-                      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'],
-                      'application/vnd.ms-excel': ['.xls']
-                    }}
-                    onDropAccepted={handleTemplateDropAccepted}
-                    disabled={isUploading}
-                  >
-                    <DropzoneZone>
-                      <FormControl>
-                        <DropzoneInput />
-                      </FormControl>
-                      <div className="flex items-center gap-6">
-                        <FileSpreadsheet className="w-8 h-8 text-gray-400" />
-                        <div className="grid gap-0.5">
-                          <DropzoneTitle>작성된 Excel 템플릿을 업로드하세요</DropzoneTitle>
-                          <DropzoneDescription>
-                            .xlsx, .xls 파일을 지원합니다
-                          </DropzoneDescription>
-                        </div>
-                      </div>
-                    </DropzoneZone>
-                  </Dropzone>
-                  
-                  {templateFile && (
-                    <div className="mt-4 p-3 bg-green-50 border border-green-200 rounded-lg">
-                      <div className="flex items-center gap-2">
-                        <CheckCircle2 className="w-4 h-4 text-green-600" />
-                        <span className="text-sm text-green-700">
-                          템플릿 업로드 완료: {templateFile.name}
-                        </span>
-                      </div>
-                    </div>
-                  )}
-                </CardContent>
-              </Card>
-            </div>
-          )}
-
-          {/* 2단계: 파일 업로드 */}
-          {currentStep === 'files' && (
-            <div className="space-y-4">
-              <Card>
-                <CardHeader>
-                  <CardTitle className="text-lg flex items-center gap-2">
-                    <Files className="w-4 h-4" />
-                    2단계: 실제 파일들 업로드
-                  </CardTitle>
-                </CardHeader>
-                <CardContent>
-                  <div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
-                    <p className="text-sm text-blue-700">
-                      템플릿에서 {parsedData.length}개 항목, 총 {parsedData.reduce((sum, item) => sum + item.fileNames.length, 0)}개 파일이 필요합니다.
+        <Form {...form}>
+          <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
+            
+            {/* 1단계: 템플릿 다운로드 및 업로드 */}
+            {currentStep === 'template' && (
+              <div className="space-y-4">
+                <Card>
+                  <CardHeader>
+                    <CardTitle className="text-lg flex items-center gap-2">
+                      <Download className="w-4 h-4" />
+                      1단계: 템플릿 다운로드
+                    </CardTitle>
+                  </CardHeader>
+                  <CardContent className="space-y-4">
+                    <p className="text-sm text-gray-600">
+                      현재 문서 목록을 기반으로 업로드 템플릿을 생성합니다. 
+                      마지막 "fileNames" 칼럼에 업로드할 파일명을 ';'로 구분하여 입력하세요.
                     </p>
-                  </div>
-
-                  <Dropzone
-                    maxSize={3e9} // 3GB
-                    multiple={true}
-                    onDropAccepted={handleFilesDropAccepted}
-                    disabled={isUploading}
-                  >
-                    <DropzoneZone>
-                      <FormControl>
-                        <DropzoneInput />
-                      </FormControl>
-                      <div className="flex items-center gap-6">
-                        <DropzoneUploadIcon />
-                        <div className="grid gap-0.5">
-                          <DropzoneTitle>실제 파일들을 여기에 드롭하세요</DropzoneTitle>
-                          <DropzoneDescription>
-                            또는 클릭하여 파일들을 선택하세요
-                          </DropzoneDescription>
+                    <Button type="button" onClick={exportTemplate} className="gap-2">
+                      <Download className="w-4 h-4" />
+                      템플릿 다운로드 ({documents.length}개 문서)
+                    </Button>
+                  </CardContent>
+                </Card>
+
+                <Card>
+                  <CardHeader>
+                    <CardTitle className="text-lg flex items-center gap-2">
+                      <Upload className="w-4 h-4" />
+                      작성된 템플릿 업로드
+                    </CardTitle>
+                  </CardHeader>
+                  <CardContent>
+                    <Dropzone
+                      maxSize={10e6} // 10MB
+                      multiple={false}
+                      accept={{ 
+                        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'],
+                        'application/vnd.ms-excel': ['.xls']
+                      }}
+                      onDropAccepted={handleTemplateDropAccepted}
+                      disabled={isUploading}
+                    >
+                      <DropzoneZone>
+                        <FormControl>
+                          <DropzoneInput />
+                        </FormControl>
+                        <div className="flex items-center gap-6">
+                          <FileSpreadsheet className="w-8 h-8 text-gray-400" />
+                          <div className="grid gap-0.5">
+                            <DropzoneTitle>작성된 Excel 템플릿을 업로드하세요</DropzoneTitle>
+                            <DropzoneDescription>
+                              .xlsx, .xls 파일을 지원합니다
+                            </DropzoneDescription>
+                          </div>
+                        </div>
+                      </DropzoneZone>
+                    </Dropzone>
+                    
+                    {templateFile && (
+                      <div className="mt-4 p-3 bg-green-50 border border-green-200 rounded-lg">
+                        <div className="flex items-center gap-2">
+                          <CheckCircle2 className="w-4 h-4 text-green-600" />
+                          <span className="text-sm text-green-700">
+                            템플릿 업로드 완료: {templateFile.name}
+                          </span>
                         </div>
                       </div>
-                    </DropzoneZone>
-                  </Dropzone>
-
-                  {selectedFiles.length > 0 && (
-                    <div className="mt-4 space-y-2">
-                      <h6 className="text-sm font-semibold">
-                        업로드된 파일 ({selectedFiles.length})
-                      </h6>
-                      <ScrollArea className="max-h-[200px]">
-                        <FileList>
-                          {selectedFiles.map((file, index) => (
-                            <FileListItem key={index} className="p-3">
-                              <FileListHeader>
-                                <FileListIcon />
-                                <FileListInfo>
-                                  <FileListName>{file.name}</FileListName>
-                                  <FileListSize>{prettyBytes(file.size)}</FileListSize>
-                                </FileListInfo>
-                                <FileListAction 
-                                  onClick={() => removeFile(index)} 
-                                  disabled={isUploading}
-                                >
-                                  <X className="h-4 w-4" />
-                                </FileListAction>
-                              </FileListHeader>
-                            </FileListItem>
-                          ))}
-                        </FileList>
-                      </ScrollArea>
-                    </div>
-                  )}
-                </CardContent>
-              </Card>
-            </div>
-          )}
-
-          {/* 3단계: 매칭 결과 검토 */}
-          {currentStep === 'review' && matchResult && (
-            <div className="space-y-4">
-              <Card>
-                <CardHeader>
-                  <CardTitle className="text-lg flex items-center gap-2">
-                    <CheckCircle2 className="w-4 h-4" />
-                    3단계: 매칭 결과 검토
-                  </CardTitle>
-                </CardHeader>
-                <CardContent className="space-y-6">
-                  
-                  {/* 통합된 매칭 결과 요약 */}
-                  <div className="grid grid-cols-3 gap-4">
-                    <div className="p-4 bg-green-50 border border-green-200 rounded-lg text-center">
-                      <div className="text-2xl font-bold text-green-600">{matchResult.matched.length}</div>
-                      <div className="text-sm text-green-700">매칭 성공</div>
-                    </div>
-                    <div className="p-4 bg-yellow-50 border border-yellow-200 rounded-lg text-center">
-                      <div className="text-2xl font-bold text-yellow-600">{matchResult.unmatched.length}</div>
-                      <div className="text-sm text-yellow-700">매칭 실패</div>
-                    </div>
-                    <div className="p-4 bg-red-50 border border-red-200 rounded-lg text-center">
-                      <div className="text-2xl font-bold text-red-600">{matchResult.missingFiles.length}</div>
-                      <div className="text-sm text-red-700">누락된 파일</div>
+                    )}
+                  </CardContent>
+                </Card>
+              </div>
+            )}
+
+            {/* 2단계: 파일 업로드 */}
+            {currentStep === 'files' && (
+              <div className="space-y-4">
+                <Card>
+                  <CardHeader>
+                    <CardTitle className="text-lg flex items-center gap-2">
+                      <Files className="w-4 h-4" />
+                      2단계: 실제 파일들 업로드
+                    </CardTitle>
+                  </CardHeader>
+                  <CardContent>
+                    <div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg">
+                      <p className="text-sm text-blue-700">
+                        템플릿에서 {parsedData.length}개 항목, 총 {parsedData.reduce((sum, item) => sum + item.fileNames.length, 0)}개 파일이 필요합니다.
+                      </p>
                     </div>
-                  </div>
-
-                  {/* 통합된 상세 결과 */}
-                  <div className="border border-gray-200 rounded-lg overflow-hidden">
-                    {/* 매칭 성공 섹션 */}
-                    {matchResult.matched.length > 0 && (
-                      <div className="border-b border-gray-200">
-                        <div className="p-4 bg-green-50 flex items-center justify-between">
-                          <h6 className="font-semibold text-green-700 flex items-center gap-2">
-                            <CheckCircle2 className="w-4 h-4" />
-                            매칭 성공 ({matchResult.matched.length}개)
-                          </h6>
-                          <Button
-                            variant="ghost"
-                            size="sm"
-                            type="button"
-                            onClick={(e) => {
-                              e.preventDefault()
-                              e.stopPropagation()
-                              const element = document.getElementById('matched-details')
-                              if (element) {
-                                element.style.display = element.style.display === 'none' ? 'block' : 'none'
-                              }
-                            }}
-                          >
-                            {matchResult.matched.length <= 5 ? '모두보기' : '상세보기'}
-                          </Button>
-                        </div>
-                        
-                        {/* 미리보기 */}
-                        <div className="p-4 bg-green-25">
-                          <div className="space-y-2">
-                            {matchResult.matched.slice(0, 5).map((match, index) => (
-                              <div key={index} className="flex items-center justify-between text-sm">
-                                <span className="font-mono text-green-600 truncate max-w-[300px]" title={match.file.name}>
-                                  {match.file.name}
-                                </span>
-                                <span className="text-green-700 ml-4 whitespace-nowrap flex-shrink-0">
-                                  → {match.item.docNumber} Rev.{match.item.revision}
-                                </span>
-                              </div>
-                            ))}
-                            {matchResult.matched.length > 5 && (
-                              <div className="text-gray-500 text-center text-sm py-2 border-t border-green-200">
-                                ... 외 {matchResult.matched.length - 5}개 (상세보기로 확인)
-                              </div>
-                            )}
-                          </div>
 
-                          {/* 펼침 상세 내용 */}
-                          <div id="matched-details" style={{ display: 'none' }} className="mt-4 pt-4 border-t border-green-200">
-                            <div className="max-h-64 overflow-y-auto">
-                              <div className="space-y-2">
-                                {matchResult.matched.map((match, index) => (
-                                  <div key={index} className="flex items-center justify-between text-sm py-1">
-                                    <span className="font-mono text-green-600 truncate max-w-[300px]" title={match.file.name}>
-                                      {match.file.name}
-                                    </span>
-                                    <span className="text-green-700 ml-4 whitespace-nowrap flex-shrink-0">
-                                      → {match.item.docNumber} ({match.item.stage} Rev.{match.item.revision})
-                                    </span>
-                                  </div>
-                                ))}
-                              </div>
-                            </div>
+                    <Dropzone
+                      maxSize={3e9} // 3GB
+                      multiple={true}
+                      onDropAccepted={handleFilesDropAccepted}
+                      disabled={isUploading}
+                    >
+                      <DropzoneZone>
+                        <FormControl>
+                          <DropzoneInput />
+                        </FormControl>
+                        <div className="flex items-center gap-6">
+                          <DropzoneUploadIcon />
+                          <div className="grid gap-0.5">
+                            <DropzoneTitle>실제 파일들을 여기에 드롭하세요</DropzoneTitle>
+                            <DropzoneDescription>
+                              또는 클릭하여 파일들을 선택하세요
+                            </DropzoneDescription>
                           </div>
                         </div>
+                      </DropzoneZone>
+                    </Dropzone>
+
+                    {selectedFiles.length > 0 && (
+                      <div className="mt-4 space-y-2">
+                        <h6 className="text-sm font-semibold">
+                          업로드된 파일 ({selectedFiles.length})
+                        </h6>
+                        <ScrollArea className="max-h-[200px]">
+                          <FileList>
+                            {selectedFiles.map((file, index) => (
+                              <FileListItem key={index} className="p-3">
+                                <FileListHeader>
+                                  <FileListIcon />
+                                  <FileListInfo>
+                                    <FileListName>{file.name}</FileListName>
+                                    <FileListSize>{prettyBytes(file.size)}</FileListSize>
+                                  </FileListInfo>
+                                  <FileListAction 
+                                    onClick={() => removeFile(index)} 
+                                    disabled={isUploading}
+                                  >
+                                    <X className="h-4 w-4" />
+                                  </FileListAction>
+                                </FileListHeader>
+                              </FileListItem>
+                            ))}
+                          </FileList>
+                        </ScrollArea>
                       </div>
                     )}
+                  </CardContent>
+                </Card>
+              </div>
+            )}
 
-                    {/* 매칭 실패 섹션 */}
-                    {matchResult.unmatched.length > 0 && (
-                      <div className="border-b border-gray-200">
-                        <div className="p-4 bg-yellow-50 flex items-center justify-between">
-                          <h6 className="font-semibold text-yellow-700 flex items-center gap-2">
-                            <AlertCircle className="w-4 h-4" />
-                            매칭되지 않은 파일 ({matchResult.unmatched.length}개)
-                          </h6>
-                          <Button
-                            variant="ghost"
-                            size="sm"
-                            type="button"
-                            onClick={() => {
-                              const element = document.getElementById('unmatched-details')
-                              if (element) {
-                                element.style.display = element.style.display === 'none' ? 'block' : 'none'
-                              }
-                            }}
-                          >
-                            상세보기
-                          </Button>
-                        </div>
-                        
-                        <div className="p-4 bg-yellow-25">
-                          <div className="space-y-1">
-                            {matchResult.unmatched.slice(0, 3).map((file, index) => (
-                              <div key={index} className="text-sm text-yellow-600 font-mono truncate max-w-full" title={file.name}>
-                                {file.name}
-                              </div>
-                            ))}
-                            {matchResult.unmatched.length > 3 && (
-                              <div className="text-gray-500 text-center text-sm py-2">
-                                ... 외 {matchResult.unmatched.length - 3}개
-                              </div>
-                            )}
+            {/* 3단계: 매칭 결과 검토 */}
+            {currentStep === 'review' && matchResult && (
+              <div className="space-y-4">
+                <Card>
+                  <CardHeader>
+                    <CardTitle className="text-lg flex items-center gap-2">
+                      <CheckCircle2 className="w-4 h-4" />
+                      3단계: 매칭 결과 검토
+                    </CardTitle>
+                  </CardHeader>
+                  <CardContent className="space-y-6">
+                    
+                    {/* 통합된 매칭 결과 요약 */}
+                    <div className="grid grid-cols-3 gap-4">
+                      <div className="p-4 bg-green-50 border border-green-200 rounded-lg text-center">
+                        <div className="text-2xl font-bold text-green-600">{matchResult.matched.length}</div>
+                        <div className="text-sm text-green-700">매칭 성공</div>
+                      </div>
+                      <div className="p-4 bg-yellow-50 border border-yellow-200 rounded-lg text-center">
+                        <div className="text-2xl font-bold text-yellow-600">{matchResult.unmatched.length}</div>
+                        <div className="text-sm text-yellow-700">매칭 실패</div>
+                      </div>
+                      <div className="p-4 bg-red-50 border border-red-200 rounded-lg text-center">
+                        <div className="text-2xl font-bold text-red-600">{matchResult.missingFiles.length}</div>
+                        <div className="text-sm text-red-700">누락된 파일</div>
+                      </div>
+                    </div>
+
+                    {/* 통합된 상세 결과 */}
+                    <div className="border border-gray-200 rounded-lg overflow-hidden">
+                      {/* 매칭 성공 섹션 */}
+                      {matchResult.matched.length > 0 && (
+                        <div className="border-b border-gray-200">
+                          <div className="p-4 bg-green-50 flex items-center justify-between">
+                            <h6 className="font-semibold text-green-700 flex items-center gap-2">
+                              <CheckCircle2 className="w-4 h-4" />
+                              매칭 성공 ({matchResult.matched.length}개)
+                            </h6>
+                            <Button
+                              variant="ghost"
+                              size="sm"
+                              type="button"
+                              onClick={(e) => {
+                                e.preventDefault()
+                                e.stopPropagation()
+                                const element = document.getElementById('matched-details')
+                                if (element) {
+                                  element.style.display = element.style.display === 'none' ? 'block' : 'none'
+                                }
+                              }}
+                            >
+                              {matchResult.matched.length <= 5 ? '모두보기' : '상세보기'}
+                            </Button>
                           </div>
+                          
+                          {/* 미리보기 */}
+                          <div className="p-4 bg-green-25">
+                            <div className="space-y-2">
+                              {matchResult.matched.slice(0, 5).map((match, index) => (
+                                <div key={index} className="flex items-center justify-between text-sm">
+                                  <span className="font-mono text-green-600 truncate max-w-[300px]" title={match.file.name}>
+                                    {match.file.name}
+                                  </span>
+                                  <span className="text-green-700 ml-4 whitespace-nowrap flex-shrink-0">
+                                    → {match.item.docNumber} Rev.{match.item.revision}
+                                  </span>
+                                </div>
+                              ))}
+                              {matchResult.matched.length > 5 && (
+                                <div className="text-gray-500 text-center text-sm py-2 border-t border-green-200">
+                                  ... 외 {matchResult.matched.length - 5}개 (상세보기로 확인)
+                                </div>
+                              )}
+                            </div>
 
-                          <div id="unmatched-details" style={{ display: 'none' }} className="mt-4 pt-4 border-t border-yellow-200">
-                            <div className="max-h-40 overflow-y-auto">
-                              <div className="space-y-1">
-                                {matchResult.unmatched.map((file, index) => (
-                                  <div key={index} className="text-sm text-yellow-600 font-mono truncate max-w-full" title={file.name}>
-                                    {file.name}
-                                  </div>
-                                ))}
+                            {/* 펼침 상세 내용 */}
+                            <div id="matched-details" style={{ display: 'none' }} className="mt-4 pt-4 border-t border-green-200">
+                              <div className="max-h-64 overflow-y-auto">
+                                <div className="space-y-2">
+                                  {matchResult.matched.map((match, index) => (
+                                    <div key={index} className="flex items-center justify-between text-sm py-1">
+                                      <span className="font-mono text-green-600 truncate max-w-[300px]" title={match.file.name}>
+                                        {match.file.name}
+                                      </span>
+                                      <span className="text-green-700 ml-4 whitespace-nowrap flex-shrink-0">
+                                        → {match.item.docNumber} ({match.item.stage} Rev.{match.item.revision})
+                                      </span>
+                                    </div>
+                                  ))}
+                                </div>
                               </div>
                             </div>
                           </div>
                         </div>
-                      </div>
-                    )}
+                      )}
+
+                      {/* 매칭 실패 섹션 */}
+                      {matchResult.unmatched.length > 0 && (
+                        <div className="border-b border-gray-200">
+                          <div className="p-4 bg-yellow-50 flex items-center justify-between">
+                            <h6 className="font-semibold text-yellow-700 flex items-center gap-2">
+                              <AlertCircle className="w-4 h-4" />
+                              매칭되지 않은 파일 ({matchResult.unmatched.length}개)
+                            </h6>
+                            <Button
+                              variant="ghost"
+                              size="sm"
+                              type="button"
+                              onClick={() => {
+                                const element = document.getElementById('unmatched-details')
+                                if (element) {
+                                  element.style.display = element.style.display === 'none' ? 'block' : 'none'
+                                }
+                              }}
+                            >
+                              상세보기
+                            </Button>
+                          </div>
+                          
+                          <div className="p-4 bg-yellow-25">
+                            <div className="space-y-1">
+                              {matchResult.unmatched.slice(0, 3).map((file, index) => (
+                                <div key={index} className="text-sm text-yellow-600 font-mono truncate max-w-full" title={file.name}>
+                                  {file.name}
+                                </div>
+                              ))}
+                              {matchResult.unmatched.length > 3 && (
+                                <div className="text-gray-500 text-center text-sm py-2">
+                                  ... 외 {matchResult.unmatched.length - 3}개
+                                </div>
+                              )}
+                            </div>
 
-                    {/* 누락된 파일 섹션 */}
-                    {matchResult.missingFiles.length > 0 && (
-                      <div>
-                        <div className="p-4 bg-red-50 flex items-center justify-between">
-                          <h6 className="font-semibold text-red-700 flex items-center gap-2">
-                            <X className="w-4 h-4" />
-                            누락된 파일 ({matchResult.missingFiles.length}개)
-                          </h6>
-                          <Button
-                            variant="ghost"
-                            size="sm"
-                            type="button"
-                            onClick={() => {
-                              const element = document.getElementById('missing-details')
-                              if (element) {
-                                element.style.display = element.style.display === 'none' ? 'block' : 'none'
-                              }
-                            }}
-                          >
-                            상세보기
-                          </Button>
-                        </div>
-                        
-                        <div className="p-4 bg-red-25">
-                          <div className="space-y-1">
-                            {matchResult.missingFiles.slice(0, 3).map((fileName, index) => (
-                              <div key={index} className="text-sm text-red-600 font-mono truncate max-w-full" title={fileName}>
-                                {fileName}
-                              </div>
-                            ))}
-                            {matchResult.missingFiles.length > 3 && (
-                              <div className="text-gray-500 text-center text-sm py-2">
-                                ... 외 {matchResult.missingFiles.length - 3}개
+                            <div id="unmatched-details" style={{ display: 'none' }} className="mt-4 pt-4 border-t border-yellow-200">
+                              <div className="max-h-40 overflow-y-auto">
+                                <div className="space-y-1">
+                                  {matchResult.unmatched.map((file, index) => (
+                                    <div key={index} className="text-sm text-yellow-600 font-mono truncate max-w-full" title={file.name}>
+                                      {file.name}
+                                    </div>
+                                  ))}
+                                </div>
                               </div>
-                            )}
+                            </div>
+                          </div>
+                        </div>
+                      )}
+
+                      {/* 누락된 파일 섹션 */}
+                      {matchResult.missingFiles.length > 0 && (
+                        <div>
+                          <div className="p-4 bg-red-50 flex items-center justify-between">
+                            <h6 className="font-semibold text-red-700 flex items-center gap-2">
+                              <X className="w-4 h-4" />
+                              누락된 파일 ({matchResult.missingFiles.length}개)
+                            </h6>
+                            <Button
+                              variant="ghost"
+                              size="sm"
+                              type="button"
+                              onClick={() => {
+                                const element = document.getElementById('missing-details')
+                                if (element) {
+                                  element.style.display = element.style.display === 'none' ? 'block' : 'none'
+                                }
+                              }}
+                            >
+                              상세보기
+                            </Button>
                           </div>
+                          
+                          <div className="p-4 bg-red-25">
+                            <div className="space-y-1">
+                              {matchResult.missingFiles.slice(0, 3).map((fileName, index) => (
+                                <div key={index} className="text-sm text-red-600 font-mono truncate max-w-full" title={fileName}>
+                                  {fileName}
+                                </div>
+                              ))}
+                              {matchResult.missingFiles.length > 3 && (
+                                <div className="text-gray-500 text-center text-sm py-2">
+                                  ... 외 {matchResult.missingFiles.length - 3}개
+                                </div>
+                              )}
+                            </div>
 
-                          <div id="missing-details" style={{ display: 'none' }} className="mt-4 pt-4 border-t border-red-200">
-                            <div className="max-h-40 overflow-y-auto">
-                              <div className="space-y-1">
-                                {matchResult.missingFiles.map((fileName, index) => (
-                                  <div key={index} className="text-sm text-red-600 font-mono truncate max-w-full" title={fileName}>
-                                    {fileName}
-                                  </div>
-                                ))}
+                            <div id="missing-details" style={{ display: 'none' }} className="mt-4 pt-4 border-t border-red-200">
+                              <div className="max-h-40 overflow-y-auto">
+                                <div className="space-y-1">
+                                  {matchResult.missingFiles.map((fileName, index) => (
+                                    <div key={index} className="text-sm text-red-600 font-mono truncate max-w-full" title={fileName}>
+                                      {fileName}
+                                    </div>
+                                  ))}
+                                </div>
                               </div>
                             </div>
                           </div>
                         </div>
+                      )}
+                    </div>
+
+                    {/* 업로드 불가 경고 */}
+                    {!canProceedToUpload && (
+                      <div className="p-4 bg-red-50 border border-red-200 rounded-lg">
+                        <div className="flex items-center gap-2">
+                          <AlertCircle className="w-5 h-5 text-red-600 flex-shrink-0" />
+                          <span className="text-sm text-red-700">
+                            누락된 파일이 있어 업로드를 진행할 수 없습니다. 누락된 파일들을 추가해주세요.
+                          </span>
+                        </div>
                       </div>
                     )}
-                  </div>
+                  </CardContent>
+                </Card>
+
+                {/* 추가 정보 입력 */}
+                <div className="grid grid-cols-1 gap-4">
+                  <FormField
+                    control={form.control}
+                    name="uploaderName"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>업로더명</FormLabel>
+                        <FormControl>
+                          <Input {...field} placeholder="업로더 이름" />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+
+                  <FormField
+                    control={form.control}
+                    name="comment"
+                    render={({ field }) => (
+                      <FormItem>
+                        <FormLabel>코멘트 (선택)</FormLabel>
+                        <FormControl>
+                          <Textarea {...field} placeholder="일괄 업로드 코멘트" rows={2} />
+                        </FormControl>
+                        <FormMessage />
+                      </FormItem>
+                    )}
+                  />
+                </div>
+              </div>
+            )}
 
-                  {/* 업로드 불가 경고 */}
-                  {!canProceedToUpload && (
-                    <div className="p-4 bg-red-50 border border-red-200 rounded-lg">
+            {/* 4단계: 업로드 진행 */}
+            {currentStep === 'upload' && (
+              <div className="space-y-4">
+                <Card>
+                  <CardHeader>
+                    <CardTitle className="text-lg flex items-center gap-2">
+                      <Upload className="w-4 h-4" />
+                      4단계: 업로드 진행중
+                    </CardTitle>
+                  </CardHeader>
+                  <CardContent>
+                    <div className="space-y-4">
                       <div className="flex items-center gap-2">
-                        <AlertCircle className="w-5 h-5 text-red-600 flex-shrink-0" />
-                        <span className="text-sm text-red-700">
-                          누락된 파일이 있어 업로드를 진행할 수 없습니다. 누락된 파일들을 추가해주세요.
-                        </span>
+                        <Loader2 className="h-4 w-4 animate-spin" />
+                        <span className="text-sm">{uploadProgress}% 업로드 중...</span>
+                      </div>
+                      <div className="h-2 w-full bg-muted rounded-full overflow-hidden">
+                        <div 
+                          className="h-full bg-primary rounded-full transition-all" 
+                          style={{ width: `${uploadProgress}%` }} 
+                        />
                       </div>
+                      {matchResult && (
+                        <p className="text-sm text-gray-600">
+                          {matchResult.matched.length}개 파일을 업로드하고 있습니다...
+                        </p>
+                      )}
                     </div>
-                  )}
-                </CardContent>
-              </Card>
-
-              {/* 추가 정보 입력 */}
-              <div className="grid grid-cols-1 gap-4">
-                <FormField
-                  control={form.control}
-                  name="uploaderName"
-                  render={({ field }) => (
-                    <FormItem>
-                      <FormLabel>업로더명</FormLabel>
-                      <FormControl>
-                        <Input {...field} placeholder="업로더 이름" />
-                      </FormControl>
-                      <FormMessage />
-                    </FormItem>
-                  )}
-                />
-
-                <FormField
-                  control={form.control}
-                  name="comment"
-                  render={({ field }) => (
-                    <FormItem>
-                      <FormLabel>코멘트 (선택)</FormLabel>
-                      <FormControl>
-                        <Textarea {...field} placeholder="일괄 업로드 코멘트" rows={2} />
-                      </FormControl>
-                      <FormMessage />
-                    </FormItem>
-                  )}
-                />
+                  </CardContent>
+                </Card>
               </div>
-            </div>
-          )}
-
-          {/* 4단계: 업로드 진행 */}
-          {currentStep === 'upload' && (
-            <div className="space-y-4">
-              <Card>
-                <CardHeader>
-                  <CardTitle className="text-lg flex items-center gap-2">
-                    <Upload className="w-4 h-4" />
-                    4단계: 업로드 진행중
-                  </CardTitle>
-                </CardHeader>
-                <CardContent>
-                  <div className="space-y-4">
-                    <div className="flex items-center gap-2">
-                      <Loader2 className="h-4 w-4 animate-spin" />
-                      <span className="text-sm">{uploadProgress}% 업로드 중...</span>
-                    </div>
-                    <div className="h-2 w-full bg-muted rounded-full overflow-hidden">
-                      <div 
-                        className="h-full bg-primary rounded-full transition-all" 
-                        style={{ width: `${uploadProgress}%` }} 
-                      />
-                    </div>
-                    {matchResult && (
-                      <p className="text-sm text-gray-600">
-                        {matchResult.matched.length}개 파일을 업로드하고 있습니다...
-                      </p>
-                    )}
-                  </div>
-                </CardContent>
-              </Card>
-            </div>
-          )}
-
-          <DialogFooter>
-            <Button
-              type="button"
-              variant="outline"
-              onClick={handleDialogClose}
-              disabled={isUploading}
-            >
-              취소
-            </Button>
-            
-            {currentStep === 'review' && (
-              <Button 
-                type="submit" 
-                disabled={!canProceedToUpload || isUploading}
-              >
-                <Upload className="mr-2 h-4 w-4" />
-                일괄 업로드 ({matchResult?.matched.length || 0}개 파일)
-              </Button>
             )}
-          </DialogFooter>
-        </form>
-      </Form>
+          </form>
+        </Form>
+      </div>
+
+      {/* 고정 푸터 */}
+      <DialogFooter className="flex-shrink-0 pt-4 border-t bg-white">
+        <Button
+          type="button"
+          variant="outline"
+          onClick={handleDialogClose}
+          disabled={isUploading}
+        >
+          취소
+        </Button>
+        
+        {currentStep === 'review' && (
+          <Button 
+            type="submit" 
+            disabled={!canProceedToUpload || isUploading}
+            onClick={form.handleSubmit(onSubmit)}
+          >
+            <Upload className="mr-2 h-4 w-4" />
+            일괄 업로드 ({matchResult?.matched.length || 0}개 파일)
+          </Button>
+        )}
+      </DialogFooter>
     </DialogContent>
   </Dialog>
 )
-- 
cgit v1.2.3

