Skip to content

Commit a05b11f

Browse files
committed
fix(ui): enhance CloneOrMoveAppModal with cluster status handling and error notifications
1 parent 5e49974 commit a05b11f

File tree

1 file changed

+105
-7
lines changed

1 file changed

+105
-7
lines changed

core/ui/src/components/software-center/CloneOrMoveAppModal.vue

Lines changed: 105 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
<NsModal
77
size="default"
88
:visible="isShown"
9-
@modal-hidden="$emit('hide')"
9+
@modal-hidden="onModalHidden"
1010
@primary-click="cloneOrMove"
11+
@modal-shown="onModalShown"
1112
:primary-button-disabled="
1213
!selectedNode ||
1314
(!isClone && installationNode == selectedNode.id) ||
14-
loading.cloneModule
15+
loading.cloneModule ||
16+
loading.getClusterStatus ||
17+
clusterStatus.length == disabledNodes.length
1518
"
1619
>
1720
<template slot="title">{{
@@ -27,9 +30,19 @@
2730
: $t("software_center.move_app_description", { instanceLabel })
2831
}}
2932
</div>
33+
<NsInlineNotification
34+
v-if="error.getClusterStatus"
35+
kind="error"
36+
:title="$t('action.get-cluster-status')"
37+
:description="error.getClusterStatus"
38+
:showCloseButton="false"
39+
/>
3040
<div>{{ $t("software_center.select_destination_node") }}:</div>
3141
<NsInlineNotification
32-
v-if="clusterNodes.length == disabledNodes.length"
42+
v-if="
43+
clusterStatus.length == disabledNodes.length &&
44+
!loading.getClusterStatus
45+
"
3346
kind="info"
3447
:title="
3548
isClone
@@ -42,6 +55,7 @@
4255
class="mg-top-xlg"
4356
@selectNode="onSelectNode"
4457
:disabledNodes="disabledNodes"
58+
:loading="loading.getClusterStatus"
4559
>
4660
<template v-for="(nodeMessages, nodeId) in nodesInfo">
4761
<template :slot="`node-${nodeId}`">
@@ -109,11 +123,14 @@ export default {
109123
data() {
110124
return {
111125
selectedNode: null,
126+
clusterStatus: [],
112127
loading: {
113128
cloneModule: false,
129+
getClusterStatus: true,
114130
},
115131
error: {
116132
cloneModule: "",
133+
getClusterStatus: "",
117134
},
118135
};
119136
},
@@ -173,6 +190,15 @@ export default {
173190
}
174191
nodesInfo[nodeInfo.node_id] = nodeMessages;
175192
}
193+
// Add offline nodes message
194+
for (const node of this.clusterStatus) {
195+
if (!node.online) {
196+
if (!nodesInfo[node.id]) {
197+
nodesInfo[node.id] = [];
198+
}
199+
nodesInfo[node.id].push(this.$t("software_center.node_offline"));
200+
}
201+
}
176202
}
177203
return nodesInfo;
178204
},
@@ -181,20 +207,92 @@ export default {
181207
return [];
182208
}
183209
210+
// Get non-eligible nodes from install_destinations
184211
const ineligibleNodes = this.app.install_destinations
185212
.filter((nodeInfo) => !nodeInfo.eligible)
186213
.map((nodeInfo) => nodeInfo.node_id);
187214
215+
// Get offline nodes from clusterStatus
216+
const offlineNodeIds = this.clusterStatus
217+
.filter((node) => !node.online)
218+
.map((node) => node.id);
219+
188220
if (this.isClone) {
189-
// cloning app
190-
return ineligibleNodes;
221+
// cloning app: combine ineligible and offline nodes
222+
return [...new Set([...ineligibleNodes, ...offlineNodeIds])];
191223
} else {
192-
// moving app: add current node to ineligible nodes and remove possible duplicates
193-
return [...new Set(ineligibleNodes.concat(this.installationNode))];
224+
// moving app: add current node to ineligible/offline nodes and remove possible duplicates
225+
return [
226+
...new Set([
227+
...ineligibleNodes.concat(this.installationNode),
228+
...offlineNodeIds,
229+
]),
230+
];
194231
}
195232
},
196233
},
197234
methods: {
235+
onModalShown() {
236+
// reset state before showing modal
237+
// Force selection to node 1 if only available
238+
if (this.clusterNodes.length == 1) {
239+
this.selectedNode = this.clusterNodes[0];
240+
this.clusterNodes[0].selected = true;
241+
} else {
242+
this.selectedNode = null;
243+
}
244+
this.clusterStatus = [];
245+
this.getClusterStatus();
246+
},
247+
onModalHidden() {
248+
// reset state
249+
this.$emit("hide");
250+
this.clearErrors();
251+
this.selectedNode = null;
252+
this.clusterStatus = [];
253+
this.loading.getClusterStatus = true;
254+
},
255+
async getClusterStatus() {
256+
this.error.getClusterStatus = "";
257+
const taskAction = "get-cluster-status";
258+
259+
// register to task error
260+
this.$root.$off(taskAction + "-aborted");
261+
this.$root.$once(taskAction + "-aborted", this.getClusterStatusAborted);
262+
263+
// register to task completion
264+
this.$root.$off(taskAction + "-completed");
265+
this.$root.$once(
266+
taskAction + "-completed",
267+
this.getClusterStatusCompleted
268+
);
269+
270+
const res = await to(
271+
this.createClusterTask({
272+
action: taskAction,
273+
extra: {
274+
title: this.$t("action." + taskAction),
275+
isNotificationHidden: true,
276+
},
277+
})
278+
);
279+
const err = res[0];
280+
281+
if (err) {
282+
console.error(`error creating task ${taskAction}`, err);
283+
this.error.getClusterStatus = this.getErrorMessage(err);
284+
return;
285+
}
286+
},
287+
getClusterStatusAborted(taskResult, taskContext) {
288+
console.error(`${taskContext.action} aborted`, taskResult);
289+
this.error.getClusterStatus = this.$t("error.generic_error");
290+
this.loading.getClusterStatus = false;
291+
},
292+
getClusterStatusCompleted(taskContext, taskResult) {
293+
this.clusterStatus = taskResult.output.nodes;
294+
this.loading.getClusterStatus = false;
295+
},
198296
async cloneOrMove() {
199297
this.loading.cloneModule = true;
200298
const taskAction = "clone-module";

0 commit comments

Comments
 (0)