FileManager Seeder

This commit is contained in:
Sedat Öztürk 2026-05-26 14:30:04 +03:00
parent bea5aabffa
commit ef201fda13
6 changed files with 472 additions and 113 deletions

View file

@ -15338,6 +15338,48 @@
"en": "Name",
"tr": "Adı"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Item",
"en": "Item",
"tr": "Öğe"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Preview",
"en": "Preview",
"tr": "Önizleme"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Download",
"en": "Download",
"tr": "İndir"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.CopyFileUrl",
"en": "Copy URL",
"tr": "URL Kopyala"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.NewFolder",
"en": "New Folder",
"tr": "Yeni Klasör"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Rename",
"en": "Rename",
"tr": "Yeniden Adlandır"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Move",
"en": "Move",
"tr": "Taşı"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.NameKey",
@ -16724,6 +16766,18 @@
"en": "Type",
"tr": "Türü"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Size",
"en": "Size",
"tr": "Boyut"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.Modified",
"en": "Modified",
"tr": "Değiştirilme"
},
{
"resourceName": "Platform",
"key": "App.Listform.ListformField.TypeId",
@ -18823,6 +18877,246 @@
"key": "ListForms.ListFormEdit.Workflow.ApprovalDescriptionFieldName",
"en": "Approval Description Field Name",
"tr": "Onay Açıklaması Alanı Adı"
},
{
"resourceName": "Platform",
"key": "FileManager.Folder",
"en": "Folder",
"tr": "Klasor"
},
{
"resourceName": "Platform",
"key": "FileManager.Picture",
"en": "Picture",
"tr": "Resim"
},
{
"resourceName": "Platform",
"key": "FileManager.Video",
"en": "Video",
"tr": "Video"
},
{
"resourceName": "Platform",
"key": "FileManager.Sound",
"en": "Sound",
"tr": "Ses"
},
{
"resourceName": "Platform",
"key": "FileManager.PDF",
"en": "PDF",
"tr": "PDF"
},
{
"resourceName": "Platform",
"key": "FileManager.Word",
"en": "Word",
"tr": "Word"
},
{
"resourceName": "Platform",
"key": "FileManager.Excel",
"en": "Excel",
"tr": "Excel"
},
{
"resourceName": "Platform",
"key": "FileManager.PowerPoint",
"en": "PowerPoint",
"tr": "PowerPoint"
},
{
"resourceName": "Platform",
"key": "FileManager.Archive",
"en": "Archive",
"tr": "Arşiv"
},
{
"resourceName": "Platform",
"key": "FileManager.Text",
"en": "Text",
"tr": "Metin"
},
{
"resourceName": "Platform",
"key": "FileManager.Code",
"en": "Code",
"tr": "Kod"
},
{
"resourceName": "Platform",
"key": "FileManager.Counting",
"en": "Counting...",
"tr": "Sayılıyor..."
},
{
"resourceName": "Platform",
"key": "FileManager.CreateFolder",
"en": "Create Folder",
"tr": "Klasör Oluştur"
},
{
"resourceName": "Platform",
"key": "FileManager.Create",
"en": "Create",
"tr": "Oluştur"
},
{
"resourceName": "Platform",
"key": "FileManager.CopySelectedItems",
"en": "Copy Selected Items",
"tr": "Seçili Öğeleri Kopyala"
},
{
"resourceName": "Platform",
"key": "FileManager.CutSelectedItems",
"en": "Cut Selected Items",
"tr": "Seçili Öğeleri Kes"
},
{
"resourceName": "Platform",
"key": "FileManager.PasteSelectedItems",
"en": "Paste Selected Items",
"tr": "Seçili Öğeleri Yapıştır"
},
{
"resourceName": "Platform",
"key": "FileManager.RenameSelectedItem",
"en": "Rename Selected Item",
"tr": "Seçili Öğeyi Yeniden Adlandır"
},
{
"resourceName": "Platform",
"key": "FileManager.DownloadSelectedFile",
"en": "Download Selected File",
"tr": "Seçili Dosyayı İndir"
},
{
"resourceName": "Platform",
"key": "FileManager.DeleteSelectedItems",
"en": "Delete selected items",
"tr": "Seçili Öğeleri Sil"
},
{
"resourceName": "Platform",
"key": "FileManager.SearchFiles",
"en": "Search...",
"tr": "Ara..."
},
{
"resourceName": "Platform",
"key": "FileManager.UploadFiles",
"en": "Upload files...",
"tr": "Dosyaları Yükle..."
},
{
"resourceName": "Platform",
"key": "FileManager.Upload",
"en": "Upload",
"tr": "Yükle"
},
{
"resourceName": "Platform",
"key": "FileManager.FolderName",
"en": "Folder Name",
"tr": "Klasör Adı"
},
{
"resourceName": "Platform",
"key": "FileManager.FileName",
"en": "File Name",
"tr": "Dosya Adı"
},
{
"resourceName": "Platform",
"key": "FileManager.File",
"en": "File",
"tr": "Dosya"
},
{
"resourceName": "Platform",
"key": "FileManager.RenameFolder",
"en": "Rename Folder",
"tr": "Klasörü Yeniden Adlandır"
},
{
"resourceName": "Platform",
"key": "FileManager.RenameFile",
"en": "Rename File",
"tr": "Dosyayı Yeniden Adlandır"
},
{
"resourceName": "Platform",
"key": "FileManager.DeleteConfirmationMessage",
"en": "Are you sure you want to delete the following items? This action cannot be undone.",
"tr": "Aşağıdaki öğeleri silmek istediğinizden emin misiniz? Bu işlem geri alınamaz."
},
{
"resourceName": "Platform",
"key": "FileManager.DeleteConfirmationTitle",
"en": "Delete Items",
"tr": "Öğeleri Sil"
},
{
"resourceName": "Platform",
"key": "FileManager.DeselectAll",
"en": "Deselect All",
"tr": "Tümünü Seçmeyi Bırak"
},
{
"resourceName": "Platform",
"key": "FileManager.SelectAll",
"en": "Select All",
"tr": "Tümünü Seç"
},
{
"resourceName": "Platform",
"key": "FileManager.Deselect",
"en": "Deselect",
"tr": "Seçmeyi Bırak"
},
{
"resourceName": "Platform",
"key": "FileManager.Select",
"en": "Select",
"tr": "Seç"
},
{
"resourceName": "Platform",
"key": "FileManager.SortByNameAsc",
"en": "Name (A-Z)",
"tr": "İsim (A-Z)"
},
{
"resourceName": "Platform",
"key": "FileManager.SortByNameDesc",
"en": "Name (Z-A)",
"tr": "İsim (Z-A)"
},
{
"resourceName": "Platform",
"key": "FileManager.SortBySizeAsc",
"en": "Size (Small to Large)",
"tr": "Boyut (Küçükten Büyüğe)"
},
{
"resourceName": "Platform",
"key": "FileManager.SortBySizeDesc",
"en": "Size (Large to Small)",
"tr": "Boyut (Büyükten Küçüğe)"
},
{
"resourceName": "Platform",
"key": "FileManager.SortByModifiedAsc",
"en": "Modified (Oldest)",
"tr": "Değiştirilme (En Eski)"
},
{
"resourceName": "Platform",
"key": "FileManager.SortByModifiedDesc",
"en": "Modified (Newest)",
"tr": "Değiştirilme (En Yeni)"
}
]
}
}

