import { PermissionAction } from '@supabase/shared-types/out/constants' import { useQueryClient } from '@tanstack/react-query' import { useParams } from 'common' import { ArrowLeft, Check, ChevronRight, Columns, List, RefreshCw, Search, Upload, X, } from 'lucide-react' import { useRef, useState, type ChangeEvent } from 'react' import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from 'ui' import { Input } from 'ui-patterns/DataInputs/Input' import { STORAGE_SORT_BY, STORAGE_SORT_BY_ORDER, STORAGE_VIEWS } from '../Storage.constants' import { useStoragePreference } from '../StorageExplorer/useStoragePreference' import { uploadFilesToBucket } from './BucketFilePickerDialog.utils' import { useBucketFilePickerStateSnapshot } from './BucketFilePickerState' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { useProjectApiUrl } from '@/data/config/project-endpoint-query' import { storageKeys } from '@/data/storage/keys' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' const VIEW_OPTIONS = [ { key: STORAGE_VIEWS.COLUMNS, name: 'As columns' }, { key: STORAGE_VIEWS.LIST, name: 'As list' }, ] const SORT_BY_OPTIONS = [ { key: STORAGE_SORT_BY.NAME, name: 'Name' }, { key: STORAGE_SORT_BY.CREATED_AT, name: 'Time created' }, { key: STORAGE_SORT_BY.UPDATED_AT, name: 'Time modified' }, { key: STORAGE_SORT_BY.LAST_ACCESSED_AT, name: 'Time last accessed' }, ] const SORT_ORDER_OPTIONS = [ { key: STORAGE_SORT_BY_ORDER.ASC, name: 'Ascending' }, { key: STORAGE_SORT_BY_ORDER.DESC, name: 'Descending' }, ] const HeaderBreadcrumbs = ({ breadcrumbs, selectBreadcrumb, }: { breadcrumbs: string[] selectBreadcrumb: (i: number) => void }) => { // Max 5 crumbs, otherwise replace middle segment with ellipsis and only // have the first 2 and last 2 crumbs visible const ellipsis = '...' const breadcrumbsWithIndexes = breadcrumbs.map((name: string, index: number) => { return { name, index } }) const formattedBreadcrumbs = breadcrumbsWithIndexes.length <= 5 ? breadcrumbsWithIndexes : breadcrumbsWithIndexes .slice(0, 2) .concat([{ name: ellipsis, index: -1 }]) .concat( breadcrumbsWithIndexes.slice( breadcrumbsWithIndexes.length - 2, breadcrumbsWithIndexes.length ) ) return (
{formattedBreadcrumbs.map((crumb, idx: number) => { const isEllipsis = crumb.name === ellipsis const isActive = crumb.index === breadcrumbs.length - 1 return (
{idx !== 0 && ( )} {isEllipsis ? ( {crumb.name} ) : isActive ? ( {crumb.name} ) : ( )}
) })}
) } export const BucketFilePickerHeader = () => { const { ref: projectRef } = useParams() const queryClient = useQueryClient() const [isSearching, setIsSearching] = useState(false) const [isRefreshing, setIsRefreshing] = useState(false) const [isUploading, setIsUploading] = useState(false) const uploadButtonRef = useRef(null) const { hostEndpoint } = useProjectApiUrl({ projectRef: projectRef! }) const { view, sortBy, sortByOrder, setSortBy, setSortByOrder, setView } = useStoragePreference( projectRef! ) const { bucket, columns, itemSearchString, setItemSearchString, popColumn, popColumnAtIndex, setSelectedFilePreview, } = useBucketFilePickerStateSnapshot() const { can: canUpdateStorage } = useAsyncCheckPermissions(PermissionAction.STORAGE_WRITE, '*') const breadcrumbs = columns const backDisabled = columns.length < 1 const onSelectBack = () => { popColumn() setSelectedFilePreview(undefined) } const onSelectUpload = () => { if (uploadButtonRef.current) { uploadButtonRef.current.click() } } const handleFilesUpload = async (event: ChangeEvent) => { if (!hostEndpoint) { console.error('Host endpoint not available') return } const files = Array.from(event.target.files || []) try { setIsUploading(true) await uploadFilesToBucket({ files, projectRef: projectRef!, hostEndpoint, bucketName: bucket.name, bucketId: bucket.id, currentPath: columns.join('/'), queryClient, }) queryClient.invalidateQueries({ queryKey: storageKeys.objects(projectRef!, bucket.id, columns.join('/')), }) } catch (error) { console.error('Failed to upload files:', error) // Consider showing a toast notification to the user } finally { event.target.value = '' setIsUploading(false) } } /** Methods for searching */ const toggleSearch = () => { setIsSearching(true) } const onCancelSearch = () => { setIsSearching(false) setItemSearchString('') } /** Methods for breadcrumbs */ const selectBreadcrumb = (columnIndex: number) => { popColumnAtIndex(columnIndex) } const refreshData = async () => { setIsRefreshing(true) const queryKey = storageKeys.objects(projectRef!, bucket.id, '').filter(Boolean) try { await queryClient.refetchQueries({ queryKey: queryKey, type: 'active' }) } finally { setIsRefreshing(false) } } return (
{/* Navigation */}
{breadcrumbs.length > 0 && ( <> {VIEW_OPTIONS.map((option) => ( setView(option.key)}>

{option.name}

{view === option.key && ( )}
))} Sort by {SORT_BY_OPTIONS.map((option) => ( setSortBy(option.key)}>

{option.name}

{sortBy === option.key && ( )}
))}
Sort order {SORT_ORDER_OPTIONS.map((option) => ( setSortByOrder(option.key)} >

{option.name}

{sortByOrder === option.key && ( )}
))}
} type="text" disabled={!canUpdateStorage} loading={isUploading} onClick={onSelectUpload} tooltip={{ content: { side: 'bottom', text: !canUpdateStorage ? 'You need additional permissions to upload files' : undefined, }, }} > Upload files
{isSearching ? ( } actions={[
) }