185 lines
4.9 KiB
JavaScript
185 lines
4.9 KiB
JavaScript
import { Component, PureComponent } from 'react';
|
|
import Autocomplete from '@mui/material/Autocomplete';
|
|
import TextField from '@mui/material/TextField';
|
|
import Typography from '@mui/material/Typography';
|
|
import Box from '@mui/material/Box';
|
|
import { EditablePanelTile } from 'dashgui/panels';
|
|
import { StorageManagerContext } from '../../../api/storageManagerContext';
|
|
import { requireObject } from '../../../utils/apiShape';
|
|
import { articleEans } from '../../../utils/articleDetail';
|
|
import { notifyError } from '../../../utils/notify';
|
|
import JsBarcode from 'jsbarcode';
|
|
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>
|
|
);
|
|
}
|
|
}
|
|
|
|
function eansDraftValue(article) {
|
|
return { eans: articleEans(article) };
|
|
}
|
|
|
|
function eansDraftDirty(draft, saved) {
|
|
return JSON.stringify(draft.eans ?? []) !== JSON.stringify(saved.eans ?? []);
|
|
}
|
|
|
|
function eansDraftValid(draft) {
|
|
return (draft.eans ?? []).every(
|
|
(ean) => typeof ean === 'string' && ean.trim().length > 0,
|
|
);
|
|
}
|
|
|
|
class SkuBarcode extends PureComponent {
|
|
componentDidMount() {
|
|
this.renderBarcode();
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
this.renderBarcode();
|
|
}
|
|
|
|
renderBarcode() {
|
|
if (!this._svg || !this.props.value) return;
|
|
try {
|
|
JsBarcode(this._svg, String(this.props.value).trim(), {
|
|
format: 'CODE128',
|
|
width: 1.5,
|
|
height: 38,
|
|
displayValue: false,
|
|
margin: 0,
|
|
background: 'transparent',
|
|
lineColor: '#1e293b',
|
|
});
|
|
} catch (e) {
|
|
console.warn('Barcode render error:', e);
|
|
}
|
|
}
|
|
|
|
render() {
|
|
const { value } = this.props;
|
|
if (!value) return null;
|
|
return (
|
|
<Box sx={{ my: 1, display: 'flex', alignItems: 'center' }}>
|
|
<svg
|
|
ref={(el) => {
|
|
this._svg = el;
|
|
}}
|
|
style={{ maxWidth: '100%', height: 'auto' }}
|
|
/>
|
|
</Box>
|
|
);
|
|
}
|
|
}
|
|
|
|
const SECOND_ROW_MIN_HEIGHT = 220;
|
|
|
|
class EansDisplay extends Component {
|
|
render() {
|
|
const { sku, eans } = this.props;
|
|
return (
|
|
<Box sx={{ width: '100%' }}>
|
|
<PanelValue label="SKU">{sku}</PanelValue>
|
|
<SkuBarcode value={sku} />
|
|
{(eans ?? []).map((ean) => (
|
|
<PanelValue key={ean} label="EAN">
|
|
{ean}
|
|
</PanelValue>
|
|
))}
|
|
</Box>
|
|
);
|
|
}
|
|
}
|
|
|
|
class EansEdit extends Component {
|
|
render() {
|
|
const { sku, draft, onChange, inputRef } = this.props;
|
|
return (
|
|
<Box sx={{ width: '100%' }}>
|
|
<PanelValue label="SKU">{sku}</PanelValue>
|
|
<SkuBarcode value={sku} />
|
|
<Box sx={{ mt: 2 }}>
|
|
<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>
|
|
);
|
|
}
|
|
}
|
|
|
|
export default class ArticleViewEANs extends PureComponent {
|
|
static contextType = StorageManagerContext;
|
|
|
|
handleSave = async (draft) => {
|
|
const { article: articleData, onArticleUpdated } = this.props;
|
|
const eans = (draft.eans ?? [])
|
|
.map((ean) => String(ean).trim())
|
|
.filter((ean) => ean.length > 0);
|
|
const res = await this.context.article.setEans(articleData.sku, { eans });
|
|
requireObject(res, 'PUT /api/article/:sku/eans');
|
|
requireObject(res.article, 'article');
|
|
onArticleUpdated?.(res.article);
|
|
};
|
|
|
|
render() {
|
|
const { article: articleData } = this.props;
|
|
const value = eansDraftValue(articleData);
|
|
|
|
return (
|
|
<EditablePanelTile
|
|
wide={1}
|
|
minHeight={SECOND_ROW_MIN_HEIGHT}
|
|
color="#e2e8f0"
|
|
contentAlignItems="flex-start"
|
|
value={value}
|
|
onSave={this.handleSave}
|
|
onError={notifyError}
|
|
isDirty={eansDraftDirty}
|
|
canSave={eansDraftValid}
|
|
display={<EansDisplay sku={articleData.sku} eans={value.eans} />}
|
|
edit={({ draft, onChange, inputRef }) => (
|
|
<EansEdit
|
|
sku={articleData.sku}
|
|
draft={draft}
|
|
onChange={onChange}
|
|
inputRef={inputRef}
|
|
/>
|
|
)}
|
|
/>
|
|
);
|
|
}
|
|
}
|