bottom-end

This commit is contained in:
Sedat ÖZTÜRK 2026-07-08 15:34:13 +03:00
parent f4d2be65e5
commit 9cd49f3f66
12 changed files with 254 additions and 183 deletions

View file

@ -227,6 +227,9 @@ export class SignalRService {
title="⚠️ Bağlantı yok. Sınıfa katılmadan önce bağlantıyı kontrol edin." title="⚠️ Bağlantı yok. Sınıfa katılmadan önce bağlantıyı kontrol edin."
type="warning" type="warning"
/>, />,
{
placement: 'bottom-end',
},
) )
return return
} }

View file

@ -96,6 +96,7 @@ export class WebRTCService {
title={`⚠️ Bağlantı başarısız, yeniden deneniyor (${retries + 1}/${this.maxRetries})`} title={`⚠️ Bağlantı başarısız, yeniden deneniyor (${retries + 1}/${this.maxRetries})`}
type="warning" type="warning"
/>, />,
{ placement: 'bottom-end' },
) )
this.retryCounts.set(userId, retries + 1) this.retryCounts.set(userId, retries + 1)
await this.restartIce(peerConnection, userId) await this.restartIce(peerConnection, userId)
@ -122,6 +123,7 @@ export class WebRTCService {
title={`⚠️ ICE candidate eklenemedi. Kullanıcı: ${userId}`} title={`⚠️ ICE candidate eklenemedi. Kullanıcı: ${userId}`}
type="warning" type="warning"
/>, />,
{ placement: 'bottom-end' },
) )
} }
} }
@ -201,6 +203,7 @@ export class WebRTCService {
title={`⚠️ ICE candidate eklenemedi. Kullanıcı: ${userId}`} title={`⚠️ ICE candidate eklenemedi. Kullanıcı: ${userId}`}
type="warning" type="warning"
/>, />,
{ placement: 'bottom-end' },
) )
} }
} else { } else {

View file

@ -139,7 +139,9 @@ const FileManager = () => {
setItems(items) setItems(items)
} catch (error) { } catch (error) {
console.error('Failed to fetch items:', error) console.error('Failed to fetch items:', error)
toast.push(<Notification type="danger">Failed to load files and folders</Notification>) toast.push(<Notification type="danger">Failed to load files and folders</Notification>, {
placement: 'bottom-end',
})
} finally { } finally {
setLoading(false) setLoading(false)
} }
@ -288,10 +290,14 @@ const FileManager = () => {
await fileManagementService.uploadFileDirectly(formData, selectedTenant?.id) await fileManagementService.uploadFileDirectly(formData, selectedTenant?.id)
} }
await fetchItems(currentFolderId) await fetchItems(currentFolderId)
toast.push(<Notification type="success">Files uploaded successfully</Notification>) toast.push(<Notification type="success">Files uploaded successfully</Notification>, {
placement: 'bottom-end',
})
} catch (error) { } catch (error) {
console.error('Upload failed:', error) console.error('Upload failed:', error)
toast.push(<Notification type="danger">Failed to upload files</Notification>) toast.push(<Notification type="danger">Failed to upload files</Notification>, {
placement: 'bottom-end',
})
throw error throw error
} finally { } finally {
setUploading(false) setUploading(false)
@ -309,10 +315,14 @@ const FileManager = () => {
selectedTenant?.id, selectedTenant?.id,
) )
await fetchItems(currentFolderId) await fetchItems(currentFolderId)
toast.push(<Notification type="success">Folder created successfully</Notification>) toast.push(<Notification type="success">Folder created successfully</Notification>, {
placement: 'bottom-end',
})
} catch (error) { } catch (error) {
console.error('Create folder failed:', error) console.error('Create folder failed:', error)
toast.push(<Notification type="danger">Failed to create folder</Notification>) toast.push(<Notification type="danger">Failed to create folder</Notification>, {
placement: 'bottom-end',
})
throw error throw error
} finally { } finally {
setCreating(false) setCreating(false)
@ -332,10 +342,14 @@ const FileManager = () => {
selectedTenant?.id, selectedTenant?.id,
) )
await fetchItems(currentFolderId) await fetchItems(currentFolderId)
toast.push(<Notification type="success">Item renamed successfully</Notification>) toast.push(<Notification type="success">Item renamed successfully</Notification>, {
placement: 'bottom-end',
})
} catch (error) { } catch (error) {
console.error('Rename failed:', error) console.error('Rename failed:', error)
toast.push(<Notification type="danger">Failed to rename item</Notification>) toast.push(<Notification type="danger">Failed to rename item</Notification>, {
placement: 'bottom-end',
})
throw error throw error
} finally { } finally {
setRenaming(false) setRenaming(false)
@ -357,10 +371,14 @@ const FileManager = () => {
await fetchItems(currentFolderId) await fetchItems(currentFolderId)
setSelectedItems([]) setSelectedItems([])
toast.push(<Notification type="success">Items deleted successfully</Notification>) toast.push(<Notification type="success">Items deleted successfully</Notification>, {
placement: 'bottom-end',
})
} catch (error) { } catch (error) {
console.error('Delete failed:', error) console.error('Delete failed:', error)
toast.push(<Notification type="danger">Failed to delete items</Notification>) toast.push(<Notification type="danger">Failed to delete items</Notification>, {
placement: 'bottom-end',
})
throw error throw error
} finally { } finally {
setDeleting(false) setDeleting(false)
@ -399,6 +417,7 @@ const FileManager = () => {
<Notification title="Warning" type="warning"> <Notification title="Warning" type="warning">
Protected system folders cannot be deleted. Protected system folders cannot be deleted.
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
return return
} }
@ -411,6 +430,7 @@ const FileManager = () => {
<Notification title="Security Warning" type="warning"> <Notification title="Security Warning" type="warning">
Folder {item.name} contains files and cannot be deleted for security reasons. Folder {item.name} contains files and cannot be deleted for security reasons.
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
return return
} }
@ -550,6 +570,7 @@ const FileManager = () => {
{protectedItems.length} protected system folder(s) cannot be deleted:{' '} {protectedItems.length} protected system folder(s) cannot be deleted:{' '}
{protectedItems.map((i) => i.name).join(', ')} {protectedItems.map((i) => i.name).join(', ')}
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
@ -559,6 +580,7 @@ const FileManager = () => {
{foldersWithFiles.length} folder(s) containing files cannot be deleted for security {foldersWithFiles.length} folder(s) containing files cannot be deleted for security
reasons: {foldersWithFiles.join(', ')} reasons: {foldersWithFiles.join(', ')}
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
@ -573,6 +595,7 @@ const FileManager = () => {
<Notification title="Info" type="info"> <Notification title="Info" type="info">
No items can be deleted. Selected items are either protected or folders containing files. No items can be deleted. Selected items are either protected or folders containing files.
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
} }
@ -588,6 +611,7 @@ const FileManager = () => {
{protectedItems.length} protected system folder(s) cannot be copied:{' '} {protectedItems.length} protected system folder(s) cannot be copied:{' '}
{protectedItems.map((i) => i.name).join(', ')} {protectedItems.map((i) => i.name).join(', ')}
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
@ -606,6 +630,7 @@ const FileManager = () => {
<Notification title="Copied" type="success"> <Notification title="Copied" type="success">
{copyableItems.length} item(s) copied to clipboard {copyableItems.length} item(s) copied to clipboard
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
} }
@ -621,6 +646,7 @@ const FileManager = () => {
{protectedItems.length} protected system folder(s) cannot be moved:{' '} {protectedItems.length} protected system folder(s) cannot be moved:{' '}
{protectedItems.map((i) => i.name).join(', ')} {protectedItems.map((i) => i.name).join(', ')}
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
@ -639,6 +665,7 @@ const FileManager = () => {
<Notification title="Cut" type="success"> <Notification title="Cut" type="success">
{cuttableItems.length} item(s) cut to clipboard {cuttableItems.length} item(s) cut to clipboard
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
} }
@ -650,6 +677,7 @@ const FileManager = () => {
<Notification title="Clipboard Empty" type="info"> <Notification title="Clipboard Empty" type="info">
No items in clipboard No items in clipboard
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
return return
} }
@ -667,6 +695,7 @@ const FileManager = () => {
<Notification title={translate('::App.Platform.Success')} type="success"> <Notification title={translate('::App.Platform.Success')} type="success">
{itemIds.length} item(s) copied successfully {itemIds.length} item(s) copied successfully
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} catch (error) { } catch (error) {
console.error('Copy failed:', error) console.error('Copy failed:', error)
@ -674,6 +703,7 @@ const FileManager = () => {
<Notification title={translate('::App.Platform.Error')} type="danger"> <Notification title={translate('::App.Platform.Error')} type="danger">
Failed to copy items Failed to copy items
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} finally { } finally {
setLoading(false) setLoading(false)
@ -685,6 +715,7 @@ const FileManager = () => {
<Notification title="Warning" type="warning"> <Notification title="Warning" type="warning">
Cannot move items to the same folder Cannot move items to the same folder
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
return return
} }
@ -700,6 +731,7 @@ const FileManager = () => {
<Notification title={translate('::App.Platform.Success')} type="success"> <Notification title={translate('::App.Platform.Success')} type="success">
{itemIds.length} item(s) moved successfully {itemIds.length} item(s) moved successfully
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} catch (error) { } catch (error) {
console.error('Move failed:', error) console.error('Move failed:', error)
@ -707,6 +739,7 @@ const FileManager = () => {
<Notification title={translate('::App.Platform.Error')} type="danger"> <Notification title={translate('::App.Platform.Error')} type="danger">
Failed to move items Failed to move items
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} finally { } finally {
setLoading(false) setLoading(false)
@ -717,6 +750,7 @@ const FileManager = () => {
<Notification title="Error" type="danger"> <Notification title="Error" type="danger">
Invalid clipboard data Invalid clipboard data
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
} }
@ -812,6 +846,7 @@ const FileManager = () => {
<Notification title="Warning" type="warning"> <Notification title="Warning" type="warning">
Protected system folders cannot be renamed Protected system folders cannot be renamed
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
} }
@ -841,6 +876,7 @@ const FileManager = () => {
<Notification title="Warning" type="warning"> <Notification title="Warning" type="warning">
Only files can be downloaded Only files can be downloaded
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
} }
@ -856,7 +892,8 @@ const FileManager = () => {
title={translate('::FileManager.DeleteSelectedItems')} title={translate('::FileManager.DeleteSelectedItems')}
onClick={deleteSelectedItems} onClick={deleteSelectedItems}
> >
<span>{translate('::Delete')}</span> {selectedItems.length > 0 && `(${selectedItems.length})`} <span>{translate('::Delete')}</span>{' '}
{selectedItems.length > 0 && `(${selectedItems.length})`}
</Button> </Button>
{/* Selection Actions */} {/* Selection Actions */}
@ -1044,10 +1081,18 @@ const FileManager = () => {
{/* List View Header */} {/* List View Header */}
{viewMode === 'list' && ( {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="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">{translate('::App.Listform.ListformField.Name')}</div> <div className="col-span-5 lg:col-span-5">
<div className="col-span-2 lg:col-span-2">{translate('::App.Listform.ListformField.Type')}</div> {translate('::App.Listform.ListformField.Name')}
<div className="col-span-2 lg:col-span-2">{translate('::App.Listform.ListformField.Size')}</div> </div>
<div className="col-span-2 lg:col-span-2">{translate('::App.Listform.ListformField.Modified')}</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 className="col-span-1"></div> {/* Actions column */}
</div> </div>
)} )}
@ -1100,6 +1145,7 @@ const FileManager = () => {
<Notification title="Cut" type="success"> <Notification title="Cut" type="success">
Item ready to move. Navigate to target folder and paste. Item ready to move. Navigate to target folder and paste.
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} }
}} }}
@ -1115,6 +1161,7 @@ const FileManager = () => {
<Notification type="info"> <Notification type="info">
Image preview feature will be added soon. Image preview feature will be added soon.
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
} else { } else {
// Diğer dosya tipleri için download // Diğer dosya tipleri için download

View file

@ -107,11 +107,15 @@ const getFileTypeLabel = (item: FileItemType, translate: (key: string) => string
if (mimeType?.startsWith('audio/')) return translate('::FileManager.Sound') // 'Ses' if (mimeType?.startsWith('audio/')) return translate('::FileManager.Sound') // 'Ses'
if (mimeType?.includes('pdf')) return translate('::FileManager.PDF') // 'PDF' if (mimeType?.includes('pdf')) return translate('::FileManager.PDF') // 'PDF'
if (mimeType?.includes('word')) return translate('::FileManager.Word') // 'Word' if (mimeType?.includes('word')) return translate('::FileManager.Word') // 'Word'
if (mimeType?.includes('excel') || mimeType?.includes('spreadsheet')) return translate('::FileManager.Excel') // 'Excel' if (mimeType?.includes('excel') || mimeType?.includes('spreadsheet'))
if (mimeType?.includes('powerpoint') || mimeType?.includes('presentation')) return translate('::FileManager.PowerPoint') // 'PowerPoint' return translate('::FileManager.Excel') // 'Excel'
if (['zip', 'rar', '7z', 'tar', 'gz'].includes(extension || '')) return translate('::FileManager.Archive') // 'Arşiv' 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 (['txt', 'md'].includes(extension || '')) return translate('::FileManager.Text') // 'Metin'
if (['json', 'xml', 'css', 'js', 'ts', 'html'].includes(extension || '')) return translate('::FileManager.Code') // 'Kod' if (['json', 'xml', 'css', 'js', 'ts', 'html'].includes(extension || ''))
return translate('::FileManager.Code') // 'Kod'
return extension?.toUpperCase() || translate('::FileManager.File') return extension?.toUpperCase() || translate('::FileManager.File')
} }
@ -134,7 +138,9 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
} = props } = props
const [dropdownOpen, setDropdownOpen] = useState(false) const [dropdownOpen, setDropdownOpen] = useState(false)
const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>(null) const [contextMenuPosition, setContextMenuPosition] = useState<{ x: number; y: number } | null>(
null,
)
const { translate } = useLocalization() const { translate } = useLocalization()
const handleClick = () => { const handleClick = () => {
onSelect?.(item) onSelect?.(item)
@ -217,6 +223,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
<Notification type="success" title="Başarılı"> <Notification type="success" title="Başarılı">
Dosya URL&apos;si panoya kopyalandı Dosya URL&apos;si panoya kopyalandı
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
}, },
() => { () => {
@ -224,6 +231,7 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
<Notification type="danger" title="Hata"> <Notification type="danger" title="Hata">
URL kopyalanamadı URL kopyalanamadı
</Notification>, </Notification>,
{ placement: 'bottom-end' },
) )
}, },
) )
@ -281,10 +289,14 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
const dropdownList = ( const dropdownList = (
<div <div
className="fixed z-50 py-1 min-w-36 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg" className="fixed z-50 py-1 min-w-36 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg"
style={contextMenuPosition ? { style={
left: `${contextMenuPosition.x}px`, contextMenuPosition
top: `${contextMenuPosition.y}px`, ? {
} : undefined} left: `${contextMenuPosition.x}px`,
top: `${contextMenuPosition.y}px`,
}
: undefined
}
> >
{actionMenuItems.map((menuItem) => ( {actionMenuItems.map((menuItem) => (
<div <div
@ -359,83 +371,81 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
onDoubleClick={handleDoubleClick} onDoubleClick={handleDoubleClick}
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
> >
{/* File Name */} {/* File Name */}
<div className="col-span-5 flex items-center min-w-0 gap-2"> <div className="col-span-5 flex items-center min-w-0 gap-2">
{/* Checkbox */} {/* Checkbox */}
{!item.isReadOnly ? ( {!item.isReadOnly ? (
<input <input
type="checkbox" type="checkbox"
checked={selected} checked={selected}
className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" className="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
onClick={handleCheckboxClick} onClick={handleCheckboxClick}
/>
) : (
<div className="w-4 h-4" /> // Boş alan bırak
)}
{/* File Icon or Preview */}
<div className="w-8 h-8">
{item.type === 'file' && item.mimeType?.startsWith('image/') ? (
<ImagePreview
src={FILE_URL(item.path, item.tenantId)}
alt={item.name}
/> />
) : ( ) : (
<div className="w-full h-full flex items-center justify-center"> <div className="w-4 h-4" /> // Boş alan bırak
{getFileIcon(item, false)} )}
</div>
{/* File Icon or Preview */}
<div className="w-8 h-8">
{item.type === 'file' && item.mimeType?.startsWith('image/') ? (
<ImagePreview src={FILE_URL(item.path, item.tenantId)} alt={item.name} />
) : (
<div className="w-full h-full flex items-center justify-center">
{getFileIcon(item, false)}
</div>
)}
</div>
<p
className="text-sm font-semibold text-gray-900 dark:text-gray-100 truncate cursor-pointer hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
title={item.name}
>
{item.name}
</p>
{item.isReadOnly && (
<span className="inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300 flex-shrink-0">
<span className="hidden sm:inline">Protected</span>
<span className="sm:hidden">!</span>
</span>
)} )}
</div> </div>
<p {/* File Type - Hidden on mobile */}
className="text-sm font-semibold text-gray-900 dark:text-gray-100 truncate cursor-pointer hover:text-blue-600 dark:hover:text-blue-400 transition-colors" <div className="hidden sm:flex col-span-2 items-center">
title={item.name}
>
{item.name}
</p>
{item.isReadOnly && (
<span className="inline-flex items-center px-1.5 py-0.5 rounded-full text-xs font-medium bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300 flex-shrink-0">
<span className="hidden sm:inline">Protected</span>
<span className="sm:hidden">!</span>
</span>
)}
</div>
{/* 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, translate)}</span>
</div>
{/* 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"> <span className="text-sm text-gray-500 dark:text-gray-400">
{formatFileSize(item.size)} {getFileTypeLabel(item, translate)}
</span> </span>
) : item.type === 'folder' && typeof item.childCount === 'number' ? ( </div>
{/* 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>
)}
</div>
{/* Modified Date - Hidden on mobile */}
<div className="hidden sm:flex col-span-2 items-center">
<span className="text-sm text-gray-500 dark:text-gray-400"> <span className="text-sm text-gray-500 dark:text-gray-400">
{item.childCount} öğe {formatDate(item.modifiedAt)}
</span> </span>
) : ( </div>
<span className="text-sm text-gray-500 dark:text-gray-400">-</span>
)} {/* Action Menu - Removed */}
<div className="col-span-1 sm:col-span-1 flex items-center justify-end"></div>
</div> </div>
{/* Modified Date - Hidden on mobile */} {/* Context Menu */}
<div className="hidden sm:flex col-span-2 items-center"> {dropdownOpen && contextMenuPosition && dropdownList}
<span className="text-sm text-gray-500 dark:text-gray-400">
{formatDate(item.modifiedAt)}
</span>
</div>
{/* Action Menu - Removed */}
<div className="col-span-1 sm:col-span-1 flex items-center justify-end">
</div>
</div>
{/* Context Menu */}
{dropdownOpen && contextMenuPosition && dropdownList}
</> </>
) )
} }
@ -457,69 +467,68 @@ const FileItem = forwardRef<HTMLDivElement, FileItemProps>((props, ref) => {
onDoubleClick={handleDoubleClick} onDoubleClick={handleDoubleClick}
onContextMenu={handleContextMenu} onContextMenu={handleContextMenu}
> >
{/* Checkbox */} {/* Checkbox */}
{!item.isReadOnly && ( {!item.isReadOnly && (
<div className="absolute top-3 left-3 z-10"> <div className="absolute top-3 left-3 z-10">
<input <input
type="checkbox" type="checkbox"
checked={selected} checked={selected}
className="w-4 h-4 text-blue-600 bg-white border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 shadow-sm" className="w-4 h-4 text-blue-600 bg-white border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 shadow-sm"
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
onClick={handleCheckboxClick} onClick={handleCheckboxClick}
/>
</div>
)}
{/* File/Folder Icon or Preview */}
<div className="flex justify-center mb-3">
{item.type === 'file' && item.mimeType?.startsWith('image/') ? (
<div className="w-16 h-16">
<ImagePreview
src={FILE_URL(item.path, item.tenantId)}
alt={item.name}
/> />
</div> </div>
) : (
<div className="flex justify-center">{getFileIcon(item, true)}</div>
)} )}
</div>
{/* File/Folder Name and Details */} {/* File/Folder Icon or Preview */}
<div className="text-center"> <div className="flex justify-center mb-3">
<div className="flex items-center justify-center gap-1 mb-1"> {item.type === 'file' && item.mimeType?.startsWith('image/') ? (
<p <div className="w-16 h-16">
className="text-sm font-semibold text-gray-900 dark:text-gray-100 truncate cursor-pointer hover:text-blue-600 dark:hover:text-blue-400 transition-colors px-2" <ImagePreview src={FILE_URL(item.path, item.tenantId)} alt={item.name} />
title={item.name} </div>
> ) : (
{item.name} <div className="flex justify-center">{getFileIcon(item, true)}</div>
</p> )}
</div> </div>
{/* File Size and Type */} {/* File/Folder Name and Details */}
{item.type === 'file' && ( <div className="text-center">
<div className="space-y-1"> <div className="flex items-center justify-center gap-1 mb-1">
<p className="text-xs text-gray-500 dark:text-gray-400">{getFileTypeLabel(item, translate)}</p> <p
{item.size && ( className="text-sm font-semibold text-gray-900 dark:text-gray-100 truncate cursor-pointer hover:text-blue-600 dark:hover:text-blue-400 transition-colors px-2"
<p className="text-xs text-gray-500 dark:text-gray-400"> title={item.name}
{formatFileSize(item.size)} >
</p> {item.name}
)} </p>
</div> </div>
)}
{/* Folder Child Count */} {/* File Size and Type */}
{item.type === 'folder' && ( {item.type === 'file' && (
<p className="text-xs text-gray-500 dark:text-gray-400"> <div className="space-y-1">
{typeof item.childCount === 'number' ? `${item.childCount} ${translate('::App.Listform.ListformField.Item')}` : translate('::FileManager.Counting')} <p className="text-xs text-gray-500 dark:text-gray-400">
</p> {getFileTypeLabel(item, translate)}
)} </p>
{item.size && (
<p className="text-xs text-gray-500 dark:text-gray-400">
{formatFileSize(item.size)}
</p>
)}
</div>
)}
{/* Folder Child Count */}
{item.type === 'folder' && (
<p className="text-xs text-gray-500 dark:text-gray-400">
{typeof item.childCount === 'number'
? `${item.childCount} ${translate('::App.Listform.ListformField.Item')}`
: translate('::FileManager.Counting')}
</p>
)}
</div>
</div> </div>
</div>
{/* Context Menu */} {/* Context Menu */}
{dropdownOpen && contextMenuPosition && dropdownList} {dropdownOpen && contextMenuPosition && dropdownList}
</> </>
) )
}) })

