Files
dashfront/src/components/manifest/ManifestFormLine.jsx
2026-10-14 15:00:31 +02:00

912 lines
29 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 { Fragment, PureComponent } from 'react';
import { Link as RouterLink } from 'react-router-dom';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Chip from '@mui/material/Chip';
import CircularProgress from '@mui/material/CircularProgress';
import IconButton from '@mui/material/IconButton';
import TableCell from '@mui/material/TableCell';
import TableRow from '@mui/material/TableRow';
import TextField from '@mui/material/TextField';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
import HelpOutlineIcon from '@mui/icons-material/HelpOutlineOutlined';
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined';
import CheckCircleOutlineOutlinedIcon from '@mui/icons-material/CheckCircleOutlineOutlined';
import AddIcon from '@mui/icons-material/Add';
import SearchIcon from '@mui/icons-material/Search';
import LinkOffOutlinedIcon from '@mui/icons-material/LinkOffOutlined';
import { getFirstVal, parseNum, LINE_COLUMNS } from './manifestConfig';
import ArticlePhoto from '../article/ArticlePhoto';
import { formatPrice } from '../../utils/price';
import { primaryPrice, priceBrutto } from '../../utils/articleDetail';
import artikelAutomat, { createArticleSelector, getArticle } from '../../automats/artikelAutomat';
import { artikelDetailPath } from '../article/articleRoute';
import { BrandLabel } from '../brand';
import {
BORDER,
} from '../../colors';
class ManifestFormLine extends PureComponent {
constructor(props) {
super(props);
const sku = this.getArticleSku(props);
this.state = {
cachedArticle: sku ? getArticle(sku) : null,
loadingArticle: false,
};
this.handleRemove = this.handleRemove.bind(this);
this.handleBlur = this.handleBlur.bind(this);
this.handleCreateArticle = this.handleCreateArticle.bind(this);
this.handleSearchArticle = this.handleSearchArticle.bind(this);
this.handleRemoveSupplierArticle = this.handleRemoveSupplierArticle.bind(this);
}
getArticleSku(props = this.props) {
if (props.kind === 'delivery') {
return props.supplierArticle?.article?.sku || null;
}
return props.line?.sku ? String(props.line.sku).trim() : null;
}
componentDidMount() {
this.subscribeArticle(this.getArticleSku());
}
componentDidUpdate(prevProps) {
const prevSku = this.getArticleSku(prevProps);
const nextSku = this.getArticleSku(this.props);
if (prevSku !== nextSku) {
this.subscribeArticle(nextSku);
}
}
componentWillUnmount() {
this.unsubArticle?.();
}
subscribeArticle(sku) {
this.unsubArticle?.();
this.unsubArticle = null;
if (!sku) {
if (this.state.cachedArticle || this.state.loadingArticle) {
this.setState({ cachedArticle: null, loadingArticle: false });
}
return;
}
const current = getArticle(sku);
if (current) {
this.setState({ cachedArticle: current, loadingArticle: false });
} else {
this.setState({ cachedArticle: null, loadingArticle: true });
artikelAutomat.actions.loadArticle(sku)
.then((art) => {
if (this.getArticleSku() === sku) {
this.setState({ cachedArticle: art, loadingArticle: false });
}
})
.catch(() => {
if (this.getArticleSku() === sku) {
this.setState({ cachedArticle: null, loadingArticle: false });
}
});
}
this.unsubArticle = artikelAutomat.subscribe(this, (state) => {
const art = createArticleSelector(sku)(state);
return {
cachedArticle: art ?? null,
...(art ? { loadingArticle: false } : {}),
};
});
}
handleRemove() {
this.props.onRemove(this.props.line.key);
}
handleBlur(e, columnId) {
if (columnId === 'sku' && this.props.onSkuBlur) {
this.props.onSkuBlur(this.props.line.key, e.target.value);
}
}
handleCreateArticle() {
const { line, onCreateArticle } = this.props;
if (onCreateArticle) {
onCreateArticle(line);
}
}
handleSearchArticle() {
const { line, onSearchArticle } = this.props;
if (onSearchArticle) {
onSearchArticle(line);
}
}
handleRemoveSupplierArticle() {
const { line, supplierArticle, onRemoveSupplierArticle } = this.props;
if (onRemoveSupplierArticle) {
onRemoveSupplierArticle(line, supplierArticle);
}
}
renderLedgerChip() {
const { ledgerLine, onBookLine } = this.props;
if (!ledgerLine) return null;
const isBooked = ledgerLine.isBooked;
const isPartial = !isBooked && ledgerLine.bookedAmount > 0;
const isClickable = Boolean(onBookLine);
return (
<Tooltip
title={
isBooked
? `Vollständig im Lager gebucht: ${ledgerLine.bookedAmount} Stück (Klicken für Details)`
: isPartial
? `Teilweise im Lager gebucht: ${ledgerLine.bookedAmount} von ${ledgerLine.targetAmount} Stück (Klicken zum Einbuchen dieser Position)`
: `Noch nicht im Lager gebucht (${ledgerLine.targetAmount} Stück – Klicken zum Einbuchen dieser Position)`
}
>
<Chip
size="small"
clickable={isClickable}
onClick={
isClickable
? (e) => {
e.stopPropagation();
onBookLine(ledgerLine);
}
: undefined
}
icon={
isBooked ? (
<CheckCircleOutlineOutlinedIcon sx={{ '&&': { fontSize: 13 } }} />
) : (
<Inventory2OutlinedIcon sx={{ '&&': { fontSize: 13 } }} />
)
}
label={
isBooked
? `Lager: ${ledgerLine.bookedAmount} Stk. gebucht`
: isPartial
? `Lager: ${ledgerLine.bookedAmount}/${ledgerLine.targetAmount} Stk.`
: 'Lager: offen'
}
color={isBooked ? 'success' : isPartial ? 'warning' : 'default'}
variant="outlined"
sx={{
height: 20,
fontSize: '0.68rem',
fontWeight: 600,
cursor: isClickable ? 'pointer' : 'default',
bgcolor: isBooked ? 'success.50' : undefined,
'&:hover': isClickable
? {
bgcolor: isBooked
? 'success.100'
: isPartial
? 'warning.100'
: 'action.hover',
}
: undefined,
}}
/>
</Tooltip>
);
}
renderField(column, colIdx, totalCols) {
const { id, align, type } = column;
const { line, saving, onChange, columns } = this.props;
if (id === 'pos') {
const posVal =
this.props.positionIndex ??
(this.props.index != null ? this.props.index + 1 : colIdx + 1);
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
minHeight: 38,
fontWeight: 700,
fontSize: '0.82rem',
color: 'text.secondary',
userSelect: 'none',
}}
>
{posVal}
</Box>
);
}
let val = line[id];
if (val == null || val === '') {
if (id === 'name' && this.state.cachedArticle?.name) {
val = this.state.cachedArticle.name;
} else if (id === 'tax' || id === 'taxrate') {
val = line.taxrate ?? line.tax ?? '';
} else if (id === 'net' || id === 'netprice') {
val = line.netprice ?? line.net ?? '';
} else if (id === 'gross') {
const netNum = parseNum(line.netprice ?? line.net);
const taxNum = parseNum(line.taxrate ?? line.tax) ?? 0;
if (netNum != null) {
val = String(Math.round((netNum * (1 + taxNum / 100) + Number.EPSILON) * 100) / 100);
}
}
}
let isError = false;
if (id === 'name') {
isError = !String(line.name ?? '').trim() && !String(line.sku ?? '').trim();
} else if (id === 'amount') {
const raw = getFirstVal(line.amount, line.count);
isError = raw == null || parseNum(raw) == null;
} else if (id === 'pu') {
const raw = getFirstVal(line.pu, line.vpe);
isError = raw == null || parseNum(raw) == null;
} else if (id === 'tax' || id === 'taxrate') {
const raw = getFirstVal(line.tax, line.taxrate, line.taxRate, line.mwst);
isError = raw == null || parseNum(raw) == null;
} else if (id === 'net' || id === 'netprice') {
const raw = getFirstVal(line.net, line.netprice, line.netPrice, line.eknetto);
isError = raw == null || parseNum(raw) == null;
} else if (id === 'gross') {
const raw = getFirstVal(line.gross, val);
isError = raw == null || parseNum(raw) == null;
} else if (id === 'sku') {
isError = !String(line.sku ?? '').trim() && !String(line.name ?? '').trim();
}
const hasPosCol = columns?.[0]?.id === 'pos';
const isFirstInput = hasPosCol ? colIdx === 1 : colIdx === 0;
const isLast = colIdx === totalCols - 1;
const { dirtyFields } = this.props;
const isFieldDirty = Boolean(
dirtyFields && (dirtyFields.__all__ || dirtyFields[id])
);
return (
<TextField
size="small"
fullWidth
type={type}
value={val ?? ''}
error={isError}
disabled={saving}
autoComplete="off"
onChange={(e) => onChange(line.key, id, e.target.value)}
onBlur={(e) => this.handleBlur(e, id)}
slotProps={{
htmlInput: {
inputMode: type === 'number' ? 'decimal' : 'text',
style: { textAlign: align },
autoComplete: 'off',
step: type === 'number' ? 'any' : undefined,
},
}}
sx={{
'& .MuiOutlinedInput-root': {
borderRadius: isFirstInput ? '4px 0 0 4px' : isLast ? '0 4px 4px 0' : 0,
ml: isFirstInput ? 0 : '-1px',
...(isFieldDirty && {
bgcolor: 'rgba(237, 108, 2, 0.08)',
}),
'&:hover': {
zIndex: 1,
},
'&.Mui-focused': {
zIndex: 2,
},
},
...(isFieldDirty && {
zIndex: 1,
'& .MuiOutlinedInput-notchedOutline': {
borderColor: 'warning.main',
borderWidth: 2,
},
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: 'warning.main',
},
}),
'& input[type=number]': {
MozAppearance: 'textfield',
},
'& input[type=number]::-webkit-outer-spin-button, & input[type=number]::-webkit-inner-spin-button':
{
WebkitAppearance: 'none',
margin: 0,
},
}}
/>
);
}
renderSecondLine() {
const { kind = 'delivery' } = this.props;
const rawSku = this.props.line?.sku != null ? String(this.props.line.sku).trim() : '';
if (kind === 'delivery') {
return this.renderDeliverySecondLine(rawSku);
}
return this.renderArticleSecondLine(rawSku);
}
renderArticleSecondLine(rawSku) {
const { loading, kind, columns } = this.props;
const isOrder = kind === 'order';
const hasNameColumn = columns?.some((c) => c.id === 'name');
const { cachedArticle, loadingArticle } = this.state;
if (loading || loadingArticle) {
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, py: 0.25 }}>
<CircularProgress size={14} thickness={5} sx={{ color: 'primary.main' }} />
<Typography variant="caption" color="text.secondary" sx={{ fontStyle: 'italic', fontSize: '0.72rem' }}>
Artikeldaten werden geladen…
</Typography>
</Box>
);
}
if (cachedArticle) {
const photo = cachedArticle.photos?.[0] || cachedArticle.articlepic?.[0];
const fullHash = photo?.hash || photo?.fullHash;
const thumbHash = photo?.thumbhash || photo?.thumbHash;
const thumbW = photo?.thumbw || photo?.thumbW;
const thumbH = photo?.thumbh || photo?.thumbH;
const hasPhoto = fullHash != null || thumbHash != null;
const eans = cachedArticle.eans ?? cachedArticle.articleean ?? [];
const primaryEan = Array.isArray(eans) ? eans[0] : eans;
const vkPrice = primaryPrice(cachedArticle);
const vkNetto = vkPrice?.net;
const vkBrutto = vkPrice
? priceBrutto(vkPrice) ??
(vkPrice.net != null && vkPrice.taxrate != null
? vkPrice.net * (1 + vkPrice.taxrate / 100)
: null)
: null;
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 1.25,
py: 0.25,
flexWrap: 'wrap',
}}
>
{/* Article Photo Thumbnail */}
<Box
sx={{
width: 28,
height: 28,
minWidth: 28,
borderRadius: 0.75,
overflow: 'hidden',
bgcolor: BORDER,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid',
borderColor: 'divider',
}}
>
{hasPhoto ? (
<ArticlePhoto
photo={{ fullHash, thumbHash, thumbW, thumbH }}
size={28}
/>
) : (
<Inventory2OutlinedIcon sx={{ fontSize: 16, color: 'text.secondary' }} />
)}
</Box>
{/* Internal Article SKU */}
{cachedArticle.sku && (
<Chip
component={RouterLink}
to={artikelDetailPath(cachedArticle)}
clickable
size="small"
label={`Art.-Nr.: ${cachedArticle.sku}`}
sx={{
height: 20,
fontSize: '0.7rem',
fontWeight: 600,
bgcolor: 'primary.50',
color: 'primary.dark',
border: '1px solid',
borderColor: 'primary.200',
cursor: 'pointer',
textDecoration: 'none',
'&:hover': {
bgcolor: 'primary.100',
},
}}
/>
)}
{/* Brand */}
{cachedArticle.brand?.name && (
<BrandLabel
brand={cachedArticle.brand}
size={16}
typographyProps={{
variant: 'caption',
color: 'text.secondary',
fontWeight: 500,
}}
/>
)}
{/* Article Name: omit if already editable in line 1 or if kind is order */}
{!hasNameColumn && !isOrder && cachedArticle.name && (
<Typography
variant="caption"
fontWeight={600}
noWrap
sx={{ color: 'text.primary', maxWidth: { xs: 200, sm: 300, md: 400 } }}
title={cachedArticle.name}
>
{cachedArticle.name}
</Typography>
)}
{/* Primary EAN: omit for order */}
{!isOrder && primaryEan && (
<Typography
variant="caption"
color="text.secondary"
sx={{
fontFamily: 'monospace',
fontSize: '0.68rem',
bgcolor: 'action.hover',
px: 0.5,
py: 0.1,
borderRadius: 0.5,
}}
>
EAN: {primaryEan}
</Typography>
)}
{/* VK Netto & VK Brutto: omit end on 2nd line for order */}
{!isOrder && (vkNetto != null || vkBrutto != null) && (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 1.5,
ml: 'auto',
whiteSpace: 'nowrap',
}}
>
{vkNetto != null && (
<Typography
variant="caption"
fontWeight={600}
sx={{ color: 'text.secondary', fontSize: '0.72rem' }}
>
VK Netto: {formatPrice(vkNetto)}
</Typography>
)}
{vkBrutto != null && (
<Typography
variant="caption"
fontWeight={600}
sx={{ color: 'primary.dark', fontSize: '0.72rem' }}
>
VK Brutto: {formatPrice(vkBrutto)}
</Typography>
)}
</Box>
)}
</Box>
);
}
if (rawSku) {
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75, py: 0.25 }}>
{this.props.onSearchArticle && (
<Button
size="small"
variant="text"
startIcon={<SearchIcon sx={{ fontSize: 14 }} />}
onClick={this.handleSearchArticle}
sx={{
fontSize: '0.7rem',
fontWeight: 600,
textTransform: 'none',
minWidth: 'auto',
py: 0.25,
px: 1,
color: 'primary.main',
'&:hover': {
bgcolor: 'primary.50',
},
}}
>
Artikel suchen
</Button>
)}
<HelpOutlineIcon sx={{ fontSize: 15, color: 'warning.main' }} />
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.72rem' }}>
Kein Artikel für SKU &bdquo;{rawSku}&ldquo; gefunden.
</Typography>
</Box>
);
}
if (this.props.onSearchArticle) {
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75, py: 0.25 }}>
<Button
size="small"
variant="text"
startIcon={<SearchIcon sx={{ fontSize: 14 }} />}
onClick={this.handleSearchArticle}
sx={{
fontSize: '0.7rem',
fontWeight: 600,
textTransform: 'none',
minWidth: 'auto',
py: 0.25,
px: 1,
color: 'primary.main',
'&:hover': {
bgcolor: 'primary.50',
},
}}
>
Artikel suchen
</Button>
</Box>
);
}
return null;
}
renderDeliverySecondLine(rawSku) {
const { supplierArticle, loading } = this.props;
if (loading) {
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, py: 0.25 }}>
<CircularProgress size={14} thickness={5} sx={{ color: 'primary.main' }} />
<Typography variant="caption" color="text.secondary" sx={{ fontStyle: 'italic', fontSize: '0.72rem' }}>
Artikeldaten werden geladen…
</Typography>
</Box>
);
}
if (supplierArticle) {
const article = supplierArticle.article || {};
const fullArticle = this.state.cachedArticle || article;
const photo = fullArticle.photos?.[0] || fullArticle.articlepic?.[0] || article.photos?.[0] || article.articlepic?.[0];
const fullHash = photo?.hash || photo?.fullHash;
const thumbHash = photo?.thumbhash || photo?.thumbHash;
const thumbW = photo?.thumbw || photo?.thumbW;
const thumbH = photo?.thumbh || photo?.thumbH;
const hasPhoto = fullHash != null || thumbHash != null;
const eans = fullArticle.eans ?? fullArticle.articleean ?? article.eans ?? article.articleean ?? [];
const primaryEan = Array.isArray(eans) ? eans[0] : eans;
const priceNet = supplierArticle.price?.net;
const taxRate = supplierArticle.price?.taxRate;
const vkPrice = primaryPrice(fullArticle);
const vkNetto = vkPrice?.net;
const vkBrutto = vkPrice
? priceBrutto(vkPrice) ??
(vkPrice.net != null && vkPrice.taxrate != null
? vkPrice.net * (1 + vkPrice.taxrate / 100)
: null)
: null;
return (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 1.25,
py: 0.25,
flexWrap: 'wrap',
}}
>
{/* Unlink button at the front */}
{this.props.onRemoveSupplierArticle && supplierArticle.id != null && (
<Tooltip title="Lieferanten-Verknüpfung aufheben">
<span>
<IconButton
size="small"
color="error"
aria-label="Lieferanten-Verknüpfung entfernen"
disabled={this.props.saving || this.props.supplierArticleBusy}
onClick={this.handleRemoveSupplierArticle}
sx={{ p: 0.25, mr: -0.25 }}
>
<LinkOffOutlinedIcon sx={{ fontSize: 16 }} />
</IconButton>
</span>
</Tooltip>
)}
{/* Article Photo Thumbnail */}
<Box
sx={{
width: 28,
height: 28,
minWidth: 28,
borderRadius: 0.75,
overflow: 'hidden',
bgcolor: BORDER,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid',
borderColor: 'divider',
}}
>
{hasPhoto ? (
<ArticlePhoto
photo={{ fullHash, thumbHash, thumbW, thumbH }}
size={28}
/>
) : (
<Inventory2OutlinedIcon sx={{ fontSize: 16, color: 'text.secondary' }} />
)}
</Box>
{/* Internal Article SKU */}
{article.sku && (
<Chip
component={RouterLink}
to={artikelDetailPath(article)}
clickable
size="small"
label={`Art.-Nr.: ${article.sku}`}
sx={{
height: 20,
fontSize: '0.7rem',
fontWeight: 600,
bgcolor: 'primary.50',
color: 'primary.dark',
border: '1px solid',
borderColor: 'primary.200',
cursor: 'pointer',
textDecoration: 'none',
'&:hover': {
bgcolor: 'primary.100',
},
}}
/>
)}
{/* Brand */}
{(fullArticle.brand?.name || article.brand?.name) && (
<BrandLabel
brand={fullArticle.brand || article.brand}
size={16}
typographyProps={{
variant: 'caption',
color: 'text.secondary',
fontWeight: 500,
}}
/>
)}
{/* Article Name */}
{(fullArticle.name || article.name) && (
<Typography
variant="caption"
fontWeight={600}
noWrap
sx={{ color: 'text.primary', maxWidth: { xs: 200, sm: 300, md: 400 } }}
title={fullArticle.name || article.name}
>
{fullArticle.name || article.name}
</Typography>
)}
{/* Supplier SKU if distinct */}
{supplierArticle.sku && supplierArticle.sku !== rawSku && supplierArticle.sku !== article.sku && (
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.7rem' }}>
(Lief.-SKU: <strong>{supplierArticle.sku}</strong>)
</Typography>
)}
{/* Primary EAN */}
{primaryEan && (
<Typography
variant="caption"
color="text.secondary"
sx={{ fontFamily: 'monospace', fontSize: '0.68rem', bgcolor: 'action.hover', px: 0.5, py: 0.1, borderRadius: 0.5 }}
>
EAN: {primaryEan}
</Typography>
)}
{/* Stored Purchase Price (Netto), VK Netto & VK Brutto */}
{(priceNet != null || vkNetto != null || vkBrutto != null) && (
<Box
sx={{
display: 'flex',
alignItems: 'center',
gap: 1.5,
ml: 'auto',
whiteSpace: 'nowrap',
}}
>
{priceNet != null && (
<Typography
variant="caption"
fontWeight={600}
sx={{ color: 'success.dark', fontSize: '0.72rem' }}
>
EK Netto: {formatPrice(priceNet)}
{taxRate != null ? ` (${taxRate}% MwSt)` : ''}
</Typography>
)}
{vkNetto != null && (
<Typography
variant="caption"
fontWeight={600}
sx={{ color: 'text.secondary', fontSize: '0.72rem' }}
>
VK Netto: {formatPrice(vkNetto)}
</Typography>
)}
{vkBrutto != null && (
<Typography
variant="caption"
fontWeight={600}
sx={{ color: 'primary.dark', fontSize: '0.72rem' }}
>
VK Brutto: {formatPrice(vkBrutto)}
</Typography>
)}
</Box>
)}
{this.renderLedgerChip()}
</Box>
);
}
if (rawSku) {
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75, py: 0.25 }}>
{this.props.onCreateArticle && (
<Button
size="small"
variant="text"
startIcon={<AddIcon sx={{ fontSize: 14 }} />}
onClick={this.handleCreateArticle}
sx={{
fontSize: '0.7rem',
fontWeight: 600,
textTransform: 'none',
minWidth: 'auto',
py: 0.25,
px: 1,
color: 'primary.main',
'&:hover': {
bgcolor: 'primary.50',
},
}}
>
Artikel anlegen
</Button>
)}
{this.props.onSearchArticle && (
<Button
size="small"
variant="text"
startIcon={<SearchIcon sx={{ fontSize: 14 }} />}
onClick={this.handleSearchArticle}
sx={{
fontSize: '0.7rem',
fontWeight: 600,
textTransform: 'none',
minWidth: 'auto',
py: 0.25,
px: 1,
color: 'primary.main',
'&:hover': {
bgcolor: 'primary.50',
},
}}
>
Artikel suchen
</Button>
)}
<HelpOutlineIcon sx={{ fontSize: 15, color: 'warning.main' }} />
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.72rem' }}>
{this.props.kind === 'delivery'
? `Kein Lieferanten-Artikel für SKU „${rawSku}“ gefunden.`
: `Kein Artikel für SKU „${rawSku}“ gefunden.`}
</Typography>
{this.renderLedgerChip()}
</Box>
);
}
if (this.props.ledgerLine) {
return (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.75, py: 0.25 }}>
{this.renderLedgerChip()}
</Box>
);
}
return null;
}
render() {
const { columns = LINE_COLUMNS, saving } = this.props;
const secondLine = this.renderSecondLine();
return (
<Fragment>
{/* Row 1: Editable Line Inputs */}
<TableRow hover sx={{ '& > td': { borderBottom: 'none', p: 0 } }}>
{columns.map((col, colIdx) => (
<TableCell
key={col.id}
align={col.align}
sx={{
p: 0,
width: col.width,
verticalAlign: 'middle',
}}
>
{this.renderField(col, colIdx, columns.length)}
</TableCell>
))}
<TableCell padding="none" sx={{ py: 0.25, pl: 0.75, pr: 0, verticalAlign: 'middle', width: 40 }}>
<IconButton
size="small"
aria-label="Zeile entfernen"
disabled={saving}
onClick={this.handleRemove}
>
<DeleteOutlinedIcon fontSize="small" />
</IconButton>
</TableCell>
</TableRow>
{/* Row 2: Matched Supplier Article Details */}
{secondLine && (
<TableRow sx={{ '& > td': { pt: 0.25, pb: 0.75, borderBottom: 'none' } }}>
<TableCell
colSpan={columns.length + 1}
sx={{
py: 0.4,
px: 1,
bgcolor: 'transparent',
}}
>
{secondLine}
</TableCell>
</TableRow>
)}
</Fragment>
);
}
}
export default ManifestFormLine;