@@ -32,7 +32,10 @@ import {
3232 integrationsParsers ,
3333 integrationsUrlKeys ,
3434} from '@/app/workspace/[workspaceId]/integrations/search-params'
35- import { SettingsEmptyState } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
35+ import {
36+ SettingsEmptyState ,
37+ SettingsQueryErrorState ,
38+ } from '@/app/workspace/[workspaceId]/settings/components/settings-empty-state'
3639import { SettingsResourceRow } from '@/app/workspace/[workspaceId]/settings/components/settings-resource-row'
3740import { PermissionAccessBoundary } from '@/ee/access-requests/components/permission-access-boundary'
3841import { useWorkspaceCredentials , type WorkspaceCredential } from '@/hooks/queries/credentials'
@@ -150,7 +153,14 @@ function IntegrationsContent() {
150153 const scrollContainerRef = useRef < HTMLDivElement > ( null )
151154 const params = useParams ( )
152155 const workspaceId = ( params ?. workspaceId as string ) || ''
153- const { integrationAvailability, isBlockPermitted } = usePermissionConfig ( )
156+ const {
157+ integrationAvailability,
158+ isBlockPermitted,
159+ isIntegrationAvailabilityReady,
160+ isIntegrationAvailabilityFetching,
161+ integrationAvailabilityError,
162+ refetchIntegrationAvailability,
163+ } = usePermissionConfig ( )
154164
155165 const [ { category : urlCategory , search : urlSearchTerm } , setIntegrationFilters ] = useQueryStates (
156166 integrationsParsers ,
@@ -207,20 +217,30 @@ function IntegrationsContent() {
207217 } )
208218 } , [ oauthCredentials ] )
209219
220+ /**
221+ * The catalog lists nothing until the deployment allowlist has loaded, so a
222+ * pending or failed availability request never shows an integration the
223+ * deployment's `ALLOWED_INTEGRATIONS` excludes.
224+ */
210225 const permittedFeatured = useMemo (
211- ( ) => FEATURED_INTEGRATIONS . filter ( ( integration ) => isBlockPermitted ( integration . type ) ) ,
212- [ isBlockPermitted ]
226+ ( ) =>
227+ isIntegrationAvailabilityReady
228+ ? FEATURED_INTEGRATIONS . filter ( ( integration ) => isBlockPermitted ( integration . type ) )
229+ : [ ] ,
230+ [ isIntegrationAvailabilityReady , isBlockPermitted ]
213231 )
214232
215233 const permittedCategorySections = useMemo (
216234 ( ) =>
217- ALL_CATEGORY_SECTIONS . flatMap ( ( section ) => {
218- const integrations = section . integrations . filter ( ( integration ) =>
219- isBlockPermitted ( integration . type )
220- )
221- return integrations . length > 0 ? [ { label : section . label , integrations } ] : [ ]
222- } ) ,
223- [ isBlockPermitted ]
235+ isIntegrationAvailabilityReady
236+ ? ALL_CATEGORY_SECTIONS . flatMap ( ( section ) => {
237+ const integrations = section . integrations . filter ( ( integration ) =>
238+ isBlockPermitted ( integration . type )
239+ )
240+ return integrations . length > 0 ? [ { label : section . label , integrations } ] : [ ]
241+ } )
242+ : [ ] ,
243+ [ isIntegrationAvailabilityReady , isBlockPermitted ]
224244 )
225245
226246 const setSelectedCategory = useCallback (
@@ -321,6 +341,7 @@ function IntegrationsContent() {
321341 ] )
322342
323343 const showNoResults =
344+ isIntegrationAvailabilityReady &&
324345 Boolean ( urlSearchTerm . trim ( ) || ! isAllCategorySelected ) &&
325346 filteredCategorySections . length === 0 &&
326347 visibleConnectedItems . length === 0
@@ -380,6 +401,16 @@ function IntegrationsContent() {
380401 </ IntegrationSection >
381402 ) }
382403
404+ { integrationAvailabilityError && (
405+ < SettingsQueryErrorState
406+ variant = 'inline'
407+ error = { integrationAvailabilityError }
408+ fallback = 'Unable to load integrations'
409+ isRetrying = { isIntegrationAvailabilityFetching }
410+ onRetry = { ( ) => void refetchIntegrationAvailability ( ) }
411+ />
412+ ) }
413+
383414 { filteredCategorySections . map ( ( section ) => (
384415 < IntegrationSection key = { section . label } label = { formatIntegrationType ( section . label ) } >
385416 { section . integrations . map ( ( integration ) => {
0 commit comments