dify/web/features/deployments/list/state/index.ts
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

181 lines
5.9 KiB
TypeScript

'use client'
import type { ReactNode } from 'react'
import { keepPreviousData } from '@tanstack/react-query'
import { atom } from 'jotai'
import { atomWithInfiniteQuery, atomWithQuery } from 'jotai-tanstack-query'
import { selectAtom, useHydrateAtoms } from 'jotai/utils'
import { parseAsString, useQueryState } from 'nuqs'
import { consoleQuery } from '@/service/client'
import { deploymentStatusPollingInterval } from '../../shared/domain/runtime-status'
const DEPLOYMENTS_LIST_PAGE_SIZE = 100
export const envFilterQueryState = parseAsString.withOptions({ history: 'push' })
export const keywordsQueryState = parseAsString.withDefault('').withOptions({ history: 'push' })
// Mirrors nuqs URL state. DeploymentsListStateBoundary force-hydrates these
// atoms on render so query atoms can read URL filters through Jotai.
const deploymentsListKeywordsAtom = atom('')
const deploymentsListEnvironmentIdAtom = atom<string | null>(null)
export type DeploymentsListEnvironmentFilterOption = {
kind: 'all' | 'environment'
value: string | null
displayName?: string
}
export function DeploymentsListStateBoundary({ children }: { children: ReactNode }) {
const [envFilter] = useQueryState('env', envFilterQueryState)
const [keywords] = useQueryState('keywords', keywordsQueryState)
useHydrateAtoms(
[
[deploymentsListEnvironmentIdAtom, envFilter],
[deploymentsListKeywordsAtom, keywords],
] as const,
{ dangerouslyForceHydrate: true },
)
return children
}
const deploymentsListEnvironmentsQueryAtom = atomWithQuery(() => {
return consoleQuery.enterprise.environmentService.listEnvironments.queryOptions({
input: {
query: {
// The filter lists every deployable environment; environment count is
// capped well below the 100-per-page maximum.
pageNumber: 1,
resultsPerPage: 100,
},
},
})
})
const deploymentsListEnvironmentsDataAtom = selectAtom(
deploymentsListEnvironmentsQueryAtom,
(query) => query.data,
)
export const deploymentsListEnvironmentFilterOptionsAtom = atom(
(get): DeploymentsListEnvironmentFilterOption[] => {
const environments = get(deploymentsListEnvironmentsDataAtom)?.environments ?? []
return [
{
kind: 'all',
value: null,
},
...environments.map((environment) => ({
kind: 'environment' as const,
value: environment.id,
displayName: environment.displayName,
})),
]
},
)
export const deploymentsListSelectedEnvironmentFilterOptionAtom = atom(
(get): DeploymentsListEnvironmentFilterOption => {
const envFilter = get(deploymentsListEnvironmentIdAtom)
const options = get(deploymentsListEnvironmentFilterOptionsAtom)
const allOption = options[0] ?? { kind: 'all' as const, value: null }
return (
options.find((option) => option.value === envFilter) ??
(envFilter
? {
kind: 'environment',
value: envFilter,
displayName: envFilter,
}
: allOption)
)
},
)
const deploymentsListQueryAtom = atomWithInfiniteQuery((get) => {
const queryKeywords = get(deploymentsListKeywordsAtom).trim()
const queryEnvironmentId = get(deploymentsListEnvironmentIdAtom) ?? undefined
return consoleQuery.enterprise.appInstanceService.listAppInstanceSummaries.infiniteOptions({
input: (pageParam) => ({
query: {
pageNumber: Number(pageParam),
resultsPerPage: DEPLOYMENTS_LIST_PAGE_SIZE,
...(queryEnvironmentId ? { environmentId: queryEnvironmentId } : {}),
...(queryKeywords ? { displayName: queryKeywords } : {}),
},
}),
getNextPageParam: (lastPage) => {
const currentPage = lastPage.pagination?.currentPage ?? 1
const totalPages = lastPage.pagination?.totalPages ?? 1
return currentPage < totalPages ? currentPage + 1 : undefined
},
initialPageParam: 1,
placeholderData: keepPreviousData,
refetchInterval: (query) => {
const rows =
query.state.data?.pages.flatMap((page) =>
page.appInstanceSummaries.flatMap((summary) => summary.environmentDeployments),
) ?? []
return deploymentStatusPollingInterval(rows)
},
})
})
const deploymentsListDataAtom = selectAtom(deploymentsListQueryAtom, (query) => query.data)
export const deploymentsListErrorAtom = selectAtom(deploymentsListQueryAtom, (query) => query.error)
export const deploymentsListFetchNextPageAtom = selectAtom(
deploymentsListQueryAtom,
(query) => query.fetchNextPage,
)
export const deploymentsListHasNextPageAtom = selectAtom(
deploymentsListQueryAtom,
(query) => query.hasNextPage,
)
export const deploymentsListIsFetchingAtom = selectAtom(
deploymentsListQueryAtom,
(query) => query.isFetching,
)
export const deploymentsListIsFetchingNextPageAtom = selectAtom(
deploymentsListQueryAtom,
(query) => query.isFetchingNextPage,
)
export const deploymentsListIsLoadingAtom = selectAtom(
deploymentsListQueryAtom,
(query) => query.isLoading,
)
const deploymentsListIsErrorAtom = selectAtom(deploymentsListQueryAtom, (query) => query.isError)
export const deploymentsListRowsAtom = atom((get) => {
return get(deploymentsListDataAtom)?.pages.flatMap((page) => page.appInstanceSummaries) ?? []
})
export const deploymentsListShowSkeletonAtom = atom((get) => {
const pages = get(deploymentsListDataAtom)?.pages ?? []
return (
get(deploymentsListIsLoadingAtom) || (get(deploymentsListIsFetchingAtom) && pages.length === 0)
)
})
export const deploymentsListShowEmptyStateAtom = atom((get) => {
return (
!get(deploymentsListShowSkeletonAtom) &&
!get(deploymentsListIsErrorAtom) &&
get(deploymentsListRowsAtom).length === 0
)
})
export const deploymentsListShowErrorStateAtom = atom((get) => {
return !get(deploymentsListShowSkeletonAtom) && get(deploymentsListIsErrorAtom)
})
export const deploymentsListHasFilterAtom = atom((get) => {
return Boolean(get(deploymentsListKeywordsAtom).trim() || get(deploymentsListEnvironmentIdAtom))
})