FileManager Seeder
This commit is contained in:
parent
bea5aabffa
commit
ef201fda13
6 changed files with 472 additions and 113 deletions
|
|
@ -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)"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
]}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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'ı açılabilir
|
||||
toast.push(
|
||||
<Notification type="info">
|
||||
Resim önizleme özelliği yakında eklenecek
|
||||
Image preview feature will be added soon.
|
||||
</Notification>,
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue