mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 10:55:06 +03:00
swap buckets and bucketsV2: make the "show deleted files" view the default
The bucket page at /storage/files/buckets/[bucketId] now shows the "Show deleted files" switch experience (previously only at bucketsV2). The Files/Deleted files tabbed view moves to /storage/files/bucketsV2 instead. Route files are untouched since they import by page path, not by component name. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TiUEvmC84bRsteqsWXHY2p
This commit is contained in:
2 files changed
+304
-304
No files matched your search
@@ -2,7 +2,7 @@ import { useParams } from 'common'
|
||||
import { ChevronDown, FolderOpen, Settings, Shield, Trash2 } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { parseAsBoolean, parseAsStringEnum, useQueryState } from 'nuqs'
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs'
|
||||
import { useEffect } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
@@ -18,8 +18,8 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
NavMenu,
|
||||
NavMenuItem,
|
||||
Label,
|
||||
Switch,
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
@@ -34,24 +34,36 @@ import { useSelectedBucket } from '@/components/interfaces/Storage/FilesBuckets/
|
||||
import { PublicBucketWarning } from '@/components/interfaces/Storage/PublicBucketWarning'
|
||||
import { PUBLIC_BUCKET_TOOLTIP } from '@/components/interfaces/Storage/Storage.constants'
|
||||
import StorageBucketsError from '@/components/interfaces/Storage/StorageBucketsError'
|
||||
import { DeletedFilesProvider } from '@/components/interfaces/Storage/StorageExplorer/DeletedFilesContext'
|
||||
import {
|
||||
DeletedFilesProvider,
|
||||
useDeletedFilesContext,
|
||||
} from '@/components/interfaces/Storage/StorageExplorer/DeletedFilesContext'
|
||||
import { StorageExplorer } from '@/components/interfaces/Storage/StorageExplorer/StorageExplorer'
|
||||
import { useIsStorageProtectionEnabled } from '@/components/interfaces/Storage/StorageProtection.constants'
|
||||
import { Trash } from '@/components/interfaces/Storage/Trash/Trash'
|
||||
import { useBucketPolicyCount } from '@/components/interfaces/Storage/useBucketPolicyCount'
|
||||
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
||||
import StorageLayout from '@/components/layouts/StorageLayout/StorageLayout'
|
||||
import { StorageExplorerStateContextProvider } from '@/state/storage-explorer'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
type BucketView = 'files' | 'trash'
|
||||
const BUCKET_VIEWS: BucketView[] = ['files', 'trash']
|
||||
|
||||
const BucketPage: NextPageWithLayout = () => {
|
||||
const { ref } = useParams()
|
||||
|
||||
return (
|
||||
<StorageExplorerStateContextProvider key={`storage-explorer-state-${ref}`}>
|
||||
<DeletedFilesProvider enabled>
|
||||
<BucketPageInner />
|
||||
</DeletedFilesProvider>
|
||||
</StorageExplorerStateContextProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const BucketPageInner = () => {
|
||||
const router = useRouter()
|
||||
const { bucketId, ref } = useParams()
|
||||
const { data: bucket, error, isSuccess, isError } = useSelectedBucket()
|
||||
|
||||
const { isShowingDeleted, setIsShowingDeleted } = useDeletedFilesContext()
|
||||
|
||||
const [showEditModal, setShowEditModal] = useQueryState(
|
||||
'edit',
|
||||
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
|
||||
@@ -65,16 +77,8 @@ const BucketPage: NextPageWithLayout = () => {
|
||||
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
|
||||
)
|
||||
|
||||
const [view, setView] = useQueryState(
|
||||
'view',
|
||||
parseAsStringEnum<BucketView>(BUCKET_VIEWS)
|
||||
.withDefault('files')
|
||||
.withOptions({ history: 'push', clearOnDefault: true })
|
||||
)
|
||||
|
||||
const { getPolicyCount } = useBucketPolicyCount()
|
||||
const policyCount = bucket ? getPolicyCount(bucket.id) : 0
|
||||
const showProtection = useIsStorageProtectionEnabled()
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && !bucket) {
|
||||
@@ -89,147 +93,138 @@ const BucketPage: NextPageWithLayout = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<StorageExplorerStateContextProvider key={`storage-explorer-state-${ref}`}>
|
||||
<DeletedFilesProvider enabled={false}>
|
||||
<div className="w-full min-h-full flex flex-col items-stretch">
|
||||
<PageBreadcrumbs
|
||||
actions={
|
||||
<PageBreadcrumbsActions>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="tiny"
|
||||
icon={<Shield size={14} />}
|
||||
iconRight={
|
||||
policyCount > 0 ? (
|
||||
<span className="w-4 h-4 bg-surface-200 text-foreground-light text-xs rounded-full flex items-center justify-center font-medium">
|
||||
{policyCount}
|
||||
</span>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<Link
|
||||
href={`/project/${ref}/storage/files/policies?search=${encodeURIComponent(bucket?.name ?? '')}`}
|
||||
>
|
||||
Policies
|
||||
</Link>
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="tiny" iconRight={<ChevronDown size={14} />}>
|
||||
Edit bucket
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-40">
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowEditModal(true)}
|
||||
>
|
||||
<Settings size={12} />
|
||||
<p>Bucket settings</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowEmptyModal(true)}
|
||||
>
|
||||
<FolderOpen size={12} />
|
||||
<p>Empty bucket</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowDeleteModal(true)}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
<p>Delete bucket</p>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</PageBreadcrumbsActions>
|
||||
}
|
||||
>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={`/project/${ref}/storage/files`}>Files</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={`/project/${ref}/storage/files`}>Buckets</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem className="min-w-0">
|
||||
<BreadcrumbPage className="flex min-w-0 items-center gap-2">
|
||||
<span className="truncate">{bucketId}</span>
|
||||
{bucket?.public && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="warning" className="flex shrink-0">
|
||||
Public
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{PUBLIC_BUCKET_TOOLTIP}</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</PageBreadcrumbs>
|
||||
|
||||
{showProtection && (
|
||||
<NavMenu aria-label="Bucket views" className="px-4">
|
||||
<NavMenuItem active={view === 'files'}>
|
||||
<button type="button" onClick={() => setView('files')}>
|
||||
Files
|
||||
</button>
|
||||
</NavMenuItem>
|
||||
<NavMenuItem active={view === 'trash'}>
|
||||
<button type="button" onClick={() => setView('trash')}>
|
||||
Deleted files
|
||||
</button>
|
||||
</NavMenuItem>
|
||||
</NavMenu>
|
||||
)}
|
||||
|
||||
{view === 'files' && (
|
||||
<PageContainer size="full" className="flex flex-1 min-h-0 flex-col px-0 xl:px-0">
|
||||
{ref && bucketId && (
|
||||
<div className="px-4 py-4 empty:hidden">
|
||||
<PublicBucketWarning projectRef={ref} bucketId={bucketId} />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 min-h-0">
|
||||
<StorageExplorer />
|
||||
<>
|
||||
<div className="w-full min-h-full flex flex-col items-stretch">
|
||||
<PageBreadcrumbs
|
||||
actions={
|
||||
<PageBreadcrumbsActions>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
id="show-deleted"
|
||||
size="small"
|
||||
checked={isShowingDeleted}
|
||||
onCheckedChange={setIsShowingDeleted}
|
||||
/>
|
||||
<Label htmlFor="show-deleted" className="text-xs cursor-pointer">
|
||||
Show deleted files
|
||||
</Label>
|
||||
</div>
|
||||
</PageContainer>
|
||||
)}
|
||||
{view === 'trash' && bucket && <Trash bucketId={bucket.name} />}
|
||||
</div>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="tiny"
|
||||
icon={<Shield size={14} />}
|
||||
iconRight={
|
||||
policyCount > 0 ? (
|
||||
<span className="w-4 h-4 bg-surface-200 text-foreground-light text-xs rounded-full flex items-center justify-center font-medium">
|
||||
{policyCount}
|
||||
</span>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<Link
|
||||
href={`/project/${ref}/storage/files/policies?search=${encodeURIComponent(bucket?.name ?? '')}`}
|
||||
>
|
||||
Policies
|
||||
</Link>
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="tiny" iconRight={<ChevronDown size={14} />}>
|
||||
Edit bucket
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-40">
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowEditModal(true)}
|
||||
>
|
||||
<Settings size={12} />
|
||||
<p>Bucket settings</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowEmptyModal(true)}
|
||||
>
|
||||
<FolderOpen size={12} />
|
||||
<p>Empty bucket</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowDeleteModal(true)}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
<p>Delete bucket</p>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</PageBreadcrumbsActions>
|
||||
}
|
||||
>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={`/project/${ref}/storage/files`}>Files</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={`/project/${ref}/storage/files`}>Buckets</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem className="min-w-0">
|
||||
<BreadcrumbPage className="flex min-w-0 items-center gap-2">
|
||||
<span className="truncate">{bucketId}</span>
|
||||
{bucket?.public && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="warning" className="flex shrink-0">
|
||||
Public
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{PUBLIC_BUCKET_TOOLTIP}</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</PageBreadcrumbs>
|
||||
|
||||
{bucket && (
|
||||
<>
|
||||
<EditBucketModal
|
||||
visible={showEditModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowEditModal(false)}
|
||||
/>
|
||||
<EmptyBucketModal
|
||||
visible={showEmptyModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowEmptyModal(false)}
|
||||
/>
|
||||
<DeleteBucketModal
|
||||
visible={showDeleteModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowDeleteModal(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</DeletedFilesProvider>
|
||||
</StorageExplorerStateContextProvider>
|
||||
<PageContainer size="full" className="flex flex-1 min-h-0 flex-col px-0 xl:px-0">
|
||||
{ref && bucketId && (
|
||||
<div className="px-4 py-4 empty:hidden">
|
||||
<PublicBucketWarning projectRef={ref} bucketId={bucketId} />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 min-h-0">
|
||||
<StorageExplorer />
|
||||
</div>
|
||||
</PageContainer>
|
||||
</div>
|
||||
|
||||
{bucket && (
|
||||
<>
|
||||
<EditBucketModal
|
||||
visible={showEditModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowEditModal(false)}
|
||||
/>
|
||||
<EmptyBucketModal
|
||||
visible={showEmptyModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowEmptyModal(false)}
|
||||
/>
|
||||
<DeleteBucketModal
|
||||
visible={showDeleteModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowDeleteModal(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useParams } from 'common'
|
||||
import { ChevronDown, FolderOpen, Settings, Shield, Trash2 } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { parseAsBoolean, useQueryState } from 'nuqs'
|
||||
import { parseAsBoolean, parseAsStringEnum, useQueryState } from 'nuqs'
|
||||
import { useEffect } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import {
|
||||
@@ -18,8 +18,8 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
Label,
|
||||
Switch,
|
||||
NavMenu,
|
||||
NavMenuItem,
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
@@ -34,36 +34,24 @@ import { useSelectedBucket } from '@/components/interfaces/Storage/FilesBuckets/
|
||||
import { PublicBucketWarning } from '@/components/interfaces/Storage/PublicBucketWarning'
|
||||
import { PUBLIC_BUCKET_TOOLTIP } from '@/components/interfaces/Storage/Storage.constants'
|
||||
import StorageBucketsError from '@/components/interfaces/Storage/StorageBucketsError'
|
||||
import {
|
||||
DeletedFilesProvider,
|
||||
useDeletedFilesContext,
|
||||
} from '@/components/interfaces/Storage/StorageExplorer/DeletedFilesContext'
|
||||
import { DeletedFilesProvider } from '@/components/interfaces/Storage/StorageExplorer/DeletedFilesContext'
|
||||
import { StorageExplorer } from '@/components/interfaces/Storage/StorageExplorer/StorageExplorer'
|
||||
import { useIsStorageProtectionEnabled } from '@/components/interfaces/Storage/StorageProtection.constants'
|
||||
import { Trash } from '@/components/interfaces/Storage/Trash/Trash'
|
||||
import { useBucketPolicyCount } from '@/components/interfaces/Storage/useBucketPolicyCount'
|
||||
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
||||
import StorageLayout from '@/components/layouts/StorageLayout/StorageLayout'
|
||||
import { StorageExplorerStateContextProvider } from '@/state/storage-explorer'
|
||||
import type { NextPageWithLayout } from '@/types'
|
||||
|
||||
type BucketView = 'files' | 'trash'
|
||||
const BUCKET_VIEWS: BucketView[] = ['files', 'trash']
|
||||
|
||||
const BucketV2Page: NextPageWithLayout = () => {
|
||||
const { ref } = useParams()
|
||||
|
||||
return (
|
||||
<StorageExplorerStateContextProvider key={`storage-explorer-state-${ref}`}>
|
||||
<DeletedFilesProvider enabled>
|
||||
<BucketV2PageInner />
|
||||
</DeletedFilesProvider>
|
||||
</StorageExplorerStateContextProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const BucketV2PageInner = () => {
|
||||
const router = useRouter()
|
||||
const { bucketId, ref } = useParams()
|
||||
const { data: bucket, error, isSuccess, isError } = useSelectedBucket()
|
||||
|
||||
const { isShowingDeleted, setIsShowingDeleted } = useDeletedFilesContext()
|
||||
|
||||
const [showEditModal, setShowEditModal] = useQueryState(
|
||||
'edit',
|
||||
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
|
||||
@@ -77,8 +65,16 @@ const BucketV2PageInner = () => {
|
||||
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
|
||||
)
|
||||
|
||||
const [view, setView] = useQueryState(
|
||||
'view',
|
||||
parseAsStringEnum<BucketView>(BUCKET_VIEWS)
|
||||
.withDefault('files')
|
||||
.withOptions({ history: 'push', clearOnDefault: true })
|
||||
)
|
||||
|
||||
const { getPolicyCount } = useBucketPolicyCount()
|
||||
const policyCount = bucket ? getPolicyCount(bucket.id) : 0
|
||||
const showProtection = useIsStorageProtectionEnabled()
|
||||
|
||||
useEffect(() => {
|
||||
if (isSuccess && !bucket) {
|
||||
@@ -93,138 +89,147 @@ const BucketV2PageInner = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full min-h-full flex flex-col items-stretch">
|
||||
<PageBreadcrumbs
|
||||
actions={
|
||||
<PageBreadcrumbsActions>
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
id="show-deleted"
|
||||
size="small"
|
||||
checked={isShowingDeleted}
|
||||
onCheckedChange={setIsShowingDeleted}
|
||||
/>
|
||||
<Label htmlFor="show-deleted" className="text-xs cursor-pointer">
|
||||
Show deleted files
|
||||
</Label>
|
||||
</div>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="tiny"
|
||||
icon={<Shield size={14} />}
|
||||
iconRight={
|
||||
policyCount > 0 ? (
|
||||
<span className="w-4 h-4 bg-surface-200 text-foreground-light text-xs rounded-full flex items-center justify-center font-medium">
|
||||
{policyCount}
|
||||
</span>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<Link
|
||||
href={`/project/${ref}/storage/files/policies?search=${encodeURIComponent(bucket?.name ?? '')}`}
|
||||
<StorageExplorerStateContextProvider key={`storage-explorer-state-${ref}`}>
|
||||
<DeletedFilesProvider enabled={false}>
|
||||
<div className="w-full min-h-full flex flex-col items-stretch">
|
||||
<PageBreadcrumbs
|
||||
actions={
|
||||
<PageBreadcrumbsActions>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="tiny"
|
||||
icon={<Shield size={14} />}
|
||||
iconRight={
|
||||
policyCount > 0 ? (
|
||||
<span className="w-4 h-4 bg-surface-200 text-foreground-light text-xs rounded-full flex items-center justify-center font-medium">
|
||||
{policyCount}
|
||||
</span>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
Policies
|
||||
</Link>
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="tiny" iconRight={<ChevronDown size={14} />}>
|
||||
Edit bucket
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-40">
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowEditModal(true)}
|
||||
<Link
|
||||
href={`/project/${ref}/storage/files/policies?search=${encodeURIComponent(bucket?.name ?? '')}`}
|
||||
>
|
||||
<Settings size={12} />
|
||||
<p>Bucket settings</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowEmptyModal(true)}
|
||||
>
|
||||
<FolderOpen size={12} />
|
||||
<p>Empty bucket</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowDeleteModal(true)}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
<p>Delete bucket</p>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</PageBreadcrumbsActions>
|
||||
}
|
||||
>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={`/project/${ref}/storage/files`}>Files</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={`/project/${ref}/storage/files`}>Buckets</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem className="min-w-0">
|
||||
<BreadcrumbPage className="flex min-w-0 items-center gap-2">
|
||||
<span className="truncate">{bucketId}</span>
|
||||
{bucket?.public && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="warning" className="flex shrink-0">
|
||||
Public
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{PUBLIC_BUCKET_TOOLTIP}</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</PageBreadcrumbs>
|
||||
Policies
|
||||
</Link>
|
||||
</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="tiny" iconRight={<ChevronDown size={14} />}>
|
||||
Edit bucket
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-40">
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowEditModal(true)}
|
||||
>
|
||||
<Settings size={12} />
|
||||
<p>Bucket settings</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowEmptyModal(true)}
|
||||
>
|
||||
<FolderOpen size={12} />
|
||||
<p>Empty bucket</p>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="flex items-center space-x-2"
|
||||
onClick={() => setShowDeleteModal(true)}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
<p>Delete bucket</p>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</PageBreadcrumbsActions>
|
||||
}
|
||||
>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={`/project/${ref}/storage/files`}>Files</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink asChild>
|
||||
<Link href={`/project/${ref}/storage/files`}>Buckets</Link>
|
||||
</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem className="min-w-0">
|
||||
<BreadcrumbPage className="flex min-w-0 items-center gap-2">
|
||||
<span className="truncate">{bucketId}</span>
|
||||
{bucket?.public && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant="warning" className="flex shrink-0">
|
||||
Public
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{PUBLIC_BUCKET_TOOLTIP}</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</PageBreadcrumbs>
|
||||
|
||||
<PageContainer size="full" className="flex flex-1 min-h-0 flex-col px-0 xl:px-0">
|
||||
{ref && bucketId && (
|
||||
<div className="px-4 py-4 empty:hidden">
|
||||
<PublicBucketWarning projectRef={ref} bucketId={bucketId} />
|
||||
</div>
|
||||
{showProtection && (
|
||||
<NavMenu aria-label="Bucket views" className="px-4">
|
||||
<NavMenuItem active={view === 'files'}>
|
||||
<button type="button" onClick={() => setView('files')}>
|
||||
Files
|
||||
</button>
|
||||
</NavMenuItem>
|
||||
<NavMenuItem active={view === 'trash'}>
|
||||
<button type="button" onClick={() => setView('trash')}>
|
||||
Deleted files
|
||||
</button>
|
||||
</NavMenuItem>
|
||||
</NavMenu>
|
||||
)}
|
||||
<div className="flex-1 min-h-0">
|
||||
<StorageExplorer />
|
||||
</div>
|
||||
</PageContainer>
|
||||
</div>
|
||||
|
||||
{bucket && (
|
||||
<>
|
||||
<EditBucketModal
|
||||
visible={showEditModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowEditModal(false)}
|
||||
/>
|
||||
<EmptyBucketModal
|
||||
visible={showEmptyModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowEmptyModal(false)}
|
||||
/>
|
||||
<DeleteBucketModal
|
||||
visible={showDeleteModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowDeleteModal(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
{view === 'files' && (
|
||||
<PageContainer size="full" className="flex flex-1 min-h-0 flex-col px-0 xl:px-0">
|
||||
{ref && bucketId && (
|
||||
<div className="px-4 py-4 empty:hidden">
|
||||
<PublicBucketWarning projectRef={ref} bucketId={bucketId} />
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 min-h-0">
|
||||
<StorageExplorer />
|
||||
</div>
|
||||
</PageContainer>
|
||||
)}
|
||||
{view === 'trash' && bucket && <Trash bucketId={bucket.name} />}
|
||||
</div>
|
||||
|
||||
{bucket && (
|
||||
<>
|
||||
<EditBucketModal
|
||||
visible={showEditModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowEditModal(false)}
|
||||
/>
|
||||
<EmptyBucketModal
|
||||
visible={showEmptyModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowEmptyModal(false)}
|
||||
/>
|
||||
<DeleteBucketModal
|
||||
visible={showDeleteModal}
|
||||
bucket={bucket}
|
||||
onClose={() => setShowDeleteModal(false)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</DeletedFilesProvider>
|
||||
</StorageExplorerStateContextProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user