Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions next/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- migrate to latest oidc-spa (v10)
- add `getAccessToken` & `decodedIdToken` to params of legacy App
- series are no longer mandatory in questionnaire details

## [2.5.0](https://github.com/InseeFr/Pogues/releases/tag/2.5.0) - 2026-03-06

Expand Down
29 changes: 19 additions & 10 deletions next/src/api/utils/questionnaireDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { computeTargetModes } from './targetModes'
export type FormDetails = {
name: string
title: string
serie: string
serie?: string
agency: string
targetModes: TargetModes[]
flowLogic: FlowLogics
Expand Down Expand Up @@ -50,10 +50,11 @@ export function computeQuestionnaireDetails(
export function computeQuestionnaireDetailsDTO(
formDetails: FormDetails,
existingDto: QuestionnaireDetailsDTO,
serieDetails: SerieDetailDTO,
serieDetails?: SerieDetailDTO,
): QuestionnaireDetailsDTO {
return {
const base = {
...existingDto,
dataCollection: undefined,
name: formDetails.name,
label: formDetails.title,
flowLogic: computePoguesFlowLogic(formDetails.flowLogic),
Expand All @@ -64,13 +65,21 @@ export function computeQuestionnaireDetailsDTO(
(mode) => TargetModes[mode as number] as string,
),
agency: formDetails.agency,
dataCollection: {
serie: {
id: serieDetails.id,
uri: serieDetails.uri,
label: serieDetails.label,
altLabel: serieDetails.altLabel,
}

if (serieDetails) {
return {
...base,
dataCollection: {
serie: {
id: serieDetails.id,
uri: serieDetails.uri,
label: serieDetails.label,
altLabel: serieDetails.altLabel,
},
},
},
}
}

return base
}
17 changes: 13 additions & 4 deletions next/src/components/details/QuestionnaireDetailsOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { FormValues } from './form/schema'
interface DetailsOverviewProps {
questionnaireId: string
questionnaireDetails: QuestionnaireDetailsDTO
series: SerieItem[]
series?: SerieItem[]
}

/**
Expand All @@ -42,7 +42,12 @@ export default function DetailsOverview({
const mutation = useMutation({
mutationFn: (params: {
data: FormValues
serieDetails: { id: string; uri: string; label: string; altLabel: string }
serieDetails?: {
id: string
uri: string
label: string
altLabel: string
}
}) => {
const dto = computeQuestionnaireDetailsDTO(
params.data,
Expand Down Expand Up @@ -71,8 +76,12 @@ export default function DetailsOverview({
if (!pendingFormData) return

try {
const serieDetails = await getSerieById(pendingFormData.serie)
mutation.mutate({ data: pendingFormData, serieDetails })
if (pendingFormData.serie) {
const serieDetails = await getSerieById(pendingFormData.serie)
mutation.mutate({ data: pendingFormData, serieDetails })
} else {
mutation.mutate({ data: pendingFormData })
}
} catch {
toast.error(t('details.form.updateError'))
}
Expand Down
26 changes: 26 additions & 0 deletions next/src/components/details/form/QuestionnaireDetailsForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,30 @@ describe('QuestionnaireDetailsForm', () => {
})
expect(screen.getByText(/must provide a title/i)).toBeInTheDocument()
})

it('clears selected serie when delete button is clicked', async () => {
const user = userEvent.setup()
const onSubmit = vi.fn()
await renderWithRouter(
<QuestionnaireDetailsForm
series={series}
defaultValues={validDefaultValues}
onSubmit={onSubmit}
submitLabel="Edit"
/>,
)

await user.click(screen.getByTitle(/delete/i))

await user.click(screen.getByTestId('form-submit-button'))

await waitFor(() => {
expect(onSubmit).toHaveBeenCalledOnce()
})
expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
serie: '',
}),
)
})
})
44 changes: 31 additions & 13 deletions next/src/components/details/form/QuestionnaireDetailsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import { useEffect, useState } from 'react'

import type { SerieDetailDTO } from '@/api/models/questionnaireDetailsDTO'
import { getSerieById } from '@/api/series'
import ButtonIcon from '@/components/ui/ButtonIcon'
import Tooltip from '@/components/ui/Tooltip'
import Autocomplete from '@/components/ui/form/Autocomplete'
import Field from '@/components/ui/form/Field'
import Form from '@/components/ui/form/Form'
import Input from '@/components/ui/form/Input'
import RadioGroup from '@/components/ui/form/RadioGroup'
import SelectTargetMode from '@/components/ui/form/SelectTargetMode'
import DeleteIcon from '@/components/ui/icons/DeleteIcon'
import InfoIcon from '@/components/ui/icons/InfoIcon'
import { FlowLogics, FormulasLanguages } from '@/models/questionnaires'
import { SerieItem } from '@/models/series'
Expand All @@ -23,7 +25,7 @@ type Props = {
defaultValues?: Partial<FormValues>
onSubmit: SubmitHandler<FormValues>
submitLabel: string
series: SerieItem[]
series?: SerieItem[]
readOnly?: boolean
}

Expand Down Expand Up @@ -58,6 +60,7 @@ export default function QuestionnaireDetailsForm({

const selectedSerie = watch('serie')

const [isSerieOpen, setIsSerieOpen] = useState(false)
const [serieDetails, setSerieDetails] = useState<SerieDetailDTO | null>(null)

useEffect(() => {
Expand All @@ -72,7 +75,7 @@ export default function QuestionnaireDetailsForm({
})
}, [selectedSerie])

const seriesOptions: { label: string; value: string }[] = series.map(
const seriesOptions: { label: string; value: string }[] = (series ?? []).map(
(s: SerieItem) => ({ label: s.label, value: s.id }),
)

Expand Down Expand Up @@ -120,7 +123,6 @@ export default function QuestionnaireDetailsForm({
<Controller
name="serie"
control={control}
rules={{ required: true }}
render={({
field: { name, value, onChange },
fieldState: { invalid, isTouched, isDirty, error },
Expand All @@ -131,19 +133,35 @@ export default function QuestionnaireDetailsForm({
invalid={invalid}
label={t('details.serie')}
name={name}
required
touched={isTouched}
disabled={readOnly}
>
<Autocomplete
options={seriesOptions}
value={value || undefined}
disabled={readOnly}
onChange={(serieValue = '') => {
onChange(serieValue)
handleSerieChange(serieValue)
}}
/>
<div className="flex items-center gap-1">
<div className="flex-1">
<Autocomplete
options={seriesOptions}
value={value || undefined}
disabled={readOnly}
open={isSerieOpen}
onOpenChange={setIsSerieOpen}
onChange={(serieValue = '') => {
onChange(serieValue)
handleSerieChange(serieValue)
}}
/>
</div>
{value ? (
<ButtonIcon
Icon={DeleteIcon}
title={t('common.delete')}
onClick={() => {
onChange('')
handleSerieChange('')
setIsSerieOpen(false)
}}
/>
) : null}
</div>
{serieDetails ? (
<div className="ml-4 mt-3 text-sm border-l-2 border-gray-300 pl-3">
<div className="m-2 text-stone-500 italic">{`${t('details.altLabel')} : ${serieDetails.altLabel ?? t('details.altLabelUndefined')}`}</div>
Expand Down
4 changes: 1 addition & 3 deletions next/src/components/details/form/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ export const schema = z.object({
.min(1, { error: i18next.t('details.form.mustProvideSerie') }),
flowLogic: z.enum(FlowLogics),
formulasLanguage: z.enum(FormulasLanguages),
serie: z
.string()
.min(1, { error: i18next.t('details.form.mustProvideSerie') }),
serie: z.string().optional(),
operation: z.string().optional(),
})

Expand Down
8 changes: 7 additions & 1 deletion next/src/components/ui/form/Autocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ type Props<T> = {
options: { label: string; value: T }[]
value?: T
disabled?: boolean
open?: boolean
onOpenChange?: (open: boolean) => void
}

export default function Autocomplete<T extends string>({
onChange,
options = [],
value,
open,
onOpenChange,
}: Readonly<Props<T>>) {
const [inputValue, setInputValue] = useState('')

Expand All @@ -25,7 +29,9 @@ export default function Autocomplete<T extends string>({
return (
<Combobox.Root
value={value ?? null}
onValueChange={(v) => onChange?.(v as T)}
open={open}
onOpenChange={onOpenChange}
onValueChange={(v) => onChange?.((v ?? '') as T)}
onInputValueChange={setInputValue}
itemToStringLabel={(v) => {
const option = options.find((o) => o.value === v)
Expand Down
8 changes: 8 additions & 0 deletions next/src/components/ui/icons/CloseSmallIcon.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { render } from '@testing-library/react'

import CloseSmallIcon from './CloseSmallIcon'

it('CloseSmallIcon renders correctly', () => {
const { asFragment } = render(<CloseSmallIcon />)
expect(asFragment()).toMatchSnapshot()
})
21 changes: 21 additions & 0 deletions next/src/components/ui/icons/CloseSmallIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Icon of a small cross which should be used when something can be cancelled by
* clicking on it.
*/
export default function CloseSmallIcon({
height = '24px',
width = '24px',
...props
}: Readonly<React.ComponentProps<'svg'>>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height={height}
viewBox="0 -960 960 960"
width={width}
{...props}
>
<path d="m336-280-56-56 144-144-144-143 56-56 144 144 143-144 56 56-144 143 144 144-56 56-143-144-144 144Z" />
</svg>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`CloseSmallIcon renders correctly 1`] = `
<DocumentFragment>
<svg
height="24px"
viewBox="0 -960 960 960"
width="24px"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m336-280-56-56 144-144-144-143 56-56 144 144 143-144 56 56-144 143 144 144-56 56-143-144-144 144Z"
/>
</svg>
</DocumentFragment>
`;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pogues",
"version": "3.7.0",
"version": "3.7.1-rc-details-form.1",
"description": "Outil de conception et de test de questionnaires.",
"scripts": {
"test:next": "cd next && pnpm test",
Expand Down
Loading
Loading