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 @@
"ecolyo",
"enedis",
"Enedis",
"firstname",
"grdf",
"lastname",
"luxon",
"toastify",
"wysiwyg",
......
import { Button, TablePagination, TextField } from '@mui/material'
import {
ColDef,
ColGroupDef,
CsvExportParams,
GridApi,
GridReadyEvent,
IRowNode,
RowSelectedEvent,
ValueFormatterParams,
} from 'ag-grid-community'
import { AgGridReact } from 'ag-grid-react'
import { DateTime } from 'luxon'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useWhoAmI } from '../../API'
import { getAxiosXSRFHeader } from '../../axios.config'
import { IConsent } from '../../models/consent.model'
import { ConsentService } from '../../services/consent.service'
import { IGrdfConsent, grdfColumnDef } from '../../models/grdfConsent'
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 './agGridOverrides.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 [search, setSearch] = useState<string>('')
const [selectedNodes, setSelectedNodes] = useState<IRowNode[]>([])
const [isShowingSelection, setIsShowingSelection] = 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 [rowsPerPage, setRowsPerPage] = useState<number>(50)
const [totalRows, setTotalRows] = useState<number>(50)
const { data: user } = useWhoAmI()
const consentService = useMemo(() => {
return new ConsentService()
}, [])
const toggleOpenModal = useCallback(() => {
setOpenDownloadModal(prev => !prev)
}, [])
const consentService = useMemo(
() => (isGRDF ? new GrdfConsentService() : new SgeConsentService()),
[isGRDF]
)
const defaultColDef = useMemo(
() => ({
sortable: true,
......@@ -48,75 +52,10 @@ const Consents: React.FC = () => {
[]
)
const dateFormatter = (data: ValueFormatterParams): string => {
return (data.value as DateTime).toLocaleString()
}
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 columnDefs = useMemo(
() => (isGRDF ? grdfColumnDef : sgeColumnDefs),
[isGRDF]
)
const handleChangePage = useCallback(
(
......@@ -139,13 +78,14 @@ const Consents: React.FC = () => {
.filter(node => node.isSelected)
.map(node => node.data.ID)
newNodes.forEach(node => {
newNodes?.forEach(node => {
if (idsToCheck.includes(node.data.ID)) node.setSelected(true, false)
})
}
}, [gridApi, selectedNodes])
const searchConsents = async () => {
setIsLoading(true)
if (user) {
const consentPagination = await consentService.searchConsents(
search,
......@@ -159,6 +99,7 @@ const Consents: React.FC = () => {
setTotalRows(consentPagination.totalRows)
}
}
setIsLoading(false)
}
const handleSearchChange = (newSearch: string) => {
......@@ -243,23 +184,23 @@ const Consents: React.FC = () => {
return () => {
window.removeEventListener('resize', handleResize)
}
}, [gridApi])
}, [gridApi, isGRDF])
/** Trigger search when page loads or when admin changes input or pagination */
useEffect(() => {
searchConsents()
// /!\ Do not change dependencies or effect will not trigger when pagination changes
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [rowsPerPage, page, search])
}, [rowsPerPage, page, search, isGRDF])
return (
<>
<div className="header">
<h1>Gestion des consentements Enedis</h1>
<h1>Consentements {isGRDF ? 'GRDF' : 'Enedis'}</h1>
</div>
<div className={styles.content}>
<TextField
placeholder="N°PDL (14 chiffres)"
placeholder={`N°${isGRDF ? 'PCE' : 'PDL'} (14 chiffres)`}
label="Recherche"
value={search}
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
......@@ -272,24 +213,27 @@ const Consents: React.FC = () => {
className="ag-theme-alpine-dark"
style={{ width: '100%', height: '75vh' }}
>
<AgGridReact
onGridReady={onGridReady}
defaultColDef={defaultColDef}
rowHeight={35}
rowData={consents}
columnDefs={columnDefs}
animateRows={true}
rowSelection="multiple"
allowDragFromColumnsToolPanel={false}
onRowSelected={onRowSelected}
sortingOrder={['asc', 'desc']}
rowMultiSelectWithClick={true}
pagination={false}
suppressCellFocus={true}
rowClassRules={{
expired: params => params.data.endDate < DateTime.now(),
}}
/>
{isLoading && <Loader />}
{!isLoading && (
<AgGridReact
onGridReady={onGridReady}
defaultColDef={defaultColDef}
rowHeight={35}
rowData={consents}
columnDefs={columnDefs}
animateRows={true}
rowSelection="multiple"
allowDragFromColumnsToolPanel={false}
onRowSelected={onRowSelected}
sortingOrder={['asc', 'desc']}
rowMultiSelectWithClick={true}
pagination={false}
suppressCellFocus={true}
rowClassRules={{
expired: params => params.data.endDate < DateTime.now(),
}}
/>
)}
{!isShowingSelection && (
<TablePagination
labelRowsPerPage="Consentements par page"
......@@ -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 { useWhoAmI } from '../../API'
import Consents from '../Consents/Consents'
import { Consents } from '../Consents/Consents'
import Loader from '../Loader/Loader'
import Login from '../Login/Login'
import Newsletter from '../Newsletter/Newsletter'
......@@ -24,9 +24,14 @@ export const links: Record<
label: 'Prix',
path: '/prices',
},
consents: {
label: 'Consentements',
path: '/consents',
sgeConsents: {
label: 'Consentements SGE',
path: '/consents/sge',
adminOnly: true,
},
grdfConsents: {
label: 'Consentements GRDF',
path: '/consents/grdf',
adminOnly: true,
},
}
......@@ -52,7 +57,16 @@ const Router = () => {
<Route path={links.prices.path} element={<Prices />} />
<Route path="/popups" element={<Popups />} />
{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
......
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'
import { DateTime } from 'luxon'
import { toast } from 'react-toastify'
import {
ConsentEntity,
ConsentPaginationEntity,
IConsent,
IConsentPagination,
} from '../models/consent.model'
ISgeConsent,
ISgeConsentPagination,
SgeConsentEntity,
SgeConsentPaginationEntity,
} from '../models/sgeConsent.model'
export class ConsentService {
export class SgeConsentService {
/**
* Search for consents
* @param search
......@@ -21,14 +21,13 @@ export class ConsentService {
limit: number,
page: number,
axiosHeaders: AxiosRequestConfig
): Promise<IConsentPagination | null> => {
): Promise<ISgeConsentPagination | null> => {
try {
const { data } = await axios.get(
const { data } = await axios.get<SgeConsentPaginationEntity>(
`/api/admin/sge/consent?search=${search}&limit=${limit}&page=${page}`,
axiosHeaders
)
const consentPagination = data as ConsentPaginationEntity
return this.parseConsentPagination(consentPagination)
return this.parseConsentPagination(data)
} catch (e) {
if (e.response.status === 403) {
toast.error("Accès refusé : vous n'avez pas les droits nécessaires")
......@@ -44,11 +43,11 @@ export class ConsentService {
* Converts consent entity into consent
* @param consentEntity
*/
public parseConsent = (consentEntity: ConsentEntity): IConsent => {
const startDate: DateTime = DateTime.fromISO(consentEntity.CreatedAt, {
public parseConsent = (consentEntity: SgeConsentEntity): ISgeConsent => {
const startDate = DateTime.fromISO(consentEntity.CreatedAt, {
zone: 'utc',
}).setLocale('fr-FR')
const endDate: DateTime = DateTime.fromISO(consentEntity.endDate, {
const endDate = DateTime.fromISO(consentEntity.endDate, {
zone: 'utc',
}).setLocale('fr-FR')
......@@ -71,13 +70,13 @@ export class ConsentService {
* @param consentPaginationEntity
*/
public parseConsentPagination = (
consentPaginationEntity: ConsentPaginationEntity
): IConsentPagination => {
consentPaginationEntity: SgeConsentPaginationEntity
): ISgeConsentPagination => {
const rows = consentPaginationEntity.rows.map(consent =>
this.parseConsent(consent)
)
const consentPagination: IConsentPagination = {
const consentPagination: ISgeConsentPagination = {
rows: rows,
totalRows: consentPaginationEntity.totalRows,
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.
Finish editing this message first!
Please register or to comment