mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-22 08:06:42 +00:00
feat(sidebar): surface partial status for multi-container stacks (#1426)
Bulk stack-status aggregation collapsed a stack to "running" as soon as any container was up, so a multi-container stack with crashed containers showed a green UP pill and the degradation was invisible from the sidebar. Add a crash-aware "partial" state: a stack is partial when at least one container is running and at least one has genuinely failed (exited with a non-zero code, dead, or crash-looping). Cleanly finished one-shot containers (exit 0) and clean restart-policy cycling do not count, so an app with a completed init job stays UP. The exit code is read from the container Status string, so no extra inspect calls are needed. Render partial as an amber PT pill with a hover tooltip showing the running/total count, fold it into the Down filter (needs-attention), and treat it as running for context-menu lifecycle actions so operators keep stop/restart/update. The dashboard stack-health table, cross-node search rows, and the command palette all pick up the new state through the shared status surfaces.
This commit is contained in:
@@ -6,9 +6,13 @@ import type { Node } from '@/context/NodeContext';
|
||||
// buildMenuCtx derives canOpenApp from the active node plus the stack's
|
||||
// published port; only the fields it reads need to be real, the handler
|
||||
// closures are never invoked here.
|
||||
function makeOptions(activeNode: Node | null, stackPorts: Record<string, number | undefined>) {
|
||||
function makeOptions(
|
||||
activeNode: Node | null,
|
||||
stackPorts: Record<string, number | undefined>,
|
||||
stackStatuses: Record<string, string> = { 'web.yml': 'running' },
|
||||
) {
|
||||
const stackListState = {
|
||||
stackStatuses: { 'web.yml': 'running' },
|
||||
stackStatuses,
|
||||
stackPorts,
|
||||
isStackBusy: () => false,
|
||||
isPinned: () => false,
|
||||
@@ -62,3 +66,21 @@ describe('useSidebarContextMenu canOpenApp', () => {
|
||||
expect(result.current('web.yml').canOpenApp).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useSidebarContextMenu stackStatus', () => {
|
||||
it('maps a partial stack to running so it gets running-stack actions', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useSidebarContextMenu(makeOptions({ id: 1, type: 'local' } as Node, {}, { 'web.yml': 'partial' })));
|
||||
expect(result.current('web.yml').stackStatus).toBe('running');
|
||||
});
|
||||
|
||||
it('passes exited and unknown through unchanged', () => {
|
||||
const exited = renderHook(() =>
|
||||
useSidebarContextMenu(makeOptions({ id: 1, type: 'local' } as Node, {}, { 'web.yml': 'exited' })));
|
||||
expect(exited.result.current('web.yml').stackStatus).toBe('exited');
|
||||
|
||||
const missing = renderHook(() =>
|
||||
useSidebarContextMenu(makeOptions({ id: 1, type: 'local' } as Node, {}, {})));
|
||||
expect(missing.result.current('web.yml').stackStatus).toBe('unknown');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user