Files
supabase/apps/studio/components/interfaces/LogDrains/LogDrains.tsx
Ivan Vasilov 56de26fe22 chore: Migrate the monorepo to use Tailwind v4 (#45318)
This PR migrates the whole monorepo to use Tailwind v4:
- Removed `@tailwindcss/container-queries` plugin since it's included by
default in v4,
- Bump all instances of Tailwind to v4. Made minimal changes to the
shared config to remove non-supported features (`alpha` mentions),
- Migrate all apps to be compatible with v4 configs,
- Fix the `typography.css` import in 3 apps,
- Add missing rules which were included by default in v3,
- Run `pnpm dlx @tailwindcss/upgrade` on all apps, which renames a lot
of classes
- Rename all misnamed classes according to
https://tailwindcss.com/docs/upgrade-guide#renamed-utilities in all
apps.

---------

Co-authored-by: Jordi Enric <jordi.err@gmail.com>
2026-04-30 10:53:24 +00:00

231 lines
7.8 KiB
TypeScript

import { IS_PLATFORM, useFlag, useParams } from 'common'
import { MoreHorizontal, TrashIcon } from 'lucide-react'
import { cloneElement, useState } from 'react'
import { toast } from 'sonner'
import {
Button,
Card,
cn,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import { LOG_DRAIN_TYPES, LogDrainType } from './LogDrains.constants'
import { LogDrainsCard } from './LogDrainsCard'
import { LogDrainsEmpty } from './LogDrainsEmpty'
import { VoteLink } from './VoteLink'
import AlertError from '@/components/ui/AlertError'
import { useDeleteLogDrainMutation } from '@/data/log-drains/delete-log-drain-mutation'
import { LogDrainData, useLogDrainsQuery } from '@/data/log-drains/log-drains-query'
import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements'
import { useTrack } from '@/lib/telemetry/track'
export function LogDrains({
onNewDrainClick,
onUpdateDrainClick: _onUpdateDrainClick,
}: {
onNewDrainClick: (src: LogDrainType) => void
onUpdateDrainClick: (drain: LogDrainData) => void
}) {
const { hasAccess: hasAccessToLogDrains, isLoading: isLoadingEntitlement } =
useCheckEntitlements('log_drains')
const track = useTrack()
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
const [selectedLogDrain, setSelectedLogDrain] = useState<LogDrainData | null>(null)
const { ref } = useParams()
const {
data: logDrains,
isPending: isLoading,
error,
isError,
} = useLogDrainsQuery(
{ ref },
{
enabled: hasAccessToLogDrains,
}
)
const sentryEnabled = useFlag('SentryLogDrain')
const s3Enabled = useFlag('S3logdrain')
const axiomEnabled = useFlag('axiomLogDrain')
const otlpEnabled = useFlag('otlpLogDrain')
const last9Enabled = useFlag('Last9LogDrain')
const hasLogDrains = !!logDrains?.length
const { mutate: deleteLogDrain } = useDeleteLogDrainMutation({
onSuccess: () => {
setIsDeleteModalOpen(false)
setSelectedLogDrain(null)
},
onError: () => {
setIsDeleteModalOpen(false)
setSelectedLogDrain(null)
toast.error('Failed to delete log drain')
},
})
if (isLoading || isLoadingEntitlement) {
return (
<div>
<GenericSkeletonLoader />
</div>
)
}
if (!isLoadingEntitlement && !hasAccessToLogDrains) {
return <LogDrainsEmpty />
}
if (isError) {
return <AlertError subject="Failed to load log drains" error={error}></AlertError>
}
if (!isLoading && !hasLogDrains) {
return (
<>
<div className="grid lg:grid-cols-3 gap-4">
{LOG_DRAIN_TYPES.filter((t) => {
if (t.value === 'sentry') return sentryEnabled
if (t.value === 's3') return s3Enabled
if (t.value === 'axiom') return axiomEnabled
if (t.value === 'otlp') return otlpEnabled
if (t.value === 'last9') return last9Enabled
return true
}).map((src) => (
<LogDrainsCard
key={src.value}
title={src.name}
description={src.description}
icon={src.icon}
rightLabel={IS_PLATFORM ? 'Additional $60' : undefined}
onClick={() => {
onNewDrainClick(src.value)
}}
/>
))}
</div>
<VoteLink />
</>
)
}
return (
<>
<Card>
<Table>
<TableHeader>
<TableRow>
<TableHead className="max-w-[200px]">Name</TableHead>
<TableHead className="w-96">Description</TableHead>
<TableHead className="w-48">Destination</TableHead>
<TableHead className="text-right">
<div className="sr-only">Actions</div>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{logDrains
?.slice()
.sort((a, b) => b.id - a.id)
.map((drain) => (
<TableRow key={drain.id}>
<TableCell className="font-medium truncate max-w-72" title={drain.name}>
{drain.name}
</TableCell>
<TableCell
className={cn(
'truncate max-w-96',
drain.description ? 'text-foreground-light' : 'text-foreground-muted'
)}
title={drain.description}
>
{drain.description || '-'}
</TableCell>
<TableCell className="text-foreground-light">
<div className="flex items-center gap-2">
{LOG_DRAIN_TYPES.find((t) => t.value === drain.type)?.icon && (
<span className="text-foreground-light">
{cloneElement(LOG_DRAIN_TYPES.find((t) => t.value === drain.type)!.icon, {
height: 16,
width: 16,
})}
</span>
)}
<span className="truncate max-w-40">
{LOG_DRAIN_TYPES.find((t) => t.value === drain.type)?.name ?? drain.type}
</span>
</div>
</TableCell>
<TableCell className="text-right">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
type="text"
className="px-1 opacity-50 hover:opacity-100 bg-transparent! shrink-0"
icon={<MoreHorizontal />}
/>
</DropdownMenuTrigger>
<DropdownMenuContent className="max-w-[140px]" align="end">
{/* Jordi: Updating log drains is disabled temporarily.
<DropdownMenuItem
onClick={() => {
onUpdateDrainClick(drain)
}}
>
<Pencil className="h-4 w-4 mr-2" />
Update
</DropdownMenuItem> */}
<DropdownMenuItem
onClick={() => {
setSelectedLogDrain(drain)
setIsDeleteModalOpen(true)
}}
>
<TrashIcon className="h-4 w-4 mr-2" />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</TableCell>
</TableRow>
))}
</TableBody>
<ConfirmationModal
confirmLabel="Delete"
variant="destructive"
title="Delete Log Drain"
visible={isDeleteModalOpen}
onConfirm={() => {
if (selectedLogDrain && ref) {
deleteLogDrain({ token: selectedLogDrain.token, projectRef: ref })
track('log_drain_confirm_button_submitted', {
destination: selectedLogDrain.type,
})
}
}}
onCancel={() => setIsDeleteModalOpen(false)}
>
<div className="text-foreground-light text-sm">
<p>
Are you sure you want to delete{' '}
<span className="text-foreground">{selectedLogDrain?.name}</span>?
</p>
<p>This action cannot be undone.</p>
</div>
</ConfirmationModal>
</Table>
</Card>
</>
)
}