11import { useVolumeQuery , useVolumesQuery } from '@linode/queries' ;
2- import {
3- CircleProgress ,
4- CloseIcon ,
5- ErrorState ,
6- IconButton ,
7- InputAdornment ,
8- TextField ,
9- } from '@linode/ui' ;
2+ import { getAPIFilterFromQuery } from '@linode/search' ;
3+ import { CircleProgress , ErrorState , Stack } from '@linode/ui' ;
104import { useNavigate , useParams , useSearch } from '@tanstack/react-router' ;
11- import * as React from 'react' ;
12- import { debounce } from 'throttle-debounce' ;
5+ import React from 'react' ;
136
7+ import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField' ;
148import { DocumentTitleSegment } from 'src/components/DocumentTitle' ;
159import { useIsBlockStorageEncryptionFeatureEnabled } from 'src/components/Encryption/utils' ;
1610import { LandingHeader } from 'src/components/LandingHeader' ;
@@ -21,6 +15,7 @@ import { TableCell } from 'src/components/TableCell';
2115import { TableHead } from 'src/components/TableHead' ;
2216import { TableRow } from 'src/components/TableRow' ;
2317import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty' ;
18+ import { TableRowError } from 'src/components/TableRowError/TableRowError' ;
2419import { TableSortCell } from 'src/components/TableSortCell' ;
2520import { getRestrictedResourceText } from 'src/features/Account/utils' ;
2621import { useOrderV2 } from 'src/hooks/useOrderV2' ;
@@ -46,31 +41,27 @@ import { VolumesLandingEmptyState } from './VolumesLandingEmptyState';
4641import { VolumeTableRow } from './VolumeTableRow' ;
4742
4843import type { Filter , Volume } from '@linode/api-v4' ;
49- import type {
50- VolumeAction ,
51- VolumesSearchParams ,
52- } from 'src/routes/volumes/index' ;
44+ import type { VolumeAction } from 'src/routes/volumes/index' ;
5345
5446export const VolumesLanding = ( ) => {
5547 const navigate = useNavigate ( ) ;
5648 const params = useParams ( { strict : false } ) ;
57- const search : VolumesSearchParams = useSearch ( {
58- from : '/volumes' ,
49+ const search = useSearch ( {
50+ from : '/volumes/' ,
51+ shouldThrow : false ,
5952 } ) ;
6053 const pagination = usePaginationV2 ( {
6154 currentRoute : '/volumes' ,
6255 preferenceKey : VOLUME_TABLE_PREFERENCE_KEY ,
6356 searchParams : ( prev ) => ( {
6457 ...prev ,
65- query : search . query ,
58+ query : search ? .query ,
6659 } ) ,
6760 } ) ;
6861 const isVolumeCreationRestricted = useRestrictedGlobalGrantCheck ( {
6962 globalGrantType : 'add_volumes' ,
7063 } ) ;
7164
72- const { query } = search ;
73-
7465 const { handleOrderChange, order, orderBy } = useOrderV2 ( {
7566 initialRoute : {
7667 defaultOrder : {
@@ -82,12 +73,17 @@ export const VolumesLanding = () => {
8273 preferenceKey : VOLUME_TABLE_PREFERENCE_KEY ,
8374 } ) ;
8475
76+ const { filter : searchFilter , error : searchError } = getAPIFilterFromQuery (
77+ search ?. query ,
78+ {
79+ searchableFieldsWithoutOperator : [ 'label' , 'tags' ] ,
80+ }
81+ ) ;
82+
8583 const filter : Filter = {
8684 [ '+order' ] : order ,
8785 [ '+order_by' ] : orderBy ,
88- ...( query && {
89- label : { '+contains' : query } ,
90- } ) ,
86+ ...searchFilter ,
9187 } ;
9288
9389 const {
@@ -120,22 +116,12 @@ export const VolumesLanding = () => {
120116 } ) ;
121117 } ;
122118
123- const resetSearch = ( ) => {
124- navigate ( {
125- search : ( prev ) => ( {
126- ...prev ,
127- query : undefined ,
128- } ) ,
129- to : '/volumes' ,
130- } ) ;
131- } ;
132-
133- const onSearch = ( e : React . ChangeEvent < HTMLInputElement > ) => {
119+ const onSearch = ( query : string ) => {
134120 navigate ( {
135121 search : ( prev ) => ( {
136122 ...prev ,
137123 page : undefined ,
138- query : e . target . value || undefined ,
124+ query : query ? query : undefined ,
139125 } ) ,
140126 to : '/volumes' ,
141127 } ) ;
@@ -148,11 +134,13 @@ export const VolumesLanding = () => {
148134 } ) ;
149135 } ;
150136
137+ const numberOfColumns = isBlockStorageEncryptionFeatureEnabled ? 7 : 6 ;
138+
151139 if ( isLoading ) {
152140 return < CircleProgress /> ;
153141 }
154142
155- if ( error ) {
143+ if ( error && ! search ?. query ) {
156144 return (
157145 < ErrorState
158146 errorText = {
@@ -162,12 +150,12 @@ export const VolumesLanding = () => {
162150 ) ;
163151 }
164152
165- if ( volumes ?. results === 0 && ! query ) {
153+ if ( volumes ?. results === 0 && ! search ?. query ) {
166154 return < VolumesLandingEmptyState /> ;
167155 }
168156
169157 return (
170- < >
158+ < Stack spacing = { 2 } >
171159 < DocumentTitleSegment segment = "Volumes" />
172160 < LandingHeader
173161 breadcrumbProps = { {
@@ -185,34 +173,17 @@ export const VolumesLanding = () => {
185173 docsLink = "https://techdocs.akamai.com/cloud-computing/docs/block-storage"
186174 entity = "Volume"
187175 onButtonClick = { ( ) => navigate ( { to : '/volumes/create' } ) }
188- spacingBottom = { 16 }
189176 title = "Volumes"
190177 />
191- < TextField
178+ < DebouncedSearchTextField
179+ clearable
180+ errorText = { searchError ?. message }
192181 hideLabel
193- InputProps = { {
194- endAdornment : query && (
195- < InputAdornment position = "end" >
196- { isFetching && < CircleProgress size = "sm" /> }
197-
198- < IconButton
199- aria-label = "Clear"
200- data-testid = "clear-volumes-search"
201- onClick = { resetSearch }
202- size = "small"
203- >
204- < CloseIcon />
205- </ IconButton >
206- </ InputAdornment >
207- ) ,
208- sx : { mb : 3 } ,
209- } }
182+ isSearching = { isFetching }
210183 label = "Search"
211- onChange = { debounce ( 400 , ( e ) => {
212- onSearch ( e ) ;
213- } ) }
184+ onSearch = { onSearch }
214185 placeholder = "Search Volumes"
215- value = { query ?? '' }
186+ value = { search ?. query ?? '' }
216187 />
217188 < Table >
218189 < TableHead >
@@ -250,8 +221,17 @@ export const VolumesLanding = () => {
250221 </ TableRow >
251222 </ TableHead >
252223 < TableBody >
224+ { search ?. query && error && (
225+ < TableRowError
226+ colSpan = { numberOfColumns }
227+ message = { error [ 0 ] . reason }
228+ />
229+ ) }
253230 { volumes ?. data . length === 0 && (
254- < TableRowEmpty colSpan = { 6 } message = "No volume found" />
231+ < TableRowEmpty
232+ colSpan = { numberOfColumns }
233+ message = "No volume found"
234+ />
255235 ) }
256236 { volumes ?. data . map ( ( volume ) => (
257237 < VolumeTableRow
@@ -346,6 +326,6 @@ export const VolumesLanding = () => {
346326 volume = { selectedVolume }
347327 volumeError = { selectedVolumeError }
348328 />
349- </ >
329+ </ Stack >
350330 ) ;
351331} ;
0 commit comments