From bbc3094e932e3d193d3223448c789461f4afc058 Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Wed, 29 Oct 2025 07:46:57 +0000
Subject: (대표님) 데이터룸 관련 변경사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/file-manager/FileManager.tsx | 156 +++++++++++++++++++++++++++-----
 1 file changed, 131 insertions(+), 25 deletions(-)

(limited to 'components/file-manager/FileManager.tsx')

diff --git a/components/file-manager/FileManager.tsx b/components/file-manager/FileManager.tsx
index a95d8c06..c56bb16a 100644
--- a/components/file-manager/FileManager.tsx
+++ b/components/file-manager/FileManager.tsx
@@ -95,6 +95,7 @@ import { decryptWithServerAction } from '@/components/drm/drmUtils';
 import { Progress } from '@/components/ui/progress';
 // Import the secure viewer component
 import { SecurePDFViewer } from './SecurePDFViewer';
+import { CreateSubfolderForm } from './CreateSubfolderForm';
 
 interface FileItem {
   id: string;
@@ -153,6 +154,7 @@ const TreeItem: React.FC<{
   onShare: (item: FileItem) => void;
   onRename: (item: FileItem) => void;
   onCategoryChange: (item: FileItem) => void;
+  onCreateSubfolder: (item: FileItem) => void;  // 추가
   isInternalUser: boolean;
 }> = ({
   item,
@@ -169,6 +171,7 @@ const TreeItem: React.FC<{
   onShare,
   onRename,
   onCategoryChange,
+  onCreateSubfolder,  // 추가
   isInternalUser
 }) => {
     const hasChildren = item.type === 'folder' && item.children && item.children.length > 0;
@@ -263,10 +266,19 @@ const TreeItem: React.FC<{
                   )}
 
                   {item.type === 'folder' && (
-                    <DropdownMenuItem onClick={() => onDownloadFolder(item)}>
-                      <Download className="h-4 w-4 mr-2" />
-                      Download Folder
-                    </DropdownMenuItem>
+                    <>
+                      {/* Create Sub-folder 추가 */}
+                      {isInternalUser && (
+                        <DropdownMenuItem onClick={() => onCreateSubfolder(item)}>
+                          <FolderPlus className="h-4 w-4 mr-2" />
+                          Create Sub-folder
+                        </DropdownMenuItem>
+                      )}
+                      <DropdownMenuItem onClick={() => onDownloadFolder(item)}>
+                        <Download className="h-4 w-4 mr-2" />
+                        Download Folder
+                      </DropdownMenuItem>
+                    </>
                   )}
 
                   {isInternalUser && (
@@ -320,6 +332,13 @@ const TreeItem: React.FC<{
 
             {item.type === 'folder' && (
               <>
+                {/* Create Sub-folder 추가 (Context Menu) */}
+                {isInternalUser && (
+                  <ContextMenuItem onClick={() => onCreateSubfolder(item)}>
+                    <FolderPlus className="h-4 w-4 mr-2" />
+                    Create Sub-folder
+                  </ContextMenuItem>
+                )}
                 <ContextMenuItem onClick={() => onDoubleClick(item)}>
                   <Folder className="h-4 w-4 mr-2" />
                   {isExpanded ? 'Collapse' : 'Expand'}
@@ -381,6 +400,7 @@ const TreeItem: React.FC<{
                 onShare={onShare}
                 onRename={onRename}
                 onCategoryChange={onCategoryChange}
+                onCreateSubfolder={onCreateSubfolder}  // 추가
                 isInternalUser={isInternalUser}
               />
             ))}
@@ -402,6 +422,59 @@ export function FileManager({ projectId }: FileManagerProps) {
   const [searchQuery, setSearchQuery] = useState('');
   const [loading, setLoading] = useState(false);
 
+  const [subfolderDialogOpen, setSubfolderDialogOpen] = useState(false);
+  const [selectedParentFolder, setSelectedParentFolder] = useState(null);
+  const [newFolderName, setNewFolderName] = useState('');
+  const [newFolderCategory, setNewFolderCategory] = useState('general');
+
+  const handleCreateSubfolder = (parentFolder) => {
+    setSelectedParentFolder(parentFolder);
+    setSubfolderDialogOpen(true);
+  };
+
+  const createSubfolder = async (name, category) => {
+    try {
+      const response = await fetch(`/api/data-room/${projectId}/folders`, {
+        method: 'POST',
+        headers: {
+          'Content-Type': 'application/json',
+        },
+        body: JSON.stringify({
+          name,
+          parentId: selectedParentFolder?.id || null,
+          category: category || selectedParentFolder?.category || 'general',
+        }),
+      });
+  
+      if (!response.ok) {
+        const error = await response.json();
+        throw new Error(error.error || 'Failed to create folder');
+      }
+  
+      const newFolder = await response.json();
+  
+      // 기존 fetchItems 함수를 재사용하여 목록 새로고침
+      await fetchItems();  // 기존에 있던 fetchItems 함수 재사용
+  
+      toast({
+        title: "Folder Created",
+        description: `Folder "${name}" has been created successfully.`,
+      });
+  
+      setSubfolderDialogOpen(false);
+      setSelectedParentFolder(null);
+  
+      return newFolder;
+    } catch (error) {
+      toast({
+        title: "Failed to Create Folder",
+        description: error.message || "An error occurred while creating the folder.",
+        variant: "destructive",
+      });
+      throw error;
+    }
+  };
+
   // Upload states
   const [uploadDialogOpen, setUploadDialogOpen] = useState(false);
   const [uploadingFiles, setUploadingFiles] = useState<UploadingFile[]>([]);
@@ -419,6 +492,10 @@ export function FileManager({ projectId }: FileManagerProps) {
   const [categoryDialogOpen, setCategoryDialogOpen] = useState(false);
   const [applyToChildren, setApplyToChildren] = useState(false);
   const [newCategory, setNewCategory] = useState('confidential');
+  const [subfolderDialog, setSubfolderDialog] = useState<{
+    open: boolean;
+    parentFolder: FileItem | null;
+  }>({ open: false, parentFolder: null });
 
   // Dialog data
   const [dialogValue, setDialogValue] = useState('');
@@ -564,22 +641,22 @@ export function FileManager({ projectId }: FileManagerProps) {
     if (preserveFolderStructure && fileArray.some((file: any) => file.webkitRelativePath)) {
       // 폴더 구조를 먼저 생성
       const folderMap = new Map<string, string>(); // path -> folderId
-      
+
       for (let i = 0; i < fileArray.length; i++) {
         const file = fileArray[i];
         const relativePath = (file as any).webkitRelativePath;
-        
+
         if (relativePath) {
           const pathParts = relativePath.split('/');
           const folders = pathParts.slice(0, -1); // 파일명 제외
-          
+
           let currentFolderPath = '';
           let parentId = currentParentId;
-          
+
           // 각 폴더를 순차적으로 생성
           for (const folderName of folders) {
             currentFolderPath = currentFolderPath ? `${currentFolderPath}/${folderName}` : folderName;
-            
+
             if (!folderMap.has(currentFolderPath)) {
               // 폴더 생성 API 호출
               try {
@@ -593,7 +670,7 @@ export function FileManager({ projectId }: FileManagerProps) {
                     parentId: parentId,
                   }),
                 });
-                
+
                 if (response.ok) {
                   const newFolder = await response.json();
                   folderMap.set(currentFolderPath, newFolder.id);
@@ -606,7 +683,7 @@ export function FileManager({ projectId }: FileManagerProps) {
               parentId = folderMap.get(currentFolderPath) || null;
             }
           }
-          
+
           // 폴더가 생성되었으면 해당 폴더에 파일 업로드
           await uploadSingleFile(file, i, parentId);
         } else {
@@ -851,7 +928,7 @@ export function FileManager({ projectId }: FileManagerProps) {
 
   // View file with PDFTron
   const viewFile = async (file: FileItem) => {
-    try {      
+    try {
       setViewerFileUrl(file.filePath || '');
       setSelectedFile(file);
       setViewerDialogOpen(true);
@@ -1051,17 +1128,17 @@ export function FileManager({ projectId }: FileManagerProps) {
   // 재귀적으로 트리 항목 검색
   const searchTreeItems = (items: FileItem[], query: string): FileItem[] => {
     const result: FileItem[] = [];
-    
+
     for (const item of items) {
       // 현재 항목이 검색어와 일치하는지 확인
       const matches = item.name.toLowerCase().includes(query.toLowerCase());
-      
+
       // 하위 항목 재귀적으로 검색
       let childrenMatches: FileItem[] = [];
       if (item.children && item.children.length > 0) {
         childrenMatches = searchTreeItems(item.children, query);
       }
-      
+
       // 현재 항목이나 하위 항목 중 하나라도 일치하면 결과에 추가
       if (matches || childrenMatches.length > 0) {
         // 하위 항목이 일치하는 경우 현재 항목도 표시하기 위해 확장된 상태로 복제
@@ -1072,7 +1149,7 @@ export function FileManager({ projectId }: FileManagerProps) {
         result.push(clonedItem);
       }
     }
-    
+
     return result;
   };
 
@@ -1362,6 +1439,12 @@ export function FileManager({ projectId }: FileManagerProps) {
                       setSelectedFile(item);
                       setShareDialogOpen(true);
                     }}
+                    onCreateSubfolder={(item) => {
+                      setSelectedParentFolder(item);
+                      setNewFolderName('');
+                      setNewFolderCategory(item.category || 'general');
+                      setSubfolderDialogOpen(true);  // setCreateSubfolderDialogOpen 대신 setSubfolderDialogOpen 사용
+                    }}
                     onRename={(item) => {
                       setSelectedFile(item);
                       setDialogValue(item.name);
@@ -1465,21 +1548,21 @@ export function FileManager({ projectId }: FileManagerProps) {
                     e.preventDefault();
                     e.stopPropagation();
                     e.currentTarget.classList.remove('border-primary', 'bg-accent');
-                    
+
                     const items = e.dataTransfer.items;
                     const files: File[] = [];
                     const filePromises: Promise<void>[] = [];
-                    
+
                     // 폴더 구조 감지를 위한 플래그
                     let hasFolderStructure = false;
-                    
+
                     // DataTransferItem을 통한 폴더 처리
                     for (let i = 0; i < items.length; i++) {
                       const item = items[i];
-                      
+
                       if (item.kind === 'file') {
                         const entry = item.webkitGetAsEntry();
-                        
+
                         if (entry) {
                           filePromises.push(
                             new Promise<void>(async (resolve) => {
@@ -1509,7 +1592,7 @@ export function FileManager({ projectId }: FileManagerProps) {
                                   });
                                 }
                               };
-                              
+
                               await traverseFileTree(entry);
                               resolve();
                             })
@@ -1517,10 +1600,10 @@ export function FileManager({ projectId }: FileManagerProps) {
                         }
                       }
                     }
-                    
+
                     // 모든 파일 처리 완료 대기
                     await Promise.all(filePromises);
-                    
+
                     // 파일이 없으면 일반 파일 처리 (폴더가 아닌 경우)
                     if (files.length === 0) {
                       const droppedFiles = Array.from(e.dataTransfer.files);
@@ -1778,6 +1861,29 @@ export function FileManager({ projectId }: FileManagerProps) {
         </DialogContent>
       </Dialog>
 
+      <Dialog open={subfolderDialogOpen} onOpenChange={setSubfolderDialogOpen}>
+        <DialogContent className="sm:max-w-[425px]">
+          <DialogHeader>
+            <DialogTitle>Create Sub-folder</DialogTitle>
+            <DialogDescription>
+              {selectedParentFolder ? (
+                <>
+                  Create a new sub-folder under <span className="font-medium">{selectedParentFolder.name}</span>
+                </>
+              ) : (
+                "Create a new folder in the current directory"
+              )}
+            </DialogDescription>
+          </DialogHeader>
+
+          <CreateSubfolderForm
+            parentFolder={selectedParentFolder}
+            onSubmit={createSubfolder}
+            onCancel={() => setSubfolderDialogOpen(false)}
+          />
+        </DialogContent>
+      </Dialog>
+
       {/* Rename Dialog */}
       <Dialog open={renameDialogOpen} onOpenChange={setRenameDialogOpen}>
         <DialogContent>
@@ -1908,7 +2014,7 @@ export function FileManager({ projectId }: FileManagerProps) {
             <Button
               onClick={() => {
                 if (selectedFile) {
-                  changeCategory(selectedFile.id, newCategory, 
+                  changeCategory(selectedFile.id, newCategory,
                     selectedFile.type === 'folder' && newCategory !== 'public' ? true : applyToChildren
                   );
                   setCategoryDialogOpen(false);
-- 
cgit v1.2.3

