From 78cce3d4fb126ccfaedff0f53da8661261e1ce44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Sun, 26 Oct 2025 19:46:50 +0300 Subject: [PATCH] =?UTF-8?q?Klas=C3=B6rlerin=20alt=C4=B1nda=20=C3=B6=C4=9Fe?= =?UTF-8?q?=20adedi=20g=C3=B6sterilecek?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FileManagement/FileItemDto.cs | 1 + .../FileManagementAppService.cs | 20 +++- .../FileManagement/FileMetadata.cs | 1 + ui/src/types/fileManagement.ts | 1 + ui/src/views/admin/files/FileManager.tsx | 98 +++++++++++++++++-- .../views/admin/files/components/FileItem.tsx | 18 ++-- 6 files changed, 123 insertions(+), 16 deletions(-) diff --git a/api/src/Kurs.Platform.Application.Contracts/FileManagement/FileItemDto.cs b/api/src/Kurs.Platform.Application.Contracts/FileManagement/FileItemDto.cs index cd57a91d..e7759fcc 100644 --- a/api/src/Kurs.Platform.Application.Contracts/FileManagement/FileItemDto.cs +++ b/api/src/Kurs.Platform.Application.Contracts/FileManagement/FileItemDto.cs @@ -16,6 +16,7 @@ public class FileItemDto public string Path { get; set; } = string.Empty; public string ParentId { get; set; } = string.Empty; public bool IsReadOnly { get; set; } + public int? ChildCount { get; set; } // Klasörler için öğe sayısı } public class GetFilesDto diff --git a/api/src/Kurs.Platform.Application/FileManagement/FileManagementAppService.cs b/api/src/Kurs.Platform.Application/FileManagement/FileManagementAppService.cs index a9ff76a5..43ee599d 100644 --- a/api/src/Kurs.Platform.Application/FileManagement/FileManagementAppService.cs +++ b/api/src/Kurs.Platform.Application/FileManagement/FileManagementAppService.cs @@ -138,6 +138,20 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe var dirInfo = new DirectoryInfo(dir); var relativePath = string.IsNullOrEmpty(folderPath) ? dirInfo.Name : $"{folderPath}/{dirInfo.Name}"; + // Klasör içindeki öğe sayısını hesapla + var childCount = 0; + try + { + var childFiles = Directory.GetFiles(dir, "*", SearchOption.TopDirectoryOnly); + var childDirectories = Directory.GetDirectories(dir, "*", SearchOption.TopDirectoryOnly); + childCount = childFiles.Length + childDirectories.Length; + } + catch (Exception ex) + { + Logger.LogWarning(ex, "Error counting items in folder: {FolderPath}", dir); + childCount = 0; + } + items.Add(new FileMetadata { Id = EncodePathAsId(relativePath), @@ -148,7 +162,8 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe Path = relativePath, ParentId = folderPath ?? "", IsReadOnly = false, - TenantId = _currentTenant.Id?.ToString() + TenantId = _currentTenant.Id?.ToString(), + ChildCount = childCount }); } @@ -248,7 +263,8 @@ public class FileManagementAppService : ApplicationService, IFileManagementAppSe ModifiedAt = metadata.ModifiedAt, Path = metadata.Path, ParentId = parentId ?? string.Empty, - IsReadOnly = finalIsReadOnly + IsReadOnly = finalIsReadOnly, + ChildCount = metadata.ChildCount }; }).OrderBy(x => x.Type == "folder" ? 0 : 1).ThenBy(x => x.Name).ToList(); diff --git a/api/src/Kurs.Platform.Application/FileManagement/FileMetadata.cs b/api/src/Kurs.Platform.Application/FileManagement/FileMetadata.cs index bd90e9f6..cfb15a5e 100644 --- a/api/src/Kurs.Platform.Application/FileManagement/FileMetadata.cs +++ b/api/src/Kurs.Platform.Application/FileManagement/FileMetadata.cs @@ -16,4 +16,5 @@ public class FileMetadata public string ParentId { get; set; } = string.Empty; public bool IsReadOnly { get; set; } public string? TenantId { get; set; } + public int? ChildCount { get; set; } // Klasörler için öğe sayısı } \ No newline at end of file diff --git a/ui/src/types/fileManagement.ts b/ui/src/types/fileManagement.ts index 24af6ef7..23e85493 100644 --- a/ui/src/types/fileManagement.ts +++ b/ui/src/types/fileManagement.ts @@ -10,6 +10,7 @@ export interface FileItem { path: string extension: string isReadOnly: boolean + childCount?: number // Klasörler için öğe sayısı } export interface FolderItem extends Omit { diff --git a/ui/src/views/admin/files/FileManager.tsx b/ui/src/views/admin/files/FileManager.tsx index 53ed914e..58e37916 100644 --- a/ui/src/views/admin/files/FileManager.tsx +++ b/ui/src/views/admin/files/FileManager.tsx @@ -90,6 +90,15 @@ const FileManager = () => { 'Protected folders check:', protectedItems.filter((item) => item.isReadOnly), ) + console.log( + 'Folders with childCount:', + protectedItems.filter((item) => item.type === 'folder').map(item => ({ + name: item.name, + childCount: item.childCount, + hasChildCount: 'childCount' in item, + type: typeof item.childCount + })) + ) setItems(protectedItems) } catch (error) { console.error('Failed to fetch items:', error) @@ -338,6 +347,34 @@ const FileManager = () => { setDeleteModalOpen(true) } + const handleSingleItemDelete = async (item: FileItemType) => { + // Check if it's a protected item + if (item.isReadOnly) { + toast.push( + + Protected system folders cannot be deleted. + , + ) + return + } + + // Check if it's a folder containing files + if (item.type === 'folder') { + const hasFiles = await checkFolderHasFiles(item.id) + if (hasFiles) { + toast.push( + + Folder '{item.name}' contains files and cannot be deleted for security reasons. + , + ) + return + } + } + + // If all checks pass, open delete modal + openDeleteModal([item]) + } + const goUpOneLevel = () => { if (breadcrumbItems.length > 1) { const parentBreadcrumb = breadcrumbItems[breadcrumbItems.length - 2] @@ -422,11 +459,44 @@ const FileManager = () => { setSelectedItems([]) } - const deleteSelectedItems = () => { + // Check if a folder contains files (for security purposes) + const checkFolderHasFiles = async (folderId: string): Promise => { + try { + const response = await fileManagementService.getItems(folderId) + const items = response.data.items || [] + // Check if folder contains any files (not just other folders) + return items.some(item => item.type === 'file') + } catch (error) { + console.error('Error checking folder contents:', error) + return false + } + } + + const deleteSelectedItems = async () => { const itemsToDelete = filteredItems.filter((item) => selectedItems.includes(item.id)) const deletableItems = itemsToDelete.filter((item) => !item.isReadOnly) const protectedItems = itemsToDelete.filter((item) => item.isReadOnly) + // Check for folders containing files + const foldersWithFiles: string[] = [] + for (const item of deletableItems) { + if (item.type === 'folder') { + const hasFiles = await checkFolderHasFiles(item.id) + if (hasFiles) { + foldersWithFiles.push(item.name) + } + } + } + + // Filter out folders that contain files + const finalDeletableItems = deletableItems.filter(item => { + if (item.type === 'folder') { + return !foldersWithFiles.includes(item.name) + } + return true + }) + + // Show warnings if (protectedItems.length > 0) { toast.push( @@ -436,11 +506,27 @@ const FileManager = () => { ) } - if (deletableItems.length > 0) { - openDeleteModal(deletableItems) - // Remove protected items from selection - const deletableIds = deletableItems.map((item) => item.id) + if (foldersWithFiles.length > 0) { + toast.push( + + {foldersWithFiles.length} folder(s) containing files cannot be deleted for security reasons:{' '} + {foldersWithFiles.join(', ')} + , + ) + } + + if (finalDeletableItems.length > 0) { + openDeleteModal(finalDeletableItems) + // Remove protected items and folders with files from selection + const deletableIds = finalDeletableItems.map((item) => item.id) setSelectedItems((prev) => prev.filter((id) => deletableIds.includes(id))) + } else if (itemsToDelete.length > 0) { + // If no items can be deleted, show info message + toast.push( + + No items can be deleted. Selected items are either protected or folders containing files. + , + ) } } @@ -927,7 +1013,7 @@ const FileManager = () => { ) } }} - onDelete={(item) => openDeleteModal([item])} + onDelete={handleSingleItemDelete} onDownload={item.type === 'file' ? handleDownload : undefined} onPreview={ item.type === 'file' diff --git a/ui/src/views/admin/files/components/FileItem.tsx b/ui/src/views/admin/files/components/FileItem.tsx index 64e9fcd1..bcbff437 100644 --- a/ui/src/views/admin/files/components/FileItem.tsx +++ b/ui/src/views/admin/files/components/FileItem.tsx @@ -338,12 +338,16 @@ const FileItem = forwardRef((props, ref) => { {getFileTypeLabel(item)} - {/* File Size */} + {/* File Size / Folder Item Count */}
{item.type === 'file' && item.size ? ( {formatFileSize(item.size)} + ) : item.type === 'folder' && typeof item.childCount === 'number' ? ( + + {item.childCount} öğe + ) : ( - )} @@ -431,13 +435,11 @@ const FileItem = forwardRef((props, ref) => { )} {/* Folder Child Count */} - {item.type === 'folder' && - 'childCount' in item && - typeof (item as any).childCount === 'number' && ( -

- {(item as any).childCount} öğe -

- )} + {item.type === 'folder' && ( +

+ {typeof item.childCount === 'number' ? `${item.childCount} öğe` : 'Sayılıyor...'} +

+ )}
)