'use client' import type { App } from '@/types/app' import { Button } from '@langgenius/dify-ui/button' import { Dialog, DialogCloseButton, DialogContent, DialogDescription, DialogTitle } from '@langgenius/dify-ui/dialog' import { toast } from '@langgenius/dify-ui/toast' import { keepPreviousData, useInfiniteQuery, useMutation } from '@tanstack/react-query' import { useAtomValue, useSetAtom } from 'jotai' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { AppPicker } from '@/app/components/plugins/plugin-detail-panel/app-selector/app-picker' import { AppTrigger } from '@/app/components/plugins/plugin-detail-panel/app-selector/app-trigger' import { useRouter } from '@/next/navigation' import { consoleQuery } from '@/service/client' import { closeCreateInstanceModalAtom, createInstanceModalOpenAtom } from '../store' const SOURCE_APP_PAGE_SIZE = 20 function SourceAppPicker({ value, onChange }: { value?: App onChange: (app: App) => void }) { 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 ( } isShow={isShow} onShowChange={setIsShow} onSelect={onChange} apps={apps} isLoading={isLoading || isFetchingNextPage} hasMore={hasNextPage ?? true} onLoadMore={() => { void fetchNextPage() }} searchText={searchText} onSearchChange={setSearchText} placement="bottom-start" offset={4} /> ) } function CreateInstanceForm() { const { t } = useTranslation('deployments') const router = useRouter() const closeModal = useSetAtom(closeCreateInstanceModalAtom) const createInstance = useMutation(consoleQuery.enterprise.appDeploy.createAppInstance.mutationOptions()) const [sourceApp, setSourceApp] = useState() const canCreate = Boolean(sourceApp?.id && !createInstance.isPending) const handleCreate = async (form: HTMLFormElement) => { if (!canCreate || !sourceApp?.id) return const formData = new FormData(form) const name = String(formData.get('name') ?? '').trim() const description = String(formData.get('description') ?? '').trim() if (!name) return try { const result = await createInstance.mutateAsync({ body: { sourceAppId: sourceApp.id, name: name.trim(), description: description.trim() || undefined, }, }) if (!result.appInstanceId) throw new Error('Create app instance did not return an appInstanceId.') closeModal() router.push(`/deployments/${result.appInstanceId}/overview`) } catch { toast.error(t('createModal.createFailed')) } } return ( { event.preventDefault() void handleCreate(event.currentTarget) }} > {t('createModal.title')} {t('createModal.description')} {t('createModal.sourceApp')} {t('createModal.nameLabel')} {t('createModal.descriptionLabel')} {t('createModal.cancel')} {t('createModal.create')} ) } export function CreateInstanceModal() { const open = useAtomValue(createInstanceModalOpenAtom) const closeModal = useSetAtom(closeCreateInstanceModalAtom) return ( !next && closeModal()} > {open && } ) }