diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/DeploymentTab/DeploymentTab.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/DeploymentTab/DeploymentTab.tsx
index 8a3bcc0e..4322e5d1 100644
--- a/ui/src/pages/AppsTab/AppDeploymentPage/DeploymentTab/DeploymentTab.tsx
+++ b/ui/src/pages/AppsTab/AppDeploymentPage/DeploymentTab/DeploymentTab.tsx
@@ -12,11 +12,9 @@ interface Props {
appSlug: string;
envSlug: string;
externalUrl: string;
- onStop: (deploymentId: string) => void;
- onStart: (deploymentId: string) => void;
}
-export function DeploymentTab({ deployments, versions, appSlug, envSlug, externalUrl, onStop, onStart }: Props) {
+export function DeploymentTab({ deployments, versions, appSlug, envSlug, externalUrl }: Props) {
const latest = deployments
.slice()
.sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? ''))[0] ?? null;
@@ -33,8 +31,6 @@ export function DeploymentTab({ deployments, versions, appSlug, envSlug, externa
deployment={latest}
version={version}
externalUrl={externalUrl}
- onStop={() => onStop(latest.id)}
- onStart={() => onStart(latest.id)}
/>
{latest.status === 'STARTING' && (
diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/DeploymentTab/StatusCard.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/DeploymentTab/StatusCard.tsx
index 617c14e2..a8a6294c 100644
--- a/ui/src/pages/AppsTab/AppDeploymentPage/DeploymentTab/StatusCard.tsx
+++ b/ui/src/pages/AppsTab/AppDeploymentPage/DeploymentTab/StatusCard.tsx
@@ -1,4 +1,4 @@
-import { Badge, StatusDot, MonoText, Button } from '@cameleer/design-system';
+import { Badge, StatusDot, MonoText } from '@cameleer/design-system';
import type { Deployment, AppVersion } from '../../../../api/queries/admin/apps';
import { timeAgo } from '../../../../utils/format-utils';
import styles from '../AppDeploymentPage.module.css';
@@ -13,21 +13,13 @@ const DEPLOY_STATUS_DOT = {
STOPPING: 'stale', STOPPED: 'dead', FAILED: 'error',
} as const;
-function formatBytes(bytes: number): string {
- if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(1)} MB`;
- if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
- return `${bytes} B`;
-}
-
interface Props {
deployment: Deployment;
version: AppVersion | null;
externalUrl: string;
- onStop: () => void;
- onStart: () => void;
}
-export function StatusCard({ deployment, version, externalUrl, onStop, onStart }: Props) {
+export function StatusCard({ deployment, version, externalUrl }: Props) {
const running = deployment.replicaStates?.filter((r) => r.status === 'RUNNING').length ?? 0;
const total = deployment.replicaStates?.length ?? 0;
@@ -56,12 +48,6 @@ export function StatusCard({ deployment, version, externalUrl, onStop, onStart }
{deployment.errorMessage}
)}
-
-
- {(deployment.status === 'RUNNING' || deployment.status === 'STARTING' || deployment.status === 'DEGRADED')
- && }
- {deployment.status === 'STOPPED' && }
-
);
}
diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx
index e12db860..503ade45 100644
--- a/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx
+++ b/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx
@@ -1,7 +1,7 @@
import { useState, useEffect, useRef } from 'react';
import { useParams, useLocation, useNavigate } from 'react-router';
import { useQueryClient } from '@tanstack/react-query';
-import { AlertDialog, Button, Tabs, useToast } from '@cameleer/design-system';
+import { AlertDialog, Badge, Button, Tabs, useToast } from '@cameleer/design-system';
import { useEnvironmentStore } from '../../../api/environment-store';
import { useEnvironments } from '../../../api/queries/admin/environments';
import {
@@ -60,6 +60,9 @@ export default function AppDeploymentPage() {
const { data: deployments = [] } = useDeployments(selectedEnv, app?.slug);
const currentDeployment = deployments.find((d) => d.status === 'RUNNING') ?? null;
const activeDeployment = deployments.find((d) => d.status === 'STARTING') ?? null;
+ const latestDeployment = deployments
+ .slice()
+ .sort((a, b) => (b.createdAt ?? '').localeCompare(a.createdAt ?? ''))[0] ?? null;
const { data: agentConfig = null } = useApplicationConfig(app?.slug, selectedEnv);
const { data: dirtyState, isLoading: dirtyLoading } = useDirtyState(selectedEnv, app?.slug);
@@ -398,7 +401,12 @@ export default function AppDeploymentPage() {
{/* ── Page header ── */}
-
{app ? app.displayName : 'Create Application'}
+
+
{app ? app.displayName : 'Create Application'}
+ {app && !deploymentInProgress && (dirty.anyLocalEdit || serverDirtyAgainstDeploy) && (
+
+ )}
+
{dirty.anyLocalEdit && (
+ {app && latestDeployment && (
+ latestDeployment.status === 'RUNNING'
+ || latestDeployment.status === 'STARTING'
+ || latestDeployment.status === 'DEGRADED'
+ ) && (
+
+ )}
+ {app && latestDeployment && latestDeployment.status === 'STOPPED' && (
+
+ )}
{app && (