Skip to content
Snippets Groups Projects
Consents.tsx 9.89 KiB
Newer Older
Guilhem CARRON's avatar
Guilhem CARRON committed
import React, {
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from 'react'
import { AgGridReact } from 'ag-grid-react'
import 'ag-grid-community/dist/styles/ag-grid.css'
import 'ag-grid-community/dist/styles/ag-theme-alpine-dark.css'
import TablePagination from '@material-ui/core/TablePagination'
import {
  ColDef,
  ColGroupDef,
  CsvExportParams,
  GridApi,
  GridReadyEvent,
  RowNode,
  RowSelectedEvent,
} from 'ag-grid-community'
import styles from './consents.module.scss'
import './agGridOverrides.scss'
import './muiPaginationOverrides.scss'
import DowloadModal from './DowloadModal'
import { ConsentService } from '../../services/consent.service'
import { UserContextProps, UserContext } from '../../hooks/userContext'
import { IConsent } from '../../models/consent.model'

const Consents: React.FC = () => {
  const [gridApi, setGridApi] = useState<GridApi | null>(null)
  const [search, setSearch] = useState<string>('')
  const [selectedNodes, setSelectedNodes] = useState<RowNode[]>([])
  const [isShowingSelection, setIsShowingSelection] = useState<boolean>(false)
  const [openDowloadModal, setOpenDowloadModal] = useState<boolean>(false)
  const [consents, setConsents] = useState<IConsent[]>([])
  const [page, setPage] = useState<number>(0)
  const [rowsPerPage, setRowsPerPage] = useState<number>(50)
  const [totalRows, setTotalRows] = useState<number>(50)
  const { user }: Partial<UserContextProps> = useContext(UserContext)
  const consentService = useMemo(() => {
    return new ConsentService()
  }, [])

  const toggleOpenModal = useCallback(() => {
    setOpenDowloadModal((prev) => !prev)
  }, [])

  const defaultColDef = useMemo(
    () => ({
      sortable: true,
      resizable: true,
    }),
    []
  )
  const [columnDefs] = useState<(ColDef | ColGroupDef)[] | null>([
    {
      field: 'ID',
      hide: true,
    },
    {
      field: 'pointID',
      headerName: 'N° PDL',
      filter: true,
      checkboxSelection: true,
    },
    {
      field: 'address',
      headerName: 'Adresse',
      initialWidth: 300,
      filter: true,
      flex: 1,
    },
    {
      field: 'startDate',
      headerName: 'Début du consentement',
      initialWidth: 200,
      filter: true,
    },
    {
      field: 'endDate',
      headerName: 'Fin du consentement',
      initialWidth: 200,
      filter: true,
    },
    {
      field: 'lastname',
      headerName: 'Nom',
      initialWidth: 200,
      filter: true,
    },
    {
      field: 'firstname',
      headerName: 'Prénom',
      initialWidth: 200,
      filter: true,
    },
  ])
  const handleChangePage = useCallback(
    (
      _event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null,
      newPage: number
    ) => {
      setPage(newPage)
    },
    []
  )
  const handleChangeRowsPerPage = useCallback((event: any) => {
    setRowsPerPage(event.target.value)
    setPage(0)
  }, [])

  const checkSelectedNodes = useCallback(() => {
    if (gridApi) {
      const newNodes = gridApi.getRenderedNodes()
      const idsToCheck: string[] = selectedNodes
        .filter((node: RowNode) => node.isSelected)
        .map((node: RowNode) => node.data.ID)

      newNodes.forEach((node: RowNode) => {
        if (idsToCheck.includes(node.data.ID))
          node.setSelected(true, false, true)
      })
    }
  }, [gridApi, selectedNodes])

  const handleSearchChange = useCallback(
    async (newSearch: string) => {
      setSearch(newSearch)
      if (user) {
        let consentsData: IConsent[] | null = []
        if (newSearch) {
          consentsData = await consentService.searchConsent(
            newSearch,
            user.xsrftoken
          )
        } else {
          const consentPagination = await consentService.getConsents(
            rowsPerPage,
            page,
            user.xsrftoken
          )
          consentsData = consentPagination && consentPagination.rows
        }
        if (consentsData) {
          setConsents(consentsData)
          checkSelectedNodes()
        }
      }
    },
    [user, consentService, rowsPerPage, page, checkSelectedNodes]
  )

  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: RowNode) => node.isSelected)
        .map((node: RowNode) => node.data.ID)

      newNodes.forEach((node: RowNode) => {
        if (idsToCheck.includes(node.data.ID)) node.setSelected(true)
      })
    }
  }, [gridApi, consents, selectedNodes])

  const showCurrentSelection = useCallback(() => {
    setIsShowingSelection(true)
    const dataFromNode = selectedNodes.map((item: RowNode) => 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)
    setOpenDowloadModal(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])

  useEffect(() => {
    async function getConsentsData() {
      if (user) {
        const consentsPaginationData = await consentService.getConsents(
          rowsPerPage,
          page,
          user.xsrftoken
        )
        if (consentsPaginationData) {
          setConsents(consentsPaginationData.rows)
          checkSelectedNodes()
          setTotalRows(consentsPaginationData.totalRows)
        }
      }
    }
    getConsentsData()
    // /!\ Do not add checkSelected in dependencies or effect will trigger on each selection
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [user, consentService, rowsPerPage, page])

  return (
    <>
      <div className={styles.header + ' header'}>
        <p className="title pagetitle">Gestion des consentements Enedis</p>
      </div>
      <div className={styles.content + ' content'}>
        <div className={styles.searchField}>
          <div className={styles.inputGroup}>
            <label htmlFor="search">N° PDL / Nom: </label>
            <input
              value={search}
              name="search"
              type="text"
              placeholder="N° PDL / Nom"
              onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                handleSearchChange(e.target.value)
              }
              disabled={isShowingSelection}
              autoComplete="off"
Guilhem CARRON's avatar
Guilhem CARRON committed
            ></input>
          </div>
        </div>
        <div
          className="ag-theme-alpine-dark"
          style={{ width: '100%', height: '65vh' }}
        >
          <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}
          ></AgGridReact>
          {search === '' && !isShowingSelection && (
            <TablePagination
              labelRowsPerPage="Consentements par page"
              component="div"
              count={totalRows}
              page={page}
              onPageChange={handleChangePage}
              rowsPerPage={rowsPerPage}
              onRowsPerPageChange={handleChangeRowsPerPage}
              rowsPerPageOptions={[10, 25, 50, 100]}
            />
          )}
          <div className={styles.footerButtons}>
            <button
              className="btnDelete"
              onClick={isShowingSelection ? continueSelection : resetSelection}
              disabled={
                !isShowingSelection &&
                selectedNodes &&
                selectedNodes.length === 0
              }
Guilhem CARRON's avatar
Guilhem CARRON committed
            >
              {isShowingSelection
                ? 'Continuer ma sélection'
                : 'Tout déselectionner'}
            </button>
            <button
              className={styles.btnSelection + ' btnValid'}
              onClick={
                !isShowingSelection ? showCurrentSelection : toggleOpenModal
              }
              disabled={selectedNodes && selectedNodes.length <= 0}
Guilhem CARRON's avatar
Guilhem CARRON committed
            >
              {!isShowingSelection ? 'Voir mes sélections' : 'Télécharger'}
              <span>{selectedNodes?.length}</span>
            </button>
          </div>
        </div>
        {openDowloadModal && (
          <DowloadModal
            toggleOpenModal={toggleOpenModal}
            exportData={exportData}
          />
        )}
      </div>
    </>
  )
}
export default Consents