Skip to content
Snippets Groups Projects
Consents.tsx 8.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • import { Button, TablePagination, TextField } from '@mui/material'
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
    import {
      CsvExportParams,
      GridApi,
      GridReadyEvent,
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
      RowSelectedEvent,
    } from 'ag-grid-community'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import { AgGridReact } from 'ag-grid-react'
    import { DateTime } from 'luxon'
    
    import React, { useCallback, useEffect, useMemo, useState } from 'react'
    import { useWhoAmI } from '../../API'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import { getAxiosXSRFHeader } from '../../axios.config'
    
    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'
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
    import './agGridOverrides.scss'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import styles from './consents.module.scss'
    
    export const Consents: React.FC<{ type: 'sge' | 'grdf' }> = ({ type }) => {
      const isGRDF = type === 'grdf'
    
      const [isLoading, setIsLoading] = useState(false)
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
      const [gridApi, setGridApi] = useState<GridApi | null>(null)
      const [search, setSearch] = useState<string>('')
    
      const [selectedNodes, setSelectedNodes] = useState<IRowNode[]>([])
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
      const [isShowingSelection, setIsShowingSelection] = useState<boolean>(false)
    
      const [openDownloadModal, setOpenDownloadModal] = useState<boolean>(false)
    
      const [consents, setConsents] = useState<ISgeConsent[] | IGrdfConsent[]>([])
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
      const [page, setPage] = useState<number>(0)
      const [rowsPerPage, setRowsPerPage] = useState<number>(50)
      const [totalRows, setTotalRows] = useState<number>(50)
    
      const { data: user } = useWhoAmI()
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
    
      const toggleOpenModal = useCallback(() => {
    
        setOpenDownloadModal(prev => !prev)
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
      }, [])
    
    
      const consentService = useMemo(
        () => (isGRDF ? new GrdfConsentService() : new SgeConsentService()),
        [isGRDF]
      )
    
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
      const defaultColDef = useMemo(
        () => ({
          sortable: true,
          resizable: true,
        }),
        []
      )
    
      const columnDefs = useMemo(
        () => (isGRDF ? grdfColumnDef : sgeColumnDefs),
        [isGRDF]
      )
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
      const handleChangePage = useCallback(
        (
          _event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null,
          newPage: number
        ) => {
          setPage(newPage)
        },
        []
      )
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
      const handleChangeRowsPerPage = useCallback(event => {
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
        setRowsPerPage(event.target.value)
        setPage(0)
      }, [])
    
      const checkSelectedNodes = useCallback(() => {
        if (gridApi) {
          const newNodes = gridApi.getRenderedNodes()
          const idsToCheck: string[] = selectedNodes
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            .filter(node => node.isSelected)
            .map(node => node.data.ID)
    
          newNodes?.forEach(node => {
    
            if (idsToCheck.includes(node.data.ID)) node.setSelected(true, false)
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
          })
        }
      }, [gridApi, selectedNodes])
    
    
      const searchConsents = async () => {
    
        setIsLoading(true)
    
        if (user) {
          const consentPagination = await consentService.searchConsents(
            search,
            rowsPerPage,
            page,
            getAxiosXSRFHeader(user.xsrftoken)
          )
          if (consentPagination) {
            setConsents(consentPagination.rows)
            checkSelectedNodes()
            setTotalRows(consentPagination.totalRows)
    
        setIsLoading(false)
    
      }
    
      const handleSearchChange = (newSearch: string) => {
        setSearch(newSearch)
        setPage(0)
      }
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
    
      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(
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
              node => node.data.ID === event.node.data.ID
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
            )
            if (index === -1) {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
              setSelectedNodes(prev => [...prev, event.node])
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
            }
          } else {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            setSelectedNodes(prev =>
              prev.filter(node => {
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
                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 => {
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
            if (idsToCheck.includes(node.data.ID)) node.setSelected(true)
          })
        }
      }, [gridApi, consents, selectedNodes])
    
      const showCurrentSelection = useCallback(() => {
        setIsShowingSelection(true)
    
        const dataFromNode = selectedNodes.map(item => item.data)
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
        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)
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
        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, isGRDF])
    
      /** Trigger search when page loads or when admin changes input or pagination */
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
      useEffect(() => {
    
        // /!\ Do not change dependencies or effect will not trigger when pagination changes
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
        // eslint-disable-next-line react-hooks/exhaustive-deps
    
      }, [rowsPerPage, page, search, isGRDF])
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
    
      return (
        <>
    
          <div className="header">
    
            <h1>Consentements {isGRDF ? 'GRDF' : 'Enedis'}</h1>
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
          </div>
    
          <div className={styles.content}>
    
              placeholder={`N°${isGRDF ? 'PCE' : 'PDL'} (14 chiffres)`}
    
              label="Recherche"
              value={search}
              onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                handleSearchChange(e.target.value)
              }
              disabled={isShowingSelection}
              autoComplete="off"
            />
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
            <div
              className="ag-theme-alpine-dark"
    
              style={{ width: '100%', height: '75vh' }}
    
              {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 && (
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
                <TablePagination
                  labelRowsPerPage="Consentements par page"
                  component="div"
                  count={totalRows}
                  page={page}
                  onPageChange={handleChangePage}
                  rowsPerPage={rowsPerPage}
                  onRowsPerPageChange={handleChangeRowsPerPage}
                  rowsPerPageOptions={[10, 25, 50, 100]}
                />
              )}
            </div>
    
              open={openDownloadModal}
    
              toggleOpenModal={toggleOpenModal}
              exportData={exportData}
            />
    
    Guilhem CARRON's avatar
    Guilhem CARRON committed
          </div>
    
          <div className={styles.footerButtons}>
    
            <Button
              variant="outlined"
    
              onClick={isShowingSelection ? continueSelection : resetSelection}
              disabled={
                !isShowingSelection && selectedNodes && selectedNodes.length === 0
              }
            >
              {isShowingSelection
                ? 'Continuer ma sélection'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
                : 'Tout désélectionner'}
    
              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>