diff --git a/apps/app/src/components/form-failures/DismissFailureDialog.vue b/apps/app/src/components/form-failures/DismissFailureDialog.vue new file mode 100644 index 00000000..ee981d39 --- /dev/null +++ b/apps/app/src/components/form-failures/DismissFailureDialog.vue @@ -0,0 +1,149 @@ + + + diff --git a/apps/app/src/components/form-failures/ResolveFailureDialog.vue b/apps/app/src/components/form-failures/ResolveFailureDialog.vue new file mode 100644 index 00000000..4317b0fa --- /dev/null +++ b/apps/app/src/components/form-failures/ResolveFailureDialog.vue @@ -0,0 +1,117 @@ + + + diff --git a/apps/app/src/components/form-failures/RetryFailureDialog.vue b/apps/app/src/components/form-failures/RetryFailureDialog.vue new file mode 100644 index 00000000..05d36edd --- /dev/null +++ b/apps/app/src/components/form-failures/RetryFailureDialog.vue @@ -0,0 +1,106 @@ + + + diff --git a/apps/app/src/components/form-failures/__tests__/DismissFailureDialog.spec.ts b/apps/app/src/components/form-failures/__tests__/DismissFailureDialog.spec.ts new file mode 100644 index 00000000..dfba377e --- /dev/null +++ b/apps/app/src/components/form-failures/__tests__/DismissFailureDialog.spec.ts @@ -0,0 +1,139 @@ +import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query' +import { flushPromises, mount } from '@vue/test-utils' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +const mockPost = vi.fn() +vi.mock('@/lib/axios', () => ({ + apiClient: { + get: vi.fn(), + post: (...args: unknown[]) => mockPost(...args), + }, +})) + +import DismissFailureDialog from '../DismissFailureDialog.vue' +import type { FormFailure } from '@/types/form-failures' + +function makeFailure(overrides: Partial = {}): FormFailure { + return { + id: '01ABC', + form_submission_id: '01SUB', + binding_id: null, + listener_class: 'App\\Listeners\\FormBuilder\\ApplyBindingsOnFormSubmit', + failed_at: '2026-04-28T12:00:00Z', + exception_class: 'RuntimeException', + exception_message: 'boom', + context: null, + retry_count: 0, + resolved_at: null, + resolved_note: null, + dismissed_at: null, + dismissed_reason_type: null, + dismissed_reason_note: null, + state: 'open', + ...overrides, + } +} + +// VRadioGroup's modelValue → emits update; VRadio is selected via parent. +// Stub them as a simple select-like control so tests can set the reason +// without rendering Vuetify's full radio implementation. +const stubs = { + VDialog: { template: '
', props: ['modelValue'] }, + VCard: { template: '
' }, + VCardTitle: { template: '
' }, + VCardText: { template: '
' }, + VCardActions: { template: '
' }, + VAlert: { template: '
' }, + VBtn: { + template: '', + props: ['disabled', 'loading', 'color', 'variant'], + }, + VTextarea: { + template: '