u
This commit is contained in:
139
src/components/TextInput.jsx
Normal file
139
src/components/TextInput.jsx
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { Component } from 'react';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
|
||||||
|
function normalizeSx(s) {
|
||||||
|
if (!s || typeof s !== 'object' || Array.isArray(s)) return s;
|
||||||
|
const bg = s.bgcolor ?? s.backgroundColor;
|
||||||
|
if (bg && bg !== 'transparent') {
|
||||||
|
const { bgcolor: _bg, backgroundColor: _bgc, ...rest } = s;
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
bgcolor: 'transparent',
|
||||||
|
'& .MuiInputBase-root': {
|
||||||
|
bgcolor: bg,
|
||||||
|
...(typeof rest['& .MuiInputBase-root'] === 'object' ? rest['& .MuiInputBase-root'] : {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveSx(s) {
|
||||||
|
if (!s) return undefined;
|
||||||
|
if (Array.isArray(s)) return s.map(resolveSx);
|
||||||
|
return normalizeSx(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TextInput:
|
||||||
|
* Reusable Class Component wrapping MUI TextField with the harmonized
|
||||||
|
* style of EANs (small outlined, shrink label by default, transparent root)
|
||||||
|
* and complete suppression of browser autocomplete/autofill popups.
|
||||||
|
*/
|
||||||
|
export default class TextInput extends Component {
|
||||||
|
state = {
|
||||||
|
// Starting as readOnly prevents Chrome/Safari/Firefox from attaching autofill popup listeners
|
||||||
|
readOnly: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleFocus = (e) => {
|
||||||
|
if (this.state.readOnly && !this.props.readOnly && !this.props.slotProps?.htmlInput?.readOnly) {
|
||||||
|
this.setState({ readOnly: false });
|
||||||
|
}
|
||||||
|
if (this.props.onFocus) {
|
||||||
|
this.props.onFocus(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleMouseDown = (e) => {
|
||||||
|
if (this.state.readOnly && !this.props.readOnly && !this.props.slotProps?.htmlInput?.readOnly) {
|
||||||
|
this.setState({ readOnly: false });
|
||||||
|
}
|
||||||
|
if (this.props.onMouseDown) {
|
||||||
|
this.props.onMouseDown(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
slotProps,
|
||||||
|
inputProps,
|
||||||
|
InputProps,
|
||||||
|
InputLabelProps,
|
||||||
|
inputRef,
|
||||||
|
label,
|
||||||
|
size = 'small',
|
||||||
|
onFocus,
|
||||||
|
onMouseDown,
|
||||||
|
readOnly: propReadOnly,
|
||||||
|
sx,
|
||||||
|
...rest
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const isSelect = Boolean(rest.select);
|
||||||
|
const effectiveReadOnly = isSelect
|
||||||
|
? (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly)
|
||||||
|
: (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly ?? this.state.readOnly);
|
||||||
|
|
||||||
|
const htmlInput = isSelect
|
||||||
|
? {
|
||||||
|
readOnly: effectiveReadOnly,
|
||||||
|
...slotProps?.htmlInput,
|
||||||
|
...inputProps,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
autoComplete: 'one-time-code',
|
||||||
|
autoCorrect: 'off',
|
||||||
|
autoCapitalize: 'off',
|
||||||
|
spellCheck: false,
|
||||||
|
'aria-autocomplete': 'none',
|
||||||
|
'data-lpignore': 'true',
|
||||||
|
'data-1p-ignore': 'true',
|
||||||
|
'data-bwignore': 'true',
|
||||||
|
'data-form-type': 'other',
|
||||||
|
readOnly: effectiveReadOnly,
|
||||||
|
...slotProps?.htmlInput,
|
||||||
|
...inputProps,
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolvedInputProps = (InputProps || slotProps?.input) ? {
|
||||||
|
...slotProps?.input,
|
||||||
|
...InputProps,
|
||||||
|
startAdornment: (slotProps?.input?.startAdornment && InputProps?.startAdornment)
|
||||||
|
? <>{slotProps.input.startAdornment}{InputProps.startAdornment}</>
|
||||||
|
: (InputProps?.startAdornment ?? slotProps?.input?.startAdornment),
|
||||||
|
endAdornment: (slotProps?.input?.endAdornment && InputProps?.endAdornment)
|
||||||
|
? <>{InputProps.endAdornment}{slotProps.input.endAdornment}</>
|
||||||
|
: (InputProps?.endAdornment ?? slotProps?.input?.endAdornment),
|
||||||
|
} : undefined;
|
||||||
|
|
||||||
|
const { input: _unusedInput, ...restSlotProps } = (slotProps || {});
|
||||||
|
|
||||||
|
const mergedSlotProps = {
|
||||||
|
...restSlotProps,
|
||||||
|
inputLabel: {
|
||||||
|
...(label ? { shrink: true } : {}),
|
||||||
|
...InputLabelProps,
|
||||||
|
...slotProps?.inputLabel,
|
||||||
|
},
|
||||||
|
htmlInput,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
size={size}
|
||||||
|
label={label}
|
||||||
|
inputRef={inputRef}
|
||||||
|
autoComplete="one-time-code"
|
||||||
|
onFocus={this.handleFocus}
|
||||||
|
onMouseDown={this.handleMouseDown}
|
||||||
|
InputProps={resolvedInputProps}
|
||||||
|
slotProps={mergedSlotProps}
|
||||||
|
sx={resolveSx(sx)}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { TextInput };
|
||||||
@@ -11,7 +11,7 @@ import IconButton from '@mui/material/IconButton';
|
|||||||
import InputAdornment from '@mui/material/InputAdornment';
|
import InputAdornment from '@mui/material/InputAdornment';
|
||||||
import LinearProgress from '@mui/material/LinearProgress';
|
import LinearProgress from '@mui/material/LinearProgress';
|
||||||
import Skeleton from '@mui/material/Skeleton';
|
import Skeleton from '@mui/material/Skeleton';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../TextInput';
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Chip from '@mui/material/Chip';
|
import Chip from '@mui/material/Chip';
|
||||||
@@ -358,9 +358,9 @@ export default class ArticleImageSearchDialog extends PureComponent {
|
|||||||
}}
|
}}
|
||||||
sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}
|
sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextInput
|
||||||
size="small"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
|
label="Suchbegriff"
|
||||||
value={query}
|
value={query}
|
||||||
disabled={isSearching || Boolean(processingId)}
|
disabled={isSearching || Boolean(processingId)}
|
||||||
onChange={(e) => this.setState({ query: e.target.value })}
|
onChange={(e) => this.setState({ query: e.target.value })}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import DialogContent from '@mui/material/DialogContent';
|
|||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import InputAdornment from '@mui/material/InputAdornment';
|
import InputAdornment from '@mui/material/InputAdornment';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../TextInput';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { formatPriceAmount, parsePriceInput } from '../../utils/price';
|
import { formatPriceAmount, parsePriceInput } from '../../utils/price';
|
||||||
|
|
||||||
@@ -111,27 +111,24 @@ class ArticlePriceTiersDialog extends Component {
|
|||||||
alignItems: 'flex-start',
|
alignItems: 'flex-start',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextInput
|
||||||
label="Ab Menge"
|
label="Ab Menge"
|
||||||
value={tier.minmultiplier}
|
value={tier.minmultiplier}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
this.handleChange(index, 'minmultiplier', event.target.value)
|
this.handleChange(index, 'minmultiplier', event.target.value)
|
||||||
}
|
}
|
||||||
size="small"
|
|
||||||
error={tier.minmultiplier !== '' && mult == null}
|
error={tier.minmultiplier !== '' && mult == null}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
inputLabel: { shrink: true },
|
|
||||||
htmlInput: { inputMode: 'numeric' },
|
htmlInput: { inputMode: 'numeric' },
|
||||||
}}
|
}}
|
||||||
sx={{ width: 120 }}
|
sx={{ width: 120 }}
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextInput
|
||||||
label="Brutto"
|
label="Brutto"
|
||||||
value={tier.gross}
|
value={tier.gross}
|
||||||
onChange={(event) =>
|
onChange={(event) =>
|
||||||
this.handleChange(index, 'gross', event.target.value)
|
this.handleChange(index, 'gross', event.target.value)
|
||||||
}
|
}
|
||||||
size="small"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
error={tier.gross !== '' && gross == null}
|
error={tier.gross !== '' && gross == null}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import InputLabel from '@mui/material/InputLabel';
|
|||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Select from '@mui/material/Select';
|
import Select from '@mui/material/Select';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../TextInput';
|
||||||
import ToggleButton from '@mui/material/ToggleButton';
|
import ToggleButton from '@mui/material/ToggleButton';
|
||||||
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
|
import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
@@ -480,9 +480,8 @@ export default class ArticleStockAdjustDialog extends PureComponent {
|
|||||||
|
|
||||||
{/* Amount input */}
|
{/* Amount input */}
|
||||||
{mode === 'diff' ? (
|
{mode === 'diff' ? (
|
||||||
<TextField
|
<TextInput
|
||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
|
||||||
label="Menge (+ für Zugang, - für Abgang)"
|
label="Menge (+ für Zugang, - für Abgang)"
|
||||||
type="number"
|
type="number"
|
||||||
value={diffValue}
|
value={diffValue}
|
||||||
@@ -509,9 +508,8 @@ export default class ArticleStockAdjustDialog extends PureComponent {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TextField
|
<TextInput
|
||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
|
||||||
label="Tatsächlicher Zählbestand (Ist-Bestand)"
|
label="Tatsächlicher Zählbestand (Ist-Bestand)"
|
||||||
type="number"
|
type="number"
|
||||||
value={targetValue}
|
value={targetValue}
|
||||||
@@ -560,9 +558,8 @@ export default class ArticleStockAdjustDialog extends PureComponent {
|
|||||||
|
|
||||||
{/* Reason / Memo */}
|
{/* Reason / Memo */}
|
||||||
<Box>
|
<Box>
|
||||||
<TextField
|
<TextInput
|
||||||
fullWidth
|
fullWidth
|
||||||
size="small"
|
|
||||||
label="Grund / Buchungstext"
|
label="Grund / Buchungstext"
|
||||||
value={reason}
|
value={reason}
|
||||||
onChange={this.handleReasonChange}
|
onChange={this.handleReasonChange}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import DialogTitle from '@mui/material/DialogTitle';
|
|||||||
import InputAdornment from '@mui/material/InputAdornment';
|
import InputAdornment from '@mui/material/InputAdornment';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../TextInput';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { companyTaxRateOptions } from '../../utils/articleDetail';
|
import { companyTaxRateOptions } from '../../utils/articleDetail';
|
||||||
import { formatPriceAmount, parsePriceInput } from '../../utils/price';
|
import { formatPriceAmount, parsePriceInput } from '../../utils/price';
|
||||||
@@ -135,7 +135,7 @@ export default class ArticleSupplierDialog extends PureComponent {
|
|||||||
value={selectedSupplier}
|
value={selectedSupplier}
|
||||||
onChange={(_e, val) => this.setState({ entityId: val ? val.id : null })}
|
onChange={(_e, val) => this.setState({ entityId: val ? val.id : null })}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextInput
|
||||||
{...params}
|
{...params}
|
||||||
label="Lieferant auswählen"
|
label="Lieferant auswählen"
|
||||||
required
|
required
|
||||||
@@ -146,7 +146,7 @@ export default class ArticleSupplierDialog extends PureComponent {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<TextField
|
<TextInput
|
||||||
label="Lieferanten-Artikelnummer (SKU)"
|
label="Lieferanten-Artikelnummer (SKU)"
|
||||||
value={sku}
|
value={sku}
|
||||||
onChange={(e) => this.setState({ sku: e.target.value })}
|
onChange={(e) => this.setState({ sku: e.target.value })}
|
||||||
@@ -155,22 +155,22 @@ export default class ArticleSupplierDialog extends PureComponent {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
|
||||||
<TextField
|
<TextInput
|
||||||
label="Verpackungseinheit (VPE / PU)"
|
label="Verpackungseinheit (VPE / PU)"
|
||||||
type="number"
|
type="number"
|
||||||
required
|
required
|
||||||
error={!puValid}
|
error={!puValid}
|
||||||
helperText={!puValid ? 'Muss größer als 0 sein' : undefined}
|
helperText={!puValid ? 'Muss größer als 0 sein' : undefined}
|
||||||
inputProps={{ step: 'any', min: 0.0001 }}
|
slotProps={{ htmlInput: { step: 'any', min: 0.0001 } }}
|
||||||
value={pu}
|
value={pu}
|
||||||
onChange={(e) => this.setState({ pu: e.target.value })}
|
onChange={(e) => this.setState({ pu: e.target.value })}
|
||||||
sx={{ flex: 1 }}
|
sx={{ flex: 1 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextInput
|
||||||
label="Bestand beim Lieferanten"
|
label="Bestand beim Lieferanten"
|
||||||
type="number"
|
type="number"
|
||||||
inputProps={{ step: 'any', min: 0 }}
|
slotProps={{ htmlInput: { step: 'any', min: 0 } }}
|
||||||
value={stock}
|
value={stock}
|
||||||
onChange={(e) => this.setState({ stock: e.target.value })}
|
onChange={(e) => this.setState({ stock: e.target.value })}
|
||||||
sx={{ flex: 1 }}
|
sx={{ flex: 1 }}
|
||||||
@@ -179,7 +179,7 @@ export default class ArticleSupplierDialog extends PureComponent {
|
|||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={2}>
|
||||||
<TextField
|
<TextInput
|
||||||
label="Einkaufspreis Netto"
|
label="Einkaufspreis Netto"
|
||||||
value={netPrice}
|
value={netPrice}
|
||||||
onChange={(e) => this.setState({ netPrice: e.target.value })}
|
onChange={(e) => this.setState({ netPrice: e.target.value })}
|
||||||
@@ -193,7 +193,7 @@ export default class ArticleSupplierDialog extends PureComponent {
|
|||||||
helperText="Optionaler Einkaufspreis (EK)"
|
helperText="Optionaler Einkaufspreis (EK)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextInput
|
||||||
select
|
select
|
||||||
label="MwSt.-Satz"
|
label="MwSt.-Satz"
|
||||||
value={taxRate}
|
value={taxRate}
|
||||||
@@ -208,7 +208,7 @@ export default class ArticleSupplierDialog extends PureComponent {
|
|||||||
{rate} %
|
{rate} %
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextInput>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import List from '@mui/material/List';
|
|||||||
import ListItem from '@mui/material/ListItem';
|
import ListItem from '@mui/material/ListItem';
|
||||||
import ListItemButton from '@mui/material/ListItemButton';
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
import ListItemText from '@mui/material/ListItemText';
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../TextInput';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import { StorageManagerContext } from '../../api/storageManagerContext';
|
import { StorageManagerContext } from '../../api/storageManagerContext';
|
||||||
import { requireObject } from '../../utils/apiShape';
|
import { requireObject } from '../../utils/apiShape';
|
||||||
@@ -241,47 +241,38 @@ export default class ArticleUnitEditorDialog extends Component {
|
|||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
|
||||||
<TextField
|
<TextInput
|
||||||
label="Name"
|
label="Name"
|
||||||
value={form.name}
|
value={form.name}
|
||||||
onChange={(e) => this.handleField('name', e.target.value)}
|
onChange={(e) => this.handleField('name', e.target.value)}
|
||||||
size="small"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
slotProps={{ inputLabel: { shrink: true } }}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextInput
|
||||||
label="Symbol"
|
label="Symbol"
|
||||||
value={form.symbol}
|
value={form.symbol}
|
||||||
onChange={(e) => this.handleField('symbol', e.target.value)}
|
onChange={(e) => this.handleField('symbol', e.target.value)}
|
||||||
size="small"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
slotProps={{ inputLabel: { shrink: true } }}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextInput
|
||||||
label="si"
|
label="si"
|
||||||
value={form.si}
|
value={form.si}
|
||||||
onChange={(e) => this.handleField('si', e.target.value)}
|
onChange={(e) => this.handleField('si', e.target.value)}
|
||||||
size="small"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
slotProps={{ inputLabel: { shrink: true } }}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextInput
|
||||||
label="basesi"
|
label="basesi"
|
||||||
value={form.basesi}
|
value={form.basesi}
|
||||||
onChange={(e) => this.handleField('basesi', e.target.value)}
|
onChange={(e) => this.handleField('basesi', e.target.value)}
|
||||||
size="small"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
slotProps={{ inputLabel: { shrink: true } }}
|
|
||||||
/>
|
/>
|
||||||
<TextField
|
<TextInput
|
||||||
label="basesifactor"
|
label="basesifactor"
|
||||||
value={form.basesifactor}
|
value={form.basesifactor}
|
||||||
onChange={(e) => this.handleField('basesifactor', e.target.value)}
|
onChange={(e) => this.handleField('basesifactor', e.target.value)}
|
||||||
size="small"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
error={
|
error={
|
||||||
@@ -290,7 +281,6 @@ export default class ArticleUnitEditorDialog extends Component {
|
|||||||
parseFactor(form.basesifactor) <= 0)
|
parseFactor(form.basesifactor) <= 0)
|
||||||
}
|
}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
inputLabel: { shrink: true },
|
|
||||||
htmlInput: { inputMode: 'decimal' },
|
htmlInput: { inputMode: 'decimal' },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, PureComponent } from 'react';
|
import { Component, PureComponent } from 'react';
|
||||||
import Autocomplete from '@mui/material/Autocomplete';
|
import Autocomplete from '@mui/material/Autocomplete';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../../TextInput';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { EditablePanelTile } from 'dashgui/panels';
|
import { EditablePanelTile } from 'dashgui/panels';
|
||||||
@@ -86,22 +86,22 @@ class BrandEdit extends Component {
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextInput
|
||||||
{...params}
|
{...params}
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
size="small"
|
label="Marke"
|
||||||
slotProps={{
|
InputProps={{
|
||||||
...params.slotProps,
|
...params.InputProps,
|
||||||
input: {
|
startAdornment: (
|
||||||
...params.slotProps?.input,
|
<>
|
||||||
startAdornment: matchingBrand ? (
|
{matchingBrand ? (
|
||||||
<InputAdornment position="start" sx={{ mr: 0.5 }}>
|
<InputAdornment position="start" sx={{ mr: 0.5 }}>
|
||||||
<BrandThumb brand={matchingBrand} size={20} />
|
<BrandThumb brand={matchingBrand} size={20} />
|
||||||
</InputAdornment>
|
</InputAdornment>
|
||||||
) : (
|
) : null}
|
||||||
params.slotProps?.input?.startAdornment
|
{params.InputProps?.startAdornment}
|
||||||
),
|
</>
|
||||||
},
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -182,6 +182,7 @@ export default class ArticleViewBrand extends PureComponent {
|
|||||||
wide={1}
|
wide={1}
|
||||||
minHeight={SECOND_ROW_MIN_HEIGHT}
|
minHeight={SECOND_ROW_MIN_HEIGHT}
|
||||||
color="#e9d5ff"
|
color="#e9d5ff"
|
||||||
|
contentAlignItems="flex-start"
|
||||||
value={value}
|
value={value}
|
||||||
onSave={this.handleSave}
|
onSave={this.handleSave}
|
||||||
onError={notifyError}
|
onError={notifyError}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, PureComponent } from 'react';
|
import { Component, PureComponent } from 'react';
|
||||||
import Autocomplete from '@mui/material/Autocomplete';
|
import Autocomplete from '@mui/material/Autocomplete';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../../TextInput';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import { EditablePanelTile } from 'dashgui/panels';
|
import { EditablePanelTile } from 'dashgui/panels';
|
||||||
@@ -117,18 +117,10 @@ class EansEdit extends Component {
|
|||||||
value={draft.eans ?? []}
|
value={draft.eans ?? []}
|
||||||
onChange={(_event, eans) => onChange({ eans })}
|
onChange={(_event, eans) => onChange({ eans })}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextInput
|
||||||
{...params}
|
{...params}
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
label="EANs (Enter zum Hinzufügen)"
|
label="EANs (Enter zum Hinzufügen)"
|
||||||
size="small"
|
|
||||||
slotProps={{
|
|
||||||
...params.slotProps,
|
|
||||||
inputLabel: {
|
|
||||||
...params.slotProps?.inputLabel,
|
|
||||||
shrink: true,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -11,14 +11,11 @@ import DialogActions from '@mui/material/DialogActions';
|
|||||||
import DialogContent from '@mui/material/DialogContent';
|
import DialogContent from '@mui/material/DialogContent';
|
||||||
import DialogContentText from '@mui/material/DialogContentText';
|
import DialogContentText from '@mui/material/DialogContentText';
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
import FormControl from '@mui/material/FormControl';
|
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import InputLabel from '@mui/material/InputLabel';
|
|
||||||
import Link from '@mui/material/Link';
|
import Link from '@mui/material/Link';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Select from '@mui/material/Select';
|
import TextInput from '../../TextInput';
|
||||||
import TextField from '@mui/material/TextField';
|
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import AddIcon from '@mui/icons-material/Add';
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
@@ -638,21 +635,18 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
}
|
}
|
||||||
isOptionEqualToValue={(opt, val) => opt.sku === val?.sku}
|
isOptionEqualToValue={(opt, val) => opt.sku === val?.sku}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextInput
|
||||||
{...params}
|
{...params}
|
||||||
|
label="Artikel"
|
||||||
placeholder="Artikel suchen..."
|
placeholder="Artikel suchen..."
|
||||||
sx={{
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
'& .MuiInputBase-input': { py: 0.5, px: 1, fontSize: '0.82rem' },
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
sx={{ flex: 1, minWidth: 0 }}
|
sx={{ flex: 1, minWidth: 0 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextInput
|
||||||
size="small"
|
|
||||||
type="number"
|
type="number"
|
||||||
|
label="Menge"
|
||||||
value={stuecklisteCount}
|
value={stuecklisteCount}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -660,11 +654,13 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
stuecklisteCount: Math.max(1, parseInt(e.target.value, 10) || 1),
|
stuecklisteCount: Math.max(1, parseInt(e.target.value, 10) || 1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
inputProps={{
|
slotProps={{
|
||||||
min: 1,
|
htmlInput: {
|
||||||
style: { padding: '4px 6px', textAlign: 'center', fontSize: '0.82rem' },
|
min: 1,
|
||||||
|
style: { textAlign: 'center' },
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
sx={{ width: 44, bgcolor: 'background.paper', flexShrink: 0 }}
|
sx={{ width: 72, flexShrink: 0 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tooltip title="Komponente hinzufügen">
|
<Tooltip title="Komponente hinzufügen">
|
||||||
@@ -809,21 +805,17 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
}
|
}
|
||||||
isOptionEqualToValue={(opt, val) => opt.sku === val?.sku}
|
isOptionEqualToValue={(opt, val) => opt.sku === val?.sku}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextInput
|
||||||
{...params}
|
{...params}
|
||||||
|
label="Artikel"
|
||||||
placeholder="Artikel suchen..."
|
placeholder="Artikel suchen..."
|
||||||
sx={{
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
'& .MuiInputBase-input': { py: 0.5, px: 1, fontSize: '0.82rem' },
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
sx={{ width: '100%' }}
|
sx={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
||||||
<TextField
|
<TextInput
|
||||||
size="small"
|
|
||||||
type="number"
|
type="number"
|
||||||
label="Menge"
|
label="Menge"
|
||||||
value={stuecklisteCount}
|
value={stuecklisteCount}
|
||||||
@@ -833,11 +825,13 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
stuecklisteCount: Math.max(1, parseInt(e.target.value, 10) || 1),
|
stuecklisteCount: Math.max(1, parseInt(e.target.value, 10) || 1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
inputProps={{
|
slotProps={{
|
||||||
min: 1,
|
htmlInput: {
|
||||||
style: { padding: '4px 6px', textAlign: 'center', fontSize: '0.82rem' },
|
min: 1,
|
||||||
|
style: { textAlign: 'center' },
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
sx={{ width: 70 }}
|
sx={{ width: 72 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', gap: 0.75 }}>
|
<Box sx={{ display: 'flex', gap: 0.75 }}>
|
||||||
@@ -917,21 +911,18 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
}
|
}
|
||||||
isOptionEqualToValue={(opt, val) => opt.sku === val?.sku}
|
isOptionEqualToValue={(opt, val) => opt.sku === val?.sku}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextInput
|
||||||
{...params}
|
{...params}
|
||||||
|
label="Option"
|
||||||
placeholder="Option suchen..."
|
placeholder="Option suchen..."
|
||||||
sx={{
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
'& .MuiInputBase-input': { py: 0.5, px: 1, fontSize: '0.82rem' },
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
sx={{ flex: 1, minWidth: 0 }}
|
sx={{ flex: 1, minWidth: 0 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField
|
<TextInput
|
||||||
size="small"
|
|
||||||
type="number"
|
type="number"
|
||||||
|
label="Menge"
|
||||||
value={optionCount}
|
value={optionCount}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
@@ -939,11 +930,13 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
optionCount: Math.max(1, parseInt(e.target.value, 10) || 1),
|
optionCount: Math.max(1, parseInt(e.target.value, 10) || 1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
inputProps={{
|
slotProps={{
|
||||||
min: 1,
|
htmlInput: {
|
||||||
style: { padding: '4px 6px', textAlign: 'center', fontSize: '0.82rem' },
|
min: 1,
|
||||||
|
style: { textAlign: 'center' },
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
sx={{ width: 44, bgcolor: 'background.paper', flexShrink: 0 }}
|
sx={{ width: 72, flexShrink: 0 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tooltip title="Option hinzufügen">
|
<Tooltip title="Option hinzufügen">
|
||||||
@@ -1119,21 +1112,17 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
}
|
}
|
||||||
isOptionEqualToValue={(opt, val) => opt.sku === val?.sku}
|
isOptionEqualToValue={(opt, val) => opt.sku === val?.sku}
|
||||||
renderInput={(params) => (
|
renderInput={(params) => (
|
||||||
<TextField
|
<TextInput
|
||||||
{...params}
|
{...params}
|
||||||
|
label="Option"
|
||||||
placeholder="Option suchen..."
|
placeholder="Option suchen..."
|
||||||
sx={{
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
'& .MuiInputBase-input': { py: 0.5, px: 1, fontSize: '0.82rem' },
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
sx={{ width: '100%' }}
|
sx={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 1 }}>
|
||||||
<TextField
|
<TextInput
|
||||||
size="small"
|
|
||||||
type="number"
|
type="number"
|
||||||
label="Menge"
|
label="Menge"
|
||||||
value={optionCount}
|
value={optionCount}
|
||||||
@@ -1143,11 +1132,13 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
optionCount: Math.max(1, parseInt(e.target.value, 10) || 1),
|
optionCount: Math.max(1, parseInt(e.target.value, 10) || 1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
inputProps={{
|
slotProps={{
|
||||||
min: 1,
|
htmlInput: {
|
||||||
style: { padding: '4px 6px', textAlign: 'center', fontSize: '0.82rem' },
|
min: 1,
|
||||||
|
style: { textAlign: 'center' },
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
sx={{ width: 70 }}
|
sx={{ width: 72 }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormControlLabel
|
<FormControlLabel
|
||||||
@@ -1231,34 +1222,27 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
Alle vorhandenen Merkmale sind diesem Artikel bereits zugewiesen.
|
Alle vorhandenen Merkmale sind diesem Artikel bereits zugewiesen.
|
||||||
</Typography>
|
</Typography>
|
||||||
) : (
|
) : (
|
||||||
<FormControl size="small" fullWidth>
|
<TextInput
|
||||||
<InputLabel id="activate-attr-label" sx={{ fontSize: '0.8rem' }}>
|
select
|
||||||
Merkmal für Varianten
|
fullWidth
|
||||||
</InputLabel>
|
label="Merkmal für Varianten"
|
||||||
<Select
|
value={selectedAttributeId}
|
||||||
labelId="activate-attr-label"
|
onChange={(e) => this.setState({ selectedAttributeId: e.target.value })}
|
||||||
label="Merkmal für Varianten"
|
>
|
||||||
value={selectedAttributeId}
|
{attributes.map((a) => {
|
||||||
size="small"
|
const isAssigned = assignedAttributeIds.has(a.id);
|
||||||
onChange={(e) => this.setState({ selectedAttributeId: e.target.value })}
|
return (
|
||||||
sx={{ fontSize: '0.82rem', height: 32 }}
|
<MenuItem
|
||||||
>
|
key={a.id}
|
||||||
{attributes.map((a) => {
|
value={a.id}
|
||||||
const isAssigned = assignedAttributeIds.has(a.id);
|
disabled={isAssigned}
|
||||||
return (
|
>
|
||||||
<MenuItem
|
{a.name}
|
||||||
key={a.id}
|
{isAssigned ? ' (bereits zugewiesen)' : ''}
|
||||||
value={a.id}
|
</MenuItem>
|
||||||
disabled={isAssigned}
|
);
|
||||||
sx={{ fontSize: '0.82rem' }}
|
})}
|
||||||
>
|
</TextInput>
|
||||||
{a.name}
|
|
||||||
{isAssigned ? ' (bereits zugewiesen)' : ''}
|
|
||||||
</MenuItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', gap: 0.75, justifyContent: 'flex-end' }}>
|
<Box sx={{ display: 'flex', gap: 0.75, justifyContent: 'flex-end' }}>
|
||||||
@@ -1475,8 +1459,8 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
{/* Add Child Variant Row */}
|
{/* Add Child Variant Row */}
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mt: 0.5 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mt: 0.5 }}>
|
||||||
{isCustomChildValue || availableFatherValues.length === 0 ? (
|
{isCustomChildValue || availableFatherValues.length === 0 ? (
|
||||||
<TextField
|
<TextInput
|
||||||
size="small"
|
label="Neuer Wert"
|
||||||
placeholder="Neuer Wert..."
|
placeholder="Neuer Wert..."
|
||||||
value={newChildText}
|
value={newChildText}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
@@ -1487,38 +1471,26 @@ export default class ArticleViewLists extends PureComponent {
|
|||||||
void this.handleAddChild();
|
void this.handleAddChild();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
sx={{
|
sx={{ flex: 1, minWidth: 0 }}
|
||||||
flex: 1,
|
|
||||||
minWidth: 0,
|
|
||||||
bgcolor: 'background.paper',
|
|
||||||
'& .MuiInputBase-input': { py: 0.5, px: 1, fontSize: '0.82rem' },
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FormControl size="small" sx={{ flex: 1, minWidth: 0 }}>
|
<TextInput
|
||||||
<Select
|
select
|
||||||
size="small"
|
label="Variante"
|
||||||
value={selectedChildValueId}
|
value={selectedChildValueId}
|
||||||
displayEmpty
|
disabled={busy}
|
||||||
disabled={busy}
|
onChange={(e) => this.setState({ selectedChildValueId: e.target.value })}
|
||||||
onChange={(e) => this.setState({ selectedChildValueId: e.target.value })}
|
sx={{ flex: 1, minWidth: 0 }}
|
||||||
sx={{
|
>
|
||||||
bgcolor: 'background.paper',
|
<MenuItem value="" disabled>
|
||||||
fontSize: '0.82rem',
|
<em>Variante wählen...</em>
|
||||||
height: 32,
|
</MenuItem>
|
||||||
'& .MuiSelect-select': { py: 0.5, px: 1 },
|
{availableFatherValues.map((v) => (
|
||||||
}}
|
<MenuItem key={v.id} value={v.id}>
|
||||||
>
|
{v.value}
|
||||||
<MenuItem value="" disabled>
|
|
||||||
<em>Variante wählen...</em>
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
{availableFatherValues.map((v) => (
|
))}
|
||||||
<MenuItem key={v.id} value={v.id} sx={{ fontSize: '0.82rem' }}>
|
</TextInput>
|
||||||
{v.value}
|
|
||||||
</MenuItem>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Tooltip title="Variante hinzufügen">
|
<Tooltip title="Variante hinzufügen">
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import MenuItem from '@mui/material/MenuItem';
|
|||||||
import Select from '@mui/material/Select';
|
import Select from '@mui/material/Select';
|
||||||
import StraightenOutlinedIcon from '@mui/icons-material/StraightenOutlined';
|
import StraightenOutlinedIcon from '@mui/icons-material/StraightenOutlined';
|
||||||
import Switch from '@mui/material/Switch';
|
import Switch from '@mui/material/Switch';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../../TextInput';
|
||||||
import { findUnitBySi, unitLabel } from '../../../utils/units';
|
import { findUnitBySi, unitLabel } from '../../../utils/units';
|
||||||
|
|
||||||
const priceUnitCornerSx = {
|
const priceUnitCornerSx = {
|
||||||
@@ -73,15 +73,13 @@ export class PriceUnitRow extends Component {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextInput
|
||||||
label="Menge"
|
label="Menge"
|
||||||
value={unitamount}
|
value={unitamount}
|
||||||
onChange={(event) => onChangeAmount(event.target.value)}
|
onChange={(event) => onChangeAmount(event.target.value)}
|
||||||
size="small"
|
|
||||||
error={amountError}
|
error={amountError}
|
||||||
sx={{ width: 88, flexShrink: 0 }}
|
sx={{ width: 88, flexShrink: 0 }}
|
||||||
slotProps={{
|
slotProps={{
|
||||||
inputLabel: { shrink: true },
|
|
||||||
htmlInput: { inputMode: 'decimal' },
|
htmlInput: { inputMode: 'decimal' },
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -3,10 +3,7 @@ import Box from '@mui/material/Box';
|
|||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import IconButton from '@mui/material/IconButton';
|
import IconButton from '@mui/material/IconButton';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Select from '@mui/material/Select';
|
import TextInput from '../../TextInput';
|
||||||
import FormControl from '@mui/material/FormControl';
|
|
||||||
import InputLabel from '@mui/material/InputLabel';
|
|
||||||
import TextField from '@mui/material/TextField';
|
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Chip from '@mui/material/Chip';
|
import Chip from '@mui/material/Chip';
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
@@ -397,51 +394,45 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||||
{/* Unified Add Bar: [Merkmal ▼] [Wert ▼ / Input] [+] */}
|
{/* Unified Add Bar: [Merkmal ▼] [Wert ▼ / Input] [+] */}
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<FormControl size="small" sx={{ minWidth: 170 }}>
|
<TextInput
|
||||||
<InputLabel id="merkmal-select-label">Merkmal</InputLabel>
|
select
|
||||||
<Select
|
label="Merkmal"
|
||||||
labelId="merkmal-select-label"
|
value={selectedMerkmalKey}
|
||||||
value={selectedMerkmalKey}
|
disabled={saving || merkmalOptions.length === 0}
|
||||||
label="Merkmal"
|
onChange={this.handleMerkmalChange}
|
||||||
disabled={saving || merkmalOptions.length === 0}
|
sx={{ minWidth: 170 }}
|
||||||
onChange={this.handleMerkmalChange}
|
>
|
||||||
sx={{ bgcolor: 'background.paper' }}
|
{merkmalOptions.map((m) => (
|
||||||
>
|
<MenuItem key={m.key} value={m.key}>
|
||||||
{merkmalOptions.map((m) => (
|
{m.name}
|
||||||
<MenuItem key={m.key} value={m.key}>
|
</MenuItem>
|
||||||
{m.name}
|
))}
|
||||||
</MenuItem>
|
</TextInput>
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
|
|
||||||
{isAttr ? (
|
{isAttr ? (
|
||||||
<FormControl size="small" sx={{ minWidth: 170 }}>
|
<TextInput
|
||||||
<InputLabel id="item-select-label">Wert</InputLabel>
|
select
|
||||||
<Select
|
label="Wert"
|
||||||
labelId="item-select-label"
|
value={selectedItemId}
|
||||||
value={selectedItemId}
|
disabled={saving || availableItems.length === 0}
|
||||||
label="Wert"
|
onChange={this.handleItemChange}
|
||||||
disabled={saving || availableItems.length === 0}
|
sx={{ minWidth: 170 }}
|
||||||
onChange={this.handleItemChange}
|
>
|
||||||
sx={{ bgcolor: 'background.paper' }}
|
{availableItems.length === 0 ? (
|
||||||
>
|
<MenuItem value="" disabled>
|
||||||
{availableItems.length === 0 ? (
|
(Keine Werte)
|
||||||
<MenuItem value="" disabled>
|
</MenuItem>
|
||||||
(Keine Werte)
|
) : (
|
||||||
|
availableItems.map((val) => (
|
||||||
|
<MenuItem key={val.id} value={val.id}>
|
||||||
|
{val.value}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
) : (
|
))
|
||||||
availableItems.map((val) => (
|
)}
|
||||||
<MenuItem key={val.id} value={val.id}>
|
</TextInput>
|
||||||
{val.value}
|
|
||||||
</MenuItem>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
) : (
|
) : (
|
||||||
<TextField
|
<TextInput
|
||||||
size="small"
|
label="Wert"
|
||||||
placeholder="Wert..."
|
placeholder="Wert..."
|
||||||
value={customValueText}
|
value={customValueText}
|
||||||
disabled={saving || !selectedMerkmalKey}
|
disabled={saving || !selectedMerkmalKey}
|
||||||
@@ -452,7 +443,7 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
void this.handleAddItem();
|
void this.handleAddItem();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
sx={{ minWidth: 170, bgcolor: 'background.paper' }}
|
sx={{ minWidth: 170 }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Box from '@mui/material/Box';
|
|||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import CircularProgress from '@mui/material/CircularProgress';
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
import Button from '@mui/material/Button';
|
import Button from '@mui/material/Button';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../../TextInput';
|
||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
@@ -210,22 +210,21 @@ export default class ArticleLabelTab extends PureComponent {
|
|||||||
|
|
||||||
<Box>
|
<Box>
|
||||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 3, alignItems: 'flex-start' }}>
|
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 3, alignItems: 'flex-start' }}>
|
||||||
<TextField
|
<TextInput
|
||||||
select
|
select
|
||||||
label="Drucker"
|
label="Drucker"
|
||||||
value={selectedPrinterId}
|
value={selectedPrinterId}
|
||||||
onChange={(e) => this.setState({ selectedPrinterId: e.target.value })}
|
onChange={(e) => this.setState({ selectedPrinterId: e.target.value })}
|
||||||
sx={{ minWidth: 200 }}
|
sx={{ minWidth: 200 }}
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
{printers.map((p) => (
|
{printers.map((p) => (
|
||||||
<MenuItem key={p.id} value={p.id}>
|
<MenuItem key={p.id} value={p.id}>
|
||||||
{p.desc ? `${p.desc} (${p.address})` : p.address}
|
{p.desc ? `${p.desc} (${p.address})` : p.address}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextInput>
|
||||||
|
|
||||||
<TextField
|
<TextInput
|
||||||
type="number"
|
type="number"
|
||||||
label="Anzahl"
|
label="Anzahl"
|
||||||
value={quantity}
|
value={quantity}
|
||||||
@@ -233,9 +232,8 @@ export default class ArticleLabelTab extends PureComponent {
|
|||||||
const val = parseInt(e.target.value, 10);
|
const val = parseInt(e.target.value, 10);
|
||||||
this.setState({ quantity: isNaN(val) ? 1 : Math.max(1, val) });
|
this.setState({ quantity: isNaN(val) ? 1 : Math.max(1, val) });
|
||||||
}}
|
}}
|
||||||
inputProps={{ min: 1, max: 9999 }}
|
slotProps={{ htmlInput: { min: 1, max: 9999 } }}
|
||||||
sx={{ width: 100 }}
|
sx={{ width: 100 }}
|
||||||
size="small"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import IconButton from '@mui/material/IconButton';
|
|||||||
import InputAdornment from '@mui/material/InputAdornment';
|
import InputAdornment from '@mui/material/InputAdornment';
|
||||||
import LinearProgress from '@mui/material/LinearProgress';
|
import LinearProgress from '@mui/material/LinearProgress';
|
||||||
import Skeleton from '@mui/material/Skeleton';
|
import Skeleton from '@mui/material/Skeleton';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextInput from '../TextInput';
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Chip from '@mui/material/Chip';
|
import Chip from '@mui/material/Chip';
|
||||||
@@ -276,9 +276,9 @@ export default class BrandImageSearchDialog extends PureComponent {
|
|||||||
}}
|
}}
|
||||||
sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1.5 }}
|
sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1.5 }}
|
||||||
>
|
>
|
||||||
<TextField
|
<TextInput
|
||||||
size="small"
|
|
||||||
fullWidth
|
fullWidth
|
||||||
|
label="Suchbegriff"
|
||||||
value={query}
|
value={query}
|
||||||
disabled={isSearching || Boolean(processingId)}
|
disabled={isSearching || Boolean(processingId)}
|
||||||
onChange={(e) => this.setState({ query: e.target.value })}
|
onChange={(e) => this.setState({ query: e.target.value })}
|
||||||
|
|||||||
@@ -310,8 +310,11 @@ export class ArticleTile extends Tile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderExtra() {
|
renderExtra() {
|
||||||
const childCount = Number(this.props.article?.childCount) || 0;
|
const { article } = this.props;
|
||||||
if (childCount <= 0) return null;
|
if (!article) return null;
|
||||||
|
const isFather = Boolean(article.isFather || article.is_father);
|
||||||
|
const childCount = Number(article.childCount) || 0;
|
||||||
|
if (!isFather && childCount <= 0) return null;
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ export const SUB_NAV = {
|
|||||||
{
|
{
|
||||||
segment: 'merkmale',
|
segment: 'merkmale',
|
||||||
label: 'Merkmale',
|
label: 'Merkmale',
|
||||||
title: 'Merkmale & Attribute',
|
title: 'Merkmale',
|
||||||
icon: CategoryIcon,
|
icon: CategoryIcon,
|
||||||
help: 'Globale Merkmale, Attribute und Werte verwalten',
|
help: 'Globale Merkmale, Attribute und Werte verwalten',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ import ListItem from '@mui/material/ListItem';
|
|||||||
import Stack from '@mui/material/Stack';
|
import Stack from '@mui/material/Stack';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import Chip from '@mui/material/Chip';
|
import Chip from '@mui/material/Chip';
|
||||||
import Tooltip from '@mui/material/Tooltip';
|
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
|
||||||
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
|
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
|
||||||
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
|
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
|
||||||
import TuneIcon from '@mui/icons-material/Tune';
|
import TuneIcon from '@mui/icons-material/Tune';
|
||||||
@@ -47,12 +44,10 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
properties: initialProperties,
|
properties: initialProperties,
|
||||||
propDrafts: draftsFrom(initialProperties, 'name'),
|
propDrafts: draftsFrom(initialProperties, 'name'),
|
||||||
newPropName: '',
|
newPropName: '',
|
||||||
newPropExclusive: true,
|
|
||||||
|
|
||||||
attributes: initialAttributes,
|
attributes: initialAttributes,
|
||||||
attrDrafts: draftsFrom(initialAttributes, 'name'),
|
attrDrafts: draftsFrom(initialAttributes, 'name'),
|
||||||
newAttrName: '',
|
newAttrName: '',
|
||||||
newAttrExclusive: true,
|
|
||||||
selectedAttributeId: initialSelectedAttrId,
|
selectedAttributeId: initialSelectedAttrId,
|
||||||
|
|
||||||
valDrafts: draftsFrom(
|
valDrafts: draftsFrom(
|
||||||
@@ -333,13 +328,13 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
|
|
||||||
async handleCreateProperty() {
|
async handleCreateProperty() {
|
||||||
const name = this.state.newPropName.trim();
|
const name = this.state.newPropName.trim();
|
||||||
if (!name || this.state.busyKey != null) return;
|
if (!name) return;
|
||||||
|
|
||||||
this.setState({ busyKey: 'new-prop' });
|
this.setState({ busyKey: 'new-prop' });
|
||||||
try {
|
try {
|
||||||
const row = await propertyAutomat.actions.createProperty({
|
const row = await propertyAutomat.actions.createProperty({
|
||||||
name,
|
name,
|
||||||
exclusive: this.state.newPropExclusive,
|
exclusive: true,
|
||||||
});
|
});
|
||||||
if (this._cancelled) return;
|
if (this._cancelled) return;
|
||||||
this.setState({ newPropName: '', busyKey: null });
|
this.setState({ newPropName: '', busyKey: null });
|
||||||
@@ -353,7 +348,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
|
|
||||||
async handleTogglePropertyExclusive(prop, e) {
|
async handleTogglePropertyExclusive(prop, e) {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
if (this.state.busyKey != null) return;
|
|
||||||
|
|
||||||
const nextExclusive = !prop.exclusive;
|
const nextExclusive = !prop.exclusive;
|
||||||
this.setState({ busyKey: `prop-ex-${prop.id}` });
|
this.setState({ busyKey: `prop-ex-${prop.id}` });
|
||||||
@@ -380,7 +374,7 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (draft === prop.name || this.state.busyKey != null) return;
|
if (draft === prop.name) return;
|
||||||
|
|
||||||
this.setState({ busyKey: `prop-${prop.id}` });
|
this.setState({ busyKey: `prop-${prop.id}` });
|
||||||
try {
|
try {
|
||||||
@@ -399,7 +393,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleDeleteProperty(prop) {
|
async handleDeleteProperty(prop) {
|
||||||
if (this.state.busyKey != null) return;
|
|
||||||
if (!window.confirm(`Merkmal „${prop.name}" wirklich löschen?`)) {
|
if (!window.confirm(`Merkmal „${prop.name}" wirklich löschen?`)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -421,13 +414,13 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
|
|
||||||
async handleCreateAttribute() {
|
async handleCreateAttribute() {
|
||||||
const name = this.state.newAttrName.trim();
|
const name = this.state.newAttrName.trim();
|
||||||
if (!name || this.state.busyKey != null) return;
|
if (!name) return;
|
||||||
|
|
||||||
this.setState({ busyKey: 'new-attr' });
|
this.setState({ busyKey: 'new-attr' });
|
||||||
try {
|
try {
|
||||||
const row = await attributeAutomat.actions.createAttribute({
|
const row = await attributeAutomat.actions.createAttribute({
|
||||||
name,
|
name,
|
||||||
exclusive: this.state.newAttrExclusive,
|
exclusive: true,
|
||||||
});
|
});
|
||||||
if (this._cancelled) return;
|
if (this._cancelled) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -451,7 +444,7 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (draft === attr.name || this.state.busyKey != null) return;
|
if (draft === attr.name) return;
|
||||||
|
|
||||||
this.setState({ busyKey: `attr-${attr.id}` });
|
this.setState({ busyKey: `attr-${attr.id}` });
|
||||||
try {
|
try {
|
||||||
@@ -471,7 +464,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
|
|
||||||
async handleToggleAttributeExclusive(attr, e) {
|
async handleToggleAttributeExclusive(attr, e) {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
if (this.state.busyKey != null) return;
|
|
||||||
|
|
||||||
const nextExclusive = !attr.exclusive;
|
const nextExclusive = !attr.exclusive;
|
||||||
this.setState({ busyKey: `attr-ex-${attr.id}` });
|
this.setState({ busyKey: `attr-ex-${attr.id}` });
|
||||||
@@ -492,7 +484,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
|
|
||||||
async handleDeleteAttribute(attr, e) {
|
async handleDeleteAttribute(attr, e) {
|
||||||
e?.stopPropagation();
|
e?.stopPropagation();
|
||||||
if (this.state.busyKey != null) return;
|
|
||||||
if (!window.confirm(`Attribut „${attr.name}" und alle zugehörigen Werte wirklich löschen?`)) {
|
if (!window.confirm(`Attribut „${attr.name}" und alle zugehörigen Werte wirklich löschen?`)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -523,7 +514,7 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
|
|
||||||
async handleCreateValue(attrId) {
|
async handleCreateValue(attrId) {
|
||||||
const text = this.state.newValText.trim();
|
const text = this.state.newValText.trim();
|
||||||
if (!text || this.state.busyKey != null || !attrId) return;
|
if (!text || !attrId) return;
|
||||||
|
|
||||||
this.setState({ busyKey: 'new-val' });
|
this.setState({ busyKey: 'new-val' });
|
||||||
try {
|
try {
|
||||||
@@ -546,7 +537,7 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
}));
|
}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (draft === val.value || this.state.busyKey != null) return;
|
if (draft === val.value) return;
|
||||||
|
|
||||||
this.setState({ busyKey: `val-${val.id}` });
|
this.setState({ busyKey: `val-${val.id}` });
|
||||||
try {
|
try {
|
||||||
@@ -565,7 +556,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async handleDeleteValue(val) {
|
async handleDeleteValue(val) {
|
||||||
if (this.state.busyKey != null) return;
|
|
||||||
if (!window.confirm(`Wert „${val.value}" wirklich löschen?`)) {
|
if (!window.confirm(`Wert „${val.value}" wirklich löschen?`)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -591,18 +581,15 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
properties,
|
properties,
|
||||||
propDrafts,
|
propDrafts,
|
||||||
newPropName,
|
newPropName,
|
||||||
newPropExclusive,
|
|
||||||
attributes,
|
attributes,
|
||||||
attrDrafts,
|
attrDrafts,
|
||||||
newAttrName,
|
newAttrName,
|
||||||
newAttrExclusive,
|
|
||||||
selectedAttributeId,
|
selectedAttributeId,
|
||||||
valDrafts,
|
valDrafts,
|
||||||
newValText,
|
newValText,
|
||||||
busyKey,
|
busyKey,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const busy = busyKey != null;
|
|
||||||
|
|
||||||
if (attrStatus === 'loading' && propStatus === 'loading' && !properties.length && !attributes.length) {
|
if (attrStatus === 'loading' && propStatus === 'loading' && !properties.length && !attributes.length) {
|
||||||
return (
|
return (
|
||||||
@@ -638,7 +625,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||||
Auswahl-Merkmale
|
Auswahl-Merkmale
|
||||||
</Typography>
|
</Typography>
|
||||||
<Chip label={attributes.length} size="small" sx={{ ml: 'auto', fontWeight: 600, height: 20 }} />
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<List dense disablePadding ref={this.attrListRef}>
|
<List dense disablePadding ref={this.attrListRef}>
|
||||||
@@ -693,7 +679,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
size="small"
|
size="small"
|
||||||
label=""
|
label=""
|
||||||
value={attrDrafts[a.id] ?? ''}
|
value={attrDrafts[a.id] ?? ''}
|
||||||
disabled={busy}
|
|
||||||
onChange={(next) =>
|
onChange={(next) =>
|
||||||
this.setState((prev) => ({
|
this.setState((prev) => ({
|
||||||
attrDrafts: { ...prev.attrDrafts, [a.id]: next },
|
attrDrafts: { ...prev.attrDrafts, [a.id]: next },
|
||||||
@@ -717,34 +702,29 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Tooltip
|
<Chip
|
||||||
title={
|
label="Multi"
|
||||||
a.exclusive
|
|
||||||
? 'Einzelauswahl (Artikel kann max. 1 Wert wählen)'
|
|
||||||
: 'Mehrfachauswahl (Artikel kann mehrere Werte wählen)'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Chip
|
|
||||||
label={a.exclusive ? '1x' : 'Multi'}
|
|
||||||
size="small"
|
size="small"
|
||||||
color={a.exclusive ? 'primary' : 'default'}
|
color={a.exclusive ? 'default' : 'success'}
|
||||||
variant={a.exclusive ? 'filled' : 'outlined'}
|
variant="filled"
|
||||||
onClick={(e) => void this.handleToggleAttributeExclusive(a, e)}
|
onClick={(e) => void this.handleToggleAttributeExclusive(a, e)}
|
||||||
|
aria-pressed={!a.exclusive}
|
||||||
sx={{
|
sx={{
|
||||||
height: 22,
|
height: 22,
|
||||||
fontSize: '0.72rem',
|
fontSize: '0.72rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
...(a.exclusive
|
||||||
|
? { bgcolor: 'grey.300', color: 'text.secondary' }
|
||||||
|
: null),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<IconButton
|
<IconButton
|
||||||
edge="end"
|
edge="end"
|
||||||
size="small"
|
size="small"
|
||||||
aria-label={`„${a.name}" löschen`}
|
aria-label={`„${a.name}" löschen`}
|
||||||
disabled={busy}
|
|
||||||
onClick={(e) => void this.handleDeleteAttribute(a, e)}
|
onClick={(e) => void this.handleDeleteAttribute(a, e)}
|
||||||
sx={{ flexShrink: 0 }}
|
sx={{ flexShrink: 0 }}
|
||||||
>
|
>
|
||||||
@@ -761,7 +741,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
size="small"
|
size="small"
|
||||||
label=""
|
label=""
|
||||||
value={newAttrName}
|
value={newAttrName}
|
||||||
disabled={busy}
|
|
||||||
onChange={(next) => this.setState({ newAttrName: next })}
|
onChange={(next) => this.setState({ newAttrName: next })}
|
||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
@@ -774,29 +753,12 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
disabled={busy || !newAttrName.trim()}
|
disabled={!newAttrName.trim()}
|
||||||
onClick={() => void this.handleCreateAttribute()}
|
onClick={() => void this.handleCreateAttribute()}
|
||||||
>
|
>
|
||||||
{busyKey === 'new-attr' ? '…' : 'Hinzufügen'}
|
{busyKey === 'new-attr' ? '…' : 'Hinzufügen'}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
size="small"
|
|
||||||
checked={newAttrExclusive}
|
|
||||||
disabled={busy}
|
|
||||||
onChange={(e) => this.setState({ newAttrExclusive: e.target.checked })}
|
|
||||||
sx={{ p: 0.5 }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Typography variant="caption" color="text.secondary">
|
|
||||||
Einzelauswahl (max. 1 Wert pro Artikel)
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
sx={{ ml: 0.5, mt: -0.5 }}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</PanelTile>
|
</PanelTile>
|
||||||
@@ -807,15 +769,8 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
<LabelImportantIcon sx={{ color: 'text.secondary', fontSize: 20 }} />
|
<LabelImportantIcon sx={{ color: 'text.secondary', fontSize: 20 }} />
|
||||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||||
{selectedAttr ? `Werte für „${selectedAttr.name}“` : 'Werte'}
|
{selectedAttr ? `Werte für Auswahl-Merkmal „${selectedAttr.name}“` : 'Werte'}
|
||||||
</Typography>
|
</Typography>
|
||||||
{selectedAttr ? (
|
|
||||||
<Chip
|
|
||||||
label={selectedValues.length}
|
|
||||||
size="small"
|
|
||||||
sx={{ ml: 'auto', fontWeight: 600, height: 20 }}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{!selectedAttr ? (
|
{!selectedAttr ? (
|
||||||
@@ -825,12 +780,7 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<List dense disablePadding ref={this.valListRef}>
|
<List dense disablePadding ref={this.valListRef}>
|
||||||
{selectedValues.length === 0 ? (
|
{selectedValues.map((v) => (
|
||||||
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
|
|
||||||
Noch keine Werte für „{selectedAttr.name}“ hinterlegt.
|
|
||||||
</Typography>
|
|
||||||
) : (
|
|
||||||
selectedValues.map((v) => (
|
|
||||||
<ListItem
|
<ListItem
|
||||||
key={v.id}
|
key={v.id}
|
||||||
disableGutters
|
disableGutters
|
||||||
@@ -855,7 +805,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
size="small"
|
size="small"
|
||||||
label=""
|
label=""
|
||||||
value={valDrafts[v.id] ?? ''}
|
value={valDrafts[v.id] ?? ''}
|
||||||
disabled={busy}
|
|
||||||
onChange={(next) =>
|
onChange={(next) =>
|
||||||
this.setState((prev) => ({
|
this.setState((prev) => ({
|
||||||
valDrafts: { ...prev.valDrafts, [v.id]: next },
|
valDrafts: { ...prev.valDrafts, [v.id]: next },
|
||||||
@@ -881,14 +830,12 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
edge="end"
|
edge="end"
|
||||||
size="small"
|
size="small"
|
||||||
aria-label={`„${v.value}" löschen`}
|
aria-label={`„${v.value}" löschen`}
|
||||||
disabled={busy}
|
|
||||||
onClick={() => void this.handleDeleteValue(v)}
|
onClick={() => void this.handleDeleteValue(v)}
|
||||||
>
|
>
|
||||||
<DeleteOutlinedIcon fontSize="small" />
|
<DeleteOutlinedIcon fontSize="small" />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))
|
))}
|
||||||
)}
|
|
||||||
</List>
|
</List>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
@@ -896,7 +843,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
size="small"
|
size="small"
|
||||||
label=""
|
label=""
|
||||||
value={newValText}
|
value={newValText}
|
||||||
disabled={busy}
|
|
||||||
onChange={(next) => this.setState({ newValText: next })}
|
onChange={(next) => this.setState({ newValText: next })}
|
||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
@@ -909,7 +855,7 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
disabled={busy || !newValText.trim()}
|
disabled={!newValText.trim()}
|
||||||
onClick={() => void this.handleCreateValue(selectedAttr.id)}
|
onClick={() => void this.handleCreateValue(selectedAttr.id)}
|
||||||
>
|
>
|
||||||
{busyKey === 'new-val' ? '…' : 'Hinzufügen'}
|
{busyKey === 'new-val' ? '…' : 'Hinzufügen'}
|
||||||
@@ -928,7 +874,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||||
Text-Merkmale
|
Text-Merkmale
|
||||||
</Typography>
|
</Typography>
|
||||||
<Chip label={properties.length} size="small" sx={{ ml: 'auto', fontWeight: 600, height: 20 }} />
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<List dense disablePadding ref={this.propListRef}>
|
<List dense disablePadding ref={this.propListRef}>
|
||||||
@@ -957,7 +902,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
size="small"
|
size="small"
|
||||||
label=""
|
label=""
|
||||||
value={propDrafts[p.id] ?? ''}
|
value={propDrafts[p.id] ?? ''}
|
||||||
disabled={busy}
|
|
||||||
onChange={(next) =>
|
onChange={(next) =>
|
||||||
this.setState((prev) => ({
|
this.setState((prev) => ({
|
||||||
propDrafts: { ...prev.propDrafts, [p.id]: next },
|
propDrafts: { ...prev.propDrafts, [p.id]: next },
|
||||||
@@ -979,33 +923,28 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
}}
|
}}
|
||||||
sx={{ flex: 1 }}
|
sx={{ flex: 1 }}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Chip
|
||||||
title={
|
label="Multi"
|
||||||
p.exclusive
|
|
||||||
? 'Einzelauswahl (Artikel kann max. 1 Wert für dieses Merkmal haben)'
|
|
||||||
: 'Mehrfachauswahl (Artikel kann mehrere Werte für dieses Merkmal haben)'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Chip
|
|
||||||
label={p.exclusive ? '1x' : 'Multi'}
|
|
||||||
size="small"
|
size="small"
|
||||||
color={p.exclusive ? 'primary' : 'default'}
|
color={p.exclusive ? 'default' : 'success'}
|
||||||
variant={p.exclusive ? 'filled' : 'outlined'}
|
variant="filled"
|
||||||
onClick={(e) => void this.handleTogglePropertyExclusive(p, e)}
|
onClick={(e) => void this.handleTogglePropertyExclusive(p, e)}
|
||||||
|
aria-pressed={!p.exclusive}
|
||||||
sx={{
|
sx={{
|
||||||
height: 22,
|
height: 22,
|
||||||
fontSize: '0.72rem',
|
fontSize: '0.72rem',
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
...(p.exclusive
|
||||||
|
? { bgcolor: 'grey.300', color: 'text.secondary' }
|
||||||
|
: null),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
edge="end"
|
edge="end"
|
||||||
size="small"
|
size="small"
|
||||||
aria-label={`„${p.name}" löschen`}
|
aria-label={`„${p.name}" löschen`}
|
||||||
disabled={busy}
|
|
||||||
onClick={() => void this.handleDeleteProperty(p)}
|
onClick={() => void this.handleDeleteProperty(p)}
|
||||||
>
|
>
|
||||||
<DeleteOutlinedIcon fontSize="small" />
|
<DeleteOutlinedIcon fontSize="small" />
|
||||||
@@ -1020,7 +959,6 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
size="small"
|
size="small"
|
||||||
label=""
|
label=""
|
||||||
value={newPropName}
|
value={newPropName}
|
||||||
disabled={busy}
|
|
||||||
onChange={(next) => this.setState({ newPropName: next })}
|
onChange={(next) => this.setState({ newPropName: next })}
|
||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
@@ -1033,29 +971,12 @@ class ArtikelMerkmalePage extends PureComponent {
|
|||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
disabled={busy || !newPropName.trim()}
|
disabled={!newPropName.trim()}
|
||||||
onClick={() => void this.handleCreateProperty()}
|
onClick={() => void this.handleCreateProperty()}
|
||||||
>
|
>
|
||||||
{busyKey === 'new-prop' ? '…' : 'Hinzufügen'}
|
{busyKey === 'new-prop' ? '…' : 'Hinzufügen'}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
<FormControlLabel
|
|
||||||
control={
|
|
||||||
<Checkbox
|
|
||||||
size="small"
|
|
||||||
checked={newPropExclusive}
|
|
||||||
disabled={busy}
|
|
||||||
onChange={(e) => this.setState({ newPropExclusive: e.target.checked })}
|
|
||||||
sx={{ p: 0.5 }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<Typography variant="caption" color="text.secondary">
|
|
||||||
Einzelauswahl (max. 1 Wert pro Artikel)
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
sx={{ ml: 0.5, mt: -0.5 }}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
</PanelTile>
|
</PanelTile>
|
||||||
|
|||||||
Reference in New Issue
Block a user