Untitled
unknown
javascript
2 years ago
32 kB
5
Indexable
/* eslint-disable react-hooks/exhaustive-deps */import React, { lazy, memo, useRef, useState, useEffect, useCallback, useMemo } from 'react'import PropTypes from 'prop-types'import { Grid, GridColumn, GridToolbar, getSelectedState } from '@progress/kendo-react-grid'import { ExcelExport, ExcelExportColumn } from '@progress/kendo-react-excel-export'import { process, groupBy } from '@progress/kendo-data-query'import { setGroupIds } from '@progress/kendo-react-data-tools'import { getTranslation, errorAlert, validateFieldAccessMode } from '../../utils'import { ToolbarItem, ToolbarSpacer, ToolbarSeparator } from '@progress/kendo-react-buttons'import { makeStyles } from '@material-ui/core/styles'import IconButton from '@material-ui/core/IconButton'import SaveIcon from '@material-ui/icons/Save'import AddIcon from '@material-ui/icons/Add'import LibraryBooksIcon from '@material-ui/icons/LibraryBooks'import { getter } from '@progress/kendo-react-common'import { loadMessages } from '@progress/kendo-react-intl'import en from '../../translations/en'import es from '../../translations/es'import { useSelector } from 'react-redux'import { pageFieldsAccessModes } from '../../redux/selectors'import { FormGroup } from '@applus/applus-components'const TableCustomKendoCommandCell = lazy(() => import('../TableCustomKendoCommandCell')) const TableCustomKendoSpinner = lazy(() => import('../TableCustomKendoSpinner')) const ButtonAlternative = lazy(() => import('../../components/ButtonAlternative')) const useStyles = makeStyles(theme => ({ textInfo: { color: '#ff6900' } })) const TableCustomKendo = ({ columns, dataEditEvaluatedValues, gridProps, height = 'auto', width = 'auto', data, isLoading = false, orderByColumns, onAdd = () => {}, onUpdate = () => {}, onCopy = () => {}, onDelete = () => {}, onEnterEdit = () => {}, onShowHistory = () => {}, onRedirect = () => {}, onSaveSelection = () => {}, onDownload = () => {}, onUpload = () => {}, onShowSignature = () => {}, onSearch = () => {}, language, translation, fieldsPage, scenario, exportCurrentPage = false, fieldPageFileName = 'exportFileName', exportErrorExcelMessage = '', customFileName = '', hideExportButton = false, hideSaveButton = false, textSaveIcon = '', minItemsPerPage = 20, buttonsTitlePrefix = '', blockFields = false, setSelection = () => {}, allLocked = false, dataBind = false, customOnExpandChange, customUpdateDataItems, customToolbar}) => { loadMessages(language?.isoCode === 'es-ES' ? es.translation : en.translation, language?.isoCode) columns = columns.map(c => (!c.minResizableWidth && !c.width ? { ...c, minResizableWidth: 50 } : c)) const initOrderBy = !!orderByColumns ? orderByColumns : [{ field: 'code', dir: 'asc' }] const fieldsAccessModes = useSelector(state => pageFieldsAccessModes(state)) const classes = useStyles() const [collapsedState, setCollapsedState] = useState([]) const [dataResult, setDataResult] = useState(data) const [dataFiltered, setDataFiltered] = useState(data) const [selectedState, setSelectedState] = useState({}) const [dataState, setDataState] = useState({ sort: initOrderBy, take: minItemsPerPage, skip: 0, group: gridProps?.initialGroup }) const [tableHeight, setTableHeight] = useState(150) const [tableWidth, setTableWidth] = useState(1600) const [disabled, setDisabled] = useState(false) const [selectedAll, setSelectedAll] = useState(false) const _export = useRef(null) const tableDiv = useRef(null) const editField = 'inEdit' let commandCellWidth = 0 const { dataItemKey, editable = false, selectable = false, groupable = false, initialGroup = [] } = gridProps const idGetter = getter(dataItemKey) const validateEditBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_edit`, 'Hidden', true ) const validateCopyBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_copy`, 'Hidden', true ) const validateDeleteBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_delete`, 'Hidden', true ) const validateDownloadBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_download`, 'Hidden', true ) const validateUploadBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_upload`, 'Hidden', true ) const validateHistoryBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_history`, 'Hidden', true ) const validateAddBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_add`, 'Hidden', true ) const validateSignatureBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_signature`, 'Hidden', true ) const validateSearchBtn = validateFieldAccessMode( fieldsPage, fieldsAccessModes, `${buttonsTitlePrefix}btn_search`, 'Hidden', true ) let addRowItem = false, editRowItem = false, copyRowItem = false, removeRowItem = false, downloadRowItem = false, historyRowItem = false, addGoRedirect = false, uploadRowItem = false, signatureRowItem = false, searchRowItem = false if (editable === true) { addRowItem = true editRowItem = true copyRowItem = true removeRowItem = true historyRowItem = true addGoRedirect = true downloadRowItem = true uploadRowItem = true signatureRowItem = true searchRowItem = true } else if (!!editable) { if (editable.addRowItem === true) addRowItem = editable.addRowItem if (editable.addGoRedirect === true) addGoRedirect = editable.addGoRedirect if (editable.editRowItem === true) editRowItem = editable.editRowItem if (editable.copyRowItem === true) copyRowItem = editable.copyRowItem if (editable.removeRowItem === true) removeRowItem = editable.removeRowItem if (editable.historyRowItem === true) historyRowItem = editable.historyRowItem if (editable.downloadRowItem === true) downloadRowItem = editable.downloadRowItem if (editable.uploadRowItem === true) uploadRowItem = editable.uploadRowItem if (editable.signatureRowItem === true) signatureRowItem = editable.signatureRowItem if (editable.searchRowItem === true) searchRowItem = editable.searchRowItem } const showEditBtn = editRowItem && validateEditBtn const showCopyBtn = copyRowItem && validateCopyBtn const showRemoveBtn = removeRowItem && validateDeleteBtn const showDownloadBtn = downloadRowItem && validateDownloadBtn const showUploadBtn = uploadRowItem && validateUploadBtn const showHistoryBtn = historyRowItem && validateHistoryBtn const showSelectable = selectable && validateAddBtn const showAddRedirect = (addRowItem || addGoRedirect) && validateAddBtn const showSignatureBtn = signatureRowItem && validateSignatureBtn const showSearchBtn = searchRowItem && validateSearchBtn if (showEditBtn) commandCellWidth += 50 if (showCopyBtn) commandCellWidth += 50 if (showRemoveBtn) commandCellWidth += 50 if (showDownloadBtn) commandCellWidth += 50 if (showUploadBtn) commandCellWidth += 50 if (showHistoryBtn) commandCellWidth += 50 if (showSignatureBtn) commandCellWidth += 50 if (showSearchBtn) commandCellWidth += 50 if (commandCellWidth === 50 && dataResult.some(d => d.inEdit)) commandCellWidth += 50 let result = Object.entries(selectedState).map(([k, v]) => ({ id: Number(k), checked: v })) let filteredData = [] for (let i = 0; i < data.length; i++) { for (let j = 0; j < result.length; j++) { if (data[i].id === result[j].id && result[j].checked === true) { filteredData.push(data[i]) } } } useEffect(() => { !!filteredData && setSelection(filteredData) }, [filteredData.length]) useEffect(() => { !!selectable ? setDataResult( data.map(dataItem => Object.assign( { selected: false }, dataItem ) ) ) : setDataResult(data) setDisabled(false) }, [data]) useEffect(() => { !!selectable && setDataFiltered(process(dataResult, { ...dataState, take: null }).data) }, [dataState, dataResult]) useEffect(() => { !!tableDiv.current.clientHeight && setTableHeight(tableDiv.current.clientHeight) !!tableDiv.current.clientWidth && setTableWidth(tableDiv.current.clientWidth) const selectionArr = Object.entries(selectedState) const filteredArr = selectionArr.filter(([key, value]) => value === true) const selectedArr = Object.fromEntries(filteredArr) setSelection(Object.keys(selectedArr)) }) const CommandCell = props => ( <TableCustomKendoCommandCell {...props} fieldsPage={fieldsPage} language={language} scenario={scenario} edit={enterEdit} editConditionsValid={editConditionsValid} remove={remove} add={add} discard={discard} update={update} copy={copy} download={download} cancel={cancel} editField={editField} showHistory={showHistory} editRowItem={showEditBtn} copyRowItem={showCopyBtn} search={search} searchRowItem={showSearchBtn} downloadRowItem={showDownloadBtn} removeRowItem={showRemoveBtn} historyRowItem={showHistoryBtn} dataItemKey={dataItemKey} buttonsTitlePrefix={buttonsTitlePrefix} uploadRowItem={showUploadBtn} signatureRowItem={showSignatureBtn} showSignature={showSignature} setFiles={upload} allLocked={allLocked} /> ) const getUpdateDataItems = (event, dataItemKey) => { const inEditID = event.dataItem[dataItemKey] const field = event.field || '' return function updateDataItems(old) { return old.map(item => (item[dataItemKey] === inEditID ? { ...item, [field]: event.value } : item)) } } const itemChange = event => { const updateDataItems = customUpdateDataItems ? customUpdateDataItems(event, dataItemKey, data) : getUpdateDataItems(event, dataItemKey) setDataResult(updateDataItems) } const addNew = e => { e.preventDefault() const newDataItem = { inEdit: true, Discontinued: false } setDataResult([newDataItem, ...data]) } const redirectToCreate = e => { e.preventDefault() onRedirect() } const enterEdit = (e, dataItem) => { e.preventDefault() const enterEditError = onEnterEdit(dataItem) if (!enterEditError) { const updatedDataItem = { ...dataItem, inEdit: true } setDataResult(old => old.map(item => (item[dataItemKey] === updatedDataItem[dataItemKey] ? updatedDataItem : item)) ) } } const update = (e, dataItem) => { e.preventDefault() const updateError = onUpdate(dataItem) if (!updateError) { const updatedDataItem = { ...dataItem, inEdit: false } setDataResult(old => old.map(item => (item[dataItemKey] === updatedDataItem[dataItemKey] ? updatedDataItem : item)) ) } } const copy = (e, dataItem) => { e.preventDefault() onCopy(dataItem) } const upload = (dataItem, file) => { onUpload(dataItem, [file], { ...dataItem, fileName: file?.name || '' }) } const cancel = (e, dataItem) => { e.preventDefault() const originalItem = data.find(p => p[dataItemKey] === dataItem[dataItemKey]) setDataResult(old => old.map(item => (item[dataItemKey] === originalItem[dataItemKey] ? originalItem : item))) } const remove = (e, dataItem) => { e.preventDefault() onDelete(dataItem) } const add = (e, dataItem) => { e.preventDefault() dataItem.inEdit = true onAdd(dataItem) } const discard = e => { e.preventDefault() setDataResult(data) } const download = (e, dataItem) => { e.preventDefault() onDownload(dataItem) } const excelExport = e => { e.preventDefault() if (_export.current) { _export.current.save(exportCurrentPage ? process(dataResult, dataState) : data) } else { errorAlert(exportErrorExcelMessage) } } const showHistory = (e, dataItem) => { e.preventDefault() onShowHistory(dataItem) } const showSignature = (e, dataItem) => { e.preventDefault() onShowSignature(dataItem) } const search = (e, dataItem) => { e.preventDefault() onSearch(dataItem) } const saveSelection = e => { e.preventDefault() const selectionArr = Object.entries(selectedState) const filteredArr = selectionArr.filter(([key, value]) => value === true) const selectedArr = Object.fromEntries(filteredArr) const ids = Object.keys(selectedArr) if (dataBind) { const dataSelected = [] ids.forEach(e => { const found = dataResult.find(el => el.id === e) if (found) dataSelected.push(found) }) onSaveSelection(dataSelected) } else { onSaveSelection(ids) setDisabled(true) } setSelectedState({}) setSelectedAll(false) } const onSelectionChange = useCallback( event => { const newSelectedState = getSelectedState({ event, selectedState: selectedState, dataItemKey: dataItemKey }) setSelectedState(newSelectedState) }, [selectedState] ) const onHeaderSelectionChange = useCallback( event => { const checkboxElement = event.syntheticEvent.target const checked = checkboxElement.checked const newSelectedState = {} dataFiltered.forEach(item => { if (dataState?.group?.length) { item?.items?.forEach(subitem => { newSelectedState[idGetter(subitem)] = checked }) } else { newSelectedState[idGetter(item)] = checked } }) setSelectedState(newSelectedState) setSelectedAll(checked) }, [dataFiltered] ) const selectedElementsCount = useCallback(() => { if (!!translation) { const text = translation('toolbar.nRowsSelected') return ( <div className={classes.textInfo}> {text.replace('{0}', Object.values(selectedState).filter(s => s === true).length)} </div> ) } }) const onExpandChange = useCallback( event => { let index = collapsedState.indexOf(event.dataItem.value) if (index >= 0) { let changed = collapsedState.filter((e, i) => i !== index) setCollapsedState(changed) } else { setCollapsedState([...collapsedState, event.dataItem.value]) } }, [collapsedState] ) const editConditionsValid = dataItem => { if (!!!dataEditEvaluatedValues || dataEditEvaluatedValues.length === 0) { return true } const answersConditions = dataEditEvaluatedValues.map(condition => { let answerTmp switch (condition.operator) { case '===': answerTmp = dataItem[condition.field] === condition.evaluateValue break case '!==': answerTmp = dataItem[condition.field] !== condition.evaluateValue break case '>=': answerTmp = dataItem[condition.field] >= condition.evaluateValue break case '<=': answerTmp = dataItem[condition.field] <= condition.evaluateValue break case '>': answerTmp = dataItem[condition.field] > condition.evaluateValue break case '<': answerTmp = dataItem[condition.field] < condition.evaluateValue break default: answerTmp = true break } return answerTmp }) let isBotonValid = answersConditions.reduce((prev, curr) => prev && curr) return isBotonValid } const findSubGroups = e => { if (e.items.some(el => el.items)) { let newEl = e.items.find(el => el.items) let index = collapsedState.indexOf(newEl.value) index === -1 ? (newEl.expanded = true) : (newEl.expanded = false) if (newEl.items.find(element => element.items)) findSubGroups(newEl.items.find(element => element.items)) return } } const closeGroups = () => { dataState?.group?.length && process( !!selectable ? dataResult.map(item => { return { ...item, selected: selectedState[idGetter(item)] } }) : dataResult, dataState )?.data?.map((e, i) => { let index = collapsedState.indexOf(e.value) index === -1 ? (e.expanded = true) : (e.expanded = false) findSubGroups(e) return e }) } const ExportButton = () => ( <ToolbarItem> <IconButton className="makeStylesIconButton" title={ getTranslation(language.id, fieldsPage, `${buttonsTitlePrefix}btn_export`, scenario.id) || 'Export to Excel' } onClick={excelExport} disabled={allLocked} > <LibraryBooksIcon /> </IconButton> </ToolbarItem> ) const getActionButtons = () => { const actionButtons = {} if (!hideExportButton) { actionButtons.exportButton = ExportButton } return actionButtons } const state = useMemo(() => ({ temporalData: dataResult, initialData: data, selectedDataIds: Object.keys(selectedState).filter(key => !!selectedState[key]), }), [dataResult, data, selectedState]) const getCell = (column) => { if (!!column.cell && typeof column.cell === 'function' && column.field === 'commandCell') { return (props) => { const propsWithActions = { ...props, actions: { discard, enterEdit: e => enterEdit(e, props.dataItem), cancel: e => cancel(e, props.dataItem), update: e => update(e, props.dataItem), } } return column.cell.call(column.cell, propsWithActions) } } return column.cell } return ( <div ref={tableDiv}> {isLoading && <TableCustomKendoSpinner tableHeight={tableHeight} tableWidth={tableWidth} />} <ExcelExport ref={_export} fileName={getTranslation(language.id, fieldsPage, fieldPageFileName, scenario.id) + customFileName} > {columns.map( column => column.export === false || ( <ExcelExportColumn key={`${column.field}-${Math.random()}`} field={column.field} title={column.title} width={column.filter === 'date' ? 150 : column.width} minResizableWidth={column.minResizableWidth} editable={column.editable} format={column.format} cell={column.cell} /> ) )} </ExcelExport> <Grid resizable={true} style={{ height: height, width: width }} data={process( !!selectable ? dataResult.map(item => ({ ...item, selected: selectedState[idGetter(item)] })) : dataResult, dataState )} editField={editField} {...dataState} {...gridProps} onItemChange={itemChange} onDataStateChange={e => { setDataState(e.dataState) }} pageable={{ buttonCount: 3, info: true, type: 'numeric', pageSizes: [10, 20, 50], previousNext: true }} dataItemKey={dataItemKey} {...(!!selectable ? { selectedField: 'selected', onSelectionChange: onSelectionChange, selectable: { enabled: true, drag: false, cell: false, mode: 'multiple' }, onHeaderSelectionChange: onHeaderSelectionChange } : {})} groupable={groupable} group={dataState.group} onGroupChange={e => { const newDataState = groupBy(dataResult, e.group) setGroupIds({ data: newDataState.data, group: e.group }) setDataState({ ...dataState, group: e.group }) }} onExpandChange={customOnExpandChange || onExpandChange} expandField="expanded" > {!!customToolbar ? customToolbar(getActionButtons(), discard, classes, state) : (showSelectable || showAddRedirect || !hideExportButton || !hideSaveButton) && ( <GridToolbar> {selectedElementsCount()} <ToolbarSpacer /> {editable && showAddRedirect && ( <ToolbarItem> <IconButton className="makeStylesIconButton" title={getTranslation( language.id, fieldsPage, `${buttonsTitlePrefix}btn_add`, scenario.id )} onClick={addGoRedirect ? redirectToCreate : addNew} disabled={blockFields || allLocked} > <AddIcon /> </IconButton> </ToolbarItem> )} {showSelectable && !hideSaveButton && ( <ToolbarItem> <IconButton className="makeStylesIconButton" title={getTranslation( language.id, fieldsPage, `${buttonsTitlePrefix}btn_add`, scenario.id )} onClick={saveSelection} disabled={data.length === 0 || disabled || allLocked} > {!textSaveIcon ? ( <SaveIcon /> ) : ( <FormGroup className="formContainerResponsive__responsiveButtonsCellInsideTable" id="TableSaveBtn" > <ButtonAlternative className="formContainerResponsive__formButtonSubmitResponsive" title={getTranslation( language.id, fieldsPage, textSaveIcon, scenario.id )} /> </FormGroup> )} </IconButton> </ToolbarItem> )} {editable && !hideExportButton && <ToolbarSeparator />} {!hideExportButton && <ExportButton />} </GridToolbar> )} {editable && (showEditBtn || showCopyBtn || showRemoveBtn || showDownloadBtn || showUploadBtn || showHistoryBtn || showSignatureBtn) && ( <GridColumn cell={CommandCell} filterable={false} groupable={false} sortable={false} width={commandCellWidth} /> )} {showSelectable && ( <GridColumn field="selected" width={40} filterable={false} headerSelectionValue={selectedAll} /> )} {columns.map( column => !column.hidden && ( <GridColumn key={`${column.field}-${Math.random()}`} field={column.field} title={column.title} width={column.width} editable={column.editable} filter={column.filter} filterCell={column.filterCell} columnMenu={column.columnMenu} filterable={column.filterable} editor={column.editor} format={column.format} cell={getCell(column)} sortable={column.sortable} groupable={column.groupable} selectable={column.selectable} minResizableWidth={column.minResizableWidth} /> ) )} </Grid> </div> )}TableCustomKendo.propTypes = { columns: PropTypes.array.isRequired, gridProps: PropTypes.shape({ dataItemKey: PropTypes.string, editable: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]), pageable: PropTypes.bool, sortable: PropTypes.bool, groupable: PropTypes.bool, selectable: PropTypes.bool, filterable: PropTypes.bool, columnMenu: PropTypes.func }).isRequired, height: PropTypes.string, data: PropTypes.array.isRequired, isLoading: PropTypes.bool, onAdd: PropTypes.func, onUpdate: PropTypes.func, onCopy: PropTypes.func, onDelete: PropTypes.func, onDownload: PropTypes.func, onEnterEdit: PropTypes.func, onShowHistory: PropTypes.func, onSearch: PropTypes.func, onSaveSelection: PropTypes.func, setSelection: PropTypes.func, onUpload: PropTypes.func, onApprove: PropTypes.func, onRefuse: PropTypes.func, onDisplayApprovals: PropTypes.func, language: PropTypes.shape({ id: PropTypes.number, createdDate: PropTypes.string, createdUserId: PropTypes.number, isActive: PropTypes.bool, isoCode: PropTypes.string, name: PropTypes.string, z_CreatedUser: PropTypes.arrayOf( PropTypes.shape({ email: PropTypes.string, id: PropTypes.number, isAD: PropTypes.bool, login: PropTypes.string, name: PropTypes.string, sapCode: PropTypes.string, surname: PropTypes.string }) ) }).isRequired, fieldsPage: PropTypes.arrayOf(PropTypes.object).isRequired, scenario: PropTypes.shape({ id: PropTypes.number, code: PropTypes.string, currencyId: PropTypes.number, name: PropTypes.string, statusId: PropTypes.number }).isRequired, exportCurrentPage: PropTypes.bool, fieldPageFileName: PropTypes.string, exportErrorExcelMessage: PropTypes.string, customFileName: PropTypes.string, hideExportButton: PropTypes.bool, hideSaveButton: PropTypes.bool, minItemsPerPage: PropTypes.number, buttonsTitlePrefix: PropTypes.string, allLocked: PropTypes.bool, customToolbar: PropTypes.func, customUpdateDataItems: PropTypes.func}export default memo(TableCustomKendo)
Editor is loading...