Skip to content
Snippets Groups Projects
Commit 7025fbbd authored by Bastien DUMONT's avatar Bastien DUMONT :angel:
Browse files

feat(grdf): display grdf consents

parent 566cb9a7
No related branches found
No related tags found
2 merge requests!141Release 2024-02-29,!137feat(grdf): display grdf consents
...@@ -48,7 +48,9 @@ ...@@ -48,7 +48,9 @@
"ecolyo", "ecolyo",
"enedis", "enedis",
"Enedis", "Enedis",
"firstname",
"grdf", "grdf",
"lastname",
"luxon", "luxon",
"toastify", "toastify",
"wysiwyg", "wysiwyg",
......
import { Button, TablePagination, TextField } from '@mui/material' import { Button, TablePagination, TextField } from '@mui/material'
import { import {
ColDef,
ColGroupDef,
CsvExportParams, CsvExportParams,
GridApi, GridApi,
GridReadyEvent, GridReadyEvent,
IRowNode, IRowNode,
RowSelectedEvent, RowSelectedEvent,
ValueFormatterParams,
} from 'ag-grid-community' } from 'ag-grid-community'
import { AgGridReact } from 'ag-grid-react' import { AgGridReact } from 'ag-grid-react'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import React, { useCallback, useEffect, useMemo, useState } from 'react' import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useWhoAmI } from '../../API' import { useWhoAmI } from '../../API'
import { getAxiosXSRFHeader } from '../../axios.config' import { getAxiosXSRFHeader } from '../../axios.config'
import { IConsent } from '../../models/consent.model' import { IGrdfConsent, grdfColumnDef } from '../../models/grdfConsent'
import { ConsentService } from '../../services/consent.service' import { ISgeConsent, sgeColumnDefs } from '../../models/sgeConsent.model'
import { GrdfConsentService } from '../../services/grdfConsent.service'
import { SgeConsentService } from '../../services/sgeConsent.service'
import Loader from '../Loader/Loader'
import DownloadModal from './DownloadModal' import DownloadModal from './DownloadModal'
import './agGridOverrides.scss' import './agGridOverrides.scss'
import styles from './consents.module.scss' import styles from './consents.module.scss'
import './muiPaginationOverrides.scss'
const Consents: React.FC = () => { export const Consents: React.FC<{ type: 'sge' | 'grdf' }> = ({ type }) => {
const isGRDF = type === 'grdf'
const [isLoading, setIsLoading] = useState(false)
const [gridApi, setGridApi] = useState<GridApi | null>(null) const [gridApi, setGridApi] = useState<GridApi | null>(null)
const [search, setSearch] = useState<string>('') const [search, setSearch] = useState<string>('')
const [selectedNodes, setSelectedNodes] = useState<IRowNode[]>([]) const [selectedNodes, setSelectedNodes] = useState<IRowNode[]>([])
const [isShowingSelection, setIsShowingSelection] = useState<boolean>(false) const [isShowingSelection, setIsShowingSelection] = useState<boolean>(false)
const [openDownloadModal, setOpenDownloadModal] = useState<boolean>(false) const [openDownloadModal, setOpenDownloadModal] = useState<boolean>(false)
const [consents, setConsents] = useState<IConsent[]>([]) const [consents, setConsents] = useState<ISgeConsent[] | IGrdfConsent[]>([])
const [page, setPage] = useState<number>(0) const [page, setPage] = useState<number>(0)
const [rowsPerPage, setRowsPerPage] = useState<number>(50) const [rowsPerPage, setRowsPerPage] = useState<number>(50)
const [totalRows, setTotalRows] = useState<number>(50) const [totalRows, setTotalRows] = useState<number>(50)
const { data: user } = useWhoAmI() const { data: user } = useWhoAmI()
const consentService = useMemo(() => {
return new ConsentService()
}, [])
const toggleOpenModal = useCallback(() => { const toggleOpenModal = useCallback(() => {
setOpenDownloadModal(prev => !prev) setOpenDownloadModal(prev => !prev)
}, []) }, [])
const consentService = useMemo(
() => (isGRDF ? new GrdfConsentService() : new SgeConsentService()),
[isGRDF]
)
const defaultColDef = useMemo( const defaultColDef = useMemo(
() => ({ () => ({
sortable: true, sortable: true,
...@@ -48,75 +52,10 @@ const Consents: React.FC = () => { ...@@ -48,75 +52,10 @@ const Consents: React.FC = () => {
[] []
) )
const dateFormatter = (data: ValueFormatterParams): string => { const columnDefs = useMemo(
return (data.value as DateTime).toLocaleString() () => (isGRDF ? grdfColumnDef : sgeColumnDefs),
} [isGRDF]
)
const [columnDefs] = useState<(ColDef | ColGroupDef)[] | null>([
{
field: 'ID',
hide: true,
},
{
field: 'pointID',
headerName: 'N° PDL',
initialWidth: 180,
filter: true,
checkboxSelection: true,
},
{
field: 'lastname',
headerName: 'Nom',
initialWidth: 180,
filter: true,
cellStyle: { textTransform: 'uppercase' },
},
{
field: 'firstname',
headerName: 'Prénom',
initialWidth: 180,
filter: true,
cellStyle: { textTransform: 'capitalize' },
},
{
field: 'address',
headerName: 'Adresse',
initialWidth: 300,
filter: true,
flex: 1,
},
{
field: 'postalCode',
headerName: 'CP',
initialWidth: 80,
filter: true,
},
{
field: 'city',
headerName: 'Ville',
},
{
field: 'safetyOnBoarding',
headerName: 'Secours',
initialWidth: 100,
},
{
field: 'startDate',
valueFormatter: dateFormatter,
headerName: 'Début du consentement',
initialWidth: 150,
filter: true,
sortable: false,
},
{
field: 'endDate',
valueFormatter: dateFormatter,
headerName: 'Fin du consentement',
initialWidth: 150,
filter: true,
sortable: false,
},
])
const handleChangePage = useCallback( const handleChangePage = useCallback(
( (
...@@ -139,13 +78,14 @@ const Consents: React.FC = () => { ...@@ -139,13 +78,14 @@ const Consents: React.FC = () => {
.filter(node => node.isSelected) .filter(node => node.isSelected)
.map(node => node.data.ID) .map(node => node.data.ID)
newNodes.forEach(node => { newNodes?.forEach(node => {
if (idsToCheck.includes(node.data.ID)) node.setSelected(true, false) if (idsToCheck.includes(node.data.ID)) node.setSelected(true, false)
}) })
} }
}, [gridApi, selectedNodes]) }, [gridApi, selectedNodes])
const searchConsents = async () => { const searchConsents = async () => {
setIsLoading(true)
if (user) { if (user) {
const consentPagination = await consentService.searchConsents( const consentPagination = await consentService.searchConsents(
search, search,
...@@ -159,6 +99,7 @@ const Consents: React.FC = () => { ...@@ -159,6 +99,7 @@ const Consents: React.FC = () => {
setTotalRows(consentPagination.totalRows) setTotalRows(consentPagination.totalRows)
} }
} }
setIsLoading(false)
} }
const handleSearchChange = (newSearch: string) => { const handleSearchChange = (newSearch: string) => {
...@@ -243,23 +184,23 @@ const Consents: React.FC = () => { ...@@ -243,23 +184,23 @@ const Consents: React.FC = () => {
return () => { return () => {
window.removeEventListener('resize', handleResize) window.removeEventListener('resize', handleResize)
} }
}, [gridApi]) }, [gridApi, isGRDF])
/** Trigger search when page loads or when admin changes input or pagination */ /** Trigger search when page loads or when admin changes input or pagination */
useEffect(() => { useEffect(() => {
searchConsents() searchConsents()
// /!\ Do not change dependencies or effect will not trigger when pagination changes // /!\ Do not change dependencies or effect will not trigger when pagination changes
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [rowsPerPage, page, search]) }, [rowsPerPage, page, search, isGRDF])
return ( return (
<> <>
<div className="header"> <div className="header">
<h1>Gestion des consentements Enedis</h1> <h1>Consentements {isGRDF ? 'GRDF' : 'Enedis'}</h1>
</div> </div>
<div className={styles.content}> <div className={styles.content}>
<TextField <TextField
placeholder="N°PDL (14 chiffres)" placeholder={`N°${isGRDF ? 'PCE' : 'PDL'} (14 chiffres)`}
label="Recherche" label="Recherche"
value={search} value={search}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
...@@ -272,24 +213,27 @@ const Consents: React.FC = () => { ...@@ -272,24 +213,27 @@ const Consents: React.FC = () => {
className="ag-theme-alpine-dark" className="ag-theme-alpine-dark"
style={{ width: '100%', height: '75vh' }} style={{ width: '100%', height: '75vh' }}
> >
<AgGridReact {isLoading && <Loader />}
onGridReady={onGridReady} {!isLoading && (
defaultColDef={defaultColDef} <AgGridReact
rowHeight={35} onGridReady={onGridReady}
rowData={consents} defaultColDef={defaultColDef}
columnDefs={columnDefs} rowHeight={35}
animateRows={true} rowData={consents}
rowSelection="multiple" columnDefs={columnDefs}
allowDragFromColumnsToolPanel={false} animateRows={true}
onRowSelected={onRowSelected} rowSelection="multiple"
sortingOrder={['asc', 'desc']} allowDragFromColumnsToolPanel={false}
rowMultiSelectWithClick={true} onRowSelected={onRowSelected}
pagination={false} sortingOrder={['asc', 'desc']}
suppressCellFocus={true} rowMultiSelectWithClick={true}
rowClassRules={{ pagination={false}
expired: params => params.data.endDate < DateTime.now(), suppressCellFocus={true}
}} rowClassRules={{
/> expired: params => params.data.endDate < DateTime.now(),
}}
/>
)}
{!isShowingSelection && ( {!isShowingSelection && (
<TablePagination <TablePagination
labelRowsPerPage="Consentements par page" labelRowsPerPage="Consentements par page"
...@@ -333,4 +277,3 @@ const Consents: React.FC = () => { ...@@ -333,4 +277,3 @@ const Consents: React.FC = () => {
</> </>
) )
} }
export default Consents
// Overrides MaterialUI Paginiation styles
.MuiMenuItem-root {
color: black !important;
}
import { Navigate, Route, Routes } from 'react-router-dom' import { Navigate, Route, Routes } from 'react-router-dom'
import { useWhoAmI } from '../../API' import { useWhoAmI } from '../../API'
import Consents from '../Consents/Consents' import { Consents } from '../Consents/Consents'
import Loader from '../Loader/Loader' import Loader from '../Loader/Loader'
import Login from '../Login/Login' import Login from '../Login/Login'
import Newsletter from '../Newsletter/Newsletter' import Newsletter from '../Newsletter/Newsletter'
...@@ -24,9 +24,14 @@ export const links: Record< ...@@ -24,9 +24,14 @@ export const links: Record<
label: 'Prix', label: 'Prix',
path: '/prices', path: '/prices',
}, },
consents: { sgeConsents: {
label: 'Consentements', label: 'Consentements SGE',
path: '/consents', path: '/consents/sge',
adminOnly: true,
},
grdfConsents: {
label: 'Consentements GRDF',
path: '/consents/grdf',
adminOnly: true, adminOnly: true,
}, },
} }
...@@ -52,7 +57,16 @@ const Router = () => { ...@@ -52,7 +57,16 @@ const Router = () => {
<Route path={links.prices.path} element={<Prices />} /> <Route path={links.prices.path} element={<Prices />} />
<Route path="/popups" element={<Popups />} /> <Route path="/popups" element={<Popups />} />
{user.isAdmin && ( {user.isAdmin && (
<Route path={links.consents.path} element={<Consents />} /> <>
<Route
path={links.sgeConsents.path}
element={<Consents type="sge" />}
/>
<Route
path={links.grdfConsents.path}
element={<Consents type="grdf" />}
/>
</>
)} )}
<Route path="/login" element={<Login />} /> <Route path="/login" element={<Login />} />
<Route <Route
......
import { DateTime } from 'luxon'
export interface IConsent
extends Omit<ConsentEntity, 'CreatedAt' | 'endDate' | 'inseeCode'> {
startDate: DateTime
endDate: DateTime
}
export interface ConsentEntity {
ID: number
CreatedAt: string
endDate: string
firstname: string
lastname: string
pointID: number
address: string
postalCode: string
inseeCode: string
city: string
safetyOnBoarding: boolean
}
export interface IConsentPagination
extends Omit<ConsentPaginationEntity, 'rows'> {
rows: IConsent[]
}
export interface ConsentPaginationEntity {
totalRows: number
totalPages: number
rows: ConsentEntity[]
}
import { ColDef } from 'ag-grid-community'
import { DateTime } from 'luxon'
import { dateFormatter } from '../utils/dateFormatter'
export interface IGrdfConsent
extends Omit<GrdfConsentEntity, 'CreatedAt' | 'endDate'> {
startDate: DateTime
endDate: DateTime
}
export interface GrdfConsentEntity {
ID: number
CreatedAt: string
endDate: string
firstname: string
lastname: string
pce: number
postalCode: string
}
export interface IGrdfConsentPagination
extends Omit<GrdfConsentPaginationEntity, 'rows'> {
rows: IGrdfConsent[]
}
export interface GrdfConsentPaginationEntity {
totalRows: number
totalPages: number
rows: GrdfConsentEntity[]
}
export const grdfColumnDef: ColDef[] = [
{
field: 'ID',
hide: true,
},
{
field: 'pce',
headerName: 'N° PCE',
initialWidth: 180,
filter: true,
checkboxSelection: true,
},
{
field: 'lastname',
headerName: 'Nom',
initialWidth: 180,
filter: true,
cellStyle: { textTransform: 'uppercase' },
},
{
field: 'firstname',
headerName: 'Prénom',
initialWidth: 180,
filter: true,
cellStyle: { textTransform: 'capitalize' },
},
{
field: 'postalCode',
headerName: 'CP',
initialWidth: 80,
filter: true,
},
{
field: 'startDate',
valueFormatter: dateFormatter,
headerName: 'Début du consentement',
initialWidth: 150,
filter: true,
sortable: false,
},
{
field: 'endDate',
valueFormatter: dateFormatter,
headerName: 'Fin du consentement',
initialWidth: 150,
filter: true,
sortable: false,
},
]
import { ColDef } from 'ag-grid-community'
import { DateTime } from 'luxon'
import { dateFormatter } from '../utils/dateFormatter'
export interface ISgeConsent
extends Omit<SgeConsentEntity, 'CreatedAt' | 'endDate' | 'inseeCode'> {
startDate: DateTime
endDate: DateTime
}
export interface SgeConsentEntity {
ID: number
CreatedAt: string
endDate: string
firstname: string
lastname: string
pointID: number
address: string
postalCode: string
inseeCode: string
city: string
safetyOnBoarding: boolean
}
export interface ISgeConsentPagination
extends Omit<SgeConsentPaginationEntity, 'rows'> {
rows: ISgeConsent[]
}
export interface SgeConsentPaginationEntity {
totalRows: number
totalPages: number
rows: SgeConsentEntity[]
}
export const sgeColumnDefs: ColDef[] = [
{
field: 'ID',
hide: true,
},
{
field: 'pointID',
headerName: 'N° PDL',
initialWidth: 180,
filter: true,
checkboxSelection: true,
},
{
field: 'lastname',
headerName: 'Nom',
initialWidth: 180,
filter: true,
cellStyle: { textTransform: 'uppercase' },
},
{
field: 'firstname',
headerName: 'Prénom',
initialWidth: 180,
filter: true,
cellStyle: { textTransform: 'capitalize' },
},
{
field: 'address',
headerName: 'Adresse',
initialWidth: 300,
filter: true,
flex: 1,
},
{
field: 'postalCode',
headerName: 'CP',
initialWidth: 80,
filter: true,
},
{
field: 'city',
headerName: 'Ville',
},
{
field: 'safetyOnBoarding',
headerName: 'Secours',
initialWidth: 100,
},
{
field: 'startDate',
valueFormatter: dateFormatter,
headerName: 'Début du consentement',
initialWidth: 150,
filter: true,
sortable: false,
},
{
field: 'endDate',
valueFormatter: dateFormatter,
headerName: 'Fin du consentement',
initialWidth: 150,
filter: true,
sortable: false,
},
]
import axios, { AxiosRequestConfig } from 'axios'
import { DateTime } from 'luxon'
import { toast } from 'react-toastify'
import {
GrdfConsentEntity,
GrdfConsentPaginationEntity,
IGrdfConsent,
IGrdfConsentPagination,
} from '../models/grdfConsent'
export class GrdfConsentService {
/**
* Search for consents
* @param search
* @param limit
* @param page
* @param axiosHeaders
*/
public searchConsents = async (
search: string,
limit: number,
page: number,
axiosHeaders: AxiosRequestConfig
): Promise<IGrdfConsentPagination | null> => {
try {
const { data } = await axios.get<GrdfConsentPaginationEntity>(
`/api/admin/grdf/consent?search=${search}&limit=${limit}&page=${page}`,
axiosHeaders
)
return this.parseConsentPagination(data)
} catch (e) {
if (e.response.status === 403) {
toast.error("Accès refusé : vous n'avez pas les droits nécessaires")
} else {
toast.error('Erreur lors de la récupération des consentements')
}
console.error(e)
return null
}
}
/**
* Converts consent entity into consent
* @param consentEntity
*/
public parseConsent = (consentEntity: GrdfConsentEntity): IGrdfConsent => {
const startDate = DateTime.fromISO(consentEntity.CreatedAt, {
zone: 'utc',
}).setLocale('fr-FR')
const endDate = DateTime.fromISO(consentEntity.endDate, {
zone: 'utc',
}).setLocale('fr-FR')
return {
ID: consentEntity.ID,
startDate: startDate,
endDate: endDate,
firstname: consentEntity.firstname,
lastname: consentEntity.lastname,
pce: consentEntity.pce,
postalCode: consentEntity.postalCode,
}
}
/**
* Converts consent pagination entity into consent pagination
* @param consentPaginationEntity
*/
public parseConsentPagination = (
consentPaginationEntity: GrdfConsentPaginationEntity
): IGrdfConsentPagination => {
const rows = consentPaginationEntity.rows.map(consent =>
this.parseConsent(consent)
)
const consentPagination: IGrdfConsentPagination = {
rows: rows,
totalRows: consentPaginationEntity.totalRows,
totalPages: consentPaginationEntity.totalPages,
}
return consentPagination
}
}
...@@ -2,13 +2,13 @@ import axios, { AxiosRequestConfig } from 'axios' ...@@ -2,13 +2,13 @@ import axios, { AxiosRequestConfig } from 'axios'
import { DateTime } from 'luxon' import { DateTime } from 'luxon'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { import {
ConsentEntity, ISgeConsent,
ConsentPaginationEntity, ISgeConsentPagination,
IConsent, SgeConsentEntity,
IConsentPagination, SgeConsentPaginationEntity,
} from '../models/consent.model' } from '../models/sgeConsent.model'
export class ConsentService { export class SgeConsentService {
/** /**
* Search for consents * Search for consents
* @param search * @param search
...@@ -21,14 +21,13 @@ export class ConsentService { ...@@ -21,14 +21,13 @@ export class ConsentService {
limit: number, limit: number,
page: number, page: number,
axiosHeaders: AxiosRequestConfig axiosHeaders: AxiosRequestConfig
): Promise<IConsentPagination | null> => { ): Promise<ISgeConsentPagination | null> => {
try { try {
const { data } = await axios.get( const { data } = await axios.get<SgeConsentPaginationEntity>(
`/api/admin/sge/consent?search=${search}&limit=${limit}&page=${page}`, `/api/admin/sge/consent?search=${search}&limit=${limit}&page=${page}`,
axiosHeaders axiosHeaders
) )
const consentPagination = data as ConsentPaginationEntity return this.parseConsentPagination(data)
return this.parseConsentPagination(consentPagination)
} catch (e) { } catch (e) {
if (e.response.status === 403) { if (e.response.status === 403) {
toast.error("Accès refusé : vous n'avez pas les droits nécessaires") toast.error("Accès refusé : vous n'avez pas les droits nécessaires")
...@@ -44,11 +43,11 @@ export class ConsentService { ...@@ -44,11 +43,11 @@ export class ConsentService {
* Converts consent entity into consent * Converts consent entity into consent
* @param consentEntity * @param consentEntity
*/ */
public parseConsent = (consentEntity: ConsentEntity): IConsent => { public parseConsent = (consentEntity: SgeConsentEntity): ISgeConsent => {
const startDate: DateTime = DateTime.fromISO(consentEntity.CreatedAt, { const startDate = DateTime.fromISO(consentEntity.CreatedAt, {
zone: 'utc', zone: 'utc',
}).setLocale('fr-FR') }).setLocale('fr-FR')
const endDate: DateTime = DateTime.fromISO(consentEntity.endDate, { const endDate = DateTime.fromISO(consentEntity.endDate, {
zone: 'utc', zone: 'utc',
}).setLocale('fr-FR') }).setLocale('fr-FR')
...@@ -71,13 +70,13 @@ export class ConsentService { ...@@ -71,13 +70,13 @@ export class ConsentService {
* @param consentPaginationEntity * @param consentPaginationEntity
*/ */
public parseConsentPagination = ( public parseConsentPagination = (
consentPaginationEntity: ConsentPaginationEntity consentPaginationEntity: SgeConsentPaginationEntity
): IConsentPagination => { ): ISgeConsentPagination => {
const rows = consentPaginationEntity.rows.map(consent => const rows = consentPaginationEntity.rows.map(consent =>
this.parseConsent(consent) this.parseConsent(consent)
) )
const consentPagination: IConsentPagination = { const consentPagination: ISgeConsentPagination = {
rows: rows, rows: rows,
totalRows: consentPaginationEntity.totalRows, totalRows: consentPaginationEntity.totalRows,
totalPages: consentPaginationEntity.totalPages, totalPages: consentPaginationEntity.totalPages,
......
import { ValueFormatterParams } from 'ag-grid-community'
import { DateTime } from 'luxon'
export const dateFormatter = (data: ValueFormatterParams): string => {
return (data.value as DateTime).toLocaleString()
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment