From 55613da90fc37ca7589e9946ccf03e5261409d72 Mon Sep 17 00:00:00 2001 From: mstrhakr Date: Fri, 10 Apr 2026 11:15:45 -0400 Subject: [PATCH 01/17] feat(stack): centralize container counts and stack state management --- .../compose.manager/include/ComposeList.php | 58 ++----- .../include/ComposeManager.php | 38 ++-- .../include/DashboardStacks.php | 30 ++-- source/compose.manager/include/Exec.php | 7 +- source/compose.manager/include/Util.php | 88 ++++++++++ .../javascript/composeStackUtils.js | 73 ++++++++ tests/unit/DashboardStacksTest.php | 102 ++++++----- tests/unit/StackInfoTest.php | 164 ++++++++++++++++++ 8 files changed, 415 insertions(+), 145 deletions(-) create mode 100644 source/compose.manager/javascript/composeStackUtils.js diff --git a/source/compose.manager/include/ComposeList.php b/source/compose.manager/include/ComposeList.php index a176f5a..4081ee6 100755 --- a/source/compose.manager/include/ComposeList.php +++ b/source/compose.manager/include/ComposeList.php @@ -24,32 +24,16 @@ $composeFile = $stackInfo->composeFilePath ?? ($stackInfo->composeSource . '/' . COMPOSE_FILE_NAMES[0]); $overridePath = $stackInfo->getOverridePath(); - // Use StackInfo's getDefinedServices for accurate service count - $definedServicesList = $stackInfo->getDefinedServices(); - $definedServices = count($definedServicesList); + // Centralized container counts and stack state + $counts = $stackInfo->getContainerCounts(); + $stackState = $stackInfo->getStackState(); - $projectContainers = $stackInfo->getContainerList(); - $runningCount = 0; - $stoppedCount = 0; - $pausedCount = 0; - $restartingCount = 0; - - foreach ($projectContainers as $ct) { - $ctState = $ct['State'] ?? ''; - if ($ctState === 'running') { - $runningCount++; - } elseif ($ctState === 'exited') { - $stoppedCount++; - } elseif ($ctState === 'paused') { - $pausedCount++; - } elseif ($ctState === 'restarting') { - $restartingCount++; - } - } - - // Container counts - $actualContainerCount = count($projectContainers); - $containerCount = $definedServices > 0 ? $definedServices : $actualContainerCount; + $runningCount = $counts['running']; + $stoppedCount = $counts['stopped']; + $pausedCount = $counts['paused']; + $restartingCount = $counts['restarting']; + $actualContainerCount = $counts['total']; + $containerCount = $counts['total']; // Collect container names for the hide-from-docker feature (data attribute) $containerNamesList = []; @@ -124,24 +108,12 @@ $hasInvalidIndirect = ($invalidIndirectPath !== null && trim($invalidIndirectPath) !== ''); $invalidIndirectPathHtml = htmlspecialchars($invalidIndirectPath ?? '', ENT_QUOTES, 'UTF-8'); - // Status like Docker tab (started/stopped with icon) - $status = $isrunning ? ($runningCount == $containerCount ? 'started' : 'partial') : 'stopped'; - // Use exclamation icon for partial state so it looks like a warning - if ($status === 'partial') { - $shape = 'exclamation-circle'; - } elseif ($isrunning) { - $shape = 'play'; - } else { - $shape = 'square'; - } - $color = $status == 'started' ? 'green-text' : ($status == 'partial' ? 'orange-text' : 'grey-text'); - // Use 'partial' outer class for partial state to allow correct styling - $outerClass = $isrunning ? ($runningCount == $containerCount ? 'started' : 'partial') : 'stopped'; - - $statusLabel = $status; - if ($status == 'partial') { - $statusLabel = "partial ($runningCount/$containerCount)"; - } + // Status icon, label, color — derived from centralized getStackState() + $status = $stackState['state']; + $shape = $stackState['shape']; + $color = $stackState['color']; + $outerClass = $status; + $statusLabel = $stackState['label']; // Get stack started_at timestamp via StackInfo $stackStartedAt = $stackInfo->getStartedAt(); diff --git a/source/compose.manager/include/ComposeManager.php b/source/compose.manager/include/ComposeManager.php index c384f77..1bffc3a 100755 --- a/source/compose.manager/include/ComposeManager.php +++ b/source/compose.manager/include/ComposeManager.php @@ -275,6 +275,7 @@ function compose_manager_cpu_spec_count($cpuSpec) +