Files
supabase/apps/studio/components/interfaces/Integrations/Queues/QueuesTab.tsx
Gildas Garcia c6fc456910 chore: cleanup duplicate exports studio (#47387)
## Problem

Knip reports many duplicate exports (both named and default). Besides,
we're moving away from default exports and even have an eslint rule to
enforce it on new code.

## Solution

- Cleanup those exports
- Update imports when necessary

No functional changes. If it builds, it's fine
2026-06-29 15:46:16 +02:00

198 lines
6.7 KiB
TypeScript

import { useParams } from 'common'
import { RefreshCw, Search, X } from 'lucide-react'
import { useRouter } from 'next/router'
import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs'
import { useMemo, useRef, useState } from 'react'
import DataGrid, { Row } from 'react-data-grid'
import { Button, cn, LoadingLine } from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import { CreateQueueSheet } from './CreateQueueSheet'
import { formatQueueColumns, prepareQueuesForDataGrid } from './Queues.utils'
import { AlertError } from '@/components/ui/AlertError'
import { useQueuesQuery } from '@/data/database-queues/database-queues-query'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { onSearchInputEscape } from '@/lib/keyboard'
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
import { useShortcut } from '@/state/shortcuts/useShortcut'
export const QueuesTab = () => {
const router = useRouter()
const { ref } = useParams()
const { data: project } = useSelectedProjectQuery()
const [searchQuery, setSearchQuery] = useQueryState('search', parseAsString.withDefault(''))
const [search, setSearch] = useState(searchQuery)
const [createQueueSheetShown, setCreateQueueSheetShown] = useQueryState(
'new',
parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
)
const searchInputRef = useRef<HTMLInputElement>(null)
const clearSearch = () => {
setSearch('')
setSearchQuery('')
}
useShortcut(
SHORTCUT_IDS.LIST_PAGE_FOCUS_SEARCH,
() => {
searchInputRef.current?.focus()
searchInputRef.current?.select()
},
{ label: 'Search queues' }
)
useShortcut(SHORTCUT_IDS.LIST_PAGE_RESET_FILTERS, clearSearch)
useShortcut(SHORTCUT_IDS.LIST_PAGE_NEW_ITEM, () => setCreateQueueSheetShown(true), {
label: 'Create queue',
})
const {
data: queues,
error,
isPending: isLoading,
isError,
isRefetching,
refetch,
} = useQueuesQuery({
projectRef: project?.ref,
connectionString: project?.connectionString,
})
// Filter queues based on search query
const filteredQueues = useMemo(() => {
if (!queues) return []
if (!searchQuery) return queues
return queues.filter((queue) =>
queue.queue_name.toLowerCase().includes(searchQuery.toLowerCase())
)
}, [queues, searchQuery])
// Prepare queues for DataGrid
const queueData = useMemo(() => prepareQueuesForDataGrid(filteredQueues), [filteredQueues])
// Get columns configuration
const columns = useMemo(() => formatQueueColumns(), [])
return (
<>
<div className="h-full w-full space-y-4">
<div className="h-full w-full flex flex-col relative">
<div className="bg-surface-200 py-3 px-10 flex items-center justify-between flex-wrap">
<Input
ref={searchInputRef}
size="tiny"
className="w-52"
placeholder="Search for a queue"
icon={<Search />}
value={search ?? ''}
onChange={(e) => setSearch(e.target.value)}
onKeyDown={(e) => {
onSearchInputEscape(search ?? '', clearSearch)(e)
if (e.code === 'Enter' || e.code === 'NumpadEnter') setSearchQuery(search.trim())
}}
actions={[
search && (
<Button
key="clear"
size="tiny"
variant="text"
icon={<X />}
onClick={clearSearch}
className="p-0 h-5 w-5"
/>
),
]}
/>
<div className="flex items-center gap-x-2">
<Button
variant="default"
icon={<RefreshCw />}
loading={isRefetching}
onClick={() => refetch()}
>
Refresh
</Button>
<Button onClick={() => setCreateQueueSheetShown(true)}>Create queue</Button>
</div>
</div>
<LoadingLine loading={isLoading || isRefetching} />
<DataGrid
className="grow border-t-0! border-b-0!"
rowHeight={44}
headerRowHeight={36}
columns={columns}
rows={queueData}
rowKeyGetter={(row) => row.id}
rowClass={() => {
return cn(
'cursor-pointer',
'[&>.rdg-cell]:border-box [&>.rdg-cell]:outline-hidden [&>.rdg-cell]:shadow-none',
'[&>.rdg-cell:first-child>div]:ml-8'
)
}}
renderers={{
renderRow(_, props) {
return (
<Row
key={props.row.queue_name}
{...props}
onClick={() => {
const { queue_name } = props.row
const url = `/project/${ref}/integrations/queues/queues/${queue_name}`
router.push(url)
}}
/>
)
},
}}
/>
{/* Render 0 rows state outside of the grid */}
{queueData.length === 0 ? (
isLoading ? (
<div className="absolute top-28 px-10 w-full">
<GenericSkeletonLoader />
</div>
) : isError ? (
<div className="absolute top-28 px-10 flex flex-col items-center justify-center w-full">
<AlertError subject="Failed to retrieve database queues" error={error} />
</div>
) : (
<div className="absolute top-32 px-6 w-full">
<div className="text-center text-sm flex flex-col gap-y-1">
<p className="text-foreground">
{!!searchQuery ? 'No queues found' : 'No queues created yet'}
</p>
<p className="text-foreground-light">
{!!searchQuery
? 'There are currently no queues based on the search applied'
: 'There are currently no queues created yet in your project'}
</p>
</div>
</div>
)
) : null}
<div className="flex justify-between min-h-9 h-9 overflow-hidden items-center px-6 w-full border-t text-xs text-foreground-light">
{`Total: ${queueData.length} queues`}
</div>
</div>
</div>
<CreateQueueSheet
visible={createQueueSheetShown}
onClose={() => {
setCreateQueueSheetShown(false)
}}
/>
</>
)
}