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:
sebseb7
2025-07-20 07:47:18 +02:00
parent 2a43b7106d
commit 429fd70497
18 changed files with 1542 additions and 149 deletions

View 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;

View File

@@ -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 && (