Klasörlerin altında öğe adedi gösterilecek
This commit is contained in:
parent
697c7c1d65
commit
78cce3d4fb
6 changed files with 123 additions and 16 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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ı
|
||||
}
|
||||
|
|
@ -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<FileItem, 'type' | 'size' | 'mimeType' | 'extension'> {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
<Notification title="Warning" type="warning">
|
||||
Protected system folders cannot be deleted.
|
||||
</Notification>,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if it's a folder containing files
|
||||
if (item.type === 'folder') {
|
||||
const hasFiles = await checkFolderHasFiles(item.id)
|
||||
if (hasFiles) {
|
||||
toast.push(
|
||||
<Notification title="Security Warning" type="warning">
|
||||
Folder '{item.name}' contains files and cannot be deleted for security reasons.
|
||||
</Notification>,
|
||||
)
|
||||
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<boolean> => {
|
||||
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(
|
||||
<Notification title="Warning" type="warning">
|
||||
|
|
@ -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(
|
||||
<Notification title="Security Warning" type="warning">
|
||||
{foldersWithFiles.length} folder(s) containing files cannot be deleted for security reasons:{' '}
|
||||
{foldersWithFiles.join(', ')}
|
||||
</Notification>,
|
||||
)
|
||||
}
|
||||
|
||||
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(
|
||||
<Notification title="Info" type="info">
|
||||
No items can be deleted. Selected items are either protected or folders containing files.
|
||||
</Notification>,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -927,7 +1013,7 @@ const FileManager = () => {
|
|||
)
|
||||
}
|
||||
}}
|
||||
onDelete={(item) => openDeleteModal([item])}
|
||||
onDelete={handleSingleItemDelete}
|
||||
onDownload={item.type === 'file' ? handleDownload : undefined}
|
||||
onPreview={
|
||||
item.type === 'file'
|
||||
|
|
|
|||
|
|
@ -338,12 +338,16 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
|
|||
<span className="text-sm text-gray-500 dark:text-gray-400">{getFileTypeLabel(item)}</span>
|
||||
</div>
|
||||
|
||||
{/* File Size */}
|
||||
{/* File Size / Folder Item Count */}
|
||||
<div className="col-span-2 sm:col-span-2 flex items-center">
|
||||
{item.type === 'file' && item.size ? (
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{formatFileSize(item.size)}
|
||||
</span>
|
||||
) : item.type === 'folder' && typeof item.childCount === 'number' ? (
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{item.childCount} öğe
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-sm text-gray-500 dark:text-gray-400">-</span>
|
||||
)}
|
||||
|
|
@ -431,13 +435,11 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
|
|||
)}
|
||||
|
||||
{/* Folder Child Count */}
|
||||
{item.type === 'folder' &&
|
||||
'childCount' in item &&
|
||||
typeof (item as any).childCount === 'number' && (
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{(item as any).childCount} öğe
|
||||
</p>
|
||||
)}
|
||||
{item.type === 'folder' && (
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{typeof item.childCount === 'number' ? `${item.childCount} öğe` : 'Sayılıyor...'}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue