This commit is contained in:
seb
2026-09-26 23:05:48 +02:00
parent 454c5ab6bf
commit 3ab9d904e6
12 changed files with 1505 additions and 65 deletions

View File

@@ -61,31 +61,36 @@ class EansDisplay extends Component {
class EansEdit extends Component {
render() {
const { draft, onChange, inputRef } = this.props;
const { sku, draft, onChange, inputRef } = this.props;
return (
<Autocomplete
fullWidth
multiple
freeSolo
options={[]}
value={draft.eans ?? []}
onChange={(_event, eans) => onChange({ eans })}
renderInput={(params) => (
<TextField
{...params}
inputRef={inputRef}
label="EANs (Enter zum Hinzufügen)"
size="small"
slotProps={{
...params.slotProps,
inputLabel: {
...params.slotProps?.inputLabel,
shrink: true,
},
}}
<Box sx={{ width: '100%' }}>
<PanelValue label="SKU">{sku}</PanelValue>
<Box sx={{ mt: 3 }}>
<Autocomplete
fullWidth
multiple
freeSolo
options={[]}
value={draft.eans ?? []}
onChange={(_event, eans) => onChange({ eans })}
renderInput={(params) => (
<TextField
{...params}
inputRef={inputRef}
label="EANs (Enter zum Hinzufügen)"
size="small"
slotProps={{
...params.slotProps,
inputLabel: {
...params.slotProps?.inputLabel,
shrink: true,
},
}}
/>
)}
/>
)}
/>
</Box>
</Box>
);
}
}
@@ -121,7 +126,12 @@ export default class ArticleViewEANs extends PureComponent {
canSave={eansDraftValid}
display={<EansDisplay sku={articleData.sku} eans={value.eans} />}
edit={({ draft, onChange, inputRef }) => (
<EansEdit draft={draft} onChange={onChange} inputRef={inputRef} />
<EansEdit
sku={articleData.sku}
draft={draft}
onChange={onChange}
inputRef={inputRef}
/>
)}
/>
);

View File

@@ -40,6 +40,8 @@ const articlePriceTextSx = {
const articlePriceFieldSx = {
flexShrink: 0,
width: 'auto',
mr: 11,
'& .MuiOutlinedInput-root': {
alignItems: 'baseline',
flexShrink: 0,
@@ -186,7 +188,6 @@ class PriceAmountField extends Component {
onChange?.(event.target.value);
}
}
fullWidth
error={error}
sx={readOnly ? articlePriceFieldHiddenSx : articlePriceFieldSx}
slotProps={{

View File

@@ -1,39 +1,210 @@
import { Component, PureComponent } from 'react';
import { PureComponent } from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import WarehouseIcon from '@mui/icons-material/Warehouse';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import { PanelTile } from 'dashgui/panels';
import { formatStock } from '../../../pages/artikel/searchFormat';
class PanelValue extends Component {
render() {
const { label, children } = this.props;
if (children == null || children === '') return null;
return (
<Box sx={{ mb: 1 }}>
{label ? (
<Typography variant="caption" color="text.secondary" display="block">
{label}
</Typography>
) : null}
<Typography variant="body2" component="div">
{children}
</Typography>
</Box>
);
}
}
import ArticleStockAdjustDialog from '../ArticleStockAdjustDialog';
const SECOND_ROW_MIN_HEIGHT = 220;
export default class ArticleViewStock extends PureComponent {
constructor(props) {
super(props);
this.state = {
adjustOpen: false,
};
this.handleOpenAdjust = this.handleOpenAdjust.bind(this);
this.handleCloseAdjust = this.handleCloseAdjust.bind(this);
}
handleOpenAdjust() {
this.setState({ adjustOpen: true });
}
handleCloseAdjust() {
this.setState({ adjustOpen: false });
}
render() {
const { article: articleData } = this.props;
const { adjustOpen } = this.state;
const warehouses = Array.isArray(articleData?.warehouses)
? articleData.warehouses
: [];
const hasWarehouses = warehouses.length > 0;
const totalStock = hasWarehouses
? warehouses.reduce((sum, wh) => sum + (Number(wh.stock) || 0), 0)
: articleData?.stock != null
? Number(articleData.stock)
: 0;
const rawUnit =
articleData?.unit?.symbol ||
articleData?.unit?.name ||
'';
const unit =
rawUnit && !['stk', 'stück', 'pc', 'stk.'].includes(rawUnit.trim().toLowerCase())
? rawUnit
: '';
return (
<PanelTile wide={1} minHeight={SECOND_ROW_MIN_HEIGHT} color="#bbf7d0">
<PanelValue>
{articleData.stock != null ? formatStock(articleData.stock) : null}
</PanelValue>
<>
<PanelTile
wide={1}
minHeight={SECOND_ROW_MIN_HEIGHT}
color="#bbf7d0"
action={{
label: 'Bestand anpassen',
icon: EditOutlinedIcon,
onClick: this.handleOpenAdjust,
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
height: '100%',
justifyContent: 'space-between',
}}
>
{/* Top section: Headline and Total Stock */}
<Box>
<Typography
variant="caption"
color="text.secondary"
sx={{
textTransform: 'uppercase',
letterSpacing: 0.6,
fontWeight: 600,
display: 'block',
}}
>
Bestand
</Typography>
<Box
sx={{
display: 'flex',
alignItems: 'baseline',
gap: 0.75,
my: 0.5,
}}
>
<Typography
component="div"
sx={{
fontSize: '2.4rem',
fontWeight: 700,
lineHeight: 1.1,
letterSpacing: '-0.02em',
color: totalStock > 0 ? 'text.primary' : 'text.secondary',
}}
>
{formatStock(totalStock)}
</Typography>
{unit ? (
<Typography
variant="body2"
color="text.secondary"
sx={{ fontWeight: 500 }}
>
{unit}
</Typography>
) : null}
</Box>
</Box>
{/* Bottom section: Per-warehouse breakdown */}
{hasWarehouses ? (
<Box
sx={{
mt: 1,
flex: 1,
minHeight: 0,
overflowY: 'auto',
pr: 0.5,
'&::-webkit-scrollbar': { width: 4 },
'&::-webkit-scrollbar-thumb': {
backgroundColor: 'rgba(0, 0, 0, 0.15)',
borderRadius: 2,
},
}}
>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
{warehouses.map((wh) => (
<Box
key={wh.id}
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
py: 0.35,
px: 0.75,
borderRadius: 1,
backgroundColor: 'rgba(255, 255, 255, 0.55)',
}}
>
<Box
sx={{
display: 'flex',
alignItems: 'center',
minWidth: 0,
mr: 1,
}}
>
<WarehouseIcon
sx={{
fontSize: 15,
color: 'text.secondary',
mr: 0.5,
flexShrink: 0,
}}
/>
<Typography
variant="body2"
noWrap
title={wh.name}
sx={{
fontWeight: 500,
fontSize: '0.82rem',
color: 'text.primary',
}}
>
{wh.name}
</Typography>
</Box>
<Typography
variant="body2"
sx={{
fontWeight: 700,
fontSize: '0.82rem',
flexShrink: 0,
color:
Number(wh.stock) > 0
? 'text.primary'
: 'text.secondary',
}}
>
{formatStock(wh.stock)}
</Typography>
</Box>
))}
</Box>
</Box>
) : null}
</Box>
</PanelTile>
);
}
<ArticleStockAdjustDialog
open={adjustOpen}
onClose={this.handleCloseAdjust}
article={articleData}
/>
</>
);
}
}