feat(ui): Credentials + OpenWrtGateway render from cached resources — B4 complete
Demo images / Build & push demo images (push) Failing after 2m10s

- Credentials: identity.list + identity.list-credentials become
  useCachedResource entries; explicit reloads still toast on failure.
- OpenWrtGateway: openwrt.get-status caches in the shared store (revisits
  paint the last router state instantly); the connect flow's
  params/No-router-configured semantics are preserved on top of the entry.
- ContainerApps assessed and left as-is: its Pinia store already persists
  across navigation, keeps last data on error, and gates the spinner on
  empty — same class as Apps/Marketplace/Fleet.

This closes the B4 rollout list from docs/FIPS-UPTIME-AND-UI-STATE-PLAN.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-28 06:27:05 -04:00
co-authored by Claude Fable 5
parent 8fd72b947a
commit 8907cc47d9
3 changed files with 56 additions and 45 deletions
+22 -14
View File
@@ -2,6 +2,7 @@
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { rpcClient } from '@/api/rpc-client'
import { useResourcesStore } from '@/stores/resources'
import BackButton from '@/components/BackButton.vue'
const router = useRouter()
@@ -73,8 +74,15 @@ interface ScannedNetwork {
encryption: string
}
const status = ref<RouterStatus | null>(null)
const loading = ref(true)
const status = computed(() => statusEntry().data)
// Router status is cached in the shared resources store so revisits paint
// the last-known state instantly while a fresh read runs behind it.
const resources = useResourcesStore()
const statusEntry = () => resources.entry<RouterStatus>('server.openwrt-status')
const loading = computed(() => {
const s = statusEntry().loadState
return s === 'loading' || s === 'refreshing' || (s === 'idle' && statusEntry().data === null)
})
const error = ref('')
const host = ref('')
const sshUser = ref('root')
@@ -115,25 +123,25 @@ const dhcpLimit = ref(150)
const masqEnabled = ref(true)
async function load(params?: Record<string, string>) {
loading.value = true
error.value = ''
try {
status.value = await rpcClient.call<RouterStatus>({
await resources.refresh<RouterStatus>('server.openwrt-status', () =>
rpcClient.call<RouterStatus>({
method: 'openwrt.get-status',
params: params ?? {},
timeout: 30000,
})
showConnectForm.value = false
if (params) connectedParams.value = params
} catch (e) {
const msg = e instanceof Error ? e.message : String(e)
if (msg.includes('No router configured')) {
dedup: true,
maxRetries: 1,
}))
const err = statusEntry().error
if (err) {
if (err.includes('No router configured')) {
showConnectForm.value = true
} else {
error.value = msg
error.value = err
}
} finally {
loading.value = false
} else {
showConnectForm.value = false
if (params) connectedParams.value = params
}
}