- Extract inline handleRestore mapping into snapshotToForm(snapshot, defaults) helper
- Export defaultForm from useDeploymentPageState for use in ConfigPanel
- Replace ConfigPanel stub with real read-only snapshot renderer + Snapshot/Diff toggle
- Add fieldDiff deep-equal field-walk helper with nested object + array support
- Forward optional currentForm prop through CheckpointDetailDrawer to ConfigPanel
- 13 new tests across diff.test.ts, snapshotToForm.test.ts, ConfigPanel.test.tsx (all pass)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract 14 inline style blocks from CheckpointDetailDrawer index.tsx and
LogsPanel.tsx into a shared CSS module using DS CSS variables throughout.
Narrow the LogsPanel useMemo dep array from the full deployment object to
deployment.id + deployment.replicaStates to prevent spurious query
invalidation on every TanStack Query poll.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds the CheckpointDetailDrawer with Logs/Config tabs. LogsPanel scopes
logs to a deployment's replicas via instanceIds derived from replicaStates
+ generation suffix. Stub ConfigPanel placeholder for Task 11.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fetched fresh openapi.json from local backend (Tasks 3-5 changes)
- Regenerated schema.d.ts via openapi-typescript
- Added createdBy: string | null to Deployment interface in apps.ts
- Added instanceIds?: string[] to UseInfiniteApplicationLogsArgs with sort/serialize/queryKey/URLSearchParams wiring
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: rename deleteTerminalByAppAndEnvironment → deleteFailedByAppAndEnvironment.
STOPPED rows were being wiped on every redeploy, so Checkpoints was always empty.
Now only FAILED rows are pruned; STOPPED deployments are retained as restorable
checkpoints (they still carry deployed_config_snapshot from their RUNNING window).
- UI filter: any deployment with a snapshot is a checkpoint (was RUNNING|DEGRADED only,
which excluded the main case — the previous blue/green deployment now in STOPPED).
- UI placement: Checkpoints disclosure now renders inside IdentitySection, matching
the design spec.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resources tab: add a hint under the Deploy Strategy dropdown that
explains the blue-green vs rolling trade-off (resource peak, failure
semantics), switching text based on the current selection.
StatusCard: show the active deployment's strategy inline in the info
grid so users can tell at a glance which path was taken for a given
deployment.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previous ring approach was too subtle against most env colors. Replace
the DS Avatar with a purpose-built circle rendered in the environment's
chosen color, showing 1–2 letter initials in white. Fills the full
circle so the color reads at a glance from across the list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1. Add a 'Pending deploy' Badge next to the app name when there are
local edits or the saved state differs from the last deploy. Makes
the undeployed-changes state visible even when the user isn't looking
at the tab asterisks.
2. Move Start/Stop buttons from StatusCard into the page header, next
to Delete. Runs off the latest deployment's status — Stop when
RUNNING/STARTING/DEGRADED, Start (triggers a redeploy of the last
version) when STOPPED. DeploymentTab and StatusCard shed their
onStop/onStart props.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DeploymentExecutor already persists errorMessage on FAILED transitions
but the UI never rendered it — users saw "FAILED" with no explanation.
Add a bordered error block above the action row when a deployment is
FAILED, preserving whitespace and wrapping long Docker error bodies
(e.g. 409 conflict JSON).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wrap Avatar in a span with box-shadow outline in the environment's
chosen color (slate/red/amber/green/teal/blue/purple/pink). Applied to
both the list row and the detail header. Keeps the Avatar's name-hash
interior so initials remain distinguishable; the ring just signals
which env you're looking at at a glance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously `dirtyState?.dirty ?? true` caused a stale `Redeploy` label
to flash briefly while the first fetch was in flight. Gate the default
on isLoading so the button starts as `Save (disabled)` until the
endpoint resolves — spurious Redeploy clicks were harmless but the
loading-state UX was wrong.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the Number.isInteger normalization hack in useDeploymentPageState
that mapped 1.0 → "1.0" but broke for values like 1.10 (which round-trip
to 1.1). Instead, useFormDirty now parseFloats samplingRate on both sides
before comparing, so "1", "1.0", and "1.00" all compare equal regardless
of how the backend serializes the number.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On long deployment histories the StartupLogPanel would render off-screen
when the user clicked a row. Ref + useEffect scrolls the panel into view
with block:'nearest' so expanding a row that's already in view doesn't
cause a disorienting jump.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handleRestore previously returned silently when deployedConfigSnapshot
was null, leaving the user wondering why their click did nothing. Show
a warning toast explaining that the checkpoint predates snapshotting.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Snapshot is written by DeploymentExecutor before the RUNNING/DEGRADED
split, so DEGRADED rows already carry a deployed_config_snapshot. Treat
them as checkpoints — partial-healthy deploys still produced a working
config worth restoring. Aligns repo query with UI filter.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bug 1: default serverDirtyAgainstDeploy to true (not false) while
dirtyState query is loading — prevents the button showing 'Save'
instead of 'Redeploy' on apps with no successful deployment yet.
- Bug 2: normalize samplingRate from server as '<n>.0' when the value
is a whole-number float so serverState matches form after save,
eliminating spurious dirty detection that kept Save enabled.
- Bug 3: add success toast after handleSave completes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Issue 1: add List<String> sensitiveKeys as 4th field to DeploymentConfigSnapshot; populate
from agentConfig.getSensitiveKeys() in DeploymentExecutor; handleRestore hydrates from
snap.sensitiveKeys directly; Deployment type in apps.ts gains sensitiveKeys field
- Issue 2: after createApp succeeds, refetchQueries(['apps', envSlug]) before navigate so the
new app is in cache before the router renders the deployed view (eliminates transient Save-
disabled flash)
- Issue 3: useDeploymentPageState useEffect now uses prevServerStateRef to detect local edits;
background refetches only overwrite form when no local changes are present
- Issue 5: handleRedeploy invalidates dirty-state + versions queries after createDeployment
resolves; handleSave invalidates dirty-state after staged save
- Issue 10: DirtyStateCalculator strips volatile agentConfig keys (version, updatedAt, updatedBy,
environment, application) before JSON comparison via scrubAgentConfig(); adds
versionBumpDoesNotMarkDirty test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AppsTab.tsx shrunk from 1387 to 109 lines — router now owns /apps/new
and /apps/:slug via AppDeploymentPage; list-only file retained.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add deployedConfigSnapshot field to Deployment interface (mirrors server shape)
- Remove the Task 10.3 cast in handleRestore now that the type has the field
- New useUnsavedChangesBlocker hook (react-router useBlocker, v7.13.1)
- Wire AlertDialog into AppDeploymentPage for in-app navigation guard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DeploymentTab composes StatusCard, DeploymentProgress, StartupLogPanel,
and HistoryDisclosure for the latest deployment. StartupLogPanel gains an
optional className prop, drops the fixed maxHeight, and its .panel rule
uses flex-column + min-height:0 so a parent can drive its height.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Collapsible deployment history table (sorted newest-first) with
click-to-expand StartupLogPanel for any historical deployment row.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Status badge, replica count, URL, JAR/checksum grid, and stop/start
actions for the latest deployment. CSS added to AppDeploymentPage.module.css.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ports the ConfigSubTab traces/taps and route recording content into
standalone tab components. Each write goes straight to live agents via
useUpdateApplicationConfig (apply='live'). A local draft state prevents
stale reads during the async flush. LiveBanner is rendered at the top of
both tabs to communicate the live-apply semantics.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a warning banner that communicates live-apply semantics (changes
bypass the Save/Redeploy cycle). Uses --warning-bg / --warning-border
DS tokens. CSS class .liveBanner added to AppDeploymentPage.module.css.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>