QueuesTab.tsx175 lines · main
1import { useParams } from 'common'
2import { RefreshCw, Search, X } from 'lucide-react'
3import { useRouter } from 'next/router'
4import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs'
5import { useMemo, useState } from 'react'
6import DataGrid, { Row } from 'react-data-grid'
7import { Button, cn, LoadingLine } from 'ui'
8import { Input } from 'ui-patterns/DataInputs/Input'
9import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
10
11import { CreateQueueSheet } from './CreateQueueSheet'
12import { formatQueueColumns, prepareQueuesForDataGrid } from './Queues.utils'
13import AlertError from '@/components/ui/AlertError'
14import { useQueuesQuery } from '@/data/database-queues/database-queues-query'
15import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
16
17export const QueuesTab = () => {
18 const router = useRouter()
19 const { ref } = useParams()
20 const { data: project } = useSelectedProjectQuery()
21
22 const [searchQuery, setSearchQuery] = useQueryState('search', parseAsString.withDefault(''))
23 const [search, setSearch] = useState(searchQuery)
24
25 const [createQueueSheetShown, setCreateQueueSheetShown] = useQueryState(
26 'new',
27 parseAsBoolean.withDefault(false).withOptions({ history: 'push', clearOnDefault: true })
28 )
29
30 const {
31 data: queues,
32 error,
33 isPending: isLoading,
34 isError,
35 isRefetching,
36 refetch,
37 } = useQueuesQuery({
38 projectRef: project?.ref,
39 connectionString: project?.connectionString,
40 })
41
42 // Filter queues based on search query
43 const filteredQueues = useMemo(() => {
44 if (!queues) return []
45 if (!searchQuery) return queues
46 return queues.filter((queue) =>
47 queue.queue_name.toLowerCase().includes(searchQuery.toLowerCase())
48 )
49 }, [queues, searchQuery])
50
51 // Prepare queues for DataGrid
52 const queueData = useMemo(() => prepareQueuesForDataGrid(filteredQueues), [filteredQueues])
53
54 // Get columns configuration
55 const columns = useMemo(() => formatQueueColumns(), [])
56
57 return (
58 <>
59 <div className="h-full w-full space-y-4">
60 <div className="h-full w-full flex flex-col relative">
61 <div className="bg-surface-200 py-3 px-10 flex items-center justify-between flex-wrap">
62 <Input
63 size="tiny"
64 className="w-52"
65 placeholder="Search for a queue"
66 icon={<Search />}
67 value={search ?? ''}
68 onChange={(e) => setSearch(e.target.value)}
69 onKeyDown={(e) => {
70 if (e.code === 'Enter' || e.code === 'NumpadEnter') setSearchQuery(search.trim())
71 }}
72 actions={[
73 search && (
74 <Button
75 key="clear"
76 size="tiny"
77 type="text"
78 icon={<X />}
79 onClick={() => {
80 setSearch('')
81 setSearchQuery('')
82 }}
83 className="p-0 h-5 w-5"
84 />
85 ),
86 ]}
87 />
88
89 <div className="flex items-center gap-x-2">
90 <Button
91 type="default"
92 icon={<RefreshCw />}
93 loading={isRefetching}
94 onClick={() => refetch()}
95 >
96 Refresh
97 </Button>
98 <Button onClick={() => setCreateQueueSheetShown(true)}>Create queue</Button>
99 </div>
100 </div>
101
102 <LoadingLine loading={isLoading || isRefetching} />
103
104 <DataGrid
105 className="grow border-t-0"
106 rowHeight={44}
107 headerRowHeight={36}
108 columns={columns}
109 rows={queueData}
110 rowKeyGetter={(row) => row.id}
111 rowClass={() => {
112 return cn(
113 'cursor-pointer',
114 '[&>.rdg-cell]:border-box [&>.rdg-cell]:outline-hidden [&>.rdg-cell]:shadow-none',
115 '[&>.rdg-cell:first-child>div]:ml-8'
116 )
117 }}
118 renderers={{
119 renderRow(_, props) {
120 return (
121 <Row
122 key={props.row.queue_name}
123 {...props}
124 onClick={() => {
125 const { queue_name } = props.row
126 const url = `/project/${ref}/integrations/queues/queues/${queue_name}`
127 router.push(url)
128 }}
129 />
130 )
131 },
132 }}
133 />
134
135 {/* Render 0 rows state outside of the grid */}
136 {queueData.length === 0 ? (
137 isLoading ? (
138 <div className="absolute top-28 px-10 w-full">
139 <GenericSkeletonLoader />
140 </div>
141 ) : isError ? (
142 <div className="absolute top-28 px-10 flex flex-col items-center justify-center w-full">
143 <AlertError subject="Failed to retrieve database queues" error={error} />
144 </div>
145 ) : (
146 <div className="absolute top-32 px-6 w-full">
147 <div className="text-center text-sm flex flex-col gap-y-1">
148 <p className="text-foreground">
149 {!!searchQuery ? 'No queues found' : 'No queues created yet'}
150 </p>
151 <p className="text-foreground-light">
152 {!!searchQuery
153 ? 'There are currently no queues based on the search applied'
154 : 'There are currently no queues created yet in your project'}
155 </p>
156 </div>
157 </div>
158 )
159 ) : null}
160
161 <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">
162 {`Total: ${queueData.length} queues`}
163 </div>
164 </div>
165 </div>
166
167 <CreateQueueSheet
168 visible={createQueueSheetShown}
169 onClose={() => {
170 setCreateQueueSheetShown(false)
171 }}
172 />
173 </>
174 )
175}