From 13aaf34191af01c2fe73d61bfd13442a308643e4 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 3 Aug 2026 11:05:41 +0100 Subject: [PATCH] fix: handle hydration state for admin ui --- app/composables/adminFetch.ts | 19 +++++++++++++++---- app/pages/admin/ama/[rkey].vue | 4 ++-- app/pages/admin/ama/index.vue | 4 ++-- app/pages/admin/entities/[rkey].vue | 4 ++-- app/pages/admin/entities/index.vue | 4 ++-- app/pages/admin/index.vue | 4 ++-- app/pages/admin/invites/[rkey].vue | 4 ++-- app/pages/admin/invites/index.vue | 4 ++-- app/pages/admin/location.vue | 4 ++-- app/pages/admin/talk-groups/[rkey].vue | 4 ++-- app/pages/admin/talks/[rkey].vue | 4 ++-- app/pages/admin/talks/index.vue | 6 +++--- app/pages/admin/uses/categories/[rkey].vue | 4 ++-- app/pages/admin/uses/index.vue | 6 +++--- app/pages/admin/uses/items/[rkey].vue | 4 ++-- 15 files changed, 45 insertions(+), 34 deletions(-) diff --git a/app/composables/adminFetch.ts b/app/composables/adminFetch.ts index 85f32554..866a9b85 100644 --- a/app/composables/adminFetch.ts +++ b/app/composables/adminFetch.ts @@ -1,7 +1,16 @@ import type { AsyncData, NuxtError, UseFetchOptions } from 'nuxt/app' +import type { ComputedRef } from 'vue' type KeysOf = Array +/** + * `status` is `idle` during SSR (the request is client-only) and flips to + * `pending` as soon as the client picks it up, which would make any + * `status === 'pending'` template branch mismatch on hydration. `loading` + * covers both so server and client agree on the first render. + */ +type AdminAsyncData = AsyncData | undefined> & { loading: ComputedRef } + const cache = new Map() /** @@ -17,15 +26,15 @@ const cache = new Map() export function useAdminFetch ( url: string, options: UseFetchOptions, T> & { default: () => T }, -): AsyncData | undefined> +): AdminAsyncData export function useAdminFetch ( url: string, options?: UseFetchOptions, undefined>, -): AsyncData | undefined> +): AdminAsyncData export function useAdminFetch ( url: string, options: UseFetchOptions = {}, -): AsyncData | undefined> { +): AdminAsyncData { const key = `admin:${url}` const hasCached = import.meta.client && cache.has(key) @@ -51,5 +60,7 @@ export function useAdminFetch ( } } - return result + return Object.assign(result, { + loading: computed(() => result.status.value === 'idle' || result.status.value === 'pending'), + }) } diff --git a/app/pages/admin/ama/[rkey].vue b/app/pages/admin/ama/[rkey].vue index c717147b..112ccfb6 100644 --- a/app/pages/admin/ama/[rkey].vue +++ b/app/pages/admin/ama/[rkey].vue @@ -30,7 +30,7 @@ interface AmaEntry { answeredAt?: string } -const { data, status } = useAdminFetch(`/api/admin/ama/${rkey.value}`, { +const { data, loading } = useAdminFetch(`/api/admin/ama/${rkey.value}`, { watch: false, }) @@ -45,7 +45,7 @@ const initialImagePreviewUrl = computed(() => {