diff --git a/src/components/campaign/activity/ActivityResultForm.tsx b/src/components/campaign/activity/ActivityResultForm.tsx index 26fdb0a4..be0a0f39 100644 --- a/src/components/campaign/activity/ActivityResultForm.tsx +++ b/src/components/campaign/activity/ActivityResultForm.tsx @@ -1,7 +1,7 @@ 'use client' import { useRouter } from 'next/navigation' -import { useActionState } from 'react' +import { startTransition, useActionState, type FormEvent } from 'react' import { registerActivityResultFormAction } from '@/app/(campaign)/campanha/(app)/atividades/[slug]/resultFormActions' import { CampaignFormActionMessage } from '@/components/campaign/shared/CampaignFormActionMessage' @@ -42,6 +42,14 @@ export const ActivityResultForm = ({ .join(' ') : null + // C139 — manual dispatch: React 19 resets uncontrolled fields after any + // settled form action, reverting the typed result to `initialSummary` on + // a validation error. `startTransition` keeps `pending` correct. + const handleSubmit = (event: FormEvent) => { + event.preventDefault() + startTransition(() => formAction(new FormData(event.currentTarget))) + } + return ( @@ -49,7 +57,7 @@ export const ActivityResultForm = ({ {recordedLabel ?

{recordedLabel}

: null}
-
+ {state.status !== 'success' ? ( diff --git a/src/components/campaign/activity/ActivityUpdateForm.tsx b/src/components/campaign/activity/ActivityUpdateForm.tsx index d98eaa51..2cd036a6 100644 --- a/src/components/campaign/activity/ActivityUpdateForm.tsx +++ b/src/components/campaign/activity/ActivityUpdateForm.tsx @@ -1,7 +1,7 @@ 'use client' import { useRouter } from 'next/navigation' -import { useActionState, useRef } from 'react' +import { startTransition, useActionState, useRef, type FormEvent } from 'react' import { createActivityUpdateFormAction } from '@/app/(campaign)/campanha/(app)/atividades/[slug]/updateFormActions' import { CampaignFormActionMessage } from '@/components/campaign/shared/CampaignFormActionMessage' @@ -25,13 +25,21 @@ export const ActivityUpdateForm = ({ activityId }: { activityId: number }) => { const bodyError = fieldError(state.fieldErrors, 'body') + // C139 — manual dispatch (no `action={formAction}`): React 19 resets + // uncontrolled fields after any settled form action, wiping the typed + // update on a validation error. `startTransition` keeps `pending` correct. + const handleSubmit = (event: FormEvent) => { + event.preventDefault() + startTransition(() => formAction(new FormData(event.currentTarget))) + } + return ( Nova atualização - + {state.status !== 'success' ? ( diff --git a/src/components/campaign/demand/DemandWorkflowCard.tsx b/src/components/campaign/demand/DemandWorkflowCard.tsx index 52bdac3d..6d4c8e0c 100644 --- a/src/components/campaign/demand/DemandWorkflowCard.tsx +++ b/src/components/campaign/demand/DemandWorkflowCard.tsx @@ -1,6 +1,6 @@ 'use client' -import { useActionState } from 'react' +import { startTransition, useActionState, type FormEvent } from 'react' import { Alert, AlertDescription } from '@/components/ui/Alert' import { Button } from '@/components/ui/button' @@ -63,6 +63,19 @@ export const DemandWorkflowCard = ({ (target !== 'aprovada' && target !== 'rejeitada'), ) + // C139 — manual dispatch (no `action={submitTransition}`/`action={submitCost}`): + // React 19 resets uncontrolled fields after any settled form action, wiping + // the typed decision note and cost on a validation error. `startTransition` + // keeps the pending flags correct for the imperative dispatch. + const handleTransitionSubmit = (event: FormEvent) => { + event.preventDefault() + startTransition(() => submitTransition(new FormData(event.currentTarget))) + } + const handleCostSubmit = (event: FormEvent) => { + event.preventDefault() + startTransition(() => submitCost(new FormData(event.currentTarget))) + } + if (readOnly) { return (
{availableTransitions.length ? ( - + Nota da decisão @@ -144,7 +157,7 @@ export const DemandWorkflowCard = ({

Esta demanda já foi decidida.

)} - + Custo estimado (R$) — controle interno diff --git a/src/components/campaign/municipality/MunicipalityUpdateDeliberation.tsx b/src/components/campaign/municipality/MunicipalityUpdateDeliberation.tsx index bf47d08c..e9f1135e 100644 --- a/src/components/campaign/municipality/MunicipalityUpdateDeliberation.tsx +++ b/src/components/campaign/municipality/MunicipalityUpdateDeliberation.tsx @@ -1,6 +1,6 @@ 'use client' -import { useActionState, useEffect, useRef } from 'react' +import { startTransition, useActionState, useEffect, useRef, type FormEvent } from 'react' import { CampaignFormActionMessage } from '@/components/campaign/shared/CampaignFormActionMessage' import { Badge } from '@/components/ui/Badge' @@ -197,6 +197,14 @@ const CommentThread = ({ if (state.status === 'success' && formRef.current) formRef.current.reset() }, [state]) + // C139 — manual dispatch: React 19 resets uncontrolled fields after any + // settled form action, wiping the typed comment on a validation error. + // The success reset above stays explicit; `startTransition` keeps `pending`. + const handleSubmit = (event: FormEvent) => { + event.preventDefault() + startTransition(() => submitAction(new FormData(event.currentTarget))) + } + return (

Discussão

@@ -224,7 +232,7 @@ const CommentThread = ({ ) : null} {canComment ? ( - +