mirror of
https://github.com/langgenius/dify.git
synced 2026-06-12 19:28:01 +08:00
Refactor shared frontend components
This commit is contained in:
@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import Link from '@/next/link'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
import { CreateDeploymentGuideProvider } from './state/provider'
|
||||
import { CreateDeploymentGuideShell } from './ui/shell/guide-shell'
|
||||
import { CreateDeploymentGuideShell } from './ui/shell'
|
||||
|
||||
export function CreateDeploymentGuide() {
|
||||
const { t } = useTranslation('deployments')
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
continueFromReleaseAtom,
|
||||
releaseCanGoNextAtom,
|
||||
stepAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
|
||||
export function ReleaseActionButtons() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const canGoNext = useAtomValue(releaseCanGoNextAtom)
|
||||
const setStep = useSetAtom(stepAtom)
|
||||
const continueFromRelease = useSetAtom(continueFromReleaseAtom)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="button" variant="secondary" onClick={() => setStep('source')}>
|
||||
{t('createGuide.actions.back')}
|
||||
</Button>
|
||||
<Button type="button" variant="primary" disabled={!canGoNext} onClick={continueFromRelease}>
|
||||
{t('createGuide.actions.next')}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,7 +1,14 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StepShell } from '../shell/layout'
|
||||
import {
|
||||
continueFromReleaseAtom,
|
||||
releaseCanGoNextAtom,
|
||||
stepAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
import { StepShell } from '../layout'
|
||||
import {
|
||||
DeploymentInfoSection,
|
||||
InitialReleaseSection,
|
||||
@ -23,3 +30,21 @@ export function ReleaseStepContent() {
|
||||
</StepShell>
|
||||
)
|
||||
}
|
||||
|
||||
export function ReleaseActionButtons() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const canGoNext = useAtomValue(releaseCanGoNextAtom)
|
||||
const setStep = useSetAtom(stepAtom)
|
||||
const continueFromRelease = useSetAtom(continueFromReleaseAtom)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type="button" variant="secondary" onClick={() => setStep('source')}>
|
||||
{t('createGuide.actions.back')}
|
||||
</Button>
|
||||
<Button type="button" variant="primary" disabled={!canGoNext} onClick={continueFromRelease}>
|
||||
{t('createGuide.actions.next')}
|
||||
</Button>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
78
web/features/deployments/create-guide/ui/shell.tsx
Normal file
78
web/features/deployments/create-guide/ui/shell.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
'use client'
|
||||
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { stepAtom } from '@/features/deployments/create-guide/state'
|
||||
import { GuideCard, GuideFrame } from './layout'
|
||||
import {
|
||||
ReleaseActionButtons,
|
||||
ReleaseStepContent,
|
||||
} from './release-step/release-step-content'
|
||||
import {
|
||||
SourceActionButtons,
|
||||
SourceStepContent,
|
||||
} from './source-step/source-step-content'
|
||||
import {
|
||||
TargetBackButton,
|
||||
TargetDeployButton,
|
||||
TargetSkipDeploymentButton,
|
||||
TargetStepContent,
|
||||
} from './target-step'
|
||||
|
||||
export function CreateDeploymentGuideShell() {
|
||||
const step = useAtomValue(stepAtom)
|
||||
|
||||
return (
|
||||
<GuideFrame activeStep={step}>
|
||||
<GuideCard
|
||||
contentScrollable={step !== 'source'}
|
||||
actions={<CreateDeploymentGuideActionBar />}
|
||||
>
|
||||
<CreateDeploymentGuideStepContent />
|
||||
</GuideCard>
|
||||
</GuideFrame>
|
||||
)
|
||||
}
|
||||
|
||||
function CreateDeploymentGuideStepContent() {
|
||||
const step = useAtomValue(stepAtom)
|
||||
|
||||
if (step === 'source') {
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col gap-7 pb-4">
|
||||
<SourceStepContent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (step === 'release') {
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col gap-7 pb-4">
|
||||
<ReleaseStepContent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-7 pb-4">
|
||||
<TargetStepContent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function CreateDeploymentGuideActionBar() {
|
||||
const step = useAtomValue(stepAtom)
|
||||
|
||||
return (
|
||||
<div className="sticky bottom-0 z-10 -mx-5 mt-auto flex items-center justify-end gap-2 border-t border-divider-subtle bg-background-default-subtle/95 px-5 py-4 backdrop-blur-sm sm:-mx-8 sm:px-8 lg:-mx-10 lg:px-10">
|
||||
{step === 'source' && <SourceActionButtons />}
|
||||
{step === 'release' && <ReleaseActionButtons />}
|
||||
{step === 'target' && (
|
||||
<>
|
||||
<TargetBackButton />
|
||||
<TargetSkipDeploymentButton />
|
||||
<TargetDeployButton />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { stepAtom } from '@/features/deployments/create-guide/state'
|
||||
import { ReleaseActionButtons } from '../release-step/actions'
|
||||
import { SourceActionButtons } from '../source-step/actions'
|
||||
import {
|
||||
TargetBackButton,
|
||||
TargetDeployButton,
|
||||
TargetSkipDeploymentButton,
|
||||
} from '../target-step/actions'
|
||||
|
||||
export function CreateDeploymentGuideActionBar() {
|
||||
const step = useAtomValue(stepAtom)
|
||||
|
||||
return (
|
||||
<div className="sticky bottom-0 z-10 -mx-5 mt-auto flex items-center justify-end gap-2 border-t border-divider-subtle bg-background-default-subtle/95 px-5 py-4 backdrop-blur-sm sm:-mx-8 sm:px-8 lg:-mx-10 lg:px-10">
|
||||
{step === 'source' && <SourceActionButtons />}
|
||||
{step === 'release' && <ReleaseActionButtons />}
|
||||
{step === 'target' && (
|
||||
<>
|
||||
<TargetBackButton />
|
||||
<TargetSkipDeploymentButton />
|
||||
<TargetDeployButton />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { stepAtom } from '@/features/deployments/create-guide/state'
|
||||
import { CreateDeploymentGuideActionBar } from './action-bar'
|
||||
import { GuideCard, GuideFrame } from './layout'
|
||||
import { CreateDeploymentGuideStepContent } from './step-content'
|
||||
|
||||
export function CreateDeploymentGuideShell() {
|
||||
const step = useAtomValue(stepAtom)
|
||||
|
||||
return (
|
||||
<GuideFrame activeStep={step}>
|
||||
<GuideCard
|
||||
contentScrollable={step !== 'source'}
|
||||
actions={<CreateDeploymentGuideActionBar />}
|
||||
>
|
||||
<CreateDeploymentGuideStepContent />
|
||||
</GuideCard>
|
||||
</GuideFrame>
|
||||
)
|
||||
}
|
||||
@ -1,33 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { stepAtom } from '@/features/deployments/create-guide/state'
|
||||
import { ReleaseStepContent } from '../release-step/release-step-content'
|
||||
import { SourceStepContent } from '../source-step/source-step-content'
|
||||
import { TargetStepContent } from '../target-step'
|
||||
|
||||
export function CreateDeploymentGuideStepContent() {
|
||||
const step = useAtomValue(stepAtom)
|
||||
|
||||
if (step === 'source') {
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col gap-7 pb-4">
|
||||
<SourceStepContent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (step === 'release') {
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col gap-7 pb-4">
|
||||
<ReleaseStepContent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-7 pb-4">
|
||||
<TargetStepContent />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
continueFromSourceAtom,
|
||||
sourceCanGoNextAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
|
||||
export function SourceActionButtons() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const canGoNext = useAtomValue(sourceCanGoNextAtom)
|
||||
const continueFromSource = useSetAtom(continueFromSourceAtom)
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
disabled={!canGoNext}
|
||||
onClick={() => continueFromSource({
|
||||
defaultDslAppName: t('createGuide.dsl.defaultAppName'),
|
||||
defaultReleaseName: t('createGuide.release.defaultName'),
|
||||
})}
|
||||
>
|
||||
{t('createGuide.actions.next')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
@ -3,9 +3,14 @@
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Uploader from '@/app/components/app/create-from-dsl-modal/uploader'
|
||||
import { dslFileAtom, selectDslFileAtom } from '@/features/deployments/create-guide/state'
|
||||
import { StepShell } from '../../shell/layout'
|
||||
import { DslReadStatus } from './read-status'
|
||||
import {
|
||||
dslFileAtom,
|
||||
dslReadErrorAtom,
|
||||
dslUnsupportedModeAtom,
|
||||
isReadingDslAtom,
|
||||
selectDslFileAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
import { StepShell } from '../layout'
|
||||
|
||||
export function DslUploadSection() {
|
||||
const { t } = useTranslation('deployments')
|
||||
@ -32,3 +37,30 @@ export function DslUploadSection() {
|
||||
</StepShell>
|
||||
)
|
||||
}
|
||||
|
||||
function DslReadStatus() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const isReadingDsl = useAtomValue(isReadingDslAtom)
|
||||
const dslReadError = useAtomValue(dslReadErrorAtom)
|
||||
const dslUnsupportedMode = useAtomValue(dslUnsupportedModeAtom)
|
||||
|
||||
return (
|
||||
<>
|
||||
{isReadingDsl && (
|
||||
<div className="system-xs-regular text-text-tertiary">
|
||||
{t('createGuide.dsl.reading')}
|
||||
</div>
|
||||
)}
|
||||
{dslReadError && (
|
||||
<div className="system-xs-regular text-text-destructive">
|
||||
{t('createGuide.dsl.readFailed')}
|
||||
</div>
|
||||
)}
|
||||
{dslUnsupportedMode && (
|
||||
<div role="alert" className="system-xs-regular text-text-destructive">
|
||||
{t('createGuide.dsl.unsupportedMode')}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
dslReadErrorAtom,
|
||||
dslUnsupportedModeAtom,
|
||||
isReadingDslAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
|
||||
export function DslReadStatus() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const isReadingDsl = useAtomValue(isReadingDslAtom)
|
||||
const dslReadError = useAtomValue(dslReadErrorAtom)
|
||||
const dslUnsupportedMode = useAtomValue(dslUnsupportedModeAtom)
|
||||
|
||||
return (
|
||||
<>
|
||||
{isReadingDsl && (
|
||||
<div className="system-xs-regular text-text-tertiary">
|
||||
{t('createGuide.dsl.reading')}
|
||||
</div>
|
||||
)}
|
||||
{dslReadError && (
|
||||
<div className="system-xs-regular text-text-destructive">
|
||||
{t('createGuide.dsl.readFailed')}
|
||||
</div>
|
||||
)}
|
||||
{dslUnsupportedMode && (
|
||||
<div role="alert" className="system-xs-regular text-text-destructive">
|
||||
{t('createGuide.dsl.unsupportedMode')}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
import type { WorkflowSourceApp } from '@/features/deployments/create-guide/state'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
@ -11,12 +12,64 @@ import { DeploymentStateMessage } from '@/features/deployments/components/empty-
|
||||
import {
|
||||
effectiveSelectedAppAtom,
|
||||
selectSourceAppAtom,
|
||||
setSourceSearchTextAtom,
|
||||
sourceAppsQueryAtom,
|
||||
sourceSearchTextAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
import { StepShell } from '../layout'
|
||||
|
||||
const sourceAppSkeletonKeys = ['first-source-app', 'second-source-app', 'third-source-app']
|
||||
|
||||
export function SourceAppList() {
|
||||
export function SourceAppSelectionSection() {
|
||||
const { t } = useTranslation('deployments')
|
||||
|
||||
return (
|
||||
<StepShell
|
||||
title={t('createGuide.source.title')}
|
||||
description={t('createGuide.source.description')}
|
||||
descriptionClassName="lg:hidden"
|
||||
hideHeader
|
||||
className="min-h-0 flex-1"
|
||||
>
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-3">
|
||||
<SourceSearchInput />
|
||||
<SourceAppList />
|
||||
</div>
|
||||
</StepShell>
|
||||
)
|
||||
}
|
||||
|
||||
function SourceSearchInput() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const sourceSearchText = useAtomValue(sourceSearchTextAtom)
|
||||
const setSourceSearchText = useSetAtom(setSourceSearchTextAtom)
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<span className="pointer-events-none absolute top-1/2 left-2.5 i-ri-search-line size-4 -translate-y-1/2 text-text-tertiary" aria-hidden="true" />
|
||||
<Input
|
||||
id="create-guide-source-search"
|
||||
aria-label={t('createGuide.source.sourceApp')}
|
||||
value={sourceSearchText}
|
||||
onChange={event => setSourceSearchText(event.target.value)}
|
||||
placeholder={t('createGuide.source.searchPlaceholder')}
|
||||
className="h-9 pr-8 pl-8"
|
||||
/>
|
||||
{sourceSearchText && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t('createGuide.source.clearSearch')}
|
||||
onClick={() => setSourceSearchText('')}
|
||||
className="absolute top-1/2 right-2.5 flex size-4 -translate-y-1/2 items-center justify-center text-text-quaternary hover:text-text-secondary"
|
||||
>
|
||||
<span className="i-ri-close-circle-fill size-4" aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function SourceAppList() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const selectSourceApp = useSetAtom(selectSourceAppAtom)
|
||||
const effectiveSelectedApp = useAtomValue(effectiveSelectedAppAtom)
|
||||
@ -1,39 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
setSourceSearchTextAtom,
|
||||
sourceSearchTextAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
|
||||
export function SourceSearchInput() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const sourceSearchText = useAtomValue(sourceSearchTextAtom)
|
||||
const setSourceSearchText = useSetAtom(setSourceSearchTextAtom)
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<span className="pointer-events-none absolute top-1/2 left-2.5 i-ri-search-line size-4 -translate-y-1/2 text-text-tertiary" aria-hidden="true" />
|
||||
<Input
|
||||
id="create-guide-source-search"
|
||||
aria-label={t('createGuide.source.sourceApp')}
|
||||
value={sourceSearchText}
|
||||
onChange={event => setSourceSearchText(event.target.value)}
|
||||
placeholder={t('createGuide.source.searchPlaceholder')}
|
||||
className="h-9 pr-8 pl-8"
|
||||
/>
|
||||
{sourceSearchText && (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t('createGuide.source.clearSearch')}
|
||||
onClick={() => setSourceSearchText('')}
|
||||
className="absolute top-1/2 right-2.5 flex size-4 -translate-y-1/2 items-center justify-center text-text-quaternary hover:text-text-secondary"
|
||||
>
|
||||
<span className="i-ri-close-circle-fill size-4" aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { StepShell } from '../../shell/layout'
|
||||
import { SourceAppList } from './list'
|
||||
import { SourceSearchInput } from './search-input'
|
||||
|
||||
export function SourceAppSelectionSection() {
|
||||
const { t } = useTranslation('deployments')
|
||||
|
||||
return (
|
||||
<StepShell
|
||||
title={t('createGuide.source.title')}
|
||||
description={t('createGuide.source.description')}
|
||||
descriptionClassName="lg:hidden"
|
||||
hideHeader
|
||||
className="min-h-0 flex-1"
|
||||
>
|
||||
<div className="flex min-h-0 flex-1 flex-col gap-3">
|
||||
<SourceSearchInput />
|
||||
<SourceAppList />
|
||||
</div>
|
||||
</StepShell>
|
||||
)
|
||||
}
|
||||
@ -8,7 +8,7 @@ import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { TitleTooltip } from '@/features/deployments/components/title-tooltip'
|
||||
import { methodAtom, selectMethodAtom } from '@/features/deployments/create-guide/state'
|
||||
import { StepShell } from '../shell/layout'
|
||||
import { StepShell } from '../layout'
|
||||
|
||||
function SourceMethodCard({ value, icon, title, description, badge }: {
|
||||
value: GuideMethod
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
'use client'
|
||||
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { UnsupportedDslNodesAlert } from '@/features/deployments/components/unsupported-dsl-nodes-alert'
|
||||
import { methodAtom, unsupportedDslNodesAtom } from '@/features/deployments/create-guide/state'
|
||||
import { DslUploadSection } from './dsl/upload-section'
|
||||
import { SourceAppSelectionSection } from './source-app/selection-section'
|
||||
import {
|
||||
continueFromSourceAtom,
|
||||
methodAtom,
|
||||
sourceCanGoNextAtom,
|
||||
unsupportedDslNodesAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
import { DslUploadSection } from './dsl-upload-section'
|
||||
import { SourceAppSelectionSection } from './source-app-selection-section'
|
||||
import { SourceMethodSection } from './source-method-section'
|
||||
|
||||
export function SourceStepContent() {
|
||||
@ -24,3 +31,23 @@ export function SourceStepContent() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function SourceActionButtons() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const canGoNext = useAtomValue(sourceCanGoNextAtom)
|
||||
const continueFromSource = useSetAtom(continueFromSourceAtom)
|
||||
|
||||
return (
|
||||
<Button
|
||||
type="button"
|
||||
variant="primary"
|
||||
disabled={!canGoNext}
|
||||
onClick={() => continueFromSource({
|
||||
defaultDslAppName: t('createGuide.dsl.defaultAppName'),
|
||||
defaultReleaseName: t('createGuide.release.defaultName'),
|
||||
})}
|
||||
>
|
||||
{t('createGuide.actions.next')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,118 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
canDeployAtom,
|
||||
canSkipDeploymentAtom,
|
||||
createDeploymentGuideSubmissionAtom,
|
||||
CreateDeploymentGuideSubmissionBlockedError,
|
||||
isCreatingReleaseOnlyAtom,
|
||||
isSubmittingDeploymentGuideAtom,
|
||||
stepAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
import { deploymentErrorMessage } from '@/features/deployments/error'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
|
||||
export function TargetBackButton() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const setStep = useSetAtom(stepAtom)
|
||||
const isSubmitting = useAtomValue(isSubmittingDeploymentGuideAtom)
|
||||
|
||||
return (
|
||||
<Button type="button" variant="secondary" onClick={() => setStep('release')} disabled={isSubmitting}>
|
||||
{t('createGuide.actions.back')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export function TargetSkipDeploymentButton() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const router = useRouter()
|
||||
const canSkipDeployment = useAtomValue(canSkipDeploymentAtom)
|
||||
const submitCreateDeploymentGuide = useSetAtom(createDeploymentGuideSubmissionAtom)
|
||||
const isSubmitting = useAtomValue(isSubmittingDeploymentGuideAtom)
|
||||
const isSkippingDeployment = useAtomValue(isCreatingReleaseOnlyAtom)
|
||||
const label = isSkippingDeployment
|
||||
? t('createGuide.actions.creating')
|
||||
: t('createGuide.actions.skipDeploy')
|
||||
|
||||
async function handleSkipDeployment() {
|
||||
if (!canSkipDeployment)
|
||||
return
|
||||
|
||||
try {
|
||||
const appInstanceId = await submitCreateDeploymentGuide({ deployToEnvironment: false })
|
||||
if (appInstanceId)
|
||||
router.push(`/deployments/${appInstanceId}/overview`)
|
||||
}
|
||||
catch (error) {
|
||||
await showSubmissionError({
|
||||
error,
|
||||
fallbackMessage: t('createGuide.errors.createReleaseFailed'),
|
||||
unsupportedDslModeMessage: t('createGuide.dsl.unsupportedMode'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Button type="button" variant="secondary" disabled={!canSkipDeployment || isSubmitting} onClick={handleSkipDeployment}>
|
||||
{label}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export function TargetDeployButton() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const router = useRouter()
|
||||
const canDeploy = useAtomValue(canDeployAtom)
|
||||
const submitCreateDeploymentGuide = useSetAtom(createDeploymentGuideSubmissionAtom)
|
||||
const isSubmitting = useAtomValue(isSubmittingDeploymentGuideAtom)
|
||||
const isSkippingDeployment = useAtomValue(isCreatingReleaseOnlyAtom)
|
||||
const label = isSubmitting && !isSkippingDeployment
|
||||
? t('createGuide.actions.deploying')
|
||||
: t('createGuide.actions.createAndDeploy')
|
||||
|
||||
async function handleDeploy() {
|
||||
if (!canDeploy)
|
||||
return
|
||||
|
||||
try {
|
||||
const appInstanceId = await submitCreateDeploymentGuide({ deployToEnvironment: true })
|
||||
if (appInstanceId)
|
||||
router.push(`/deployments/${appInstanceId}/overview`)
|
||||
}
|
||||
catch (error) {
|
||||
await showSubmissionError({
|
||||
error,
|
||||
fallbackMessage: t('createGuide.errors.deployFailed'),
|
||||
unsupportedDslModeMessage: t('createGuide.dsl.unsupportedMode'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Button type="button" variant="primary" disabled={!canDeploy || isSubmitting} onClick={handleDeploy}>
|
||||
{label}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
async function showSubmissionError({
|
||||
error,
|
||||
fallbackMessage,
|
||||
unsupportedDslModeMessage,
|
||||
}: {
|
||||
error: unknown
|
||||
fallbackMessage: string
|
||||
unsupportedDslModeMessage: string
|
||||
}) {
|
||||
if (error instanceof CreateDeploymentGuideSubmissionBlockedError) {
|
||||
toast.error(error.reason === 'unsupportedDslMode' ? unsupportedDslModeMessage : fallbackMessage)
|
||||
return
|
||||
}
|
||||
|
||||
toast.error(await deploymentErrorMessage(error) || fallbackMessage)
|
||||
}
|
||||
@ -12,7 +12,7 @@ import {
|
||||
selectBindingAtom,
|
||||
unsupportedDslNodesAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
import { TargetBindingSkeleton } from '../skeletons'
|
||||
import { TargetBindingSkeleton } from './skeletons'
|
||||
|
||||
export function TargetBindingSection() {
|
||||
const { t } = useTranslation('deployments')
|
||||
@ -13,7 +13,7 @@ import {
|
||||
effectiveSelectedEnvironmentIdAtom,
|
||||
selectedEnvironmentIdAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
import { TargetEnvironmentSkeleton } from '../skeletons'
|
||||
import { TargetEnvironmentSkeleton } from './skeletons'
|
||||
|
||||
function EnvironmentOptionRow({ environment }: {
|
||||
environment: Environment
|
||||
@ -1,13 +1,26 @@
|
||||
'use client'
|
||||
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { toast } from '@langgenius/dify-ui/toast'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { UnsupportedDslNodesAlert } from '@/features/deployments/components/unsupported-dsl-nodes-alert'
|
||||
import { unsupportedDslNodesAtom } from '@/features/deployments/create-guide/state'
|
||||
import { StepShell } from '../shell/layout'
|
||||
import { TargetBindingSection } from './bindings/section'
|
||||
import { TargetEnvVarSection } from './env-vars/section'
|
||||
import { TargetEnvironmentSection } from './environment/section'
|
||||
import {
|
||||
canDeployAtom,
|
||||
canSkipDeploymentAtom,
|
||||
createDeploymentGuideSubmissionAtom,
|
||||
CreateDeploymentGuideSubmissionBlockedError,
|
||||
isCreatingReleaseOnlyAtom,
|
||||
isSubmittingDeploymentGuideAtom,
|
||||
stepAtom,
|
||||
unsupportedDslNodesAtom,
|
||||
} from '@/features/deployments/create-guide/state'
|
||||
import { deploymentErrorMessage } from '@/features/deployments/error'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
import { StepShell } from '../layout'
|
||||
import { TargetBindingSection } from './binding-section'
|
||||
import { TargetEnvVarSection } from './env-var-section'
|
||||
import { TargetEnvironmentSection } from './environment-section'
|
||||
|
||||
export function TargetStepContent() {
|
||||
const { t } = useTranslation('deployments')
|
||||
@ -28,3 +41,104 @@ export function TargetStepContent() {
|
||||
</StepShell>
|
||||
)
|
||||
}
|
||||
|
||||
export function TargetBackButton() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const setStep = useSetAtom(stepAtom)
|
||||
const isSubmitting = useAtomValue(isSubmittingDeploymentGuideAtom)
|
||||
|
||||
return (
|
||||
<Button type="button" variant="secondary" onClick={() => setStep('release')} disabled={isSubmitting}>
|
||||
{t('createGuide.actions.back')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export function TargetSkipDeploymentButton() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const router = useRouter()
|
||||
const canSkipDeployment = useAtomValue(canSkipDeploymentAtom)
|
||||
const submitCreateDeploymentGuide = useSetAtom(createDeploymentGuideSubmissionAtom)
|
||||
const isSubmitting = useAtomValue(isSubmittingDeploymentGuideAtom)
|
||||
const isSkippingDeployment = useAtomValue(isCreatingReleaseOnlyAtom)
|
||||
const label = isSkippingDeployment
|
||||
? t('createGuide.actions.creating')
|
||||
: t('createGuide.actions.skipDeploy')
|
||||
|
||||
async function handleSkipDeployment() {
|
||||
if (!canSkipDeployment)
|
||||
return
|
||||
|
||||
try {
|
||||
const appInstanceId = await submitCreateDeploymentGuide({ deployToEnvironment: false })
|
||||
if (appInstanceId)
|
||||
router.push(`/deployments/${appInstanceId}/overview`)
|
||||
}
|
||||
catch (error) {
|
||||
await showSubmissionError({
|
||||
error,
|
||||
fallbackMessage: t('createGuide.errors.createReleaseFailed'),
|
||||
unsupportedDslModeMessage: t('createGuide.dsl.unsupportedMode'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Button type="button" variant="secondary" disabled={!canSkipDeployment || isSubmitting} onClick={handleSkipDeployment}>
|
||||
{label}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export function TargetDeployButton() {
|
||||
const { t } = useTranslation('deployments')
|
||||
const router = useRouter()
|
||||
const canDeploy = useAtomValue(canDeployAtom)
|
||||
const submitCreateDeploymentGuide = useSetAtom(createDeploymentGuideSubmissionAtom)
|
||||
const isSubmitting = useAtomValue(isSubmittingDeploymentGuideAtom)
|
||||
const isSkippingDeployment = useAtomValue(isCreatingReleaseOnlyAtom)
|
||||
const label = isSubmitting && !isSkippingDeployment
|
||||
? t('createGuide.actions.deploying')
|
||||
: t('createGuide.actions.createAndDeploy')
|
||||
|
||||
async function handleDeploy() {
|
||||
if (!canDeploy)
|
||||
return
|
||||
|
||||
try {
|
||||
const appInstanceId = await submitCreateDeploymentGuide({ deployToEnvironment: true })
|
||||
if (appInstanceId)
|
||||
router.push(`/deployments/${appInstanceId}/overview`)
|
||||
}
|
||||
catch (error) {
|
||||
await showSubmissionError({
|
||||
error,
|
||||
fallbackMessage: t('createGuide.errors.deployFailed'),
|
||||
unsupportedDslModeMessage: t('createGuide.dsl.unsupportedMode'),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Button type="button" variant="primary" disabled={!canDeploy || isSubmitting} onClick={handleDeploy}>
|
||||
{label}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
async function showSubmissionError({
|
||||
error,
|
||||
fallbackMessage,
|
||||
unsupportedDslModeMessage,
|
||||
}: {
|
||||
error: unknown
|
||||
fallbackMessage: string
|
||||
unsupportedDslModeMessage: string
|
||||
}) {
|
||||
if (error instanceof CreateDeploymentGuideSubmissionBlockedError) {
|
||||
toast.error(error.reason === 'unsupportedDslMode' ? unsupportedDslModeMessage : fallbackMessage)
|
||||
return
|
||||
}
|
||||
|
||||
toast.error(await deploymentErrorMessage(error) || fallbackMessage)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user