diff --git a/frontend/src/colors.scss b/frontend/src/colors.scss index edad85039..f442fb575 100644 --- a/frontend/src/colors.scss +++ b/frontend/src/colors.scss @@ -314,4 +314,6 @@ --beige-gris-galet-moon-125: #2e2924; --beige-gris-galet-moon-100: #28231f; --beige-gris-galet-moon-75: #211d19; + + } diff --git a/frontend/src/components/AddressSearchableSelect/AddressSearchableSelectNext.tsx b/frontend/src/components/AddressSearchableSelect/AddressSearchableSelectNext.tsx index 3c72721b9..02a44f86f 100644 --- a/frontend/src/components/AddressSearchableSelect/AddressSearchableSelectNext.tsx +++ b/frontend/src/components/AddressSearchableSelect/AddressSearchableSelectNext.tsx @@ -1,7 +1,6 @@ import { fr } from '@codegouvfr/react-dsfr'; import Input, { InputProps } from '@codegouvfr/react-dsfr/Input'; import Autocomplete from '@mui/material/Autocomplete'; -import Typography from '@mui/material/Typography'; import Grid from '@mui/material/Unstable_Grid2'; import { useDebounce, useList, usePreviousDistinct } from 'react-use'; import type { MarkOptional } from 'ts-essentials'; @@ -86,17 +85,6 @@ function AddressSearchableSelectNext(props: Props) { justifyContent="space-between" sm > - - Adresse (source :  - - Base Adresse Nationale - - ) - } - hintText="Commencez à taper votre recherche dans le champ de saisi et choisissez une des options proposées dans la liste (exemple : 72 rue de Varenne, Paris)" + hintText="Cette adresse est la plus proche identifiée dans la Base Adresse Nationale. Ce format est recommandé pour vos courriers. Pour modifier l'adresse, commencez à saisir votre recherche et choisissez une des options dans la liste." nativeInputProps={{ type: 'search', placeholder: 'Rechercher une adresse', diff --git a/frontend/src/components/HousingList/HousingList.tsx b/frontend/src/components/HousingList/HousingList.tsx index deefb2db5..db541feb1 100644 --- a/frontend/src/components/HousingList/HousingList.tsx +++ b/frontend/src/components/HousingList/HousingList.tsx @@ -175,9 +175,11 @@ const HousingList = ({ name: 'address', headerRender: () => getSortButton('rawAddress', 'Adresse du logement'), render: ({ id, rawAddress }: Housing) => ( - - {rawAddress.map((line) => capitalize(line)).join('\n')} - + <> + + {rawAddress.map((line) => capitalize(line)).join('\n')} + + ) }; diff --git a/frontend/src/components/OwnerAddressEdition/OwnerAddressEdition.tsx b/frontend/src/components/OwnerAddressEdition/OwnerAddressEdition.tsx index 4b287f729..113d10f55 100644 --- a/frontend/src/components/OwnerAddressEdition/OwnerAddressEdition.tsx +++ b/frontend/src/components/OwnerAddressEdition/OwnerAddressEdition.tsx @@ -1,12 +1,9 @@ -import Button from '@codegouvfr/react-dsfr/Button'; -import ButtonsGroup from '@codegouvfr/react-dsfr/ButtonsGroup'; -import Typography from '@mui/material/Typography'; import { useState } from 'react'; -import { Text } from '../_dsfr'; import { Address, isBanEligible } from '../../models/Address'; import { AddressSearchResult } from '../../services/address.service'; import AddressSearchableSelectNext from '../AddressSearchableSelect/AddressSearchableSelectNext'; +import CallOut from '@codegouvfr/react-dsfr/CallOut'; interface Props { banAddress?: Address; @@ -15,66 +12,15 @@ interface Props { help?: boolean; rawAddress: string[]; onSelectAddress(address: AddressSearchResult | null): void; + warningVisible: boolean; + setWarningVisible: (visible: boolean) => void; } function OwnerAddressEdition(props: Props) { - const [searchAddressFromLovac, setSearchAddressFromLovac] = useState(false); - const [previousAddress, setPreviousAddress] = useState(); const [inputValue, setInputValue] = useState(''); const [open, setOpen] = useState(false); - if (searchAddressFromLovac) { - return ( - <> - { - props.onSelectAddress( - address - ? { - ...address, - banId: address.banId ?? '', - latitude: address.latitude ?? 0, - longitude: address.longitude ?? 0, - // Consider that the user has validated the address - score: 1 - } - : null - ); - }} - onInputChange={setInputValue} - onOpen={() => setOpen(true)} - onClose={() => setOpen(false)} - stateRelatedMessage={props.errorMessage} - /> - {previousAddress && ( -
- Adresse précédente : - - {previousAddress.label} - -
- -
-
- )} - - ); - } - return ( <> setOpen(true)} onClose={() => setOpen(false)} /> - {props.banAddress && !isBanEligible(props.banAddress) && ( -
- - Amélioration possible - - - L’adresse issue de la Base Adresse Nationale, indiquée dans - le champ Adresse ci-dessus, semble différente de l’adresse issue de - la DGFIP : {props.rawAddress.join(' ')} - - - Modifier le champ Adresse à partir de l’adresse DGFIP ? - - { - if (props.banAddress) { - setPreviousAddress({ - ...props.banAddress, - banId: props.banAddress.banId ?? '', - latitude: props.banAddress.latitude ?? 0, - longitude: props.banAddress.longitude ?? 0, - score: props.banAddress.score ?? Number.NaN - }); - } - setInputValue(props.rawAddress.join(' ')); - setSearchAddressFromLovac(true); - setOpen(true); - } - }, - { - children: 'Non', - priority: 'secondary', - onClick: () => { - if (props.banAddress) { - props.onSelectAddress({ - ...props.banAddress, - banId: props.banAddress.banId ?? '', - latitude: props.banAddress.latitude ?? 0, - longitude: props.banAddress.longitude ?? 0, - // Consider that the user has validated the address - score: 1 - }); - } - } - } - ]} - alignment="left" - inlineLayoutWhen="sm and up" - > -
+ {props.warningVisible && !isBanEligible(props.banAddress) && ( + {props.setWarningVisible(false);}, + }}> + L’adresse de la Base Adresse Nationale diffère de celle de la DGFIP. Veuillez vérifier attentivement ces informations ou ignorez l’alerte. + )} ); diff --git a/frontend/src/components/OwnerCard/OwnerCard.tsx b/frontend/src/components/OwnerCard/OwnerCard.tsx index c7aa2adc8..31eb35ad6 100644 --- a/frontend/src/components/OwnerCard/OwnerCard.tsx +++ b/frontend/src/components/OwnerCard/OwnerCard.tsx @@ -29,12 +29,6 @@ function OwnerCard(props: OwnerCardProps) { const archivedOwners = props.coOwners?.filter((owner) => owner.rank <= 0) ?? []; - const address: ReadonlyArray = ( - props.owner.banAddress - ? formatAddress(props.owner.banAddress) - : props.owner.rawAddress - ).map((line: string) => {line}); - return ( @@ -64,6 +58,22 @@ function OwnerCard(props: OwnerCardProps) { ) : null} + + + + Adresse fiscale (source: DGFIP) + + {props.owner.rawAddress ? props.owner.rawAddress.join(' ') : 'Inconnue'} + + + - Adresse postale + Adresse postale (source: Base Adresse Nationale) - {address} + {props.owner.banAddress ? formatAddress(props.owner.banAddress).join(' ') : 'Inconnue'} + {!isBanEligible(props.owner.banAddress) && ( - Cette adresse issue de la BAN est différente de l’adresse - fiscale. + L’adresse Base Adresse Nationale ne correspond pas à celle de la DGFIP. - Cliquez sur “Modifier” pour valider l’adresse que vous - souhaitez utiliser. + Nous vous recommandons de vérifier en cliquant sur "Modifier". } diff --git a/frontend/src/components/OwnerEditionSideMenu/OwnerEditionSideMenu.tsx b/frontend/src/components/OwnerEditionSideMenu/OwnerEditionSideMenu.tsx index 6bfa4cb97..1d508152b 100644 --- a/frontend/src/components/OwnerEditionSideMenu/OwnerEditionSideMenu.tsx +++ b/frontend/src/components/OwnerEditionSideMenu/OwnerEditionSideMenu.tsx @@ -1,3 +1,4 @@ +import { fr } from '@codegouvfr/react-dsfr'; import Button from '@codegouvfr/react-dsfr/Button'; import { FormEvent, useState } from 'react'; import { object, string } from 'yup'; @@ -10,6 +11,7 @@ import { Owner } from '../../models/Owner'; import { banAddressValidator, useForm } from '../../hooks/useForm'; import { useUpdateOwnerMutation } from '../../services/owner.service'; import { useNotification } from '../../hooks/useNotification'; +import { Grid, Typography } from '@mui/material'; interface Props { className?: string; @@ -19,15 +21,16 @@ interface Props { function OwnerEditionSideMenu(props: Props) { const { active, setActive, toggle } = useToggle(); + const storedWarningVisible = localStorage.getItem('OwnerEdition.warningVisible'); + const [warningVisible, setWarningVisible] = useState(storedWarningVisible === null || storedWarningVisible === 'true'); + const shape = { - fullName: string().optional(), address: banAddressValidator.optional(), additionalAddress: string().optional().nullable() }; type FormShape = typeof shape; const schema = object(shape); - const [fullName, setFullName] = useState(props.owner.fullName); const [address, setAddress] = useState(props.owner.banAddress); const [additionalAddress, setAdditionalAddress] = useState( props.owner.additionalAddress ?? '' @@ -35,8 +38,7 @@ function OwnerEditionSideMenu(props: Props) { const formId = 'owner-edition-form'; const form = useForm(schema, { address, - additionalAddress, - fullName + additionalAddress }); function open(): void { @@ -50,11 +52,11 @@ function OwnerEditionSideMenu(props: Props) { const [updateOwner, mutation] = useUpdateOwnerMutation(); async function save(event: FormEvent): Promise { + localStorage.setItem('OwnerEdition.warningVisible', warningVisible.toString()); event.preventDefault(); await form.validate(async () => { await updateOwner({ ...props.owner, - fullName, banAddress: address, additionalAddress }).unwrap(); @@ -97,20 +99,43 @@ function OwnerEditionSideMenu(props: Props) { title="Modifier les informations du propriétaire" content={
- - inputForm={form} - inputKey="fullName" - label="Nom prénom" - value={fullName} - onChange={(e) => setFullName(e.target.value)} - /> -
+ + + + Adresse fiscale (source: DGFIP) + + + Cette adresse est issue du fichier LOVAC, récupérée via le fichier 1767BIS-COM. Celle-ci n’est pas modifiable. + {props.owner.rawAddress ? props.owner.rawAddress.join(' ') : 'Inconnue'} + + +
+ + + Adresse postale (source: Base Adresse Nationale) + setAddress(value ?? undefined)} + warningVisible={warningVisible} + setWarningVisible={setWarningVisible} />
diff --git a/frontend/src/components/modals/HousingOwnersModal/HousingOwnersModal.tsx b/frontend/src/components/modals/HousingOwnersModal/HousingOwnersModal.tsx index 50b3d69b4..dc337a309 100644 --- a/frontend/src/components/modals/HousingOwnersModal/HousingOwnersModal.tsx +++ b/frontend/src/components/modals/HousingOwnersModal/HousingOwnersModal.tsx @@ -49,6 +49,9 @@ function HousingOwnersModal({ }: Props) { const { isVisitor } = useUser(); + const storedWarningVisible = localStorage.getItem('OwnerEdition.warningVisible'); + const [warningVisible, setWarningVisible] = useState(storedWarningVisible === null || storedWarningVisible === 'true'); + const [modalMode, setModalMode] = useState<'list' | 'add'>('list'); const [ housingOwners, @@ -224,7 +227,7 @@ function HousingOwnersModal({ {!isBanEligible(housingOwner.banAddress) && ( - ADRESSE À VÉRIFIER + ADRESSE AMÉLIORABLE )} @@ -289,6 +292,32 @@ function HousingOwnersModal({ /> + + + Adresse fiscale (source: DGFIP) + + Cette adresse est issue du fichier LOVAC, récupérée via le fichier 1767BIS-COM. Celle-ci n’est pas modifiable. + {housingOwner.rawAddress ? housingOwner.rawAddress.join(' ') : 'Inconnue'} + + + + + Adresse postale (source: Base Adresse Nationale) + diff --git a/frontend/src/components/modals/OwnerEditionModal/OwnerEditionModal.tsx b/frontend/src/components/modals/OwnerEditionModal/OwnerEditionModal.tsx index 48f6b1612..daadc7b76 100644 --- a/frontend/src/components/modals/OwnerEditionModal/OwnerEditionModal.tsx +++ b/frontend/src/components/modals/OwnerEditionModal/OwnerEditionModal.tsx @@ -17,6 +17,8 @@ import { createModal } from '@codegouvfr/react-dsfr/Modal'; import Button from '@codegouvfr/react-dsfr/Button'; import OwnerAddressEdition from '../../OwnerAddressEdition/OwnerAddressEdition'; import { useUser } from '../../../hooks/useUser'; +import { Typography } from '@mui/material'; +import { fr } from '@codegouvfr/react-dsfr'; const modal = createModal({ id: 'owner-edition-modal', @@ -39,6 +41,9 @@ const OwnerEditionModal = ({ owner, onCancel }: Props) => { owner?.additionalAddress ); + const storedWarningVisible = localStorage.getItem('OwnerEdition.warningVisible'); + const [warningVisible, setWarningVisible] = useState(storedWarningVisible === null || storedWarningVisible === 'true'); + const [updateOwner, { isError: isUpdateError }] = useUpdateOwnerMutation(); const shape = { @@ -128,11 +133,39 @@ const OwnerEditionModal = ({ owner, onCancel }: Props) => { /> + + + Adresse fiscale (source: DGFIP) + + Cette adresse est issue du fichier LOVAC, récupérée via le fichier 1767BIS-COM. Celle-ci n’est pas modifiable. + {owner.rawAddress ? owner.rawAddress.join(' ') : 'Inconnue'} + + + + + Adresse postale (source: Base Adresse Nationale) + setBanAddress(a ?? undefined)} errorMessage={form.message('banAddress')} + warningVisible={warningVisible} + setWarningVisible={setWarningVisible} /> diff --git a/frontend/src/components/modals/OwnerEditionModal/owner-edition-modal.module.scss b/frontend/src/components/modals/OwnerEditionModal/owner-edition-modal.module.scss deleted file mode 100644 index 37b734835..000000000 --- a/frontend/src/components/modals/OwnerEditionModal/owner-edition-modal.module.scss +++ /dev/null @@ -1,10 +0,0 @@ -.itemError { - section { - border: 1px solid var(--error-425); - } - h3 { - button { - color: var(--error-425) !important; - } - } -} diff --git a/frontend/src/views/Campaign/test/CampaignView.test.tsx b/frontend/src/views/Campaign/test/CampaignView.test.tsx index 7058f5561..f9e35c35d 100644 --- a/frontend/src/views/Campaign/test/CampaignView.test.tsx +++ b/frontend/src/views/Campaign/test/CampaignView.test.tsx @@ -9,6 +9,7 @@ import { AppStore } from '../../../store/store'; import CampaignView from '../CampaignView'; import Notification from '../../../components/Notification/Notification'; import { + AddressKinds, CampaignDTO, DraftDTO, HousingDTO, @@ -43,6 +44,17 @@ describe('Campaign view', () => { sender = genSenderDTO(); draft = genDraftDTO(sender); owner = genOwnerDTO(); + + owner.banAddress = { + street: '1 rue de la vallée', + postalCode: '85130', + city: 'Tiffauges', + refId: faker.string.uuid(), + addressKind: AddressKinds.Owner, + label: 'Home Address', + score: 0.7 + }; + housings = Array.from({ length: 3 }, () => genHousingDTO(owner)); data.housings.push(...housings); @@ -261,15 +273,41 @@ describe('Campaign view', () => { }); await user.click(edit); const [aside] = await screen.findAllByRole('complementary'); - const fullName = await within(aside).findByLabelText('Nom prénom'); - await user.clear(fullName); - await user.type(fullName, 'John Doe'); + const address = await within(aside).findByPlaceholderText('Rechercher une adresse'); + await user.clear(address); + await user.type(address, 'Rue de la vallée 85130 Tiffauges'); const save = await within(aside).findByRole('button', { name: /^Enregistrer/ }); await user.click(save); }); + it('should dismiss the warning message when the user clicks the \'Ignore\' button while editing the recipient’s address', async () => { + renderComponent(); + localStorage.clear(); + + const tab = await screen.findByRole('tab', { name: /^Destinataires/ }); + await user.click(tab); + const [edit] = await screen.findAllByRole('button', { + name: /^Éditer l’adresse/ + }); + await user.click(edit); + const [aside] = await screen.findAllByRole('complementary'); + const [ ignoreButton ] = await within(aside).findAllByRole('button', { + name: /^Ignorer/ + }); + expect(ignoreButton).toBeInTheDocument(); + await user.click(ignoreButton); + const save = await within(aside).findByRole('button', { + name: /^Enregistrer/ + }); + await user.click(save); + + await user.click(edit); + + expect(ignoreButton).not.toBeInTheDocument(); + }); + it('should update the page when the campaign has been generated', async () => { const campaign: CampaignDTO = { ...genCampaignDTO(), status: 'sending' }; data.campaigns.push(campaign); diff --git a/server/src/controllers/housingController.ts b/server/src/controllers/housingController.ts index dae9f747c..020fe6fd5 100644 --- a/server/src/controllers/housingController.ts +++ b/server/src/controllers/housingController.ts @@ -58,7 +58,7 @@ async function get(request: Request, response: Response) { id, localId, includes: ['events', 'owner', 'perimeters', 'campaigns'] - }); + }); if (!housing) { throw new HousingMissingError(params.id); }