mirror of
https://github.com/supabase/supabase.git
synced 2026-05-31 01:42:45 +08:00
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>
193 lines
6.9 KiB
TypeScript
193 lines
6.9 KiB
TypeScript
import { useParams } from 'common'
|
|
import dayjs from 'dayjs'
|
|
import { RefreshCw, StopCircle } from 'lucide-react'
|
|
import { useEffect, useState } from 'react'
|
|
import { toast } from 'sonner'
|
|
import {
|
|
Button,
|
|
cn,
|
|
Sheet,
|
|
SheetContent,
|
|
SheetDescription,
|
|
SheetHeader,
|
|
SheetSection,
|
|
SheetTitle,
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from 'ui'
|
|
import { CodeBlock } from 'ui-patterns/CodeBlock'
|
|
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
|
|
|
import AlertError from '@/components/ui/AlertError'
|
|
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
|
|
import { useQueryAbortMutation } from '@/data/sql/abort-query-mutation'
|
|
import { useOngoingQueriesQuery } from '@/data/sql/ongoing-queries-query'
|
|
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
|
|
import { useUrlState } from '@/hooks/ui/useUrlState'
|
|
import { IS_PLATFORM } from '@/lib/constants'
|
|
import { useAppStateSnapshot } from '@/state/app-state'
|
|
import { useDatabaseSelectorStateSnapshot } from '@/state/database-selector'
|
|
import type { ResponseError } from '@/types'
|
|
|
|
export const OngoingQueriesPanel = () => {
|
|
const [_, setParams] = useUrlState({ replace: true })
|
|
const { viewOngoingQueries } = useParams()
|
|
const { data: project } = useSelectedProjectQuery()
|
|
const state = useDatabaseSelectorStateSnapshot()
|
|
const appState = useAppStateSnapshot()
|
|
const [selectedId, setSelectedId] = useState<number>()
|
|
|
|
const { data: databases } = useReadReplicasQuery({ projectRef: project?.ref })
|
|
const database = (databases ?? []).find((db) => db.identifier === state.selectedDatabaseId)
|
|
|
|
const {
|
|
data,
|
|
error,
|
|
isError,
|
|
isPending: isLoadingOngoingQueries,
|
|
isFetching: isFetchingOngoingQueries,
|
|
refetch,
|
|
} = useOngoingQueriesQuery(
|
|
{
|
|
projectRef: project?.ref,
|
|
connectionString: database?.connectionString,
|
|
},
|
|
{
|
|
enabled: !IS_PLATFORM || (IS_PLATFORM && database?.connectionString !== undefined),
|
|
staleTime: 5000,
|
|
}
|
|
)
|
|
const queries = data ?? []
|
|
|
|
useEffect(() => {
|
|
if (viewOngoingQueries) {
|
|
appState.setOnGoingQueriesPanelOpen(true)
|
|
setParams({ viewOngoingQueries: undefined })
|
|
}
|
|
}, [viewOngoingQueries])
|
|
|
|
const { mutate: abortQuery, isPending } = useQueryAbortMutation({
|
|
onSuccess: () => {
|
|
toast.success(`Successfully aborted query (ID: ${selectedId})`)
|
|
setSelectedId(undefined)
|
|
},
|
|
})
|
|
|
|
const closePanel = () => {
|
|
setParams({ viewOngoingQueries: undefined })
|
|
appState.setOnGoingQueriesPanelOpen(false)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Sheet open={appState.ongoingQueriesPanelOpen} onOpenChange={() => closePanel()}>
|
|
<SheetContent size="lg">
|
|
<SheetHeader>
|
|
<SheetTitle className="flex items-center gap-x-2">
|
|
Running queries on{' '}
|
|
{database?.identifier === project?.ref ? 'primary database' : 'read replica'}
|
|
<Button
|
|
type="default"
|
|
className="px-1.5"
|
|
loading={isLoadingOngoingQueries || isFetchingOngoingQueries}
|
|
icon={<RefreshCw />}
|
|
onClick={() => refetch()}
|
|
/>
|
|
</SheetTitle>
|
|
<SheetDescription>
|
|
There {queries.length === 1 ? 'is' : 'are'}{' '}
|
|
<span className="text-foreground-light">{queries.length}</span> quer
|
|
{queries.length === 1 ? 'y' : 'ies'} currently running{' '}
|
|
{database?.identifier !== project?.ref ? `on replica ${database?.identifier}` : ''}
|
|
</SheetDescription>
|
|
</SheetHeader>
|
|
<div className="max-h-full h-full divide-y overflow-y-auto">
|
|
{isError && (
|
|
<div className="flex items-center justify-center h-full px-16">
|
|
<AlertError
|
|
subject="Failed to retrieve ongoing queries"
|
|
error={error as ResponseError}
|
|
/>
|
|
</div>
|
|
)}
|
|
{queries.length === 0 && (
|
|
<div className="flex flex-col gap-y-2 items-center justify-center h-full text-foreground-light text-sm">
|
|
<span>
|
|
No queries are currently running on the{' '}
|
|
{database?.identifier !== project?.ref
|
|
? `read replica ${database?.identifier}`
|
|
: (databases ?? []).length > 1
|
|
? 'primary database'
|
|
: 'database'}
|
|
</span>
|
|
<Button
|
|
type="default"
|
|
loading={isLoadingOngoingQueries || isFetchingOngoingQueries}
|
|
icon={<RefreshCw />}
|
|
onClick={() => refetch()}
|
|
>
|
|
Refresh
|
|
</Button>
|
|
</div>
|
|
)}
|
|
{queries.map((query) => (
|
|
<SheetSection key={query.pid} className="flex justify-between gap-x-4">
|
|
<div className="flex flex-col gap-y-2 w-full">
|
|
<CodeBlock
|
|
hideLineNumbers
|
|
value={query.query}
|
|
language="sql"
|
|
className={cn(
|
|
'max-w-none max-h-52 w-full',
|
|
'bg-transparent! py-3! px-3.5! prose dark:prose-dark',
|
|
'[&>code]:m-0 [&>code>span]:flex [&>code>span]:flex-wrap'
|
|
)}
|
|
/>
|
|
<div className="flex items-center gap-x-2">
|
|
<p className="text-foreground-light text-xs">PID: {query.pid}</p>
|
|
<p className="text-foreground-light text-xs">•</p>
|
|
<p className="text-foreground-light text-xs">
|
|
Started since: {dayjs(query.query_start).format('DD MMM YYYY HH:mm (ZZ)')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Tooltip>
|
|
<TooltipTrigger asChild>
|
|
<Button
|
|
type="warning"
|
|
className="px-1.5"
|
|
icon={<StopCircle />}
|
|
onClick={() => setSelectedId(query.pid)}
|
|
/>
|
|
</TooltipTrigger>
|
|
<TooltipContent side="bottom">Abort query</TooltipContent>
|
|
</Tooltip>
|
|
</SheetSection>
|
|
))}
|
|
</div>
|
|
</SheetContent>
|
|
</Sheet>
|
|
|
|
<ConfirmationModal
|
|
loading={isPending}
|
|
variant="warning"
|
|
title={`Confirm to abort this query? (ID: ${selectedId})`}
|
|
visible={selectedId !== undefined}
|
|
onCancel={() => setSelectedId(undefined)}
|
|
onConfirm={() => {
|
|
if (selectedId !== undefined)
|
|
abortQuery({
|
|
pid: selectedId,
|
|
projectRef: project?.ref,
|
|
connectionString: database?.connectionString,
|
|
})
|
|
}}
|
|
>
|
|
<p className="text-sm">This will force the query to stop running.</p>
|
|
</ConfirmationModal>
|
|
</>
|
|
)
|
|
}
|