mirror of
https://github.com/supabase/supabase.git
synced 2026-10-13 13:19:09 +03:00
* specialized dialog for analytics (and vector) bucket creation * handle missing wrapper extension and iceberg wrapper integration * bucket creation improvements * extension and integration checks * dialog header consistency * simplify bucket dialog creation button trigger * fix subtitle ternary * example bucket without dynamic route * create dynamic segment * fancy connection table * improve bucket page * connection table polish * polish * delete bucket modal * improve missing installations design * new table modal * fix copy icon * copy all env button placeholder * list all analytics buckets * specialised bucket modal * shared installer hooks and components * make iceberg wrapper not dependent on wrapper extension There are cases where the Iceberg Wrapper can be installed but the Wrapper extension has since been removed * better admonition copywriting * todo * remove later features * more removal of later work * remove old comment * remove unused content * Nits and hook up create analytics bucket logic * Nit --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
176 lines
6.5 KiB
TypeScript
176 lines
6.5 KiB
TypeScript
import { MoreVertical, Search, Trash2 } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { useRouter } from 'next/router'
|
|
import { useState } from 'react'
|
|
|
|
import { useParams } from 'common'
|
|
import { ScaffoldHeader, ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold'
|
|
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
|
import { Bucket, useBucketsQuery } from 'data/storage/buckets-query'
|
|
import {
|
|
Button,
|
|
Card,
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuTrigger,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from 'ui'
|
|
import { TimestampInfo } from 'ui-patterns'
|
|
import { Input } from 'ui-patterns/DataInputs/Input'
|
|
import { CreateSpecializedBucketModal } from './CreateSpecializedBucketModal'
|
|
import { DeleteBucketModal } from './DeleteBucketModal'
|
|
import { EmptyBucketState } from './EmptyBucketState'
|
|
|
|
export const AnalyticsBuckets = () => {
|
|
const router = useRouter()
|
|
const { ref } = useParams()
|
|
|
|
const [modal, setModal] = useState<'edit' | 'empty' | 'delete' | null>(null)
|
|
const [selectedBucket, setSelectedBucket] = useState<Bucket>()
|
|
const [filterString, setFilterString] = useState('')
|
|
|
|
const { data: buckets = [], isLoading: isLoadingBuckets } = useBucketsQuery({ projectRef: ref })
|
|
|
|
const analyticsBuckets = buckets
|
|
.filter((bucket) => !('type' in bucket) || bucket.type === 'ANALYTICS')
|
|
.filter((bucket) =>
|
|
filterString.length === 0
|
|
? true
|
|
: bucket.name.toLowerCase().includes(filterString.toLowerCase())
|
|
)
|
|
|
|
return (
|
|
<>
|
|
{!isLoadingBuckets &&
|
|
buckets.filter((bucket) => !('type' in bucket) || bucket.type === 'ANALYTICS').length ===
|
|
0 ? (
|
|
<EmptyBucketState bucketType="analytics" />
|
|
) : (
|
|
// Override the default first:pt-12 to match other storage types
|
|
<ScaffoldSection isFullWidth className="gap-y-4 first:pt-8">
|
|
<ScaffoldHeader className="py-0">
|
|
<ScaffoldSectionTitle>Buckets</ScaffoldSectionTitle>
|
|
</ScaffoldHeader>
|
|
<div className="flex flex-grow justify-between gap-x-2 items-center">
|
|
<Input
|
|
size="tiny"
|
|
className="flex-grow lg:flex-grow-0 w-52"
|
|
placeholder="Search for a bucket"
|
|
value={filterString}
|
|
onChange={(e) => setFilterString(e.target.value)}
|
|
icon={<Search size={12} />}
|
|
/>
|
|
<CreateSpecializedBucketModal
|
|
buttonType="primary"
|
|
buttonClassName="w-fit"
|
|
bucketType="analytics"
|
|
/>
|
|
</div>
|
|
|
|
{isLoadingBuckets ? (
|
|
<GenericSkeletonLoader />
|
|
) : (
|
|
<Card>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Name</TableHead>
|
|
<TableHead>Created at</TableHead>
|
|
<TableHead />
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{analyticsBuckets.length === 0 && filterString.length > 0 && (
|
|
<TableRow>
|
|
<TableCell colSpan={3}>
|
|
<p className="text-sm text-foreground">No results found</p>
|
|
<p className="text-sm text-foreground-light">
|
|
Your search for "{filterString}" did not return any results
|
|
</p>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{analyticsBuckets.map((bucket) => (
|
|
<TableRow
|
|
key={bucket.id}
|
|
className="cursor-pointer"
|
|
onClick={(event) => {
|
|
const url = `/project/${ref}/storage/analytics/buckets/${bucket.id}`
|
|
if (event.metaKey) window.open(url, '_blank')
|
|
else router.push(url)
|
|
}}
|
|
>
|
|
<TableCell>
|
|
<p className="text-foreground">{bucket.name}</p>
|
|
</TableCell>
|
|
|
|
<TableCell>
|
|
<p className="text-foreground-light">
|
|
<TimestampInfo
|
|
utcTimestamp={bucket.created_at}
|
|
className="text-sm text-foreground-light"
|
|
/>
|
|
</p>
|
|
</TableCell>
|
|
|
|
<TableCell>
|
|
<div className="flex justify-end gap-2">
|
|
<Button asChild type="default">
|
|
<Link
|
|
href={`/project/${ref}/storage/analytics/buckets/${encodeURIComponent(bucket.id)}`}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
View contents
|
|
</Link>
|
|
</Button>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
type="default"
|
|
className="px-1"
|
|
icon={<MoreVertical />}
|
|
onClick={(e) => e.stopPropagation()}
|
|
/>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent side="bottom" align="end" className="w-40">
|
|
<DropdownMenuItem
|
|
className="flex items-center space-x-2"
|
|
onClick={(e) => {
|
|
e.stopPropagation()
|
|
setModal('delete')
|
|
setSelectedBucket(bucket)
|
|
}}
|
|
>
|
|
<Trash2 size={12} />
|
|
<p>Delete bucket</p>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</Card>
|
|
)}
|
|
</ScaffoldSection>
|
|
)}
|
|
|
|
{selectedBucket && (
|
|
<DeleteBucketModal
|
|
visible={modal === `delete`}
|
|
bucket={selectedBucket}
|
|
onClose={() => setModal(null)}
|
|
/>
|
|
)}
|
|
</>
|
|
)
|
|
}
|