'use client' import type { App } from '@/types/app' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxInputGroup, ComboboxItem, ComboboxItemText, ComboboxList, ComboboxTrigger, } from '@langgenius/dify-ui/combobox' import { keepPreviousData, useInfiniteQuery } from '@tanstack/react-query' import { useState } from 'react' import { useTranslation } from 'react-i18next' import AppIcon from '@/app/components/base/app-icon' import { SkeletonRectangle, SkeletonRow } from '@/app/components/base/skeleton' import { consoleQuery } from '@/service/client' const SOURCE_APP_PAGE_SIZE = 20 const SOURCE_APP_PICKER_SKELETON_KEYS = ['first-source-app', 'second-source-app', 'third-source-app'] export type SourceAppPickerValue = Pick & Partial> function sourceAppSearchText(app: App) { return `${app.name} ${app.id} ${app.mode}`.toLowerCase() } function SourceAppTrigger({ open, app }: { open: boolean app?: SourceAppPickerValue }) { const { t } = useTranslation('deployments') return ( {app && ( )} {app?.name ?? t('createModal.appPickerPlaceholder')} ) } function SourceAppOption({ app }: { app: App }) { const { t } = useTranslation('deployments') const modeLabel = t(`appMode.${app.mode}`, { defaultValue: app.mode }) return ( {app.name} ( {app.id.slice(0, 8)} ) {modeLabel} ) } function SourceAppPickerSkeleton() { return (
{SOURCE_APP_PICKER_SKELETON_KEYS.map(key => ( ))}
) } export function SourceAppPicker({ value, onChange, ariaLabel }: { value?: SourceAppPickerValue onChange: (app: App) => void ariaLabel?: string }) { const { t } = useTranslation('deployments') const [isShow, setIsShow] = useState(false) const [searchText, setSearchText] = useState('') const { data, isLoading, isFetchingNextPage, fetchNextPage, hasNextPage, } = useInfiniteQuery({ ...consoleQuery.apps.list.infiniteOptions({ input: pageParam => ({ query: { page: Number(pageParam), limit: SOURCE_APP_PAGE_SIZE, name: searchText, }, }), getNextPageParam: lastPage => lastPage.has_more ? lastPage.page + 1 : undefined, initialPageParam: 1, placeholderData: keepPreviousData, }), }) const apps = data?.pages.flatMap(page => page.data) ?? [] return ( items={apps} open={isShow} inputValue={searchText} onOpenChange={setIsShow} onInputValueChange={setSearchText} onValueChange={(app) => { if (!app) return onChange(app) setIsShow(false) }} itemToStringLabel={app => app?.name ?? ''} itemToStringValue={app => app?.id ?? ''} filter={(app, query) => sourceAppSearchText(app).includes(query.toLowerCase())} disabled={false} >
{(isLoading || isFetchingNextPage) && apps.length === 0 && } {(app: App) => ( )} {!(isLoading || isFetchingNextPage) && ( {t('createModal.appSearchEmpty')} )} {hasNextPage && (
)}
) }