Phase 1 UX: StrictCombobox municipality in SupporterForm, AlertDialog in RemoveSupporterDataButton. Phase 2 DRY: campaignListUrl + CampaignListPagination + campaignFormFields + mapCampaignFormActionError shared across nuclei, supporters and action plans. Phase 3: single SQL aggregate for supporter list KPIs via drizzle in the Payload txn. Phase 4: bulk drizzle insert for CSV import inside the existing Payload txn with ON CONFLICT DO NOTHING, plus a fail-closed skipContactPhoneInvariant context flag. Phase 5: HMAC-signed single-use import token backed by a new supporterImportBatch staging collection so the full ok-row set never crosses the Server Action boundary twice.
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import { ZodError } from 'zod'
|
|
|
|
import { FormDataBoundaryError, validationFieldErrors } from '@/lib/formData'
|
|
|
|
export type CampaignFormErrorState<Values> = {
|
|
fieldErrors?: Record<string, string[]>
|
|
message?: string
|
|
values?: Values
|
|
revision?: number
|
|
}
|
|
|
|
export type MapCampaignFormActionErrorOptions<Values> = {
|
|
error: unknown
|
|
safeMessages: readonly string[]
|
|
genericMessage: string
|
|
values?: Values
|
|
revision?: number
|
|
resolveBoundaryMessage?: (error: FormDataBoundaryError) => string
|
|
}
|
|
|
|
export const mapCampaignFormActionError = <Values>(
|
|
options: MapCampaignFormActionErrorOptions<Values>,
|
|
): CampaignFormErrorState<Values> => {
|
|
const { error, safeMessages, genericMessage, values, revision, resolveBoundaryMessage } = options
|
|
|
|
if (error instanceof FormDataBoundaryError) {
|
|
const message = resolveBoundaryMessage ? resolveBoundaryMessage(error) : error.message
|
|
return { fieldErrors: { [error.field]: [message] }, values, revision }
|
|
}
|
|
if (error instanceof ZodError) {
|
|
return { fieldErrors: validationFieldErrors(error), values, revision }
|
|
}
|
|
if (
|
|
error instanceof Error &&
|
|
safeMessages.includes(error.message as (typeof safeMessages)[number])
|
|
) {
|
|
return { message: error.message, values, revision }
|
|
}
|
|
return { message: genericMessage, values, revision }
|
|
}
|