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, ValueFormatterParams, } 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' import { getAxiosXSRFHeader } from '../../axios.config' import { DateTime } from 'luxon' 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 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: 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, getAxiosXSRFHeader(user.xsrftoken) ) } else { const consentPagination = await consentService.getConsents( rowsPerPage, page, getAxiosXSRFHeader(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, getAxiosXSRFHeader(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="header"> <p className="title pagetitle">Gestion des consentements Enedis</p> </div> <div className={styles.content}> <div className={styles.searchField}> <div className={styles.inputGroup}> <label htmlFor="search">Recherche</label> <input value={search} name="search" type="text" placeholder="N°PDL, Nom, Prénom..." onChange={(e: React.ChangeEvent<HTMLInputElement>) => handleSearchChange(e.target.value) } disabled={isShowingSelection} autoComplete="off" ></input> </div> </div> <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} ></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> {openDowloadModal && ( <DowloadModal toggleOpenModal={toggleOpenModal} exportData={exportData} /> )} </div> <div className={styles.footerButtons}> <button className="btnDelete" onClick={isShowingSelection ? continueSelection : resetSelection} disabled={ !isShowingSelection && selectedNodes && selectedNodes.length === 0 } > {isShowingSelection ? 'Continuer ma sélection' : 'Tout déselectionner'} </button> <button className={styles.btnSelection + ' btnValid'} onClick={!isShowingSelection ? showCurrentSelection : toggleOpenModal} disabled={selectedNodes && selectedNodes.length <= 0} > {!isShowingSelection ? 'Voir mes sélections' : 'Télécharger'} <span>{selectedNodes?.length}</span> </button> </div> </> ) } export default Consents