Implement navigation and view management in DataViewer, adding Dashboard and TableManagement components. Update export data handling based on current view. Create new components for managing Kreditor, Konto, and BU tables with CRUD functionality. Refactor admin routes to remove admin access checks and streamline data handling for various entities.

This commit is contained in:
sebseb7
2025-08-01 10:22:43 +02:00
parent 092fa0f8bd
commit 5470bebfc4
9 changed files with 1382 additions and 114 deletions

View File

@@ -0,0 +1,325 @@
import React, { Component } from 'react';
import {
Box,
Button,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
Dialog,
DialogTitle,
DialogContent,
DialogActions,
TextField,
IconButton,
Typography,
Alert,
CircularProgress,
} from '@mui/material';
import {
Add as AddIcon,
Edit as EditIcon,
Delete as DeleteIcon,
} from '@mui/icons-material';
import AuthService from '../../services/AuthService';
class BUTable extends Component {
constructor(props) {
super(props);
this.state = {
buchungsschluessel: [],
loading: true,
error: null,
dialogOpen: false,
editingBU: null,
confirmDialogOpen: false,
itemToDelete: null,
formData: {
bu: '',
name: '',
vst: '',
},
};
this.authService = new AuthService();
}
componentDidMount() {
this.loadBuchungsschluessel();
}
loadBuchungsschluessel = async () => {
try {
const response = await this.authService.apiCall('/admin/buchungsschluessel');
if (response && response.ok) {
const data = await response.json();
this.setState({ buchungsschluessel: data.buchungsschluessel, loading: false });
} else {
this.setState({ error: 'Fehler beim Laden der Buchungsschlüssel', loading: false });
}
} catch (error) {
console.error('Error loading buchungsschluessel:', error);
this.setState({ error: 'Fehler beim Laden der Buchungsschlüssel', loading: false });
}
};
handleOpenDialog = (bu = null) => {
this.setState({
dialogOpen: true,
editingBU: bu,
formData: bu ? {
bu: bu.bu,
name: bu.name,
vst: bu.vst || '',
} : {
bu: '',
name: '',
vst: '',
},
});
};
handleCloseDialog = () => {
this.setState({
dialogOpen: false,
editingBU: null,
formData: {
bu: '',
name: '',
vst: '',
},
});
};
handleInputChange = (field) => (event) => {
this.setState({
formData: {
...this.state.formData,
[field]: event.target.value,
},
});
};
handleSave = async () => {
const { editingBU, formData } = this.state;
// Convert vst to number or null
const payload = {
...formData,
vst: formData.vst ? parseFloat(formData.vst) : null,
};
try {
const url = editingBU
? `/admin/buchungsschluessel/${editingBU.id}`
: '/admin/buchungsschluessel';
const method = editingBU ? 'PUT' : 'POST';
const response = await this.authService.apiCall(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (response && response.ok) {
this.handleCloseDialog();
this.loadBuchungsschluessel();
} else {
const errorData = await response.json();
this.setState({ error: errorData.error || 'Fehler beim Speichern' });
}
} catch (error) {
console.error('Error saving BU:', error);
this.setState({ error: 'Fehler beim Speichern des Buchungsschlüssels' });
}
};
handleDeleteClick = (bu) => {
this.setState({
confirmDialogOpen: true,
itemToDelete: bu,
});
};
handleDeleteConfirm = async () => {
const { itemToDelete } = this.state;
if (!itemToDelete) return;
this.setState({ confirmDialogOpen: false, itemToDelete: null });
try {
const response = await this.authService.apiCall(`/admin/buchungsschluessel/${itemToDelete.id}`, {
method: 'DELETE',
});
if (response && response.ok) {
this.loadBuchungsschluessel();
} else {
const errorData = await response.json();
this.setState({ error: errorData.error || 'Fehler beim Löschen' });
}
} catch (error) {
console.error('Error deleting BU:', error);
this.setState({ error: 'Fehler beim Löschen des Buchungsschlüssels' });
}
};
handleDeleteCancel = () => {
this.setState({
confirmDialogOpen: false,
itemToDelete: null,
});
};
render() {
const { buchungsschluessel, loading, error, dialogOpen, editingBU, formData } = this.state;
if (loading) {
return (
<Box display="flex" justifyContent="center" alignItems="center" minHeight="200px">
<CircularProgress />
</Box>
);
}
return (
<Box>
<Box display="flex" justifyContent="space-between" alignItems="center" mb={2}>
<Typography variant="h6">Buchungsschlüssel</Typography>
<Button
variant="contained"
startIcon={<AddIcon />}
onClick={() => this.handleOpenDialog()}
>
Neuer Buchungsschlüssel
</Button>
</Box>
{error && (
<Alert severity="error" sx={{ mb: 2 }}>
{error}
</Alert>
)}
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
<TableCell>BU</TableCell>
<TableCell>Name</TableCell>
<TableCell align="right">VST %</TableCell>
<TableCell align="right">Aktionen</TableCell>
</TableRow>
</TableHead>
<TableBody>
{buchungsschluessel.map((bu) => (
<TableRow key={bu.id}>
<TableCell>{bu.bu}</TableCell>
<TableCell>{bu.name}</TableCell>
<TableCell align="right">
{bu.vst ? `${bu.vst}%` : '-'}
</TableCell>
<TableCell align="right">
<IconButton
size="small"
onClick={() => this.handleOpenDialog(bu)}
>
<EditIcon />
</IconButton>
<IconButton
size="small"
onClick={() => this.handleDeleteClick(bu)}
color="error"
>
<DeleteIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<Dialog open={dialogOpen} onClose={this.handleCloseDialog} maxWidth="sm" fullWidth>
<DialogTitle>
{editingBU ? 'Buchungsschlüssel bearbeiten' : 'Neuer Buchungsschlüssel'}
</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
label="BU"
fullWidth
variant="outlined"
value={formData.bu}
onChange={this.handleInputChange('bu')}
sx={{ mb: 2 }}
helperText="z.B. 9, 8, 506, 511"
/>
<TextField
margin="dense"
label="Name"
fullWidth
variant="outlined"
value={formData.name}
onChange={this.handleInputChange('name')}
sx={{ mb: 2 }}
helperText="z.B. 19% VST, 7% VST, Dienstleistung aus EU"
/>
<TextField
margin="dense"
label="Vorsteuer %"
fullWidth
variant="outlined"
type="number"
value={formData.vst}
onChange={this.handleInputChange('vst')}
helperText="z.B. 19.00 für 19% (optional)"
inputProps={{
step: 0.01,
min: 0,
max: 100,
}}
/>
</DialogContent>
<DialogActions>
<Button onClick={this.handleCloseDialog}>Abbrechen</Button>
<Button onClick={this.handleSave} variant="contained">
Speichern
</Button>
</DialogActions>
</Dialog>
{/* Confirmation Dialog */}
<Dialog
open={this.state.confirmDialogOpen}
onClose={this.handleDeleteCancel}
maxWidth="sm"
fullWidth
>
<DialogTitle>Löschen bestätigen</DialogTitle>
<DialogContent>
<Typography>
{this.state.itemToDelete &&
`Buchungsschlüssel "${this.state.itemToDelete.bu} - ${this.state.itemToDelete.name}" wirklich löschen?`
}
</Typography>
</DialogContent>
<DialogActions>
<Button onClick={this.handleDeleteCancel}>
Abbrechen
</Button>
<Button onClick={this.handleDeleteConfirm} color="error" variant="contained">
Löschen
</Button>
</DialogActions>
</Dialog>
</Box>
);
}
}
export default BUTable;