diff --git a/src/components/Consents/GrdfConsents.tsx b/src/components/Consents/Consents.tsx similarity index 83% rename from src/components/Consents/GrdfConsents.tsx rename to src/components/Consents/Consents.tsx index af2f868500f09068859074feaae5ab44899e31e8..3e0c14017f86df2343b2cb23e784f9a131f82507 100644 --- a/src/components/Consents/GrdfConsents.tsx +++ b/src/components/Consents/Consents.tsx @@ -1,39 +1,41 @@ 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 { IGrdfConsent } from '../../models/grdfConsent' +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 DownloadModal from './DownloadModal' import './agGridOverrides.scss' import styles from './consents.module.scss' -export const GrdfConsents: React.FC = () => { +export const Consents: React.FC<{ type: 'sge' | 'grdf' }> = ({ type }) => { + const isGRDF = type === 'grdf' + 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<IGrdfConsent[]>([]) + 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 GrdfConsentService() - }, []) + return isGRDF ? new GrdfConsentService() : new SgeConsentService() + }, [isGRDF]) const toggleOpenModal = useCallback(() => { setOpenDownloadModal(prev => !prev) @@ -47,58 +49,10 @@ export const GrdfConsents: React.FC = () => { [] ) - const dateFormatter = (data: ValueFormatterParams): string => { - return (data.value as DateTime).toLocaleString() - } - - const [columnDefs] = useState<(ColDef | ColGroupDef)[] | null>([ - { - 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, - sort: 'desc', - }, - { - field: 'endDate', - valueFormatter: dateFormatter, - headerName: 'Fin du consentement', - initialWidth: 150, - filter: true, - }, - ]) + const columnDefs = useMemo( + () => (isGRDF ? grdfColumnDef : sgeColumnDefs), + [isGRDF] + ) const handleChangePage = useCallback( ( @@ -225,23 +179,23 @@ export const GrdfConsents: 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>Consentements GRDF</h1> + <h1>Consentements Enedis</h1> </div> <div className={styles.content}> <TextField - placeholder="N°PCE (14 chiffres)" + placeholder="N°PDL (14 chiffres)" label="Recherche" value={search} onChange={(e: React.ChangeEvent<HTMLInputElement>) => diff --git a/src/components/Consents/SgeConsents.tsx b/src/components/Consents/SgeConsents.tsx deleted file mode 100644 index f31a476746e6e4c7b75bdba99a2052086d7d2bc6..0000000000000000000000000000000000000000 --- a/src/components/Consents/SgeConsents.tsx +++ /dev/null @@ -1,333 +0,0 @@ -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 { ISgeConsent } from '../../models/sgeConsent.model' -import { SgeConsentService } from '../../services/sgeConsent.service' -import DownloadModal from './DownloadModal' -import './agGridOverrides.scss' -import styles from './consents.module.scss' - -export const SgeConsents: React.FC = () => { - 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<ISgeConsent[]>([]) - 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 SgeConsentService() - }, []) - - const toggleOpenModal = useCallback(() => { - setOpenDownloadModal(prev => !prev) - }, []) - - const defaultColDef = useMemo( - () => ({ - sortable: true, - resizable: true, - }), - [] - ) - - 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, - sort: 'desc', - }, - { - field: 'endDate', - valueFormatter: dateFormatter, - headerName: 'Fin du consentement', - initialWidth: 150, - filter: true, - }, - ]) - - const handleChangePage = useCallback( - ( - _event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null, - newPage: number - ) => { - setPage(newPage) - }, - [] - ) - const handleChangeRowsPerPage = useCallback(event => { - setRowsPerPage(event.target.value) - setPage(0) - }, []) - - const checkSelectedNodes = useCallback(() => { - if (gridApi) { - const newNodes = gridApi.getRenderedNodes() - const idsToCheck: string[] = selectedNodes - .filter(node => node.isSelected) - .map(node => node.data.ID) - - newNodes.forEach(node => { - if (idsToCheck.includes(node.data.ID)) node.setSelected(true, false) - }) - } - }, [gridApi, selectedNodes]) - - const searchConsents = async () => { - if (user) { - const consentPagination = await consentService.searchConsents( - search, - rowsPerPage, - page, - getAxiosXSRFHeader(user.xsrftoken) - ) - if (consentPagination) { - setConsents(consentPagination.rows) - checkSelectedNodes() - setTotalRows(consentPagination.totalRows) - } - } - } - - const handleSearchChange = (newSearch: string) => { - setSearch(newSearch) - setPage(0) - } - - const resetSelection = useCallback(() => { - if (gridApi) { - setIsShowingSelection(false) - gridApi.setRowData(consents) - gridApi.deselectAll() - setSelectedNodes([]) - } - }, [gridApi, consents]) - - const onRowSelected = useCallback( - (event: RowSelectedEvent) => { - if (event.node.isSelected()) { - const index = selectedNodes.findIndex( - node => node.data.ID === event.node.data.ID - ) - if (index === -1) { - setSelectedNodes(prev => [...prev, event.node]) - } - } else { - setSelectedNodes(prev => - prev.filter(node => { - return node.data.ID != event.node.data.ID - }) - ) - } - }, - [selectedNodes] - ) - - const continueSelection = useCallback(() => { - if (gridApi) { - setIsShowingSelection(false) - gridApi?.setRowData(consents) - const newNodes = gridApi.getRenderedNodes() - // We have to select nodes that have already been selected since we cannot pass a Node array to init AgGrid - const idsToCheck: string[] = selectedNodes - .filter(node => node.isSelected) - .map(node => node.data.ID) - - newNodes.forEach(node => { - if (idsToCheck.includes(node.data.ID)) node.setSelected(true) - }) - } - }, [gridApi, consents, selectedNodes]) - - const showCurrentSelection = useCallback(() => { - setIsShowingSelection(true) - const dataFromNode = selectedNodes.map(item => item.data) - selectedNodes && gridApi?.setRowData(dataFromNode) - gridApi?.selectAll() - }, [gridApi, selectedNodes]) - - const exportData = useCallback(() => { - //You can change default column separator - const params: CsvExportParams = { - columnSeparator: ',', - } - gridApi?.exportDataAsCsv(params) - setOpenDownloadModal(false) - resetSelection() - }, [gridApi, resetSelection]) - - const onGridReady = ({ api }: GridReadyEvent) => { - //Grid init method - setGridApi(api) - api.sizeColumnsToFit() - } - - useEffect(() => { - function handleResize() { - gridApi?.sizeColumnsToFit() - } - handleResize() - window.addEventListener('resize', handleResize) - return () => { - window.removeEventListener('resize', handleResize) - } - }, [gridApi]) - - /** 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]) - - return ( - <> - <div className="header"> - <h1>Consentements Enedis</h1> - </div> - <div className={styles.content}> - <TextField - placeholder="N°PDL (14 chiffres)" - label="Recherche" - value={search} - onChange={(e: React.ChangeEvent<HTMLInputElement>) => - handleSearchChange(e.target.value) - } - disabled={isShowingSelection} - autoComplete="off" - /> - <div - 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(), - }} - /> - {!isShowingSelection && ( - <TablePagination - labelRowsPerPage="Consentements par page" - component="div" - count={totalRows} - page={page} - onPageChange={handleChangePage} - rowsPerPage={rowsPerPage} - onRowsPerPageChange={handleChangeRowsPerPage} - rowsPerPageOptions={[10, 25, 50, 100]} - /> - )} - </div> - <DownloadModal - open={openDownloadModal} - toggleOpenModal={toggleOpenModal} - exportData={exportData} - /> - </div> - <div className={styles.footerButtons}> - <Button - variant="outlined" - onClick={isShowingSelection ? continueSelection : resetSelection} - disabled={ - !isShowingSelection && selectedNodes && selectedNodes.length === 0 - } - > - {isShowingSelection - ? 'Continuer ma sélection' - : 'Tout désélectionner'} - </Button> - <Button - onClick={!isShowingSelection ? showCurrentSelection : toggleOpenModal} - disabled={selectedNodes && selectedNodes.length <= 0} - classes={{ contained: styles.btnText }} - > - {!isShowingSelection ? 'Voir mes sélections' : 'Télécharger'} - <div>{selectedNodes?.length}</div> - </Button> - </div> - </> - ) -} diff --git a/src/components/Routes/Router.tsx b/src/components/Routes/Router.tsx index 9b651a64d09b326796a7df3861a320e6d62efbf6..85051f1b76835a79c69b0d2e68ab390160ac0bba 100644 --- a/src/components/Routes/Router.tsx +++ b/src/components/Routes/Router.tsx @@ -1,7 +1,6 @@ import { Navigate, Route, Routes } from 'react-router-dom' import { useWhoAmI } from '../../API' -import { GrdfConsents } from '../Consents/GrdfConsents' -import { SgeConsents } from '../Consents/SgeConsents' +import { Consents } from '../Consents/Consents' import Loader from '../Loader/Loader' import Login from '../Login/Login' import Newsletter from '../Newsletter/Newsletter' @@ -61,11 +60,11 @@ const Router = () => { <> <Route path={links.sgeConsents.path} - element={<SgeConsents />} + element={<Consents type="sge" />} /> <Route path={links.grdfConsents.path} - element={<GrdfConsents />} + element={<Consents type="grdf" />} /> </> )} diff --git a/src/models/grdfConsent.ts b/src/models/grdfConsent.ts index 7f7e3c5f54952c713b566a14ecb2816034a3670b..63e8e7fc5db18156beb5103a9b815247278909e3 100644 --- a/src/models/grdfConsent.ts +++ b/src/models/grdfConsent.ts @@ -1,4 +1,6 @@ +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 @@ -25,3 +27,52 @@ export interface GrdfConsentPaginationEntity { 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, + sort: 'desc', + }, + { + field: 'endDate', + valueFormatter: dateFormatter, + headerName: 'Fin du consentement', + initialWidth: 150, + filter: true, + }, +] diff --git a/src/models/sgeConsent.model.ts b/src/models/sgeConsent.model.ts index 224ba0e913409ea408c6669b234990d552960378..a751a1f45dcb215ccc0b8e68f54b4e29d09cd76c 100644 --- a/src/models/sgeConsent.model.ts +++ b/src/models/sgeConsent.model.ts @@ -1,4 +1,6 @@ +import { ColDef } from 'ag-grid-community' import { DateTime } from 'luxon' +import { dateFormatter } from '../utils/dateFormatter' export interface ISgeConsent extends Omit<SgeConsentEntity, 'CreatedAt' | 'endDate' | 'inseeCode'> { @@ -30,3 +32,68 @@ export interface SgeConsentPaginationEntity { 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, + sort: 'desc', + }, + { + field: 'endDate', + valueFormatter: dateFormatter, + headerName: 'Fin du consentement', + initialWidth: 150, + filter: true, + }, +] diff --git a/src/utils/dateFormatter.ts b/src/utils/dateFormatter.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b4858f4d55cd35dd30b55a736a57dece3df61f4 --- /dev/null +++ b/src/utils/dateFormatter.ts @@ -0,0 +1,6 @@ +import { ValueFormatterParams } from 'ag-grid-community' +import { DateTime } from 'luxon' + +export const dateFormatter = (data: ValueFormatterParams): string => { + return (data.value as DateTime).toLocaleString() +}