Skip to content

Commit 8f14547

Browse files
authored
Merge pull request louislam#800 from louislam/dockge
Merged @maca134's pull request that improves the stack list ui when using agents.
2 parents 9854a46 + 5111d01 commit 8f14547

File tree

3 files changed

+68
-27
lines changed

3 files changed

+68
-27
lines changed

frontend/src/components/StackList.vue

Lines changed: 64 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
<div class="list-header">
44
<div class="header-top">
55
<!-- TODO -->
6-
<button v-if="false" class="btn btn-outline-normal ms-2" :class="{ 'active': selectMode }" type="button" @click="selectMode = !selectMode">
6+
<button v-if="false" class="btn btn-outline-normal ms-2" :class="{ 'active': selectMode }" type="button"
7+
@click="selectMode = !selectMode">
78
{{ $t("Select") }}
89
</button>
910

@@ -28,34 +29,36 @@
2829

2930
<!-- TODO: Selection Controls -->
3031
<div v-if="selectMode && false" class="selection-controls px-2 pt-2">
31-
<input
32-
v-model="selectAll"
33-
class="form-check-input select-input"
34-
type="checkbox"
35-
/>
32+
<input v-model="selectAll" class="form-check-input select-input" type="checkbox" />
3633

37-
<button class="btn-outline-normal" @click="pauseDialog"><font-awesome-icon icon="pause" size="sm" /> {{ $t("Pause") }}</button>
38-
<button class="btn-outline-normal" @click="resumeSelected"><font-awesome-icon icon="play" size="sm" /> {{ $t("Resume") }}</button>
34+
<button class="btn-outline-normal" @click="pauseDialog"><font-awesome-icon icon="pause" size="sm" /> {{
35+
$t("Pause") }}</button>
36+
<button class="btn-outline-normal" @click="resumeSelected"><font-awesome-icon icon="play" size="sm" />
37+
{{ $t("Resume") }}</button>
3938

4039
<span v-if="selectedStackCount > 0">
41-
{{ $t("selectedStackCount", [ selectedStackCount ]) }}
40+
{{ $t("selectedStackCount", [selectedStackCount]) }}
4241
</span>
4342
</div>
4443
</div>
4544
<div ref="stackList" class="stack-list" :class="{ scrollbar: scrollbar }" :style="stackListStyle">
46-
<div v-if="Object.keys(sortedStackList).length === 0" class="text-center mt-3">
45+
<div v-if="agentStackList[0] && agentStackList[0].stacks.length === 0" class="text-center mt-3">
4746
<router-link to="/compose">{{ $t("addFirstStackMsg") }}</router-link>
4847
</div>
49-
50-
<StackListItem
51-
v-for="(item, index) in sortedStackList"
52-
:key="index"
53-
:stack="item"
54-
:isSelectMode="selectMode"
55-
:isSelected="isSelected"
56-
:select="select"
57-
:deselect="deselect"
58-
/>
48+
<div class="stack-list-inner" v-for="(agent, index) in agentStackList" :key="index">
49+
<div v-if="$root.agentCount > 1" class="p-2 agent-select"
50+
@click="closedAgents.set(agent.endpoint, !closedAgents.get(agent.endpoint))">
51+
<span class="me-1">
52+
<font-awesome-icon v-show="closedAgents.get(agent.endpoint)" icon="chevron-circle-right" />
53+
<font-awesome-icon v-show="!closedAgents.get(agent.endpoint)" icon="chevron-circle-down" />
54+
</span>
55+
<span v-if="agent.endpoint === 'current'">{{ $t("currentEndpoint") }}</span>
56+
<span v-else>{{ agent.endpoint }}</span>
57+
</div>
58+
<StackListItem v-show="$root.agentCount === 1 || !closedAgents.get(agent.endpoint)"
59+
v-for="(item, index) in agent.stacks" :key="index" :stack="item" :isSelectMode="selectMode"
60+
:isSelected="isSelected" :select="select" :deselect="deselect" />
61+
</div>
5962
</div>
6063
</div>
6164

