feat(form-failures): admin detail view (WS-6)

FormFailureDetail shared component drives both detail pages:
  - apps/app/src/pages/platform/form-failures/[id].vue
  - apps/app/src/pages/organisation/form-failures/[id].vue

Layout (per design schets):
  - Header with state badge (large) + title (Form failure {short-id})
    + relative-time subtitle + listener short-name
  - Action button row (Retry / Markeren als opgelost / Dismiss),
    disabled for non-open states
  - 60/40 two-column layout via VRow/VCol(md=7/md=5)

Left column:
  - Exception card: class + message in code blocks + "Bericht
    kopiëren" button (navigator.clipboard)
  - Context card (only when context is non-null): pretty-printed
    JSON in <pre> with copy-as-JSON button
  - Tijdlijn (VTimeline): Failed → Retry-pogingen → Opgelost or
    Dismissed → "In afwachting van actie..." for open with no retries

Right column:
  - Inzending card: form_submission_id with copy button. The
    submission detail-pagina link is documented as "nog niet
    beschikbaar in v1" inline; opening submissions in the SPA isn't
    yet implemented (forward-pointed).
  - Listener card: full FQN listener_class
  - Retry-geschiedenis card: count chip + caveat that per-attempt
    detail (timestamp + outcome) is not yet shipped by the backend
    resource (the FormSubmissionActionFailureResource ships only
    retry_count, not a retry history array)

Action dialogs reused from Task 2; refetch on success.

8 Vitest tests cover loading state, header rendering, all 6 cards
present, action button disabled-ness per state (open/resolved/
dismissed), and timeline content for resolved + open-no-retries
states.

Refs: WS-6 sessie 3b admin UI Task 4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-28 21:50:36 +02:00
parent 4c80289c47
commit 786bca8cf1
4 changed files with 580 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import FormFailureDetail from '@/components/form-failures/FormFailureDetail.vue'
import { useOrganisationStore } from '@/stores/useOrganisationStore'
definePage({
meta: {
navActiveLink: 'organisation-form-failures',
},
})
const route = useRoute()
const failureId = computed(() => route.params.id as string)
const orgStore = useOrganisationStore()
const orgId = computed(() => orgStore.activeOrganisationId ?? '')
</script>
<template>
<FormFailureDetail
v-if="orgId"
:failure-id="failureId"
scope="org"
:org-id="orgId"
/>
</template>