Files
dashfront/src/components/manifest/ArticleSearchDialog.jsx
2026-10-17 17:11:11 +02:00

200 lines
6.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { PureComponent } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import AppDialog from '../AppDialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import Alert from '@mui/material/Alert';
import CloseIcon from '@mui/icons-material/Close';
import ArticleSearch from '../article/ArticleSearch';
import deliveryEditorAutomat from '../../automats/deliveryEditorAutomat';
export default class ArticleSearchDialog extends PureComponent {
constructor(props) {
super(props);
this.state = {
assigningSku: null,
error: null,
};
}
componentDidUpdate(prevProps) {
if (!prevProps.open && this.props.open) {
this.setState({ assigningSku: null, error: null });
}
}
handleClose = () => {
this.setState({ assigningSku: null, error: null });
this.props.onClose?.();
};
handleSelect = async (article) => {
const { onAssign, line, kind = 'delivery' } = this.props;
const isDelivery = kind === 'delivery';
if (!onAssign || !article?.sku) return;
if (article.isParent || article.is_parent) {
this.setState({ error: 'Ein Vaterartikel kann nicht auf Belege gebucht werden.' });
return;
}
this.setState({ assigningSku: article.sku, error: null });
try {
await onAssign(article, line);
} catch (err) {
this.setState({
error:
err?.message ||
(isDelivery
? (line ? 'Fehler beim Verknüpfen des Lieferanten-Artikels.' : 'Fehler beim Hinzufügen des Artikels.')
: 'Fehler beim Zuordnen des Artikels.'),
});
} finally {
this.setState({ assigningSku: null });
}
};
render() {
const { open, onClose, line, kind = 'delivery', supplierArticles: propSupplierArticles } = this.props;
const { assigningSku, error } = this.state;
const isDelivery = kind === 'delivery';
const onlySupplierArticles = isDelivery && !line;
const rawSku = line?.sku != null ? String(line.sku).trim() : '';
const supplierArticlesList = onlySupplierArticles
? (propSupplierArticles ?? deliveryEditorAutomat.state.supplierArticles ?? [])
: null;
const includeSkus = onlySupplierArticles
? supplierArticlesList.map((sa) => sa.article?.sku || sa.sku).filter(Boolean)
: null;
const extraArticles = onlySupplierArticles
? supplierArticlesList.map((sa) => ({
id: sa.article?.id ?? sa.id,
sku: sa.article?.sku || sa.sku,
name: sa.article?.name || sa.name || sa.sku || '',
brand: sa.article?.brand,
brandName: sa.article?.brand?.name || sa.article?.brandName,
stock: sa.article?.stock ?? sa.stock,
available: sa.article?.available,
price: sa.price?.net ?? sa.article?.price ?? sa.article?.vknetto,
eans: sa.article?.eans || [sa.sku].filter(Boolean),
isParent: false,
}))
: [];
return (
<AppDialog
open={Boolean(open)}
onClose={this.handleClose}
maxWidth="md"
fullWidth
aria-labelledby="article-search-dialog-title"
slotProps={{
paper: {
sx: {
height: '80vh',
minHeight: '80vh',
maxHeight: '80vh',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
},
},
}}
>
<DialogTitle
id="article-search-dialog-title"
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
pb: 1,
flexShrink: 0,
}}
>
<Box>
<Typography variant="h6" component="div" fontWeight={600}>
{line
? 'Existierenden Artikel zuordnen'
: isDelivery
? 'Lieferanten-Artikel auswählen'
: 'Artikel auswählen'}
</Typography>
<Typography variant="caption" color="text.secondary">
{line ? (
<>
{isDelivery ? 'Lieferanten-SKU' : 'SKU'}: <strong>{rawSku || '–'}</strong>
{line?.name ? ` · ${line.name}` : ''}
</>
) : isDelivery ? (
'Nur mit dem ausgewählten Lieferanten verknüpfte Artikel'
) : (
'Artikel für Beleg auswählen'
)}
</Typography>
</Box>
<IconButton
size="small"
aria-label="Schließen"
onClick={this.handleClose}
>
<CloseIcon fontSize="small" />
</IconButton>
</DialogTitle>
<DialogContent
dividers
sx={{
p: 2,
display: 'flex',
flexDirection: 'column',
flex: 1,
minHeight: 0,
overflow: 'hidden',
}}
>
{error && (
<Alert severity="error" sx={{ mb: 2, flexShrink: 0 }}>
{error}
</Alert>
)}
{onlySupplierArticles && supplierArticlesList.length === 0 && (
<Alert severity="info" sx={{ mb: 2, flexShrink: 0 }}>
Für diesen Lieferanten sind noch keine Artikel verknüpft.
</Alert>
)}
<ArticleSearch
mode="list"
autoFocus
allowParent={false}
busySku={assigningSku}
initialQuery={line?.name ? String(line.name) : ''}
includeSkus={includeSkus}
extraArticles={extraArticles}
actionLabel={assigningSku ? (line ? 'Zuordnen…' : 'Hinzufügen…') : (line ? 'Zuordnen' : 'Hinzufügen')}
onSelect={this.handleSelect}
/>
</DialogContent>
<DialogActions sx={{ px: 2, py: 1.5 }}>
<Button
size="small"
onClick={this.handleClose}
>
Abbrechen
</Button>
</DialogActions>
</AppDialog>
);
}
}