diff --git a/web/features/deployments/detail/overview-tab.tsx b/web/features/deployments/detail/overview-tab.tsx
index 9b51663781..6e57591e40 100644
--- a/web/features/deployments/detail/overview-tab.tsx
+++ b/web/features/deployments/detail/overview-tab.tsx
@@ -2,11 +2,11 @@
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
+import Link from '@/next/link'
import { consoleQuery } from '@/service/client'
import { SectionState } from './common'
import { EnvironmentStrip, EnvironmentStripSkeleton } from './overview-tab/environment-strip'
import { computeOverviewStats } from './overview-tab/overview-drift'
-import { PreviousReleases } from './overview-tab/previous-releases'
import { ReleaseHero, ReleaseHeroSkeleton } from './overview-tab/release-hero'
import { useSourceAppAvailability } from './source-app-availability'
@@ -41,6 +41,33 @@ function SourceAppDeletedNotice() {
)
}
+function ReleaseOverviewSection({ appInstanceId, children }: {
+ appInstanceId: string
+ children: React.ReactNode
+}) {
+ const { t } = useTranslation('deployments')
+
+ return (
+
+
+
+ {t('overview.recentReleases')}
+
+
+ {t('overview.previousReleases.viewAll')}
+
+
+
+
+ {children}
+
+
+ )
+}
+
export function OverviewTab({ appInstanceId }: {
appInstanceId: string
}) {
@@ -60,7 +87,9 @@ export function OverviewTab({ appInstanceId }: {
if (overviewQuery.isLoading) {
return (
-
+
+
+
)
}
@@ -84,7 +113,10 @@ export function OverviewTab({ appInstanceId }: {
if (releasesQuery.isLoading) {
return (
-
+ {sourceAppAvailability.sourceAppUnavailable && }
+
+
+
)
@@ -105,25 +137,24 @@ export function OverviewTab({ appInstanceId }: {
return (
-
{sourceAppAvailability.sourceAppUnavailable && }
-
-
+
+
+
+
+
+
)
}
diff --git a/web/features/deployments/detail/overview-tab/previous-releases.tsx b/web/features/deployments/detail/overview-tab/previous-releases.tsx
deleted file mode 100644
index 64aa518221..0000000000
--- a/web/features/deployments/detail/overview-tab/previous-releases.tsx
+++ /dev/null
@@ -1,86 +0,0 @@
-'use client'
-
-import type { ReleaseRow } from '@dify/contracts/enterprise/types.gen'
-import type { OverviewStats } from './overview-drift'
-import { useSetAtom } from 'jotai'
-import { useTranslation } from 'react-i18next'
-import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
-import Link from '@/next/link'
-import { formatDate, releaseLabel } from '../../release'
-import { openDeployDrawerAtom } from '../../store'
-
-type PreviousReleasesProps = {
- appInstanceId: string
- releaseRows: ReleaseRow[]
- stats: OverviewStats
-}
-
-const PREVIOUS_RELEASES_LIMIT = 5
-
-export function PreviousReleases({ appInstanceId, releaseRows, stats }: PreviousReleasesProps) {
- const { t } = useTranslation('deployments')
- const { formatTimeFromNow } = useFormatTimeFromNow()
- const openDeployDrawer = useSetAtom(openDeployDrawerAtom)
-
- const previous = releaseRows.slice(1, 1 + PREVIOUS_RELEASES_LIMIT).filter(row => row.id)
-
- if (previous.length === 0)
- return null
-
- return (
-
-
-
- {t('overview.previousReleases.title')}
-
-
- {t('overview.previousReleases.viewAll')}
-
-
-
-
-
- {previous.map((row) => {
- const author = row.createdBy?.name ?? ''
- const ago = row.createdAt ? formatTimeFromNow(new Date(row.createdAt).getTime()) : ''
- const deployedCount = row.deployedTo?.length ?? 0
- const propagation = stats.total === 0
- ? t('overview.hero.untargeted')
- : deployedCount === 0
- ? t('overview.previousReleases.retired')
- : t('overview.hero.propagation', { count: deployedCount, total: stats.total })
-
- return (
- -
-
-
- )
- })}
-
-
- )
-}
diff --git a/web/features/deployments/detail/overview-tab/release-hero.tsx b/web/features/deployments/detail/overview-tab/release-hero.tsx
index 9b620c8661..aef24531ed 100644
--- a/web/features/deployments/detail/overview-tab/release-hero.tsx
+++ b/web/features/deployments/detail/overview-tab/release-hero.tsx
@@ -15,68 +15,76 @@ type ReleaseHeroProps = {
}
export function ReleaseHero({ appInstanceId, latestRelease, stats }: ReleaseHeroProps) {
- const { t } = useTranslation('deployments')
+ const { t, i18n } = useTranslation('deployments')
const { formatTimeFromNow } = useFormatTimeFromNow()
const hasRelease = Boolean(latestRelease?.id)
const author = latestRelease?.createdBy?.name ?? ''
const ago = latestRelease?.createdAt ? formatTimeFromNow(new Date(latestRelease.createdAt).getTime()) : ''
+ const deployedEnvironmentNames = Array.from(new Set(
+ latestRelease?.deployedTo
+ ?.map(item => item.environmentName || item.environmentId)
+ .filter((name): name is string => Boolean(name)) ?? [],
+ ))
+ const deployedTargets = deployedEnvironmentNames.join(i18n.language.startsWith('zh') ? '、' : ', ')
const metaParts: { key: string, value: string }[] = []
if (author)
metaParts.push({ key: 'author', value: t('overview.hero.byName', { name: author }) })
if (ago)
metaParts.push({ key: 'ago', value: ago })
- if (stats.total > 0)
- metaParts.push({ key: 'propagation', value: t('overview.hero.propagation', { count: stats.ready, total: stats.total }) })
- else if (hasRelease)
+ if (deployedTargets)
+ metaParts.push({ key: 'deployedTo', value: `${t('versions.col.deployedTo')} ${deployedTargets}` })
+ else if (hasRelease && stats.total === 0)
metaParts.push({ key: 'untargeted', value: t('overview.hero.untargeted') })
return (
-
-
- {hasRelease
- ? (
- <>
-
-
-
-
-
- {releaseLabel(latestRelease)}
+
+
+
+ {hasRelease
+ ? (
+ <>
+
+
+
+
+
+ {releaseLabel(latestRelease)}
+
+
+ {metaParts.length > 0 && (
+
+ {metaParts.map((part, index) => (
+
+ {index > 0 && ·}
+ {part.value}
+
+ ))}
+
+ )}
+ >
+ )
+ : (
+ <>
+
+ {t('overview.hero.empty')}
-
- {metaParts.length > 0 && (
-
- {metaParts.map((part, index) => (
-
- {index > 0 && ·}
- {part.value}
-
- ))}
+
+ {t('overview.hero.emptyDescription')}
- )}
- >
- )
- : (
- <>
-
- {t('overview.hero.empty')}
-
-
- {t('overview.hero.emptyDescription')}
-
- >
- )}
-
-
-
+ >
+ )}
+
+
+
+
)