'use client'
import type { SourceAppPickerValue } from '../state'
import type { App } from '@/types/app'
import { cn } from '@langgenius/dify-ui/cn'
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxInputGroup,
ComboboxItem,
ComboboxItemText,
ComboboxList,
ComboboxTrigger,
} from '@langgenius/dify-ui/combobox'
import { useAtomValue, useSetAtom } from 'jotai'
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 { useInfiniteScroll } from '@/features/deployments/shared/hooks/use-infinite-scroll'
import { TitleTooltip } from '../../shared/components/title-tooltip'
import {
createReleaseSourceAppsAtom,
createReleaseSourceAppSearchTextAtom,
createReleaseSourceAppsErrorAtom,
createReleaseSourceAppsFetchNextPageAtom,
createReleaseSourceAppsHasNextPageAtom,
createReleaseSourceAppsIsFetchingAtom,
createReleaseSourceAppsIsFetchingNextPageAtom,
createReleaseSourceAppsIsLoadingAtom,
} from '../state'
const SOURCE_APP_PICKER_SKELETON_KEYS = [
'first-source-app',
'second-source-app',
'third-source-app',
]
function sourceAppSearchText(app: App) {
return `${app.name} ${app.id}`.toLowerCase()
}
function SourceAppTrigger({ app }: { app?: SourceAppPickerValue }) {
const { t } = useTranslation('deployments')
return (
{app && (
)}
{app?.name ?? t(($) => $['createModal.appPickerPlaceholder'])}
)
}
function SourceAppOption({ app }: { app: App }) {
return (
{app.name}
({app.id.slice(0, 8)})
)
}
function SourceAppPickerSkeleton() {
return (
{SOURCE_APP_PICKER_SKELETON_KEYS.map((key) => (
))}
)
}
export function SourceAppPicker({
value,
onChange,
disabled = false,
}: {
value?: SourceAppPickerValue
onChange: (app: App) => void
disabled?: boolean
}) {
const { t } = useTranslation('deployments')
const [isShow, setIsShow] = useState(false)
const searchText = useAtomValue(createReleaseSourceAppSearchTextAtom)
const setSearchText = useSetAtom(createReleaseSourceAppSearchTextAtom)
const apps = useAtomValue(createReleaseSourceAppsAtom)
const sourceAppsError = useAtomValue(createReleaseSourceAppsErrorAtom)
const sourceAppsFetchNextPage = useAtomValue(createReleaseSourceAppsFetchNextPageAtom)
const sourceAppsHasNextPage = useAtomValue(createReleaseSourceAppsHasNextPageAtom)
const sourceAppsIsFetching = useAtomValue(createReleaseSourceAppsIsFetchingAtom)
const sourceAppsIsFetchingNextPage = useAtomValue(createReleaseSourceAppsIsFetchingNextPageAtom)
const sourceAppsIsLoading = useAtomValue(createReleaseSourceAppsIsLoadingAtom)
const { rootRef, sentinelRef } = useInfiniteScroll(
{
error: sourceAppsError,
fetchNextPage: sourceAppsFetchNextPage,
hasNextPage: sourceAppsHasNextPage,
isFetching: sourceAppsIsFetching,
isFetchingNextPage: sourceAppsIsFetchingNextPage,
isLoading: sourceAppsIsLoading,
},
{
enabled: isShow && !disabled,
rootMargin: '0px 0px 160px 0px',
threshold: 0.1,
},
)
return (
items={apps}
open={!disabled && isShow}
inputValue={searchText}
onOpenChange={(open) => {
setIsShow(disabled ? false : open)
}}
onInputValueChange={(value) => {
if (!disabled) setSearchText(value)
}}
onValueChange={(app) => {
if (disabled) return
if (!app) return
onChange(app)
setIsShow(false)
}}
itemToStringLabel={(app) => {
if (!app) return ''
return app.name
}}
itemToStringValue={(app) => {
if (!app) return ''
return app.id
}}
filter={(app, query) => sourceAppSearchText(app).includes(query.toLowerCase())}
disabled={disabled}
>
$['versions.sourceAppOption'])}
icon={false}
className="block h-auto w-full border-0 bg-transparent p-0 text-left hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 data-open:bg-transparent"
>
$['createModal.appSearchPlaceholder'])}
placeholder={t(($) => $['createModal.appSearchPlaceholder'])}
className="block h-4.5 grow px-1 py-0 text-[13px] text-text-primary"
/>
{(sourceAppsIsLoading || sourceAppsIsFetchingNextPage) && apps.length === 0 && (
)}
{(app: App) => }
{!(sourceAppsIsLoading || sourceAppsIsFetchingNextPage) && (
{t(($) => $['createModal.appSearchEmpty'])}
)}
{sourceAppsIsFetchingNextPage && apps.length > 0 && (
{t(($) => $['createModal.loadingApps'])}
)}
{sourceAppsHasNextPage &&
}
)
}