View file

@ -2682,7 +2682,7 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
new EditingFormItemDto { Order = 4, DataField = "Category", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order = 5, DataField = "UserId", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxSelectBox, EditorOptions=EditorOptionValues.ShowClearButton },
new EditingFormItemDto { Order = 6, DataField = "PublishDate", ColSpan=1, EditorType2 = EditorTypes.dxDateBox },
new EditingFormItemDto { Order = 7, DataField = "ExpiryDate", ColSpan=1, IsRequired = true, EditorType2 = EditorTypes.dxDateBox },
new EditingFormItemDto { Order = 7, DataField = "ExpiryDate", ColSpan=1, EditorType2 = EditorTypes.dxDateBox },
new EditingFormItemDto { Order = 8, DataField = "IsPinned", ColSpan=1, EditorType2 = EditorTypes.dxCheckBox },
new EditingFormItemDto { Order = 9, DataField = "ImageUrl", ColSpan=1, EditorType2 = EditorTypes.dxImageUpload, EditorOptions = EditorOptionValues.ImageUploadOptions},
]}

View file

@ -1,6 +1,6 @@
import { forwardRef, Ref } from 'react'
import { forwardRef, useEffect, useRef } from 'react'
import classNames from 'classnames'
import ReactSelect from 'react-select'
import ReactSelect, { components as ReactSelectComponents } from 'react-select'
import CreatableSelect from 'react-select/creatable'
import AsyncSelect from 'react-select/async'
import { TW_COLORS, BORDER_RADIUS, SPACING } from '@/utils/tailwind'
@ -17,15 +17,17 @@ import type {
ControlProps,
Props as ReactSelectProps,
GroupBase,
MenuListProps,
} from 'react-select'
import type { AsyncProps } from 'react-select/async'
import type { CreatableProps } from 'react-select/creatable'
import type { ForwardedRef } from 'react'
import type { ForwardedRef, ReactNode, Ref } from 'react'
interface DefaultOptionProps {
innerProps: JSX.IntrinsicElements['div']
label: string
selectProps: { themeColor?: string }
children?: ReactNode
isSelected: boolean
isDisabled: boolean
isFocused: boolean
@ -38,6 +40,7 @@ const DefaultOption = ({
isSelected,
isDisabled,
isFocused,
children,
}: DefaultOptionProps) => {
const { themeColor } = selectProps
return (
@ -50,7 +53,7 @@ const DefaultOption = ({
)}
{...innerProps}
>
<span className="ml-2">{label}</span>
<div className="ml-2 min-w-0">{children ?? label}</div>
{isSelected && (
<FaCheck
className={`text-${themeColor} dark:text-white text-xl`}
@ -60,6 +63,39 @@ const DefaultOption = ({
)
}
const DefaultMenuList = <
Option,
IsMulti extends boolean = false,
Group extends GroupBase<Option> = GroupBase<Option>
>(
props: MenuListProps<Option, IsMulti, Group>
) => {
const menuListRef = useRef<HTMLDivElement | null>(null)
useEffect(() => {
const frame = requestAnimationFrame(() => {
menuListRef.current
?.querySelector('.select-option.selected')
?.scrollIntoView({ block: 'center' })
})
return () => cancelAnimationFrame(frame)
}, [props.selectProps.value])
return (
<ReactSelectComponents.MenuList
{...props}
innerRef={(element) => {
menuListRef.current = element
if (typeof props.innerRef === 'function') {
props.innerRef(element)
}
}}
/>
)
}
const DefaultDropdownIndicator = () => {
return (
<div className="select-dropdown-indicator">
@ -235,6 +271,7 @@ function _Select<
components={{
IndicatorSeparator: () => null,
Option: DefaultOption,
MenuList: DefaultMenuList,
LoadingIndicator: DefaultLoadingIndicator,
DropdownIndicator: DefaultDropdownIndicator,
ClearIndicator: DefaultClearIndicator,

View file

@ -18,6 +18,8 @@ import {
FaDownload,
FaPaste,
FaBuilding,
FaUsers,
FaChevronRight,
} from 'react-icons/fa'
import Container from '@/components/shared/Container'
import { useLocalization } from '@/utils/hooks/useLocalization'
@ -136,7 +138,9 @@ const FileManager = () => {
const items = response.data.items || []
// Manual protection for system folders
const protectedItems = items.map((item) => {
const isSystemFolder = ['intranet', 'avatar', 'import', 'note', 'backup'].includes(item.name.toLowerCase())
const isSystemFolder = ['intranet', 'avatar', 'import', 'note', 'backup'].includes(
item.name.toLowerCase(),
)
return {
...item,
isReadOnly: item.isReadOnly || isSystemFolder,
@ -735,48 +739,11 @@ const FileManager = () => {
></Helmet>
{/* Enhanced Unified Toolbar */}
<div className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg mb-4 mt-2">
<div className="dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg p-2 mb-4 mt-2">
{/* Main Toolbar Row */}
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-3 sm:gap-4">
{/* Left Section - Primary Actions */}
<div className="flex items-center gap-2 flex-wrap min-w-0">
{/* Tenant Selector Row */}
<div className="flex items-center gap-2">
<FaBuilding className="text-gray-500 flex-shrink-0" />
{isHostContext ? (
<Select
size="sm"
isLoading={tenantsLoading}
options={[
{ value: '', label: 'Host' },
...tenants.map((t) => ({ value: t.id ?? '', label: t.name ?? '' })),
]}
value={{
value: selectedTenant ? selectedTenant.id : '',
label: selectedTenant ? selectedTenant.name : 'Host',
}}
onChange={(option) => {
if (option && 'value' in option) {
const val = option.value as string
if (!val) {
setSelectedTenant(undefined)
} else {
const found = tenants.find((t) => t.id === val)
if (found) setSelectedTenant({ id: found.id!, name: found.name ?? '' })
}
}
}}
/>
) : (
<div
className="text-sm font-medium text-gray-700 dark:text-gray-200 truncate max-w-[220px]"
title={authTenantName || selectedTenant?.name || ''}
>
{authTenantName || selectedTenant?.name || ''}
</div>
)}
</div>
{/* File Operations */}
{/* Navigation */}
<Button
@ -795,8 +762,8 @@ const FileManager = () => {
size="sm"
className="flex-shrink-0"
>
<span className="hidden sm:inline">Upload Files</span>
<span className="sm:hidden">Upload</span>
<span className="hidden sm:inline">{translate('::FileManager.UploadFiles')}</span>
<span className="sm:hidden">{translate('::FileManager.Upload')}</span>
</Button>
<Button
variant="default"
@ -805,8 +772,8 @@ const FileManager = () => {
size="sm"
className="flex-shrink-0"
>
<span className="hidden sm:inline">Create Folder</span>
<span className="sm:hidden">Create</span>
<span className="hidden sm:inline">{translate('::FileManager.CreateFolder')}</span>
<span className="sm:hidden">{translate('::FileManager.Create')}</span>
</Button>
{/* Clipboard Operations */}
@ -817,7 +784,7 @@ const FileManager = () => {
disabled={selectedItems.length === 0}
size="sm"
className="text-gray-600 hover:text-blue-600 disabled:opacity-50 flex-shrink-0"
title="Copy selected items"
title={translate('::FileManager.CopySelectedItems')}
/>
<Button
variant="plain"
@ -826,7 +793,7 @@ const FileManager = () => {
disabled={selectedItems.length === 0}
size="sm"
className="text-gray-600 hover:text-orange-600 disabled:opacity-50 flex-shrink-0"
title="Cut selected items"
title={translate('::FileManager.CutSelectedItems')}
/>
<Button
variant="plain"
@ -835,7 +802,7 @@ const FileManager = () => {
disabled={!hasClipboardData}
size="sm"
className="text-gray-600 hover:text-green-600 disabled:opacity-50 flex-shrink-0"
title="Paste items"
title={translate('::FileManager.PasteSelectedItems')}
/>
<Button
variant="plain"
@ -857,7 +824,7 @@ const FileManager = () => {
size="sm"
disabled={selectedItems.length !== 1}
className="text-gray-600 hover:text-blue-600 flex-shrink-0"
title="Rename selected item"
title={translate('::FileManager.RenameSelectedItem')}
></Button>
<Button
@ -886,7 +853,7 @@ const FileManager = () => {
})()
}
className="text-gray-600 hover:text-green-600 flex-shrink-0"
title="Download selected file"
title={translate('::FileManager.DownloadSelectedFile')}
></Button>
<Button
@ -896,9 +863,9 @@ const FileManager = () => {
size="sm"
disabled={selectedItems.length === 0}
className="text-gray-600 hover:text-red-600 flex-shrink-0"
title="Delete selected items"
title={translate('::FileManager.DeleteSelectedItems')}
>
<span>Delete {selectedItems.length > 0 && `(${selectedItems.length})`}</span>
<span>{translate('::Delete')}</span> {selectedItems.length > 0 && `(${selectedItems.length})`}
</Button>
{/* Selection Actions */}
@ -928,13 +895,13 @@ const FileManager = () => {
>
<span className="hidden lg:inline">
{selectedItems.length === filteredItems.filter((item) => !item.isReadOnly).length
? 'Deselect All'
: 'Select All'}
? translate('::FileManager.DeselectAll')
: translate('::FileManager.SelectAll')}
</span>
<span className="lg:hidden">
{selectedItems.length === filteredItems.filter((item) => !item.isReadOnly).length
? 'Deselect'
: 'Select'}
? translate('::FileManager.Deselect')
: translate('::FileManager.Select')}
</span>
</Button>
)}
@ -946,7 +913,7 @@ const FileManager = () => {
<div className="flex items-center w-full sm:w-auto">
<Input
size="sm"
placeholder="Search files..."
placeholder={translate('::FileManager.SearchFiles')}
value={filters.searchTerm}
onChange={(e) => setFilters((prev) => ({ ...prev, searchTerm: e.target.value }))}
prefix={<FaSearch className="text-gray-400" />}
@ -958,12 +925,12 @@ const FileManager = () => {
<Select
size="xs"
options={[
{ value: 'name-asc', label: 'Name (A-Z)' },
{ value: 'name-desc', label: 'Name (Z-A)' },
{ value: 'size-asc', label: 'Size (Small to Large)' },
{ value: 'size-desc', label: 'Size (Large to Small)' },
{ value: 'modified-desc', label: 'Modified (Newest)' },
{ value: 'modified-asc', label: 'Modified (Oldest)' },
{ value: 'name-asc', label: translate('::FileManager.SortByNameAsc') },
{ value: 'name-desc', label: translate('::FileManager.SortByNameDesc') },
{ value: 'size-asc', label: translate('::FileManager.SortBySizeAsc') },
{ value: 'size-desc', label: translate('::FileManager.SortBySizeDesc') },
{ value: 'modified-desc', label: translate('::FileManager.SortByModifiedDesc') },
{ value: 'modified-asc', label: translate('::FileManager.SortByModifiedAsc') },
]}
value={{
value: `${filters.sortBy}-${filters.sortOrder}`,
@ -1014,9 +981,65 @@ const FileManager = () => {
</div>
{/* Breadcrumb */}
<div className="mb-4 sm:mb-6 overflow-x-auto">
<div className="min-w-max">
<Breadcrumb items={breadcrumbItems} onNavigate={handleBreadcrumbNavigate} />
<div className="flex items-center gap-2 mb-4 sm:mb-6">
<div className="flex items-center gap-2">
{isHostContext ? (
<Select
size="xs"
isLoading={tenantsLoading}
options={[
{
value: '',
label: 'Host',
icon: <FaBuilding className="flex-shrink-0 text-gray-500" />,
},
...tenants.map((t) => ({
value: t.id ?? '',
label: t.name ?? '',
icon: <FaUsers className="flex-shrink-0 text-blue-500" />,
})),
]}
value={{
value: selectedTenant ? selectedTenant.id : '',
label: selectedTenant ? selectedTenant.name : 'Host',
icon: selectedTenant ? (
<FaUsers className="flex-shrink-0 text-blue-500" />
) : (
<FaBuilding className="flex-shrink-0 text-gray-500" />
),
}}
formatOptionLabel={(option) => (
<div className="flex items-center gap-2">
{option.icon}
<span className="truncate">{option.label}</span>
</div>
)}
onChange={(option) => {
if (option && 'value' in option) {
const val = option.value as string
if (!val) {
setSelectedTenant(undefined)
} else {
const found = tenants.find((t) => t.id === val)
if (found) setSelectedTenant({ id: found.id!, name: found.name ?? '' })
}
}
}}
/>
) : (
<div
className="max-w-[220px] truncate text-sm font-medium text-gray-700 dark:text-gray-200"
title={authTenantName || selectedTenant?.name || ''}
>
{authTenantName || selectedTenant?.name || ''}
</div>
)}
</div>
<FaChevronRight className="mx-2 h-4 w-4 text-gray-400" />
<div className="overflow-x-auto">
<div className="flex min-w-max items-center gap-2">
<Breadcrumb items={breadcrumbItems} onNavigate={handleBreadcrumbNavigate} />
</div>
</div>
</div>
@ -1030,10 +1053,10 @@ const FileManager = () => {
{/* List View Header */}
{viewMode === 'list' && (
<div className="hidden sm:grid grid-cols-12 gap-4 px-4 py-2 text-sm font-medium text-gray-500 dark:text-gray-400 border-b dark:border-gray-700 mb-2">
<div className="col-span-5 lg:col-span-5">İsim</div>
<div className="col-span-2 lg:col-span-2">Tür</div>
<div className="col-span-2 lg:col-span-2">Boyut</div>
<div className="col-span-2 lg:col-span-2">Değiştirilme</div>
<div className="col-span-5 lg:col-span-5">{translate('::App.Listform.ListformField.Name')}</div>
<div className="col-span-2 lg:col-span-2">{translate('::App.Listform.ListformField.Type')}</div>
<div className="col-span-2 lg:col-span-2">{translate('::App.Listform.ListformField.Size')}</div>
<div className="col-span-2 lg:col-span-2">{translate('::App.Listform.ListformField.Modified')}</div>
<div className="col-span-1"></div> {/* Actions column */}
</div>
)}
@ -1099,7 +1122,7 @@ const FileManager = () => {
// Resim preview modal'ıılabilir
toast.push(
<Notification type="info">
Resim önizleme özelliği yakında eklenecek
Image preview feature will be added soon.
</Notification>,
)
} else {

View file

@ -19,6 +19,7 @@ import { toast, Notification } from '@/components/ui'
// import { Dropdown } from '@/components/ui' // Artık kullanmıyoruz
import type { FileItem as FileItemType, FileActionMenuItem } from '@/types/fileManagement'
import { FILE_URL } from '@/constants/app.constant'
import { useLocalization } from '@/utils/hooks/useLocalization'
export interface FileItemProps {
item: FileItemType
@ -96,24 +97,24 @@ const formatDate = (date?: string | Date): string => {
}
}
const getFileTypeLabel = (item: FileItemType): string => {
if (item.type === 'folder') return 'Klasör'
const getFileTypeLabel = (item: FileItemType, translate: (key: string) => string): string => {
if (item.type === 'folder') return translate('::FileManager.Folder')
const extension = item.extension?.toLowerCase()
const mimeType = item.mimeType?.toLowerCase()
if (mimeType?.startsWith('image/')) return 'Resim'
if (mimeType?.startsWith('video/')) return 'Video'
if (mimeType?.startsWith('audio/')) return 'Ses'
if (mimeType?.includes('pdf')) return 'PDF'
if (mimeType?.includes('word')) return 'Word'
if (mimeType?.includes('excel') || mimeType?.includes('spreadsheet')) return 'Excel'
if (mimeType?.includes('powerpoint') || mimeType?.includes('presentation')) return 'PowerPoint'
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension || '')) return 'Arşiv'
if (['txt', 'md'].includes(extension || '')) return 'Metin'
if (['json', 'xml', 'css', 'js', 'ts', 'html'].includes(extension || '')) return 'Kod'
if (mimeType?.startsWith('image/')) return translate('::FileManager.Picture') // 'Resim'
if (mimeType?.startsWith('video/')) return translate('::FileManager.Video') // 'Video'
if (mimeType?.startsWith('audio/')) return translate('::FileManager.Sound') // 'Ses'
if (mimeType?.includes('pdf')) return translate('::FileManager.PDF') // 'PDF'
if (mimeType?.includes('word')) return translate('::FileManager.Word') // 'Word'
if (mimeType?.includes('excel') || mimeType?.includes('spreadsheet')) return translate('::FileManager.Excel') // 'Excel'
if (mimeType?.includes('powerpoint') || mimeType?.includes('presentation')) return translate('::FileManager.PowerPoint') // 'PowerPoint'
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension || '')) return translate('::FileManager.Archive') // 'Arşiv'
if (['txt', 'md'].includes(extension || '')) return translate('::FileManager.Text') // 'Metin'
if (['json', 'xml', 'css', 'js', 'ts', 'html'].includes(extension || '')) return translate('::FileManager.Code') // 'Kod'
return extension?.toUpperCase() || 'Dosya'
return extension?.toUpperCase() || translate('::FileManager.File')
}
const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
@ -135,7 +136,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
const [dropdownOpen, setDropdownOpen] = useState(false)
const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>(null)
const { translate } = useLocalization()
const handleClick = () => {
onSelect?.(item)
}
@ -185,7 +186,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
? [
{
key: 'preview',
label: 'Önizle',
label: translate('::App.Listform.ListformField.Preview'),
icon: 'HiEye',
onClick: () => onPreview(item),
},
@ -196,7 +197,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
? [
{
key: 'download',
label: 'İndir',
label: translate('::App.Listform.ListformField.Download'),
icon: 'HiArrowDownTray',
onClick: () => onDownload(item),
},
@ -207,7 +208,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
? [
{
key: 'copyUrl',
label: 'URL Kopyala',
label: translate('::App.Listform.ListformField.CopyFileUrl'),
icon: 'HiLink',
onClick: () => {
const fileUrl = FILE_URL(item.path, item.tenantId)
@ -236,7 +237,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
? [
{
key: 'createFolder',
label: 'Yeni Klasör',
label: translate('::App.Listform.ListformField.NewFolder'),
icon: 'HiFolderPlus',
onClick: () => onCreateFolder(item),
},
@ -247,7 +248,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
? [
{
key: 'rename',
label: 'Yeniden Adlandır',
label: translate('::App.Listform.ListformField.Rename'),
icon: 'HiPencil',
onClick: () => onRename(item),
},
@ -258,7 +259,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
? [
{
key: 'move',
label: 'Taşı',
label: translate('::App.Listform.ListformField.Move'),
icon: 'HiArrowRightOnRectangle',
onClick: () => onMove(item),
},
@ -269,7 +270,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
? [
{
key: 'delete',
label: 'Sil',
label: translate('::Delete'),
icon: 'HiTrash',
dangerous: true,
onClick: () => onDelete(item),
@ -404,7 +405,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
{/* File Type - Hidden on mobile */}
<div className="hidden sm:flex col-span-2 items-center">
<span className="text-sm text-gray-500 dark:text-gray-400">{getFileTypeLabel(item)}</span>
<span className="text-sm text-gray-500 dark:text-gray-400">{getFileTypeLabel(item, translate)}</span>
</div>
{/* File Size / Folder Item Count */}
@ -500,7 +501,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
{/* File Size and Type */}
{item.type === 'file' && (
<div className="space-y-1">
<p className="text-xs text-gray-500 dark:text-gray-400">{getFileTypeLabel(item)}</p>
<p className="text-xs text-gray-500 dark:text-gray-400">{getFileTypeLabel(item, translate)}</p>
{item.size && (
<p className="text-xs text-gray-500 dark:text-gray-400">
{formatFileSize(item.size)}
@ -512,7 +513,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
{/* Folder Child Count */}
{item.type === 'folder' && (
<p className="text-xs text-gray-500 dark:text-gray-400">
{typeof item.childCount === 'number' ? `${item.childCount} öğe` : 'Sayılıyor...'}
{typeof item.childCount === 'number' ? `${item.childCount} ${translate('::App.Listform.ListformField.Item')}` : translate('::FileManager.Counting')}
</p>
)}
</div>

View file

@ -2,6 +2,7 @@ import { forwardRef, useState, useEffect } from 'react'
import { Dialog, Button, Input, FormItem } from '@/components/ui'
import { FaTimes } from 'react-icons/fa'
import type { FileItem } from '@/types/fileManagement'
import { useLocalization } from '@/utils/hooks/useLocalization'
// Create Folder Modal
export interface CreateFolderModalProps {
@ -17,6 +18,7 @@ export const CreateFolderModal = forwardRef<HTMLDivElement, CreateFolderModalPro
const { isOpen, onClose, onCreate, loading = false } = props
const [folderName, setFolderName] = useState('')
const [error, setError] = useState('')
const { translate } = useLocalization()
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
@ -46,11 +48,10 @@ export const CreateFolderModal = forwardRef<HTMLDivElement, CreateFolderModalPro
<Dialog isOpen={isOpen} onClose={handleClose}>
<div ref={ref}>
<form onSubmit={handleSubmit} className="py-6">
<FormItem label="Folder Name" invalid={!!error} errorMessage={error}>
<FormItem label={translate('::FileManager.FolderName')} invalid={!!error} errorMessage={error}>
<Input
value={folderName}
onChange={(e) => setFolderName(e.target.value)}
placeholder="Enter folder name"
autoFocus
className="mt-2"
/>
@ -59,7 +60,7 @@ export const CreateFolderModal = forwardRef<HTMLDivElement, CreateFolderModalPro
<div className="flex justify-end space-x-2 pt-4 border-t">
<Button variant="default" onClick={handleClose} disabled={loading}>
Cancel
{translate('::Cancel')}
</Button>
<Button
variant="solid"
@ -67,7 +68,7 @@ export const CreateFolderModal = forwardRef<HTMLDivElement, CreateFolderModalPro
loading={loading}
disabled={!folderName.trim()}
>
Create Folder
{translate('::FileManager.CreateFolder')}
</Button>
</div>
</div>
@ -91,6 +92,7 @@ export const RenameItemModal = forwardRef<HTMLDivElement, RenameItemModalProps>(
const { isOpen, onClose, onRename, item, loading = false } = props
const [newName, setNewName] = useState('')
const [error, setError] = useState('')
const { translate } = useLocalization()
// Update input when item changes
useEffect(() => {
@ -134,21 +136,20 @@ export const RenameItemModal = forwardRef<HTMLDivElement, RenameItemModalProps>(
<div ref={ref} className="max-w-md">
<div className="flex items-center justify-between pb-4 border-b">
<h3 className="text-lg font-semibold">
Rename {item.type === 'folder' ? 'Folder' : 'File'}
{translate(`::FileManager.${item.type === 'folder' ? 'RenameFolder' : 'RenameFile'}`)}
</h3>
<Button variant="plain" size="sm" icon={<FaTimes />} onClick={handleClose} />
</div>
<form onSubmit={handleSubmit} className="py-6">
<FormItem
label={`${item.type === 'folder' ? 'Folder' : 'File'} Name`}
label={translate(`::FileManager.${item.type === 'folder' ? 'FolderName' : 'FileName'}`)}
invalid={!!error}
errorMessage={error}
>
<Input
value={newName}
onChange={(e) => setNewName(e.target.value)}
placeholder={`Enter ${item.type} name`}
placeholder={`Enter ${translate(`::FileManager.${item.type === 'folder' ? 'FolderName' : 'FileName'}`)}`}
autoFocus
/>
</FormItem>
@ -156,7 +157,7 @@ export const RenameItemModal = forwardRef<HTMLDivElement, RenameItemModalProps>(
<div className="flex justify-end space-x-2 pt-4 border-t">
<Button variant="default" onClick={handleClose} disabled={loading}>
Cancel
{translate('::Cancel')}
</Button>
<Button
variant="solid"
@ -164,7 +165,7 @@ export const RenameItemModal = forwardRef<HTMLDivElement, RenameItemModalProps>(
loading={loading}
disabled={!newName.trim()}
>
Rename
{translate('::App.Listform.ListformField.Rename')}
</Button>
</div>
</div>
@ -186,6 +187,7 @@ export interface DeleteConfirmModalProps {
export const DeleteConfirmModal = forwardRef<HTMLDivElement, DeleteConfirmModalProps>(
(props, ref) => {
const { isOpen, onClose, onDelete, items, loading = false } = props
const { translate } = useLocalization()
const handleDelete = async () => {
try {
@ -203,12 +205,14 @@ export const DeleteConfirmModal = forwardRef<HTMLDivElement, DeleteConfirmModalP
<Dialog isOpen={isOpen} onClose={onClose}>
<div ref={ref}>
<div className="flex items-center justify-between pb-4 border-b">
<h3 className="text-lg font-semibold text-red-600">Delete Items</h3>
<h3 className="text-lg font-semibold text-red-600">
{translate('::FileManager.DeleteConfirmationTitle')}
</h3>
</div>
<div className="py-6">
<p className="text-gray-900 dark:text-gray-100 mb-4">
Are you sure you want to delete the following items? This action cannot be undone.
{translate('::FileManager.DeleteConfirmationMessage')}
</p>
<div className="bg-gray-50 dark:bg-gray-700 rounded-lg p-2">
@ -235,7 +239,7 @@ export const DeleteConfirmModal = forwardRef<HTMLDivElement, DeleteConfirmModalP
<div className="flex justify-end space-x-2 pt-4 border-t">
<Button variant="default" onClick={onClose} disabled={loading}>
Cancel
{translate('::Cancel')}
</Button>
<Button
variant="solid"
@ -243,7 +247,7 @@ export const DeleteConfirmModal = forwardRef<HTMLDivElement, DeleteConfirmModalP
onClick={handleDelete}
loading={loading}
>
Delete
{translate('::Delete')}
</Button>
</div>
</div>