Refactor CSVImportDialog to CSVImportPanel and enhance UI components

- Renamed CSVImportDialog component to CSVImportPanel for clarity.
- Replaced Dialog with Paper component for improved layout.
- Removed unused code and comments to streamline the component.
- Updated import result messages for better user feedback.
- Enhanced button styles and layout for a more user-friendly interface.
- Added new API route for importing DATEV Beleglinks to the database, including validation and error handling.
This commit is contained in:
sebseb7
2025-08-05 10:17:54 +02:00
parent 46c9e9b97d
commit d60da0a7aa
3 changed files with 243 additions and 122 deletions

View File

@@ -1,9 +1,5 @@
import React, { Component } from 'react';
import {
Dialog,
DialogTitle,
DialogContent,
DialogActions,
Button,
Typography,
Box,
@@ -14,6 +10,7 @@ import {
Tabs,
Tab,
Divider,
Paper,
} from '@mui/material';
import {
CloudUpload as UploadIcon,
@@ -30,7 +27,7 @@ const IMPORT_TYPES = {
DATEV_LINKS: 'DATEV_LINKS',
};
class CSVImportDialog extends Component {
class CSVImportPanel extends Component {
constructor(props) {
super(props);
this.state = {
@@ -276,10 +273,6 @@ class CSVImportDialog extends Component {
datevCsvData: null,
datevHeaders: null,
});
if (this.props.onClose) {
this.props.onClose();
}
};
renderUploadPanel = ({ isBanking }) => {
@@ -341,86 +334,6 @@ class CSVImportDialog extends Component {
</Typography>
</Box>
{!isBanking && (
<Box sx={{ mb: 2 }}>
<Divider sx={{ mb: 2 }} />
<Box sx={{ display: 'flex', alignItems: 'flex-start', gap: 1, mb: 1 }}>
<InfoIcon sx={{ color: 'info.main', mt: '2px' }} />
<Typography variant="subtitle1">Hinweise zum DATEV Beleglink-Upload</Typography>
</Box>
<Typography variant="body2" paragraph sx={{ color: 'text.secondary' }}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere, neque at efficitur
blandit, sapien libero finibus nunc, a facilisis lacus arcu sed urna. Suspendisse potenti.
Phasellus tincidunt, lorem in dictum lacinia, sem tortor ultrices risus, vitae porta odio
mauris non neque. Sed vitae nibh dapibus, viverra velit nec, aliquet odio.
</Typography>
<Typography variant="body2" paragraph sx={{ color: 'text.secondary' }}>
Cras lacinia, massa a sagittis placerat, enim dolor fermentum lectus, in pulvinar mi risus ut
ipsum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis
egestas. Mauris mattis lorem sit amet risus mattis volutpat. Proin sit amet hendrerit lectus.
</Typography>
<Box
sx={{
width: '100%',
borderRadius: 1,
border: '1px solid',
borderColor: 'divider',
display: 'block',
mb: 1.5,
overflow: 'hidden',
bgcolor: 'background.paper',
}}
>
<Box
component="svg"
viewBox="0 0 640 300"
xmlns="http://www.w3.org/2000/svg"
sx={{ width: '100%', height: 'auto', display: 'block' }}
>
<defs>
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style={{ stopColor: '#e3f2fd', stopOpacity: 1 }} />
<stop offset="100%" style={{ stopColor: '#e8f5e9', stopOpacity: 1 }} />
</linearGradient>
</defs>
<rect x="0" y="0" width="640" height="300" fill="url(#grad)" />
<rect x="20" y="20" width="600" height="60" rx="8" fill="#ffffff" stroke="#cfd8dc" />
<circle cx="50" cy="50" r="12" fill="#81c784" />
<rect x="75" y="38" width="200" height="12" rx="6" fill="#90caf9" />
<rect x="75" y="56" width="140" height="10" rx="5" fill="#b0bec5" />
<rect x="20" y="100" width="600" height="160" rx="8" fill="#ffffff" stroke="#cfd8dc" />
<rect x="40" y="120" width="160" height="20" rx="4" fill="#ffe082" />
<rect x="40" y="150" width="260" height="12" rx="6" fill="#b0bec5" />
<rect x="40" y="170" width="220" height="10" rx="5" fill="#eceff1" />
<rect x="40" y="190" width="300" height="10" rx="5" fill="#eceff1" />
<rect x="330" y="120" width="270" height="120" rx="8" fill="#f1f8e9" stroke="#c5e1a5" />
<rect x="350" y="140" width="230" height="12" rx="6" fill="#c5e1a5" />
<rect x="350" y="160" width="180" height="10" rx="5" fill="#dcedc8" />
<g>
<line x1="350" y1="190" x2="580" y2="190" stroke="#aed581" strokeWidth="2" />
<line x1="350" y1="200" x2="560" y2="200" stroke="#aed581" strokeWidth="2" />
<line x1="350" y1="210" x2="520" y2="210" stroke="#aed581" strokeWidth="2" />
</g>
<g fill="#90a4ae">
<rect x="540" y="28" width="20" height="6" rx="3" />
<rect x="565" y="28" width="20" height="6" rx="3" />
<rect x="590" y="28" width="20" height="6" rx="3" />
</g>
<text x="320" y="285" textAnchor="middle" fontFamily="Arial, Helvetica, sans-serif" fontSize="12" fill="#90a4ae">
DATEV Beleglinks Beispiel Platzhalter Illustration
</text>
</Box>
</Box>
<Typography variant="caption" sx={{ color: 'text.disabled' }}>
Beispiel-Screenshot (SVG Platzhalter mit Shapes). Ersetzen Sie dieses Bild später durch die endgültige Anleitungsgrafik.
</Typography>
</Box>
)}
{currentFile && (
<Box sx={{ mb: 2 }}>
<Typography variant="subtitle2" gutterBottom>
@@ -461,7 +374,6 @@ class CSVImportDialog extends Component {
};
render() {
const { open } = this.props;
const {
activeTab,
importing,
@@ -476,25 +388,22 @@ class CSVImportDialog extends Component {
const hasData = isBanking ? csvData : datevCsvData;
return (
<Dialog
open={open}
onClose={!importing ? this.handleClose : undefined}
maxWidth="md"
fullWidth
>
<DialogTitle>CSV Import</DialogTitle>
<Paper sx={{ p: 3 }}>
<Typography variant="h5" gutterBottom>
CSV Import
</Typography>
<Tabs
value={activeTab}
onChange={this.handleTabChange}
variant="fullWidth"
sx={{ borderBottom: 1, borderColor: 'divider' }}
sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}
>
<Tab value={IMPORT_TYPES.BANKING} iconPosition="start" icon={<AccountIcon />} label="Banking Umsätze" />
<Tab value={IMPORT_TYPES.DATEV_LINKS} iconPosition="start" icon={<LinkIcon />} label="DATEV Beleglinks" />
</Tabs>
<DialogContent>
<Box>
{!imported ? (
<>
{this.renderUploadPanel({ isBanking })}
@@ -524,11 +433,21 @@ class CSVImportDialog extends Component {
{importResult && (
<Box sx={{ mt: 2 }}>
<Typography variant="body1" gutterBottom>
<strong>Importiert:</strong> {importResult.imported} {isBanking ? 'Transaktionen' : 'Beleglinks'}
<strong>Hinzugefügt:</strong> {importResult.imported} {isBanking ? 'Transaktionen' : 'Datevlinks'}
</Typography>
{importResult.skipped > 0 && (
<Typography variant="body1" color="info.main">
<strong>Übersprungen:</strong> {importResult.skipped} Zeilen (bereits vorhanden, unbekanntes Format, etc.)
</Typography>
)}
{importResult.errors > 0 && (
<Typography variant="body1" color="warning.main">
<strong>Fehler:</strong> {importResult.errors} Zeilen übersprungen
<strong>Fehler:</strong> {importResult.errors} Zeilen konnten nicht verarbeitet werden
</Typography>
)}
{importResult.message && (
<Typography variant="body2" color="textSecondary" sx={{ mt: 1 }}>
{importResult.message}
</Typography>
)}
<Typography variant="body2" color="textSecondary" sx={{ mt: 1 }}>
@@ -538,26 +457,31 @@ class CSVImportDialog extends Component {
)}
</Box>
)}
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} disabled={importing}>
{imported ? 'Schließen' : 'Abbrechen'}
</Button>
{!imported && hasData && (
<Button
onClick={this.handleImport}
variant="contained"
disabled={importing || !hasData}
startIcon={importing ? <CircularProgress size={16} /> : <UploadIcon />}
>
{importing ? 'Importiere...' : 'Importieren'}
</Button>
<Box sx={{ mt: 3, textAlign: 'center' }}>
<Button
onClick={this.handleImport}
variant="contained"
size="large"
disabled={importing || !hasData}
startIcon={importing ? <CircularProgress size={16} /> : <UploadIcon />}
>
{importing ? 'Importiere...' : 'Importieren'}
</Button>
</Box>
)}
</DialogActions>
</Dialog>
{imported && (
<Box sx={{ mt: 3, textAlign: 'center' }}>
<Button onClick={this.handleClose} variant="outlined" size="large">
Neuer Import
</Button>
</Box>
)}
</Box>
</Paper>
);
}
}
export default CSVImportDialog;
export default CSVImportPanel;

View File

@@ -15,7 +15,7 @@ import {
import KreditorTable from './admin/KreditorTable';
import KontoTable from './admin/KontoTable';
import BUTable from './admin/BUTable';
import CSVImportDialog from './CSVImportDialog';
import CSVImportPanel from './CSVImportDialog';
class TableManagement extends Component {
constructor(props) {
@@ -90,9 +90,7 @@ class TableManagement extends Component {
<Typography variant="body2" color="text.secondary" paragraph>
Hier können Sie CSV-Dateien von Ihrer Bank importieren. Die Daten werden in die Datenbank gespeichert und können dann Banking-Konten zugeordnet werden.
</Typography>
<CSVImportDialog
open={true}
onClose={() => {}} // Always open in this tab
<CSVImportPanel
user={user}
/>
</Box>