diff --git a/frontend/package.json b/frontend/package.json
index 1e69c57d62..be24286c07 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -51,7 +51,7 @@
"jose": "4.15.9",
"lexical": "0.38.2",
"lodash-es": "4.17.21",
- "maplibre-gl": "5.13.0",
+ "maplibre-gl": "5.16.0",
"mime": "4.1.0",
"msw": "2.12.4",
"posthog-js": "1.298.0",
diff --git a/frontend/src/components/EventsHistory/HistoryCard.tsx b/frontend/src/components/EventsHistory/HistoryCard.tsx
index f5f600b5b9..3d63358050 100644
--- a/frontend/src/components/EventsHistory/HistoryCard.tsx
+++ b/frontend/src/components/EventsHistory/HistoryCard.tsx
@@ -6,6 +6,7 @@ import type { PropsWithChildren } from 'react';
export type HistoryCardProps = PropsWithChildren<{
icon: FrIconClassName | RiIconClassName;
+ hideIcon?: boolean;
}>;
function HistoryCard(props: HistoryCardProps) {
@@ -14,25 +15,28 @@ function HistoryCard(props: HistoryCardProps) {
component="article"
direction="row"
spacing="1rem"
+ useFlexGap
sx={{ alignItems: 'center' }}
>
-
-
-
+ {!props.hideIcon && (
+
+
+
+ )}
{props.children}
diff --git a/frontend/src/components/EventsHistory/NoteCard.tsx b/frontend/src/components/EventsHistory/NoteCard.tsx
index 89d900f6b4..8387998cdd 100644
--- a/frontend/src/components/EventsHistory/NoteCard.tsx
+++ b/frontend/src/components/EventsHistory/NoteCard.tsx
@@ -10,10 +10,10 @@ import localeFR from 'date-fns/locale/fr';
import { useMemo, useState } from 'react';
import { FormProvider, type SubmitHandler, useForm } from 'react-hook-form';
import { match } from 'ts-pattern';
+import * as yup from 'yup';
+
import { useNotification } from '../../hooks/useNotification';
import { useUser } from '../../hooks/useUser';
-
-import * as yup from 'yup';
import type { Establishment } from '../../models/Establishment';
import type { Note } from '../../models/Note';
import { formatAuthor } from '../../models/User';
@@ -29,6 +29,12 @@ import HistoryCard from './HistoryCard';
export interface NoteCardProps {
note: Note;
establishment: Pick | null;
+ hideIcon?: boolean;
+ /**
+ * If true, the note cannot be edited or deleted.
+ * @default false
+ */
+ readOnly?: boolean;
}
function NoteCard(props: NoteCardProps) {
@@ -66,7 +72,9 @@ function NoteCard(props: NoteCardProps) {
});
const { isAdmin, isUsual, user } = useUser();
- const canUpdate = isAdmin || (isUsual && user?.id === props.note.createdBy);
+ const canUpdate =
+ !props.readOnly &&
+ (isAdmin || (isUsual && user?.id === props.note.createdBy));
const removeModal = useMemo(
() =>
@@ -123,7 +131,7 @@ function NoteCard(props: NoteCardProps) {
}
return (
-
+
{document ? (
diff --git a/frontend/src/components/HousingDetails/DocumentsTab.tsx b/frontend/src/components/HousingDetails/DocumentsTab.tsx
index a6655b4e25..e28f6858fe 100644
--- a/frontend/src/components/HousingDetails/DocumentsTab.tsx
+++ b/frontend/src/components/HousingDetails/DocumentsTab.tsx
@@ -82,7 +82,6 @@ function DocumentsTab() {
function onCancelRename(): void {
setSelectedDocument(null);
- documentRenameModal.close();
}
function rename(filename: string): void {
diff --git a/frontend/src/components/HousingEdition/HousingEditionNoteTab.tsx b/frontend/src/components/HousingEdition/HousingEditionNoteTab.tsx
new file mode 100644
index 0000000000..040a2ce441
--- /dev/null
+++ b/frontend/src/components/HousingEdition/HousingEditionNoteTab.tsx
@@ -0,0 +1,44 @@
+import Stack from '@mui/material/Stack';
+import { skipToken } from '@reduxjs/toolkit/query';
+
+import AppTextInputNext from '~/components/_app/AppTextInput/AppTextInputNext';
+import NoteCard from '~/components/EventsHistory/NoteCard';
+import type { Housing } from '~/models/Housing';
+import { useFindNotesByHousingQuery } from '~/services/note.service';
+
+interface Props {
+ housingId: Housing['id'] | null;
+}
+
+function HousingEditionNoteTab(props: Props) {
+ const { data: notes = [] } = useFindNotesByHousingQuery(
+ props.housingId ?? skipToken
+ );
+
+ return (
+
+
+
+ {notes.length > 0 && (
+
+ {notes.map((note) => (
+
+ ))}
+
+ )}
+
+ );
+}
+
+export default HousingEditionNoteTab;
diff --git a/frontend/src/components/HousingEdition/HousingEditionSideMenu.tsx b/frontend/src/components/HousingEdition/HousingEditionSideMenu.tsx
index 9a5850f7db..24df518ff2 100644
--- a/frontend/src/components/HousingEdition/HousingEditionSideMenu.tsx
+++ b/frontend/src/components/HousingEdition/HousingEditionSideMenu.tsx
@@ -19,11 +19,11 @@ import { HousingStates } from '../../models/HousingState';
import { useUpdateHousingMutation } from '../../services/housing.service';
import { useCreateNoteByHousingMutation } from '../../services/note.service';
import AppLink from '../_app/AppLink/AppLink';
-import AppTextInputNext from '../_app/AppTextInput/AppTextInputNext';
import OccupancySelect from '../HousingListFilters/OccupancySelect';
import AsideNext from '../Aside/AsideNext';
import LabelNext from '../Label/LabelNext';
import HousingEditionMobilizationTab from './HousingEditionMobilizationTab';
+import HousingEditionNoteTab from './HousingEditionNoteTab';
import { useHousingEdition } from './useHousingEdition';
import type { Housing, HousingUpdate } from '../../models/Housing';
import type { HousingEditionContext } from './useHousingEdition';
@@ -173,12 +173,7 @@ function HousingEditionSideMenu(props: HousingEditionSideMenuProps) {
))
.with('note', () => (
-
+
))
.exhaustive();
diff --git a/frontend/src/components/Precision/PrecisionColumn.tsx b/frontend/src/components/Precision/PrecisionColumn.tsx
index ad9d74342a..cb0c02fd44 100644
--- a/frontend/src/components/Precision/PrecisionColumn.tsx
+++ b/frontend/src/components/Precision/PrecisionColumn.tsx
@@ -5,27 +5,84 @@ import type { CheckboxProps } from '@codegouvfr/react-dsfr/Checkbox';
import RadioButtons from '@codegouvfr/react-dsfr/RadioButtons';
import Typography from '@mui/material/Typography';
import classNames from 'classnames';
-import type { ChangeEvent } from 'react';
import type { ElementOf } from 'ts-essentials';
import type { Precision, PrecisionCategory } from '@zerologementvacant/models';
+import { NULL_PRECISION_ID } from '../../models/Precision';
import styles from './precision-modal.module.scss';
-interface PrecisionColumnProps {
+type PrecisionColumnCommonProps = {
category: PrecisionCategory;
icon: FrIconClassName | RiIconClassName;
options: Precision[];
- value: Precision[];
title: string;
- /**
- * @default 'checkbox'
- */
- input?: 'checkbox' | 'radio';
- onChange(event: ChangeEvent): void;
-}
+};
+
+type PrecisionColumnCheckboxProps = PrecisionColumnCommonProps & {
+ input?: 'checkbox';
+ value: Precision[];
+ onChange(value: Precision[]): void;
+};
+
+type PrecisionColumnRadioProps = PrecisionColumnCommonProps & {
+ input: 'radio';
+ value: Precision | null;
+ onChange(value: Precision | null): void;
+};
+
+type PrecisionColumnProps =
+ | PrecisionColumnCheckboxProps
+ | PrecisionColumnRadioProps;
function PrecisionColumn(props: PrecisionColumnProps) {
- const Fieldset = props.input === 'radio' ? RadioButtons : Checkbox;
+ const isRadio = props.input === 'radio';
+ const Fieldset = isRadio ? RadioButtons : Checkbox;
+
+ // Add null option for radio inputs
+ const nullOption: Precision | null = isRadio
+ ? {
+ id: NULL_PRECISION_ID,
+ label: 'Pas d’information',
+ category: props.category
+ }
+ : null;
+
+ const allOptions = nullOption
+ ? [nullOption, ...props.options]
+ : props.options;
+
+ function isOptionChecked(option: Precision): boolean {
+ if (option.id === NULL_PRECISION_ID) {
+ // Null option is checked when there's no selection
+ return isRadio && props.value === null;
+ }
+
+ if (isRadio) {
+ return props.value?.id === option.id;
+ } else {
+ return props.value.some((value) => value.id === option.id);
+ }
+ }
+
+ function handleOptionClick(option: Precision, checked: boolean): void {
+ if (isRadio) {
+ // Radio button mode: always set the clicked option
+ if (option.id === NULL_PRECISION_ID) {
+ props.onChange(null);
+ } else {
+ props.onChange(option);
+ }
+ } else {
+ // Checkbox mode: toggle
+ if (checked) {
+ props.onChange([...props.value, option]);
+ } else {
+ props.onChange(
+ props.value.filter((precision) => precision.id !== option.id)
+ );
+ }
+ }
+ }
return (
<>
@@ -36,13 +93,13 @@ function PrecisionColumn(props: PrecisionColumnProps) {
{props.title}