From 6193a009de71634c69729416312874ef4b9ed96a Mon Sep 17 00:00:00 2001 From: archipelago Date: Sun, 9 Aug 2026 06:50:05 -0400 Subject: [PATCH] fix(federation-ui): show trust-change failures inside the modal, not behind it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit changeTrust() wrote failures into `error`, which renders inside — page content that sits BEHIND the open NodeDetailModal. So when promoting a peer to Trusted failed, the operator saw the dropdown snap back and nothing else: the error banner was painted on a surface the modal was covering. Reported as "the error doesn't show in the modal", 2026-08-09. NodeDetailModal gains an actionError prop rendered directly under the Trust Level control, and Federation.vue routes failures there whenever that node's modal is open (clearing on close and on the next attempt). The page banner still serves failures with no modal in the way, and the password-retry path keeps its own in-modal error as before. Verified: vue-tsc clean; federation component tests 5/5. Co-Authored-By: Claude Fable 5 --- neode-ui/src/views/Federation.vue | 14 ++++++++++++-- neode-ui/src/views/federation/NodeDetailModal.vue | 6 ++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/neode-ui/src/views/Federation.vue b/neode-ui/src/views/Federation.vue index dc01c548..9fc43ddd 100644 --- a/neode-ui/src/views/Federation.vue +++ b/neode-ui/src/views/Federation.vue @@ -190,7 +190,8 @@ :dwn-syncing="dwnSyncing" :deploying="deploying" :deploy-result="deployResult" - @close="selectedNode = null" + :action-error="nodeActionError" + @close="selectedNode = null; nodeActionError = ''" @change-trust="changeTrust" @remove-node="removeNode" @deploy-app="deployApp" @@ -272,6 +273,9 @@ const nodesRes = useCachedResource({ const nodes = computed(() => nodesRes.data.value ?? []) const loading = computed(() => nodesRes.loadState.value === 'loading') const error = ref('') +/** Failure from an action taken inside NodeDetailModal (trust dropdown). Kept + * separate from `error`, which renders in NodeList behind the modal. */ +const nodeActionError = ref('') const selectedNode = ref(null) const inviteType = ref<'trusted' | 'observer'>('trusted') @@ -656,6 +660,7 @@ async function requestTrustChange(did: string, level: string, password?: string) } async function changeTrust(did: string, level: string) { + nodeActionError.value = '' try { await requestTrustChange(did, level) } catch (e) { @@ -667,7 +672,12 @@ async function changeTrust(did: string, level: string) { ) return } - error.value = e instanceof Error ? e.message : 'Failed to update trust level' + // Show the failure INSIDE the open modal. `error` renders in NodeList, + // which sits behind NodeDetailModal — so routing it there made a failed + // trust change look like the dropdown simply doing nothing. + const msg = e instanceof Error ? e.message : 'Failed to update trust level' + if (selectedNode.value?.did === did) nodeActionError.value = msg + else error.value = msg } } diff --git a/neode-ui/src/views/federation/NodeDetailModal.vue b/neode-ui/src/views/federation/NodeDetailModal.vue index 1fd7ded7..9c91f665 100644 --- a/neode-ui/src/views/federation/NodeDetailModal.vue +++ b/neode-ui/src/views/federation/NodeDetailModal.vue @@ -37,6 +37,7 @@

Granted via: {{ trustSourceLabel }}

+

Added

@@ -146,6 +147,11 @@ const props = defineProps<{ dwnSyncing: boolean deploying: boolean deployResult: string + /** Failure from an action taken INSIDE this modal (e.g. the trust dropdown). + * Federation.vue used to route these to the page-level banner in NodeList, + * which sits behind this modal — so a failed trust change looked like + * nothing happening at all. */ + actionError?: string }>() const emit = defineEmits<{