263 lines
8.5 KiB
JavaScript
263 lines
8.5 KiB
JavaScript
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 Tooltip from '@mui/material/Tooltip';
|
|
import { PanelTile } from 'dashgui/panels';
|
|
import { formatStock } from '../../../pages/artikel/searchFormat';
|
|
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 isTrackStock =
|
|
articleData?.track_stock !== false && articleData?.track_stock !== 0;
|
|
|
|
const rawWarehouses = Array.isArray(articleData?.warehouses)
|
|
? articleData.warehouses
|
|
: [];
|
|
const warehouses = rawWarehouses.filter((wh) => (Number(wh.stock) || 0) > 0);
|
|
const hasWarehouses = warehouses.length > 0;
|
|
|
|
const totalStock = rawWarehouses.length > 0
|
|
? rawWarehouses.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"
|
|
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>
|
|
|
|
{isTrackStock ? (
|
|
<Tooltip title="Klicken, um Bestand anzupassen" arrow placement="top-start">
|
|
<Box
|
|
onClick={this.handleOpenAdjust}
|
|
sx={{
|
|
display: 'inline-flex',
|
|
alignItems: 'baseline',
|
|
gap: 0.75,
|
|
my: 0.5,
|
|
cursor: 'pointer',
|
|
borderRadius: 1,
|
|
px: 0.5,
|
|
mx: -0.5,
|
|
transition: 'background-color 0.15s ease',
|
|
'&:hover': {
|
|
backgroundColor: 'rgba(0, 0, 0, 0.05)',
|
|
},
|
|
}}
|
|
>
|
|
<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>
|
|
</Tooltip>
|
|
) : (
|
|
<Box
|
|
onClick={this.handleOpenAdjust}
|
|
sx={{
|
|
my: 0.75,
|
|
cursor: 'pointer',
|
|
borderRadius: 1,
|
|
px: 0.5,
|
|
mx: -0.5,
|
|
transition: 'background-color 0.15s ease',
|
|
'&:hover': {
|
|
backgroundColor: 'rgba(0, 0, 0, 0.05)',
|
|
},
|
|
}}
|
|
>
|
|
<Typography
|
|
component="div"
|
|
sx={{
|
|
fontSize: '1.25rem',
|
|
fontWeight: 700,
|
|
lineHeight: 1.2,
|
|
color: 'text.secondary',
|
|
}}
|
|
>
|
|
Keine Erfassung
|
|
</Typography>
|
|
<Typography
|
|
variant="caption"
|
|
color="text.secondary"
|
|
sx={{ display: 'block', mt: 0.25 }}
|
|
>
|
|
Bestand wird nicht überwacht
|
|
</Typography>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
|
|
{/* Bottom section: Per-warehouse breakdown */}
|
|
{isTrackStock && 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}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
}
|