1+ import {
2+ useAllListMyDelegatedChildAccountsQuery ,
3+ useChildAccountsInfiniteQuery ,
4+ } from '@linode/queries' ;
15import { Drawer , LinkButton , Notice , Typography } from '@linode/ui' ;
2- import React from 'react' ;
6+ import React , { useMemo , useState } from 'react' ;
37
48import { DebouncedSearchTextField } from 'src/components/DebouncedSearchTextField' ;
59import { PARENT_USER_SESSION_EXPIRED } from 'src/features/Account/constants' ;
610import { useParentChildAuthentication } from 'src/features/Account/SwitchAccounts/useParentChildAuthentication' ;
711import { setTokenInLocalStorage } from 'src/features/Account/SwitchAccounts/utils' ;
12+ import { useIsIAMDelegationEnabled } from 'src/features/IAM/hooks/useIsIAMEnabled' ;
813import { sendSwitchToParentAccountEvent } from 'src/utilities/analytics/customEventAnalytics' ;
914import { getStorage , setStorage , storage } from 'src/utilities/storage' ;
1015
1116import { ChildAccountList } from './SwitchAccounts/ChildAccountList' ;
1217import { updateParentTokenInLocalStorage } from './SwitchAccounts/utils' ;
1318
14- import type { APIError , UserType } from '@linode/api-v4' ;
19+ import type { APIError , Filter , UserType } from '@linode/api-v4' ;
1520
1621interface Props {
1722 onClose : ( ) => void ;
@@ -33,8 +38,8 @@ export const SwitchAccountDrawer = (props: Props) => {
3338 const [ isParentTokenError , setIsParentTokenError ] = React . useState <
3439 APIError [ ]
3540 > ( [ ] ) ;
36- const [ query , setQuery ] = React . useState < string > ( '' ) ;
37-
41+ const [ searchQuery , setSearchQuery ] = React . useState < string > ( '' ) ;
42+ const { isIAMDelegationEnabled } = useIsIAMDelegationEnabled ( ) ;
3843 const isProxyUser = userType === 'proxy' ;
3944 const currentParentTokenWithBearer =
4045 getStorage ( 'authentication/parent_token/token' ) ?? '' ;
@@ -50,6 +55,48 @@ export const SwitchAccountDrawer = (props: Props) => {
5055
5156 const createTokenErrorReason = createTokenError ?. [ 0 ] ?. reason ;
5257
58+ const filter : Filter = {
59+ [ '+order' ] : 'asc' ,
60+ [ '+order_by' ] : 'company' ,
61+ ...( searchQuery && { company : { '+contains' : searchQuery } } ) ,
62+ } ;
63+
64+ const {
65+ data,
66+ fetchNextPage,
67+ hasNextPage,
68+ isError : childAccountInfiniteError ,
69+ isFetchingNextPage,
70+ isInitialLoading,
71+ isRefetching,
72+ refetch : refetchChildAccounts ,
73+ } = useChildAccountsInfiniteQuery (
74+ {
75+ filter,
76+ headers :
77+ userType === 'proxy'
78+ ? {
79+ Authorization : currentTokenWithBearer ,
80+ }
81+ : undefined ,
82+ } ,
83+ isIAMDelegationEnabled === false
84+ ) ;
85+ const {
86+ data : allChildAccounts ,
87+ error : allChildAccountsError ,
88+ isLoading : allChildAccountsLoading ,
89+ isRefetching : allChildAccountsIsRefetching ,
90+ refetch : refetchAllChildAccounts ,
91+ } = useAllListMyDelegatedChildAccountsQuery ( {
92+ params : { } ,
93+ enabled : isIAMDelegationEnabled ,
94+ } ) ;
95+
96+ const refetchFn = isIAMDelegationEnabled
97+ ? refetchAllChildAccounts
98+ : refetchChildAccounts ;
99+
53100 const handleSwitchToChildAccount = React . useCallback (
54101 async ( {
55102 currentTokenWithBearer,
@@ -120,8 +167,30 @@ export const SwitchAccountDrawer = (props: Props) => {
120167 location . reload ( ) ;
121168 } , [ onClose , revokeToken , validateParentToken , updateCurrentToken ] ) ;
122169
170+ const [ isSwitchingChildAccounts , setIsSwitchingChildAccounts ] =
171+ useState < boolean > ( false ) ;
172+
173+ const handleClose = ( ) => {
174+ setIsSwitchingChildAccounts ( false ) ;
175+ onClose ( ) ;
176+ } ;
177+
178+ const childAccounts = useMemo ( ( ) => {
179+ if ( isIAMDelegationEnabled ) {
180+ if ( searchQuery && allChildAccounts ) {
181+ // Client-side filter: match company field with searchQuery (case-insensitive, contains)
182+ const normalizedQuery = searchQuery . toLowerCase ( ) ;
183+ return allChildAccounts . filter ( ( account ) =>
184+ account . company ?. toLowerCase ( ) . includes ( normalizedQuery )
185+ ) ;
186+ }
187+ return allChildAccounts ;
188+ }
189+ return data ?. pages . flatMap ( ( page ) => page . data ) ;
190+ } , [ isIAMDelegationEnabled , searchQuery , allChildAccounts , data ] ) ;
191+
123192 return (
124- < Drawer onClose = { onClose } open = { open } title = "Switch Account" >
193+ < Drawer onClose = { handleClose } open = { open } title = "Switch Account" >
125194 { createTokenErrorReason && (
126195 < Notice text = { createTokenErrorReason } variant = "error" />
127196 ) }
@@ -130,7 +199,7 @@ export const SwitchAccountDrawer = (props: Props) => {
130199 ) }
131200 < Typography
132201 sx = { ( theme ) => ( {
133- margin : `${ theme . spacing ( 3 ) } 0` ,
202+ margin : `${ theme . spacingFunction ( 24 ) } 0` ,
134203 } ) }
135204 >
136205 Select an account to view and manage its settings and configurations
@@ -151,24 +220,65 @@ export const SwitchAccountDrawer = (props: Props) => {
151220 ) }
152221 .
153222 </ Typography >
154- < DebouncedSearchTextField
155- clearable
156- debounceTime = { 250 }
157- hideLabel
158- label = "Search"
159- onSearch = { setQuery }
160- placeholder = "Search"
161- sx = { { marginBottom : 3 } }
162- value = { query }
163- />
223+ { isIAMDelegationEnabled &&
224+ allChildAccounts &&
225+ allChildAccounts . length !== 0 && (
226+ < >
227+ < DebouncedSearchTextField
228+ clearable
229+ debounceTime = { 250 }
230+ hideLabel
231+ label = "Search"
232+ onSearch = { setSearchQuery }
233+ placeholder = "Search"
234+ sx = { { marginBottom : 3 } }
235+ value = { searchQuery }
236+ />
237+ { searchQuery && childAccounts && childAccounts . length === 0 && (
238+ < Typography sx = { { fontStyle : 'italic' } } >
239+ No search results
240+ </ Typography >
241+ ) }
242+ </ >
243+ ) }
244+ { ! isIAMDelegationEnabled && (
245+ < DebouncedSearchTextField
246+ clearable
247+ debounceTime = { 250 }
248+ hideLabel
249+ label = "Search"
250+ onSearch = { setSearchQuery }
251+ placeholder = "Search"
252+ sx = { { marginBottom : 3 } }
253+ value = { searchQuery }
254+ />
255+ ) }
164256 < ChildAccountList
257+ childAccounts = { childAccounts }
165258 currentTokenWithBearer = {
166259 isProxyUser ? currentParentTokenWithBearer : currentTokenWithBearer
167260 }
168- isLoading = { isSubmitting }
261+ errors = { {
262+ childAccountInfiniteError,
263+ allChildAccountsError,
264+ } }
265+ fetchNextPage = { fetchNextPage }
266+ filter = { filter }
267+ hasNextPage = { hasNextPage }
268+ isFetchingNextPage = { isFetchingNextPage }
269+ isLoading = {
270+ isInitialLoading ||
271+ isSubmitting ||
272+ isSwitchingChildAccounts ||
273+ isRefetching ||
274+ allChildAccountsLoading ||
275+ allChildAccountsIsRefetching
276+ }
277+ isSwitchingChildAccounts = { isSwitchingChildAccounts }
169278 onClose = { onClose }
170279 onSwitchAccount = { handleSwitchToChildAccount }
171- searchQuery = { query }
280+ refetchFn = { refetchFn }
281+ setIsSwitchingChildAccounts = { setIsSwitchingChildAccounts }
172282 userType = { userType }
173283 />
174284 </ Drawer >
0 commit comments