Implement DATEV export functionality in DataViewer and enhance TransactionsTable with selection features and improved row styling. Update environment variables and add devServer configuration in webpack for better development experience.
This commit is contained in:
64
client/src/components/headers/SelectionHeader.js
Normal file
64
client/src/components/headers/SelectionHeader.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import React, { useState, useEffect, useImperativeHandle, forwardRef } from 'react';
|
||||
import { Checkbox } from '@mui/material';
|
||||
|
||||
const SelectionHeader = forwardRef((params, ref) => {
|
||||
const [checkboxState, setCheckboxState] = useState({ checked: false, indeterminate: false });
|
||||
|
||||
// Expose updateState method to parent via ref
|
||||
useImperativeHandle(ref, () => ({
|
||||
updateState: (selectedRows, displayedRows) => {
|
||||
const allSelected = displayedRows > 0 && selectedRows.size >= displayedRows;
|
||||
const someSelected = selectedRows.size > 0;
|
||||
const indeterminate = someSelected && !allSelected;
|
||||
setCheckboxState({ checked: allSelected, indeterminate });
|
||||
}
|
||||
}));
|
||||
|
||||
// Register this component with parent on mount
|
||||
useEffect(() => {
|
||||
if (params.api?.setHeaderCheckboxRef) {
|
||||
params.api.setHeaderCheckboxRef(ref.current || { updateState: (selectedRows, displayedRows) => {
|
||||
const allSelected = displayedRows > 0 && selectedRows.size >= displayedRows;
|
||||
const someSelected = selectedRows.size > 0;
|
||||
const indeterminate = someSelected && !allSelected;
|
||||
setCheckboxState({ checked: allSelected, indeterminate });
|
||||
}});
|
||||
}
|
||||
}, [params.api, ref]);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
height: '100%',
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
size="small"
|
||||
checked={checkboxState.checked}
|
||||
indeterminate={checkboxState.indeterminate}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
const parentComponent = params.api?.fibdashComponent;
|
||||
if (parentComponent && parentComponent.onSelectAll) {
|
||||
const { selectedRows } = parentComponent.state;
|
||||
const shouldSelectAll = selectedRows.size === 0;
|
||||
parentComponent.onSelectAll(shouldSelectAll);
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
padding: 0,
|
||||
'& .MuiSvgIcon-root': {
|
||||
fontSize: 18
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default SelectionHeader;
|
||||
@@ -43,6 +43,17 @@ class HeaderComponent extends Component {
|
||||
}
|
||||
|
||||
onFilterChanged = () => {
|
||||
// Check if this column's filter was cleared and reset local state
|
||||
if (this.props.params && this.props.params.api && this.props.params.column) {
|
||||
const colId = this.props.params.column.colId;
|
||||
const filterModel = this.props.params.api.getFilterModel();
|
||||
|
||||
// If this column no longer has a filter, clear the local filterValue
|
||||
if (!filterModel || !filterModel[colId]) {
|
||||
this.setState({ filterValue: '' });
|
||||
}
|
||||
}
|
||||
|
||||
// Force re-render to update filter icon color
|
||||
this.forceUpdate();
|
||||
};
|
||||
@@ -134,6 +145,9 @@ class HeaderComponent extends Component {
|
||||
const isTextColumn = column.colDef.field === 'description' ||
|
||||
column.colDef.field === 'Beguenstigter/Zahlungspflichtiger';
|
||||
const showTextFilter = isTextColumn;
|
||||
|
||||
// Check if sorting is disabled for this column
|
||||
const isSortingDisabled = column.colDef.sortable === false || column.colDef.suppressSorting === true;
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
@@ -153,44 +167,46 @@ class HeaderComponent extends Component {
|
||||
fontWeight: 600,
|
||||
fontSize: '13px',
|
||||
color: '#212529',
|
||||
cursor: 'pointer',
|
||||
cursor: isSortingDisabled ? 'default' : 'pointer',
|
||||
minWidth: 'fit-content',
|
||||
marginRight: '6px',
|
||||
userSelect: 'none',
|
||||
'&:hover': {
|
||||
color: '#0969da'
|
||||
color: isSortingDisabled ? '#212529' : '#0969da'
|
||||
}
|
||||
}}
|
||||
onClick={(e) => this.onSortRequested(sortDirection === 'asc' ? 'desc' : 'asc', e)}
|
||||
onClick={isSortingDisabled ? undefined : (e) => this.onSortRequested(sortDirection === 'asc' ? 'desc' : 'asc', e)}
|
||||
>
|
||||
{displayName}
|
||||
</Typography>
|
||||
|
||||
{/* Sort Icons - Always visible */}
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
marginRight: '6px',
|
||||
gap: 0
|
||||
}}>
|
||||
<SortUpIcon sx={{
|
||||
fontSize: '10px',
|
||||
color: sortDirection === 'asc' ? '#0969da' : '#ccc',
|
||||
opacity: sortDirection === 'asc' ? 1 : 0.5,
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={(e) => this.onSortRequested('asc', e)}
|
||||
/>
|
||||
<SortDownIcon sx={{
|
||||
fontSize: '10px',
|
||||
color: sortDirection === 'desc' ? '#0969da' : '#ccc',
|
||||
opacity: sortDirection === 'desc' ? 1 : 0.5,
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={(e) => this.onSortRequested('desc', e)}
|
||||
/>
|
||||
</Box>
|
||||
{/* Sort Icons - Only visible when sorting is enabled */}
|
||||
{!isSortingDisabled && (
|
||||
<Box sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
marginRight: '6px',
|
||||
gap: 0
|
||||
}}>
|
||||
<SortUpIcon sx={{
|
||||
fontSize: '10px',
|
||||
color: sortDirection === 'asc' ? '#0969da' : '#ccc',
|
||||
opacity: sortDirection === 'asc' ? 1 : 0.5,
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={(e) => this.onSortRequested('asc', e)}
|
||||
/>
|
||||
<SortDownIcon sx={{
|
||||
fontSize: '10px',
|
||||
color: sortDirection === 'desc' ? '#0969da' : '#ccc',
|
||||
opacity: sortDirection === 'desc' ? 1 : 0.5,
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
onClick={(e) => this.onSortRequested('desc', e)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Filter Input - only for text columns */}
|
||||
{showTextFilter && (
|
||||
|
||||
Reference in New Issue
Block a user