Untitled
unknown
javascript
3 years ago
32 kB
11
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...