diff --git a/frontend/src/components/StackList.vue b/frontend/src/components/StackList.vue index 6b00cb24..96c81a1f 100644 --- a/frontend/src/components/StackList.vue +++ b/frontend/src/components/StackList.vue @@ -3,7 +3,8 @@
- @@ -28,34 +29,36 @@
- + - - + + - {{ $t("selectedStackCount", [ selectedStackCount ]) }} + {{ $t("selectedStackCount", [selectedStackCount]) }}
-
+
{{ $t("addFirstStackMsg") }}
- - +
+
+ + + + + {{ $t("currentEndpoint") }} + {{ agent.endpoint }} +
+ +
@@ -92,7 +95,8 @@ export default { status: null, active: null, tags: null, - } + }, + closedAgents: new Map(), }; }, computed: { @@ -119,7 +123,7 @@ export default { * Returns a sorted list of stacks based on the applied filters and search text. * @returns {Array} The sorted list of stacks. */ - sortedStackList() { + agentStackList() { let result = Object.values(this.$root.completeStackList); result = result.filter(stack => { @@ -187,6 +191,29 @@ export default { return m1.name.localeCompare(m2.name); }); + // Group stacks by endpoint, sorting them so the local endpoint is first + // and the rest are sorted alphabetically + result = [ + ...result.reduce((acc, stack) => { + const endpoint = stack.endpoint || 'current'; + if (!acc.has(endpoint)) { + acc.set(endpoint, []); + } + acc.get(endpoint).push(stack); + return acc; + }, new Map()).entries() + ].map(([endpoint, stacks]) => ({ + endpoint, + stacks + })).sort((a, b) => { + if (a.endpoint === 'current' && b.endpoint !== 'current') { + return -1; + } else if (a.endpoint !== 'current' && b.endpoint === 'current') { + return 1; + } + return a.endpoint.localeCompare(b.endpoint); + }); + return result; }, @@ -221,7 +248,7 @@ export default { }, watch: { searchText() { - for (let stack of this.sortedStackList) { + for (let stack of this.agentStackList) { if (!this.selectedStacks[stack.id]) { if (this.selectAll) { this.disableSelectAllWatcher = true; @@ -236,7 +263,7 @@ export default { this.selectedStacks = {}; if (this.selectAll) { - this.sortedStackList.forEach((item) => { + this.agentStackList.forEach((item) => { this.selectedStacks[item.id] = true; }); } @@ -331,7 +358,7 @@ export default { pauseSelected() { Object.keys(this.selectedStacks) .filter(id => this.$root.stackList[id].active) - .forEach(id => this.$root.getSocket().emit("pauseStack", id, () => {})); + .forEach(id => this.$root.getSocket().emit("pauseStack", id, () => { })); this.cancelSelectMode(); }, @@ -342,7 +369,7 @@ export default { resumeSelected() { Object.keys(this.selectedStacks) .filter(id => !this.$root.stackList[id].active) - .forEach(id => this.$root.getSocket().emit("resumeStack", id, () => {})); + .forEach(id => this.$root.getSocket().emit("resumeStack", id, () => { })); this.cancelSelectMode(); }, @@ -444,4 +471,15 @@ export default { gap: 10px; } +.agent-select { + cursor: pointer; + font-size: 14px; + font-weight: 500; + color: $dark-font-color3; + padding-left: 10px; + padding-right: 10px; + display: flex; + align-items: center; + user-select: none; +} diff --git a/frontend/src/components/StackListItem.vue b/frontend/src/components/StackListItem.vue index 7e7561b2..d1e48cd1 100644 --- a/frontend/src/components/StackListItem.vue +++ b/frontend/src/components/StackListItem.vue @@ -3,7 +3,6 @@
{{ stackName }} -
{{ endpointDisplay }}
diff --git a/frontend/src/icon.ts b/frontend/src/icon.ts index 0599e6af..588b3fc1 100644 --- a/frontend/src/icon.ts +++ b/frontend/src/icon.ts @@ -54,6 +54,8 @@ import { faTerminal, faWarehouse, faHome, faRocket, faRotate, faCloudArrowDown, faArrowsRotate, + faChevronCircleRight, + faChevronCircleDown, } from "@fortawesome/free-solid-svg-icons"; library.add( @@ -109,6 +111,8 @@ library.add( faRotate, faCloudArrowDown, faArrowsRotate, + faChevronCircleRight, + faChevronCircleDown, ); export { FontAwesomeIcon };