From 752e8ad4f681bfa3114c05e5d3c3b6e0eb719b5e Mon Sep 17 00:00:00 2001
From: Bastien Dumont <bdumont@grandlyon.com>
Date: Mon, 15 Jan 2024 15:30:25 +0100
Subject: [PATCH] use single component

---
 .../{GrdfConsents.tsx => Consents.tsx}        |  82 +----
 src/components/Consents/SgeConsents.tsx       | 333 ------------------
 src/components/Routes/Router.tsx              |   7 +-
 src/models/grdfConsent.ts                     |  51 +++
 src/models/sgeConsent.model.ts                |  67 ++++
 src/utils/dateFormatter.ts                    |   6 +
 6 files changed, 145 insertions(+), 401 deletions(-)
 rename src/components/Consents/{GrdfConsents.tsx => Consents.tsx} (83%)
 delete mode 100644 src/components/Consents/SgeConsents.tsx
 create mode 100644 src/utils/dateFormatter.ts

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 af2f8685..3e0c1401 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 f31a4767..00000000
--- 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 9b651a64..85051f1b 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 7f7e3c5f..63e8e7fc 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 224ba0e9..a751a1f4 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 00000000..0b4858f4
--- /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()
+}
-- 
GitLab