mirror of
https://github.com/langgenius/dify.git
synced 2026-05-09 04:36:31 +08:00
tweaks
This commit is contained in:
parent
0a477fc767
commit
de7795aa80
@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import type { App, AppModeEnum } from '@/types/app'
|
||||
import type { App } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Dialog, DialogCloseButton, DialogContent, DialogDescription, DialogTitle } from '@langgenius/dify-ui/dialog'
|
||||
@ -13,6 +13,7 @@ import AppIcon from '@/app/components/base/app-icon'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/client'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { useDeploymentsStore } from '../store'
|
||||
|
||||
const MAX_STUDIO_SOURCE_APPS = 100
|
||||
@ -20,7 +21,7 @@ const MAX_STUDIO_SOURCE_APPS = 100
|
||||
type StudioSourceApp = {
|
||||
id: string
|
||||
name: string
|
||||
mode: string
|
||||
mode: AppModeEnum
|
||||
iconType?: App['icon_type']
|
||||
icon?: string
|
||||
iconBackground?: string
|
||||
@ -32,7 +33,7 @@ function toStudioSourceAppInfo(app: App): StudioSourceApp {
|
||||
return {
|
||||
id: app.id,
|
||||
name: app.name,
|
||||
mode: app.mode || 'workflow',
|
||||
mode: app.mode || AppModeEnum.WORKFLOW,
|
||||
iconType: app.icon_type,
|
||||
icon: app.icon,
|
||||
iconBackground: app.icon_background ?? undefined,
|
||||
@ -118,7 +119,7 @@ function AppPicker({ apps, isLoading, value, onChange }: AppPickerProps) {
|
||||
imageUrl={selected.iconUrl}
|
||||
/>
|
||||
<AppTypeIcon
|
||||
type={selected.mode as unknown as AppModeEnum}
|
||||
type={selected.mode}
|
||||
wrapperClassName="absolute -bottom-0.5 -right-0.5 w-3 h-3 shadow-sm"
|
||||
className="h-2 w-2"
|
||||
/>
|
||||
@ -187,7 +188,7 @@ function AppPicker({ apps, isLoading, value, onChange }: AppPickerProps) {
|
||||
imageUrl={app.iconUrl}
|
||||
/>
|
||||
<AppTypeIcon
|
||||
type={app.mode as unknown as AppModeEnum}
|
||||
type={app.mode}
|
||||
wrapperClassName="absolute -bottom-0.5 -right-0.5 w-3 h-3 shadow-sm"
|
||||
className="h-2 w-2"
|
||||
/>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import type { KeyboardEvent } from 'react'
|
||||
import type { EnvironmentOption } from '../types'
|
||||
import type { EnvironmentDeploymentRow, EnvironmentOption } from '../types'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import {
|
||||
@ -93,6 +93,90 @@ function NewDeploymentMenu({ appInstanceId, availableEnvs }: {
|
||||
)
|
||||
}
|
||||
|
||||
function DeploymentRowActions({ appInstanceId, envId, row }: {
|
||||
appInstanceId: string
|
||||
envId: string
|
||||
row: EnvironmentDeploymentRow
|
||||
}) {
|
||||
const { t } = useTranslation('deployments')
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
|
||||
const cancelDeployment = useMutation(consoleQuery.enterprise.appDeploy.cancelRuntimeDeployment.mutationOptions())
|
||||
const undeployDeployment = useMutation(consoleQuery.enterprise.appDeploy.undeployRuntimeInstance.mutationOptions())
|
||||
const isUndeployed = isUndeployedDeploymentRow(row)
|
||||
const status = deploymentStatus(row)
|
||||
|
||||
function handleRuntimeAction() {
|
||||
const runtimeInstanceId = deploymentId(row)
|
||||
setMenuOpen(false)
|
||||
|
||||
if (status === 'deploying') {
|
||||
cancelDeployment.mutate({
|
||||
params: {
|
||||
appInstanceId,
|
||||
runtimeInstanceId,
|
||||
},
|
||||
body: {
|
||||
appInstanceId,
|
||||
runtimeInstanceId,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
undeployDeployment.mutate({
|
||||
params: {
|
||||
appInstanceId,
|
||||
runtimeInstanceId,
|
||||
},
|
||||
body: {
|
||||
appInstanceId,
|
||||
runtimeInstanceId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex shrink-0 items-center gap-1"
|
||||
onClick={e => e.stopPropagation()}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
>
|
||||
<Button size="small" variant="secondary" onClick={() => openDeployDrawer({ appInstanceId, environmentId: envId })}>
|
||||
{isUndeployed
|
||||
? t('deployDrawer.deploy')
|
||||
: status === 'ready'
|
||||
? t('deployTab.deployOtherVersion')
|
||||
: status === 'deploying'
|
||||
? t('deployTab.viewProgress')
|
||||
: t('deployTab.viewError')}
|
||||
</Button>
|
||||
{!isUndeployed && (
|
||||
<DropdownMenu modal={false} open={menuOpen} onOpenChange={setMenuOpen}>
|
||||
<DropdownMenuTrigger
|
||||
aria-label={t('deployTab.moreActions')}
|
||||
className="flex h-7 w-7 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
>
|
||||
<span className="i-ri-more-line h-4 w-4" />
|
||||
</DropdownMenuTrigger>
|
||||
{menuOpen && (
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[200px]">
|
||||
<DropdownMenuItem
|
||||
className="gap-2 px-3"
|
||||
onClick={handleRuntimeAction}
|
||||
>
|
||||
<span className="system-sm-regular text-text-destructive">
|
||||
{status === 'deploying' ? t('deployTab.cancelDeployment') : t('deployTab.undeploy')}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function DeployTab({ instanceId: appInstanceId }: {
|
||||
instanceId: string
|
||||
}) {
|
||||
@ -103,9 +187,6 @@ export function DeployTab({ instanceId: appInstanceId }: {
|
||||
},
|
||||
}))
|
||||
const { data: environmentOptionsReply } = useQuery(consoleQuery.enterprise.appDeploy.listDeploymentEnvironmentOptions.queryOptions())
|
||||
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
|
||||
const cancelDeployment = useMutation(consoleQuery.enterprise.appDeploy.cancelRuntimeDeployment.mutationOptions())
|
||||
const undeployDeployment = useMutation(consoleQuery.enterprise.appDeploy.undeployRuntimeInstance.mutationOptions())
|
||||
const environmentOptions = environmentOptionsFromOptionsReply(environmentOptionsReply)
|
||||
const rows = environmentDeployments?.data?.filter(row => row.environment?.id) ?? []
|
||||
const deployedRuntimeRows = deployedRows(environmentDeployments?.data)
|
||||
@ -163,71 +244,7 @@ export function DeployTab({ instanceId: appInstanceId }: {
|
||||
const envId = environmentId(row.environment)
|
||||
const isUndeployed = isUndeployedDeploymentRow(row)
|
||||
const isExpanded = !isUndeployed && activeExpanded === envId
|
||||
const status = deploymentStatus(row)
|
||||
const release = activeRelease(row)
|
||||
const actions = (
|
||||
<div
|
||||
className="flex shrink-0 items-center gap-1"
|
||||
onClick={e => e.stopPropagation()}
|
||||
onKeyDown={e => e.stopPropagation()}
|
||||
>
|
||||
<Button size="small" variant="secondary" onClick={() => openDeployDrawer({ appInstanceId, environmentId: envId })}>
|
||||
{isUndeployed
|
||||
? t('deployDrawer.deploy')
|
||||
: status === 'ready'
|
||||
? t('deployTab.deployOtherVersion')
|
||||
: status === 'deploying'
|
||||
? t('deployTab.viewProgress')
|
||||
: t('deployTab.viewError')}
|
||||
</Button>
|
||||
{!isUndeployed && (
|
||||
<DropdownMenu modal={false}>
|
||||
<DropdownMenuTrigger
|
||||
aria-label={t('deployTab.moreActions')}
|
||||
className="flex h-7 w-7 items-center justify-center rounded-md text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary"
|
||||
>
|
||||
<span className="i-ri-more-line h-4 w-4" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[200px]">
|
||||
<DropdownMenuItem
|
||||
className="gap-2 px-3"
|
||||
onClick={() => {
|
||||
const runtimeInstanceId = deploymentId(row)
|
||||
if (status === 'deploying') {
|
||||
cancelDeployment.mutate({
|
||||
params: {
|
||||
appInstanceId,
|
||||
runtimeInstanceId,
|
||||
},
|
||||
body: {
|
||||
appInstanceId,
|
||||
runtimeInstanceId,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
undeployDeployment.mutate({
|
||||
params: {
|
||||
appInstanceId,
|
||||
runtimeInstanceId,
|
||||
},
|
||||
body: {
|
||||
appInstanceId,
|
||||
runtimeInstanceId,
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<span className="system-sm-regular text-text-destructive">
|
||||
{status === 'deploying' ? t('deployTab.cancelDeployment') : t('deployTab.undeploy')}
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
const chevron = !isUndeployed && (
|
||||
<span
|
||||
className={cn(
|
||||
@ -273,7 +290,7 @@ export function DeployTab({ instanceId: appInstanceId }: {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1 lg:hidden">
|
||||
{actions}
|
||||
<DeploymentRowActions appInstanceId={appInstanceId} envId={envId} row={row} />
|
||||
{chevron}
|
||||
</div>
|
||||
</div>
|
||||
@ -287,7 +304,7 @@ export function DeployTab({ instanceId: appInstanceId }: {
|
||||
<DeploymentStatusSummary row={row} />
|
||||
</div>
|
||||
<div className="hidden min-w-0 items-center justify-end gap-1 lg:flex">
|
||||
{actions}
|
||||
<DeploymentRowActions appInstanceId={appInstanceId} envId={envId} row={row} />
|
||||
{chevron}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -25,8 +25,6 @@ export function InstanceCard({ app }: {
|
||||
}) {
|
||||
const { t } = useTranslation('deployments')
|
||||
const { formatTimeFromNow } = useFormatTimeFromNow()
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
|
||||
|
||||
if (!app.id)
|
||||
return null
|
||||
@ -185,58 +183,71 @@ export function InstanceCard({ app }: {
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<div className="pointer-events-none absolute right-0 bottom-1 left-0 flex h-[42px] shrink-0 items-center pt-1 pr-[6px] pb-[6px] pl-[14px]">
|
||||
<div
|
||||
className={cn(
|
||||
'absolute top-1/2 right-[6px] flex -translate-y-1/2 items-center transition-opacity',
|
||||
menuOpen
|
||||
? 'pointer-events-auto opacity-100'
|
||||
: 'pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100',
|
||||
)}
|
||||
>
|
||||
<DropdownMenu modal={false} open={menuOpen} onOpenChange={setMenuOpen}>
|
||||
<DropdownMenuTrigger
|
||||
aria-label={t('card.moreActions')}
|
||||
className={cn(
|
||||
menuOpen ? 'bg-state-base-hover shadow-none' : 'bg-transparent',
|
||||
'flex h-8 w-8 items-center justify-center rounded-md border-none p-2 hover:bg-state-base-hover',
|
||||
)}
|
||||
>
|
||||
<span aria-hidden className="i-ri-more-fill h-4 w-4 text-text-tertiary" />
|
||||
</DropdownMenuTrigger>
|
||||
{menuOpen && (
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[216px]">
|
||||
<DropdownMenuItem
|
||||
className="gap-2 px-3"
|
||||
onClick={() => {
|
||||
setMenuOpen(false)
|
||||
openDeployDrawer({ appInstanceId: appId })
|
||||
}}
|
||||
>
|
||||
<span className="system-sm-regular text-text-secondary">{t('card.menu.deploy')}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuLinkItem
|
||||
className="gap-2 px-3"
|
||||
render={<Link href={detailHref} />}
|
||||
>
|
||||
<span className="system-sm-regular text-text-secondary">{t('card.menu.viewDetail')}</span>
|
||||
</DropdownMenuLinkItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
aria-disabled
|
||||
title={t('card.menu.deleteDisabled')}
|
||||
className="cursor-not-allowed gap-2 px-3 opacity-50"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<span className="system-sm-regular text-text-destructive">{t('card.menu.delete')}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
<InstanceCardActions appId={appId} detailHref={detailHref} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function InstanceCardActions({ appId, detailHref }: {
|
||||
appId: string
|
||||
detailHref: string
|
||||
}) {
|
||||
const { t } = useTranslation('deployments')
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
|
||||
|
||||
return (
|
||||
<div className="pointer-events-none absolute right-0 bottom-1 left-0 flex h-[42px] shrink-0 items-center pt-1 pr-[6px] pb-[6px] pl-[14px]">
|
||||
<div
|
||||
className={cn(
|
||||
'absolute top-1/2 right-[6px] flex -translate-y-1/2 items-center transition-opacity',
|
||||
menuOpen
|
||||
? 'pointer-events-auto opacity-100'
|
||||
: 'pointer-events-none opacity-0 group-hover:pointer-events-auto group-hover:opacity-100',
|
||||
)}
|
||||
>
|
||||
<DropdownMenu modal={false} open={menuOpen} onOpenChange={setMenuOpen}>
|
||||
<DropdownMenuTrigger
|
||||
aria-label={t('card.moreActions')}
|
||||
className={cn(
|
||||
menuOpen ? 'bg-state-base-hover shadow-none' : 'bg-transparent',
|
||||
'flex h-8 w-8 items-center justify-center rounded-md border-none p-2 hover:bg-state-base-hover',
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
>
|
||||
<span aria-hidden className="i-ri-more-fill h-4 w-4 text-text-tertiary" />
|
||||
</DropdownMenuTrigger>
|
||||
{menuOpen && (
|
||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[216px]">
|
||||
<DropdownMenuItem
|
||||
className="gap-2 px-3"
|
||||
onClick={() => {
|
||||
setMenuOpen(false)
|
||||
openDeployDrawer({ appInstanceId: appId })
|
||||
}}
|
||||
>
|
||||
<span className="system-sm-regular text-text-secondary">{t('card.menu.deploy')}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuLinkItem
|
||||
className="gap-2 px-3"
|
||||
render={<Link href={detailHref} />}
|
||||
>
|
||||
<span className="system-sm-regular text-text-secondary">{t('card.menu.viewDetail')}</span>
|
||||
</DropdownMenuLinkItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
aria-disabled
|
||||
title={t('card.menu.deleteDisabled')}
|
||||
className="cursor-not-allowed gap-2 px-3 opacity-50"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}}
|
||||
>
|
||||
<span className="system-sm-regular text-text-destructive">{t('card.menu.delete')}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
)}
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user