Moves formBuilder types, formValidation, useConditionalLogic, useFormSteps, and formatFieldValue from apps/portal/src to packages/form-schema/src. Adds @form-schema path alias to both apps/portal and apps/app. Vue field components remain per-app to allow independent visual evolution. Behavior-neutral: all 35 Vitest tests green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
import { useMutation, useQuery } from '@tanstack/vue-query'
|
|
import type { AxiosError } from 'axios'
|
|
import type { MaybeRefOrGetter } from 'vue'
|
|
import { apiClient } from '@/lib/axios'
|
|
import type {
|
|
PublicFormErrorBody,
|
|
PublicFormSchema,
|
|
PublicFormSubmission,
|
|
SaveDraftBody,
|
|
StartDraftBody,
|
|
SubmitBody,
|
|
} from '@form-schema/types/formBuilder'
|
|
|
|
interface ApiResponse<T> {
|
|
data: T
|
|
message?: string
|
|
success?: boolean
|
|
}
|
|
|
|
/**
|
|
* The backend standardises public-form errors as
|
|
* { message, code?, errors? }
|
|
* See api/app/Exceptions/FormBuilder/PublicFormApiException.php.
|
|
*/
|
|
export type PublicFormAxiosError = AxiosError<PublicFormErrorBody>
|
|
|
|
const TERMINAL_STATUSES = new Set([404, 410, 409])
|
|
|
|
export function useFetchPublicFormSchema(token: MaybeRefOrGetter<string | null | undefined>) {
|
|
return useQuery({
|
|
queryKey: ['public-form-schema', () => toValue(token)],
|
|
queryFn: async (): Promise<PublicFormSchema> => {
|
|
const t = toValue(token)
|
|
if (!t) throw new Error('Missing public_token')
|
|
const { data } = await apiClient.get<ApiResponse<PublicFormSchema>>(`/public/forms/${t}`)
|
|
|
|
return data.data
|
|
},
|
|
enabled: () => !!toValue(token),
|
|
retry: (failureCount, error) => {
|
|
const status = (error as PublicFormAxiosError | undefined)?.response?.status
|
|
if (status && TERMINAL_STATUSES.has(status)) return false
|
|
|
|
return failureCount < 1
|
|
},
|
|
staleTime: 1000 * 60 * 5,
|
|
})
|
|
}
|
|
|
|
export function useCreateFormDraft(token: MaybeRefOrGetter<string | null | undefined>) {
|
|
return useMutation({
|
|
mutationFn: async (body: StartDraftBody): Promise<PublicFormSubmission> => {
|
|
const t = toValue(token)
|
|
if (!t) throw new Error('Missing public_token')
|
|
const { data } = await apiClient.post<ApiResponse<PublicFormSubmission>>(
|
|
`/public/forms/${t}/submissions`,
|
|
body,
|
|
)
|
|
|
|
return data.data
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useSaveFormDraft(token: MaybeRefOrGetter<string | null | undefined>) {
|
|
return useMutation({
|
|
mutationFn: async ({ submissionId, body }: { submissionId: string; body: SaveDraftBody }): Promise<PublicFormSubmission> => {
|
|
const t = toValue(token)
|
|
if (!t) throw new Error('Missing public_token')
|
|
const { data } = await apiClient.put<ApiResponse<PublicFormSubmission>>(
|
|
`/public/forms/${t}/submissions/${submissionId}`,
|
|
body,
|
|
)
|
|
|
|
return data.data
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useSubmitForm(token: MaybeRefOrGetter<string | null | undefined>) {
|
|
return useMutation({
|
|
mutationFn: async ({ submissionId, body }: { submissionId: string; body: SubmitBody }): Promise<PublicFormSubmission> => {
|
|
const t = toValue(token)
|
|
if (!t) throw new Error('Missing public_token')
|
|
const { data } = await apiClient.post<ApiResponse<PublicFormSubmission>>(
|
|
`/public/forms/${t}/submissions/${submissionId}/submit`,
|
|
body,
|
|
)
|
|
|
|
return data.data
|
|
},
|
|
})
|
|
}
|
|
|
|
export function extractErrorBody(err: unknown): PublicFormErrorBody | null {
|
|
const axiosErr = err as PublicFormAxiosError | undefined
|
|
const body = axiosErr?.response?.data
|
|
if (body && typeof body === 'object' && 'message' in body) return body as PublicFormErrorBody
|
|
|
|
return null
|
|
}
|
|
|
|
export function extractRetryAfterSeconds(err: unknown): number | null {
|
|
const axiosErr = err as PublicFormAxiosError | undefined
|
|
const raw = axiosErr?.response?.headers?.['retry-after']
|
|
if (!raw) return null
|
|
const n = Number(raw)
|
|
|
|
return Number.isFinite(n) && n >= 0 ? n : null
|
|
}
|