@@ -92,7 +95,8 @@ export default {
9295
status: null,
9396
active: null,
9497
tags: null,
95-
}
98+
},
99+
closedAgents: new Map(),
96100
};
97101
},
98102
computed: {
@@ -119,7 +123,7 @@ export default {
119123
* Returns a sorted list of stacks based on the applied filters and search text.
120124
* @returns {Array} The sorted list of stacks.
121125
*/
122-
sortedStackList() {
126+
agentStackList() {
123127
let result = Object.values(this.$root.completeStackList);
124128
125129
result = result.filter(stack => {
@@ -187,6 +191,29 @@ export default {
187191
return m1.name.localeCompare(m2.name);
188192
});
189193
194+
// Group stacks by endpoint, sorting them so the local endpoint is first
195+
// and the rest are sorted alphabetically
196+
result = [
197+
...result.reduce((acc, stack) => {
198+
const endpoint = stack.endpoint || 'current';
199+
if (!acc.has(endpoint)) {
200+
acc.set(endpoint, []);
201+
}
202+
acc.get(endpoint).push(stack);
203+
return acc;
204+
}, new Map()).entries()
205+
].map(([endpoint, stacks]) => ({
206+
endpoint,
207+
stacks
208+
})).sort((a, b) => {
209+
if (a.endpoint === 'current' && b.endpoint !== 'current') {
210+
return -1;
211+
} else if (a.endpoint !== 'current' && b.endpoint === 'current') {
212+
return 1;
213+
}
214+
return a.endpoint.localeCompare(b.endpoint);
215+
});
216+
190217
return result;
191218
},
192219
@@ -221,7 +248,7 @@ export default {
221248
},
222249
watch: {
223250
searchText() {
224-
for (let stack of this.sortedStackList) {
251+
for (let stack of this.agentStackList) {
225252
if (!this.selectedStacks[stack.id]) {
226253
if (this.selectAll) {
227254
this.disableSelectAllWatcher = true;
@@ -236,7 +263,7 @@ export default {
236263
this.selectedStacks = {};
237264
238265
if (this.selectAll) {
239-
this.sortedStackList.forEach((item) => {
266+
this.agentStackList.forEach((item) => {
240267
this.selectedStacks[item.id] = true;
241268
});
242269
}
@@ -331,7 +358,7 @@ export default {
331358
pauseSelected() {
332359
Object.keys(this.selectedStacks)
333360
.filter(id => this.$root.stackList[id].active)
334-
.forEach(id => this.$root.getSocket().emit("pauseStack", id, () => {}));
361+
.forEach(id => this.$root.getSocket().emit("pauseStack", id, () => { }));
335362
336363
this.cancelSelectMode();
337364
},
@@ -342,7 +369,7 @@ export default {
342369
resumeSelected() {
343370
Object.keys(this.selectedStacks)
344371
.filter(id => !this.$root.stackList[id].active)
345-
.forEach(id => this.$root.getSocket().emit("resumeStack", id, () => {}));
372+
.forEach(id => this.$root.getSocket().emit("resumeStack", id, () => { }));
346373
347374
this.cancelSelectMode();
348375
},
@@ -444,4 +471,15 @@ export default {
444471
gap: 10px;
445472
}
446473
474+
.agent-select {
475+
cursor: pointer;
476+
font-size: 14px;
477+
font-weight: 500;
478+
color: $dark-font-color3;
479+
padding-left: 10px;
480+
padding-right: 10px;
481+
display: flex;
482+
align-items: center;
483+
user-select: none;
484+
}
447485
</style>

frontend/src/components/StackListItem.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<Uptime :stack="stack" :fixed-width="true" class="me-2" />
44
<div class="title">
55
<span>{{ stackName }}</span>
6-
<div v-if="$root.agentCount > 1" class="endpoint">{{ endpointDisplay }}</div>
76
</div>
87
</router-link>
98
</template>

frontend/src/icon.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ import {
5454
faTerminal, faWarehouse, faHome, faRocket,
5555
faRotate,
5656
faCloudArrowDown, faArrowsRotate,
57+
faChevronCircleRight,
58+
faChevronCircleDown,
5759
} from "@fortawesome/free-solid-svg-icons";
5860

5961
library.add(
@@ -109,6 +111,8 @@ library.add(
109111
faRotate,
110112
faCloudArrowDown,
111113
faArrowsRotate,
114+
faChevronCircleRight,
115+
faChevronCircleDown,
112116
);
113117

114118
export { FontAwesomeIcon };

0 commit comments

Comments
 (0)