View file

@ -894,7 +894,7 @@ function JsonRowOpDialogEditForm({
</Button> </Button>
<div className="flex gap-2"> <div className="flex gap-2">
<Button size="sm" variant="plain" onClick={handleClose}> <Button size="sm" variant="plain" onClick={handleClose}>
{ translate('::Cancel') } {translate('::Cancel')}
</Button> </Button>
<Button size="sm" variant="solid" loading={isSubmitting} type="submit"> <Button size="sm" variant="solid" loading={isSubmitting} type="submit">
{isSubmitting ? translate('::Saving') : translate('::Save')} {isSubmitting ? translate('::Saving') : translate('::Save')}
@ -912,7 +912,9 @@ function JsonRowOpDialogEditForm({
setSubmitting(true) setSubmitting(true)
try { try {
await deleteListFormJsonRow(data.id, data.tabName, values.index) await deleteListFormJsonRow(data.id, data.tabName, values.index)
toast.push(<Notification type="success">Kayıt silindi </Notification>) toast.push(<Notification type="success">Kayıt silindi </Notification>, {
placement: 'bottom-end',
})
handleClose() handleClose()
} catch (error: any) { } catch (error: any) {
toast.push( toast.push(
@ -935,7 +937,7 @@ function JsonRowOpDialogEditForm({
</Dialog.Body> </Dialog.Body>
<Dialog.Footer className="flex justify-end gap-2 border-t pt-3 mt-1"> <Dialog.Footer className="flex justify-end gap-2 border-t pt-3 mt-1">
<Button size="sm" variant="plain" onClick={handleClose}> <Button size="sm" variant="plain" onClick={handleClose}>
{ translate('::Cancel') } {translate('::Cancel')}
</Button> </Button>
<Button size="sm" variant="solid" loading={isSubmitting} type="submit"> <Button size="sm" variant="solid" loading={isSubmitting} type="submit">
{isSubmitting ? 'Deleting' : 'Delete'} {isSubmitting ? 'Deleting' : 'Delete'}

View file

@ -344,7 +344,9 @@ function MenuTreeInline({
onReload() onReload()
setEditingCode(null) setEditingCode(null)
} catch (e: any) { } catch (e: any) {
toast.push(<Notification title={e.message} type="danger" />, { placement: 'bottom-end' }) toast.push(<Notification title={e.message} type="danger" />, {
placement: 'bottom-end',
})
} finally { } finally {
setSaving(false) setSaving(false)
} }
@ -363,7 +365,9 @@ function MenuTreeInline({
await menuService.delete(node.id) await menuService.delete(node.id)
onReload() onReload()
} catch (e: any) { } catch (e: any) {
toast.push(<Notification title={e.message} type="danger" />, { placement: 'bottom-end' }) toast.push(<Notification title={e.message} type="danger" />, {
placement: 'bottom-end',
})
} finally { } finally {
setSaving(false) setSaving(false)
} }

View file

@ -31,7 +31,7 @@ import {
getVideoroomAttandances, getVideoroomAttandances,
getVideoroomById, getVideoroomById,
getVideoroomChats, getVideoroomChats,
endVideoroom endVideoroom,
} from '@/services/videoroom.service' } from '@/services/videoroom.service'
import { showDbDateAsIs } from '@/utils/dateUtils' import { showDbDateAsIs } from '@/utils/dateUtils'
import { useNavigate, useParams } from 'react-router-dom' import { useNavigate, useParams } from 'react-router-dom'
@ -665,6 +665,7 @@ const RoomDetail = () => {
title="⚠️ Mikrofon alınamadı. Sadece ekran paylaşımı yapılacak." title="⚠️ Mikrofon alınamadı. Sadece ekran paylaşımı yapılacak."
type="warning" type="warning"
/>, />,
{ placement: 'bottom-end' },
) )
} }

View file

@ -129,7 +129,7 @@ const SqlQueryManager = () => {
<Notification type="danger" title={translate('::App.Platform.Error')}> <Notification type="danger" title={translate('::App.Platform.Error')}>
{translate('::App.Platform.FailedtoloadDatasources')} {translate('::App.Platform.FailedtoloadDatasources')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} }
} }
@ -551,7 +551,7 @@ GO`,
<Notification type="warning" title={translate('::App.Platform.Warning')}> <Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseSelectDataSource')} {translate('::App.Platform.PleaseSelectDataSource')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -565,7 +565,7 @@ GO`,
<Notification type="warning" title={translate('::App.Platform.Warning')}> <Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseEnterQuery')} {translate('::App.Platform.PleaseEnterQuery')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -614,7 +614,7 @@ GO`,
<Notification type="danger" title={translate('::App.Platform.Error')}> <Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || translate('::App.Platform.FailedToExecuteQuery')} {error.response?.data?.error?.message || translate('::App.Platform.FailedToExecuteQuery')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} }
} }
@ -643,7 +643,7 @@ GO`,
{error.response?.data?.error?.message || {error.response?.data?.error?.message ||
translate('::App.Platform.FailedToLoadDefinition')} translate('::App.Platform.FailedToLoadDefinition')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} }
} }
@ -659,7 +659,7 @@ GO`,
<Notification type="warning" title={translate('::App.Platform.Warning')}> <Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.ScriptNotGenerated') || 'Tablo scripti olusturulamadi.'} {translate('::App.Platform.ScriptNotGenerated') || 'Tablo scripti olusturulamadi.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -676,14 +676,14 @@ GO`,
<Notification type="success" title={translate('::App.Platform.Success') || 'Basarili'}> <Notification type="success" title={translate('::App.Platform.Success') || 'Basarili'}>
{'Script Query Editor alanina yuklendi.'} {'Script Query Editor alanina yuklendi.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} catch (error: any) { } catch (error: any) {
toast.push( toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}> <Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || 'Tablo scripti olusturulurken hata olustu.'} {error.response?.data?.error?.message || 'Tablo scripti olusturulurken hata olustu.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} }
} }
@ -813,7 +813,7 @@ GO`,
{translate('::App.Platform.CopyCompleted') || {translate('::App.Platform.CopyCompleted') ||
`translate('::App.Platform.Successful'): ${successCount}, ${translate('::App.Platform.Error')}: ${errorCount}, ${translate('::App.Platform.Skipped')}: ${skippedCount}`} `translate('::App.Platform.Successful'): ${successCount}, ${translate('::App.Platform.Error')}: ${errorCount}, ${translate('::App.Platform.Skipped')}: ${skippedCount}`}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
setCopyResults(results) setCopyResults(results)
@ -831,7 +831,7 @@ GO`,
<Notification type="warning" title={translate('::App.Platform.Warning')}> <Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseEnterQuery')} {translate('::App.Platform.PleaseEnterQuery')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -841,7 +841,7 @@ GO`,
<Notification type="warning" title={translate('::App.Platform.Warning')}> <Notification type="warning" title={translate('::App.Platform.Warning')}>
{translate('::App.Platform.PleaseSelectAtLeastOneTarget')} {translate('::App.Platform.PleaseSelectAtLeastOneTarget')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -890,7 +890,7 @@ GO`,
{translate('::App.Platform.ExecutionCompleted') || {translate('::App.Platform.ExecutionCompleted') ||
`translate('::App.Platform.Successful'): ${successCount}, ${translate('::App.Platform.Error')}: ${errorCount}`} `translate('::App.Platform.Successful'): ${successCount}, ${translate('::App.Platform.Error')}: ${errorCount}`}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
setCopyResults(results) setCopyResults(results)
@ -946,7 +946,7 @@ GO`,
translate('::App.Platform.FailedToLoadFiles') || translate('::App.Platform.FailedToLoadFiles') ||
'SQL dosya listesi yuklenemedi.'} 'SQL dosya listesi yuklenemedi.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} finally { } finally {
setIsLoadingSqlDataFiles(false) setIsLoadingSqlDataFiles(false)
@ -1014,14 +1014,14 @@ GO`,
<Notification type="success" title={translate('::App.Platform.Success')}> <Notification type="success" title={translate('::App.Platform.Success')}>
{translate('::App.Platform.OperationCompleted') || 'Dosya tasindi.'} {translate('::App.Platform.OperationCompleted') || 'Dosya tasindi.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} catch (error: any) { } catch (error: any) {
toast.push( toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}> <Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || 'Dosya tasinamadi.'} {error.response?.data?.error?.message || 'Dosya tasinamadi.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} finally { } finally {
setIsMovingSqlDataFile(false) setIsMovingSqlDataFile(false)
@ -1050,7 +1050,7 @@ GO`,
<Notification type="danger" title={translate('::App.Platform.Error')}> <Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || error.message || 'Record operation failed.'} {error.response?.data?.error?.message || error.message || 'Record operation failed.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
throw error throw error
} }
@ -1083,14 +1083,14 @@ GO`,
{translate('::App.SqlQueryManager.ScriptLoadedToEditor') || {translate('::App.SqlQueryManager.ScriptLoadedToEditor') ||
'SQL dosyasi Query Editor icine yuklendi.'} 'SQL dosyasi Query Editor icine yuklendi.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} catch (error: any) { } catch (error: any) {
toast.push( toast.push(
<Notification type="danger" title={translate('::App.Platform.Error')}> <Notification type="danger" title={translate('::App.Platform.Error')}>
{error.response?.data?.error?.message || 'SQL dosyasi okunamadi.'} {error.response?.data?.error?.message || 'SQL dosyasi okunamadi.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} finally { } finally {
setIsPreviewingSqlDataFile(false) setIsPreviewingSqlDataFile(false)

View file

@ -1298,7 +1298,7 @@ const SqlTableDesignerDialog = ({
<Notification type="warning" title={translate('::App.Platform.Warning')}> <Notification type="warning" title={translate('::App.Platform.Warning')}>
CREATE TABLE script bulunamadı. Scripti panoya kopyalayıp tekrar deneyin. CREATE TABLE script bulunamadı. Scripti panoya kopyalayıp tekrar deneyin.
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -1309,7 +1309,7 @@ const SqlTableDesignerDialog = ({
<Notification type="warning" title={translate('::App.Platform.Warning')}> <Notification type="warning" title={translate('::App.Platform.Warning')}>
CREATE TABLE scriptinden sütunlar ayrıştırılamadı. CREATE TABLE scriptinden sütunlar ayrıştırılamadı.
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -1348,7 +1348,7 @@ const SqlTableDesignerDialog = ({
? `${addedCount} sütun eklendi${skippedCount > 0 ? `, ${skippedCount} sütun zaten mevcut olduğu için atlandı.` : '.'}` ? `${addedCount} sütun eklendi${skippedCount > 0 ? `, ${skippedCount} sütun zaten mevcut olduğu için atlandı.` : '.'}`
: 'Yeni sütun eklenmedi. Scriptteki sütunların tamamı zaten mevcut.'} : 'Yeni sütun eklenmedi. Scriptteki sütunların tamamı zaten mevcut.'}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} }
@ -1504,7 +1504,7 @@ const SqlTableDesignerDialog = ({
<Notification type="warning" title={translate('::App.SqlQueryManager.Warning')}> <Notification type="warning" title={translate('::App.SqlQueryManager.Warning')}>
Referans kolon PK/UNIQUE olmalı. Lütfen hedef tablodan anahtar bir kolon seçin. Referans kolon PK/UNIQUE olmalı. Lütfen hedef tablodan anahtar bir kolon seçin.
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -1602,7 +1602,7 @@ const SqlTableDesignerDialog = ({
<Notification type="warning" title={translate('::App.SqlQueryManager.Warning')}> <Notification type="warning" title={translate('::App.SqlQueryManager.Warning')}>
{translate('::App.SqlQueryManager.PleaseSelectDataSource')} {translate('::App.SqlQueryManager.PleaseSelectDataSource')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
return return
} }
@ -1625,7 +1625,7 @@ const SqlTableDesignerDialog = ({
<Notification type="success" title={translate('::App.Platform.Success')}> <Notification type="success" title={translate('::App.Platform.Success')}>
{`${translate(isEditMode ? '::App.SqlQueryManager.TableUpdated' : '::App.SqlQueryManager.TableCreated')}: [dbo].[${deployedTable}]`} {`${translate(isEditMode ? '::App.SqlQueryManager.TableUpdated' : '::App.SqlQueryManager.TableCreated')}: [dbo].[${deployedTable}]`}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
// Save seed file to DbMigrator/Seeds/SqlData after successful deploy. // Save seed file to DbMigrator/Seeds/SqlData after successful deploy.
// Always use the full CREATE TABLE script (not the ALTER diff) so the seed file // Always use the full CREATE TABLE script (not the ALTER diff) so the seed file
@ -1649,7 +1649,7 @@ const SqlTableDesignerDialog = ({
<Notification type="danger" title={translate('::App.Platform.Error')}> <Notification type="danger" title={translate('::App.Platform.Error')}>
{result.data.message || translate('::App.SqlQueryManager.TableCreationFailed')} {result.data.message || translate('::App.SqlQueryManager.TableCreationFailed')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} }
} catch (error: any) { } catch (error: any) {
@ -1658,7 +1658,7 @@ const SqlTableDesignerDialog = ({
{error.response?.data?.error?.message || {error.response?.data?.error?.message ||
translate('::App.SqlQueryManager.TableDeployFailed')} translate('::App.SqlQueryManager.TableDeployFailed')}
</Notification>, </Notification>,
{ placement: 'top-center' }, { placement: 'bottom-end' },
) )
} finally { } finally {
setIsDeploying(false) setIsDeploying(false)

View file

@ -261,7 +261,7 @@ const useListFormCustomDataSource = ({
// {'multiValue Error'} // {'multiValue Error'}
// </Notification>, // </Notification>,
// { // {
// placement: 'top-end', // placement: 'bottom-end',
// }, // },
// ) // )
console.log('multiValue Error', e) console.log('multiValue Error', e)
@ -297,7 +297,7 @@ const useListFormCustomDataSource = ({
// {error.toString()} // {error.toString()}
// </Notification>, // </Notification>,
// { // {
// placement: 'top-end', // placement: 'bottom-end',
// }, // },
// ) // )
return null return null
@ -341,7 +341,7 @@ const useListFormCustomDataSource = ({
// {error.toString()} // {error.toString()}
// </Notification>, // </Notification>,
// { // {
// placement: 'top-end', // placement: 'bottom-end',
// }, // },
// ) // )
return null return null
@ -364,7 +364,7 @@ const useListFormCustomDataSource = ({
// {error.toString()} // {error.toString()}
// </Notification>, // </Notification>,
// { // {
// placement: 'top-end', // placement: 'bottom-end',
// }, // },
// ) // )
return null return null

View file

@ -619,7 +619,7 @@ const Contact: React.FC = () => {
{translate('::ListForms.FormBilgileriKaydedildi')} {translate('::ListForms.FormBilgileriKaydedildi')}
</Notification>, </Notification>,
{ {
placement: 'top-center', placement: 'bottom-end',
}, },
) )

View file

@ -208,7 +208,9 @@ export function MenuAddDialog({
}) })
onClose() onClose()
} catch (e: any) { } catch (e: any) {
toast.push(<Notification title={e.message} type="danger" />, { placement: 'bottom-end' }) toast.push(<Notification title={e.message} type="danger" />, {
placement: 'bottom-end',
})
} finally { } finally {
setSaving(false) setSaving(false)
} }