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')} - - -
- - -
- ) -} 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')} -

- - )} -
-
- + + )} +
+
+ +

)