diff options
Diffstat (limited to 'components/file-manager/FileManager.tsx')
| -rw-r--r-- | components/file-manager/FileManager.tsx | 156 |
1 files changed, 131 insertions, 25 deletions
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); |
