diff --git a/components/bal/commune-tab.js b/components/bal/commune-tab.js
deleted file mode 100644
index af92be895..000000000
--- a/components/bal/commune-tab.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import {useContext} from 'react'
-import PropTypes from 'prop-types'
-import {Pane} from 'evergreen-ui'
-
-import BalDataContext from '@/contexts/bal-data'
-
-import CertificationInfos from './certification-infos'
-import HabilitationInfos from './habilitation-infos'
-
-function CommuneTab({commune}) {
- const {baseLocale} = useContext(BalDataContext)
-
- return (
-
- {baseLocale.status !== 'demo' && }
-
-
- )
-}
-
-CommuneTab.propTypes = {
- commune: PropTypes.shape({
- isCOM: PropTypes.bool.isRequired,
- }).isRequired,
-}
-
-export default CommuneTab
-
diff --git a/components/bal/commune-tab.tsx b/components/bal/commune-tab.tsx
new file mode 100644
index 000000000..085fcfc4f
--- /dev/null
+++ b/components/bal/commune-tab.tsx
@@ -0,0 +1,31 @@
+import React, {useContext} from 'react'
+import {Pane} from 'evergreen-ui'
+
+import BalDataContext from '@/contexts/bal-data'
+import TokenContext from '@/contexts/token'
+
+import CertificationInfos from './certification-infos'
+import HabilitationInfos from './habilitation-infos'
+import ReadOnlyInfos from './read-only-infos'
+import {CommmuneType} from '@/types/commune'
+
+interface CommuneTabProps {
+ commune: CommmuneType;
+ openRecoveryDialog: () => void;
+}
+
+function CommuneTab({commune, openRecoveryDialog}: CommuneTabProps) {
+ const {baseLocale} = useContext(BalDataContext)
+ const {token} = useContext(TokenContext)
+
+ return (
+
+ {!token && }
+ {token && baseLocale.status !== 'demo' && }
+
+
+ )
+}
+
+export default CommuneTab
+
diff --git a/components/bal/read-only-infos.tsx b/components/bal/read-only-infos.tsx
new file mode 100644
index 000000000..78db2339e
--- /dev/null
+++ b/components/bal/read-only-infos.tsx
@@ -0,0 +1,35 @@
+import React from 'react'
+import {Pane, Alert, Text, Button} from 'evergreen-ui'
+
+interface BALReadOnlyProps {
+ openRecoveryDialog: () => void;
+}
+
+function BALReadOnly({openRecoveryDialog}: BALReadOnlyProps) {
+ return (
+
+
+
+ Vous ne pouvez pas modifier cette Base Adresse Locale car vous n’êtes pas authentifié comme administrateur.
+
+
+ Si vous êtes administrateur de cette Base Adresse Locale, vous pouvez récupérer vos accès en cliquant sur le bouton ci-dessous.
+
+
+
+
+ )
+}
+
+export default BALReadOnly
diff --git a/components/bal/toponymes-list.js b/components/bal/toponymes-list.tsx
similarity index 76%
rename from components/bal/toponymes-list.js
rename to components/bal/toponymes-list.tsx
index 671f080b9..0f0bc1002 100644
--- a/components/bal/toponymes-list.js
+++ b/components/bal/toponymes-list.tsx
@@ -1,5 +1,4 @@
import {useContext, useMemo, useState} from 'react'
-import PropTypes from 'prop-types'
import {sortBy} from 'lodash'
import {Table, Paragraph, Pane} from 'evergreen-ui'
import {useRouter} from 'next/router'
@@ -16,8 +15,18 @@ import TableRow from '@/components/table-row'
import DeleteWarning from '@/components/delete-warning'
import InfiniteScrollList from '@/components/infinite-scroll-list'
import CommentsContent from '@/components/comments-content'
+import {ToponymeType} from '@/types/toponyme'
-function ToponymesList({toponymes, onEnableEditing, onRemove, balId, addButton}) {
+interface ToponymesListProps {
+ toponymes: ToponymeType[];
+ onRemove: () => Promise;
+ onEnableEditing: (id: string) => void;
+ balId: string;
+ addButton: React.ReactNode;
+ openRecoveryDialog: () => void;
+}
+
+function ToponymesList({toponymes, onEnableEditing, onRemove, balId, addButton, openRecoveryDialog}: ToponymesListProps) {
const {token} = useContext(TokenContext)
const [toRemove, setToRemove] = useState(null)
const [isDisabled, setIsDisabled] = useState(false)
@@ -33,8 +42,8 @@ function ToponymesList({toponymes, onEnableEditing, onRemove, balId, addButton})
setIsDisabled(false)
}
- const onSelect = id => {
- router.push(`/bal/${balId}/toponymes/${id}`)
+ const onSelect = (id: string) => {
+ void router.push(`/bal/${balId}/toponymes/${id}`)
}
const [filtered, setFilter] = useFuse(toponymes, 200, {
@@ -57,7 +66,9 @@ function ToponymesList({toponymes, onEnableEditing, onRemove, balId, addButton})
)}
isDisabled={isDisabled}
- onCancel={() => setToRemove(null)}
+ onCancel={() => {
+ setToRemove(null)
+ }}
onConfirm={handleRemove}
/>
- {toponyme => (
+ {(toponyme: ToponymeType) => (
0 ? : null,
certification: toponyme.isAllCertified ? 'Toutes les adresses de ce toponyme sont certifiées par la commune' : null
}}
actions={{
- onSelect: () => onSelect(toponyme._id),
- onEdit: () => onEnableEditing(toponyme._id),
- onRemove: () => setToRemove(toponyme._id)
+ onSelect: () => {
+ onSelect(toponyme._id)
+ },
+ onEdit: () => {
+ onEnableEditing(toponyme._id)
+ },
+ onRemove: () => {
+ setToRemove(toponyme._id)
+ }
}}
+ openRecoveryDialog={openRecoveryDialog}
/>
)}
@@ -112,12 +131,4 @@ function ToponymesList({toponymes, onEnableEditing, onRemove, balId, addButton})
)
}
-ToponymesList.propTypes = {
- toponymes: PropTypes.array.isRequired,
- onRemove: PropTypes.func,
- onEnableEditing: PropTypes.func.isRequired,
- balId: PropTypes.string.isRequired,
- addButton: PropTypes.object
-}
-
export default ToponymesList
diff --git a/components/bal/voies-list.js b/components/bal/voies-list.tsx
similarity index 76%
rename from components/bal/voies-list.js
rename to components/bal/voies-list.tsx
index 046a56db9..47354f7ec 100644
--- a/components/bal/voies-list.js
+++ b/components/bal/voies-list.tsx
@@ -17,8 +17,19 @@ import CommentsContent from '@/components/comments-content'
import DeleteWarning from '@/components/delete-warning'
import InfiniteScrollList from '../infinite-scroll-list'
+import {VoieType} from '@/types/voie'
-function VoiesList({voies, onEnableEditing, setToConvert, balId, onRemove, addButton}) {
+interface VoiesListProps {
+ voies: VoieType[];
+ onRemove: () => Promise;
+ onEnableEditing: (id: string) => void;
+ balId: string;
+ setToConvert: (id: string) => void;
+ addButton: React.ReactNode;
+ openRecoveryDialog: () => void;
+}
+
+function VoiesList({voies, onEnableEditing, setToConvert, balId, onRemove, addButton, openRecoveryDialog}: VoiesListProps) {
const {token} = useContext(TokenContext)
const [toRemove, setToRemove] = useState(null)
const {isEditing, reloadVoies} = useContext(BalDataContext)
@@ -34,8 +45,8 @@ function VoiesList({voies, onEnableEditing, setToConvert, balId, onRemove, addBu
setIsDisabled(false)
}
- const onSelect = id => {
- router.push(`/bal/${balId}/voies/${id}`)
+ const onSelect = (id: string) => {
+ void router.push(`/bal/${balId}/voies/${id}`)
}
const [filtered, setFilter] = useFuse(voies, 200, {
@@ -57,7 +68,9 @@ function VoiesList({voies, onEnableEditing, setToConvert, balId, onRemove, addBu
Êtes vous bien sûr de vouloir supprimer cette voie ainsi que tous ses numéros ?
)}
- onCancel={() => setToRemove(null)}
+ onCancel={() => {
+ setToRemove(null)
+ }}
onConfirm={handleRemove}
isDisabled={isDisabled}
/>
@@ -89,20 +102,29 @@ function VoiesList({voies, onEnableEditing, setToConvert, balId, onRemove, addBu
)}
- {voie => (
+ {(voie: VoieType) => (
onSelect(voie._id),
- onEdit: () => onEnableEditing(voie._id),
- onRemove: () => setToRemove(voie._id),
+ onSelect: () => {
+ onSelect(voie._id)
+ },
+ onEdit: () => {
+ onEnableEditing(voie._id)
+ },
+ onRemove: () => {
+ setToRemove(voie._id)
+ },
extra: voie.nbNumeros === 0 ? {
- callback: () => setToConvert(voie._id),
+ callback: () => {
+ setToConvert(voie._id)
+ },
icon: KeyTabIcon,
- text: 'Convertir en toponyme',
+ text: 'Convertir en toponyme'
} : null
}}
notifications={{
@@ -110,6 +132,7 @@ function VoiesList({voies, onEnableEditing, setToConvert, balId, onRemove, addBu
comment: voie.commentedNumeros.length > 0 ? : null,
warning: voie.nbNumeros === 0 ? 'Cette voie ne contient aucun numéro' : null
}}
+ openRecoveryDialog={openRecoveryDialog}
/>
)}
diff --git a/components/delete-warning.js b/components/delete-warning.tsx
similarity index 62%
rename from components/delete-warning.js
rename to components/delete-warning.tsx
index 56f73ff9e..d11c1616f 100644
--- a/components/delete-warning.js
+++ b/components/delete-warning.tsx
@@ -1,7 +1,14 @@
-import PropTypes from 'prop-types'
import {Pane, Dialog} from 'evergreen-ui'
-function DeleteWarning({isShown, content, onCancel, onConfirm, isDisabled}) {
+interface DeleteWarningProps {
+ isShown: boolean;
+ content: React.ReactNode;
+ onCancel: () => void;
+ onConfirm: () => void;
+ isDisabled?: boolean;
+}
+
+function DeleteWarning({isShown, content, onCancel, onConfirm, isDisabled}: DeleteWarningProps) {
return (
- {token && (
-
-
-
- )}
+
+
+
{isGroupedActionsShown && (
@@ -143,7 +154,9 @@ function NumerosList({token, voieId, numeros, handleEditing}) {
idVoie={voieId}
numeros={numeros}
selectedNumerosIds={selectedNumerosIds}
- resetSelectedNumerosIds={() => setSelectedNumerosIds([])}
+ resetSelectedNumerosIds={() => {
+ setSelectedNumerosIds([])
+ }}
setIsRemoveWarningShown={setIsRemoveWarningShown}
isAllSelectedCertifie={isAllSelectedCertifie}
onSubmit={onMultipleEdit}
@@ -157,7 +170,9 @@ function NumerosList({token, voieId, numeros, handleEditing}) {
Êtes vous bien sûr de vouloir supprimer tous les numéros sélectionnés ?
)}
- onCancel={() => setIsRemoveWarningShown(false)}
+ onCancel={() => {
+ setIsRemoveWarningShown(false)
+ }}
onConfirm={onMultipleRemove}
isDisabled={isDisabled}
/>
@@ -187,23 +202,31 @@ function NumerosList({token, voieId, numeros, handleEditing}) {
)}
- {(numero => (
+ {((numero: NumeroType) => (
1 ? `${numero.positions.length} positions` : null}
complement={getToponymeName(numero.toponyme)}
- handleSelect={filtered.length > 1 ? () => handleSelect(numero._id) : null}
+ handleSelect={filtered.length > 1 ? () => {
+ handleSelect(numero._id)
+ } : null}
isSelected={selectedNumerosIds.includes(numero._id)}
- isEditingEnabled={Boolean(!isEditing && token)}
+ isEditing={isEditing}
+ isAdmin={Boolean(token)}
notifications={{
certification: numero.certifie ? 'Cette adresse est certifiée par la commune' : null,
comment: numero.comment,
warning: numero.positions.some(p => p.type === 'inconnue') ? 'Le type d’une position est inconnu' : null
}}
actions={{
- onRemove: () => onRemove(numero._id),
- onEdit: () => handleEditing(numero._id)
+ onRemove: async () => onRemove(numero._id),
+ onEdit: () => {
+ handleEditing(numero._id)
+ }
+ }}
+ openRecoveryDialog={() => {
+ setIsRecoveryDisplayed(true)
}}
/>
))}
@@ -213,15 +236,4 @@ function NumerosList({token, voieId, numeros, handleEditing}) {
)
}
-NumerosList.defaultProps = {
- token: null
-}
-
-NumerosList.propTypes = {
- token: PropTypes.string,
- voieId: PropTypes.string.isRequired,
- numeros: PropTypes.array.isRequired,
- handleEditing: PropTypes.func.isRequired
-}
-
export default NumerosList
diff --git a/contexts/bal-recovery.tsx b/contexts/bal-recovery.tsx
new file mode 100644
index 000000000..cf11c3317
--- /dev/null
+++ b/contexts/bal-recovery.tsx
@@ -0,0 +1,31 @@
+import RecoverBALAlert from '@/components/bal-recovery/recover-bal-alert'
+import React, {useState, useMemo} from 'react'
+
+interface BALRecoveryContextType {
+ isRecoveryDisplayed: boolean;
+ setIsRecoveryDisplayed: (value: boolean) => void;
+}
+
+const BALRecoveryContext = React.createContext(null)
+
+export function BALRecoveryProvider(props) {
+ const [isRecoveryDisplayed, setIsRecoveryDisplayed] = useState(false)
+
+ const value = useMemo(() => ({
+ isRecoveryDisplayed,
+ setIsRecoveryDisplayed
+ }), [isRecoveryDisplayed])
+
+ return (
+ <>
+ {
+ setIsRecoveryDisplayed(false)
+ }} />
+
+ >
+ )
+}
+
+export const BALRecoveryConsumer = BALRecoveryContext.Consumer
+
+export default BALRecoveryContext
diff --git a/layouts/sidebar.tsx b/layouts/sidebar.tsx
index d2feca080..bac436f11 100644
--- a/layouts/sidebar.tsx
+++ b/layouts/sidebar.tsx
@@ -2,6 +2,7 @@ import React, {useContext, useEffect} from 'react'
import {Pane, Button, ChevronRightIcon, CrossIcon, ChevronLeftIcon, PaneProps} from 'evergreen-ui'
import BalDataContext from '@/contexts/bal-data'
+import useWindowSize from '@/hooks/useWindowSize'
interface SidebarProps extends PaneProps {
isHidden: boolean;
@@ -13,7 +14,7 @@ interface SidebarProps extends PaneProps {
function Sidebar({isHidden = false, size, onToggle, top, bottom = 0, ...props}: SidebarProps) {
const {setEditingId, isEditing, setIsEditing} = useContext(BalDataContext)
-
+ const {isMobile} = useWindowSize()
const handleClick = () => {
if (isEditing) {
setEditingId(null)
@@ -41,7 +42,7 @@ function Sidebar({isHidden = false, size, onToggle, top, bottom = 0, ...props}:
bottom={bottom}
zIndex={2}
>
- {innerWidth > 800 && (
+ {!isMobile && (
+
-
+
-
-
- {error ? (
-
- ) : (
- <>
-
- {query.balId ? (
-
+
+
+ {error ? (
+
+ ) : (
+ <>
+
+ {query.balId ? (
+
+
+
+ ) : (
-
- ) : (
-
- )}
- >
- )}
-
+ )}
+ >
+ )}
+
+
diff --git a/pages/bal/[balId]/index.tsx b/pages/bal/[balId]/index.tsx
index 7074d7069..189ca8bc2 100644
--- a/pages/bal/[balId]/index.tsx
+++ b/pages/bal/[balId]/index.tsx
@@ -1,5 +1,5 @@
-import React, {useState, useCallback, useContext} from 'react'
-import {Pane, Heading, Text, Paragraph, Button, AddIcon, Tablist, Tab} from 'evergreen-ui'
+import React, {useState, useCallback, useContext, useEffect} from 'react'
+import {Pane, Heading, Text, Paragraph, Button, AddIcon, Tablist, Tab, LockIcon} from 'evergreen-ui'
import {populateCommune, convertVoieToToponyme} from '@/lib/bal-api'
@@ -17,6 +17,7 @@ import ToponymeEditor from '@/components/bal/toponyme-editor'
import CommuneTab from '@/components/bal/commune-tab'
import {CommmuneType} from '@/types/commune'
import {getBaseEditorProps} from '@/layouts/editor'
+import BALRecoveryContext from '@/contexts/bal-recovery'
const TABS = ['Commune', 'Voies', 'Toponymes']
@@ -29,10 +30,11 @@ function BaseLocalePage({commune}: BaseLocalePageProps) {
const [isFormOpen, setIsFormOpen] = useState(false)
const [toConvert, setToConvert] = useState(null)
const [onConvertLoading, setOnConvertLoading] = useState(false)
- const [selectedTabIndex, setSelectedTabIndex] = useState(1)
- const {voies, toponymes, baseLocale} = useContext(BalDataContext)
const {token} = useContext(TokenContext)
+ const [selectedTabIndex, setSelectedTabIndex] = useState(0)
+ const {voies, toponymes, baseLocale} = useContext(BalDataContext)
const {reloadTiles} = useContext(MapContext)
+ const {setIsRecoveryDisplayed} = useContext(BALRecoveryContext)
const {
refreshBALSync,
@@ -45,6 +47,12 @@ function BaseLocalePage({commune}: BaseLocalePageProps) {
useHelp(selectedTabIndex)
+ useEffect(() => {
+ if (token) {
+ setSelectedTabIndex(1)
+ }
+ }, [token])
+
const onPopulate = useCallback(async () => {
setIsEditing(true)
@@ -99,14 +107,21 @@ function BaseLocalePage({commune}: BaseLocalePageProps) {
onEnableEditing={onEdit}
setToConvert={setToConvert}
onRemove={onRemove}
- addButton={token &&
+ openRecoveryDialog={() => {
+ setIsRecoveryDisplayed(true)
+ }}
+ addButton={