This commit is contained in:
seb
2026-10-08 21:17:30 +02:00
parent 064dd2bd31
commit 7f988c9d63
5 changed files with 110 additions and 56 deletions

View File

@@ -3,9 +3,9 @@ import Autocomplete from '@mui/material/Autocomplete';
import TextInput from '../../TextInput';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import { EditablePanelTile } from 'dashgui/panels';
import { EditablePanelTile, PanelTile } from 'dashgui/panels';
import brandAutomat from '../../../automats/brandAutomat';
import artikelAutomat from '../../../automats/artikelAutomat';
import artikelAutomat, { getArticle } from '../../../automats/artikelAutomat';
import { requireObject } from '../../../utils/apiShape';
import { notifyError } from '../../../utils/notify';
import InputAdornment from '@mui/material/InputAdornment';
@@ -33,12 +33,13 @@ class PanelValue extends Component {
}
}
function brandDraftValue(article) {
function brandDraftValue(article, parentArticle) {
const brand = article?.brand || parentArticle?.brand;
return {
name: article?.brand?.name ?? '',
thumbHash: article?.brand?.thumbHash ?? null,
thumbW: article?.brand?.thumbW ?? null,
thumbH: article?.brand?.thumbH ?? null,
name: brand?.name ?? '',
thumbHash: brand?.thumbHash ?? null,
thumbW: brand?.thumbW ?? null,
thumbH: brand?.thumbH ?? null,
};
}
@@ -128,12 +129,27 @@ export default class ArticleViewBrand extends PureComponent {
this.unsub = brandAutomat.subscribe(this, (state) => ({
brands: state.brands ?? [],
}));
const parentSku = this.props.article?.parentSku || this.props.article?.parent_sku;
if (parentSku && !getArticle(parentSku)) {
void artikelAutomat.actions.loadArticle(parentSku).catch(() => {});
}
this.unsubArtikel = artikelAutomat.subscribe(this, () => ({}));
}
componentDidUpdate(prevProps) {
const parentSku = this.props.article?.parentSku || this.props.article?.parent_sku;
const prevParentSku = prevProps.article?.parentSku || prevProps.article?.parent_sku;
if (parentSku && parentSku !== prevParentSku && !getArticle(parentSku)) {
void artikelAutomat.actions.loadArticle(parentSku).catch(() => {});
}
}
componentWillUnmount() {
this._cancelled = true;
this.unsub?.();
this.unsub = null;
this.unsubArtikel?.();
this.unsubArtikel = null;
}
handleSave = async (draft) => {
@@ -170,7 +186,40 @@ export default class ArticleViewBrand extends PureComponent {
render() {
const { article: articleData } = this.props;
const { brands } = this.state;
const value = brandDraftValue(articleData);
const isChild = Boolean(
articleData?.parentSku ||
articleData?.parent_sku ||
articleData?.parentArticleId != null ||
articleData?.parentId != null ||
articleData?.parent_id != null
);
const parentSku = articleData?.parentSku || articleData?.parent_sku;
const parentArticle = parentSku ? getArticle(parentSku) : null;
const value = brandDraftValue(articleData, parentArticle);
if (isChild) {
const brandObj = articleData?.brand || parentArticle?.brand || (value.name ? value : null);
return (
<PanelTile
title="Marke"
wide={1}
minHeight={SECOND_ROW_MIN_HEIGHT}
color={PURPLE_BG}
>
<PanelValue>
{value.name ? (
<BrandLabel
brand={brandObj}
name={value.name}
size={22}
typographyProps={{ variant: 'body2', fontWeight: 600 }}
/>
) : null}
</PanelValue>
</PanelTile>
);
}
return (
<EditablePanelTile

View File

@@ -85,10 +85,18 @@ export default class ArticleViewStock extends PureComponent {
const hasWarehouses = warehouses.length > 0;
let totalVariantsStock = 0;
let anyVariantTracksStock = false;
if (isParent) {
const children = Array.isArray(articleData?.children) ? articleData.children : [];
for (const child of children) {
const fullChild = getArticle(child.sku) || child;
const tracks =
fullChild.track_stock !== false &&
fullChild.track_stock !== 0 &&
fullChild.track_stock !== '0';
if (tracks) {
anyVariantTracksStock = true;
}
if (Array.isArray(fullChild.warehouses) && fullChild.warehouses.length > 0) {
totalVariantsStock += fullChild.warehouses.reduce(
(sum, wh) => sum + (Number(wh.stock) || 0),
@@ -155,19 +163,21 @@ export default class ArticleViewStock extends PureComponent {
</Typography>
{isParent ? (
<Box sx={{ my: 0.75 }}>
<Typography
component="div"
sx={{
fontSize: '1.25rem',
fontWeight: 700,
lineHeight: 1.2,
color: 'text.primary',
}}
>
Alle Varianten zusammen: {formatStock(totalVariantsStock)}
</Typography>
</Box>
anyVariantTracksStock || totalVariantsStock > 0 ? (
<Box sx={{ my: 0.75 }}>
<Typography
component="div"
sx={{
fontSize: '1.25rem',
fontWeight: 700,
lineHeight: 1.2,
color: 'text.primary',
}}
>
Alle Varianten zusammen: {formatStock(totalVariantsStock)}
</Typography>
</Box>
) : null
) : isStueckliste ? (
<Tooltip title="Virtueller Bestand – Anpassung erfolgt an den einzelnen Komponenten" arrow placement="top-start">
<Box sx={{ my: 0.5 }}>

View File

@@ -112,7 +112,19 @@ export default class ChatDrawer extends PureComponent {
}, delay);
};
updateGutterClass(isOpen) {
if (typeof document === 'undefined') return;
const html = document.documentElement;
if (!html) return;
if (isOpen) {
html.classList.add('df-chat-open');
} else {
html.classList.remove('df-chat-open');
}
}
componentDidMount() {
this.updateGutterClass(this.state.isOpen);
if (this.state.isOpen) {
if (chatAutomat.state.pendingInput != null) {
this.focusInputAtEnd(150);
@@ -177,7 +189,14 @@ export default class ChatDrawer extends PureComponent {
}));
}
componentDidUpdate(prevProps, prevState) {
if (this.state.isOpen !== prevState.isOpen) {
this.updateGutterClass(this.state.isOpen);
}
}
componentWillUnmount() {
this.updateGutterClass(false);
if (this.focusTimeout) clearTimeout(this.focusTimeout);
this.unsub?.();
this.unsubSettings?.();
@@ -1605,7 +1624,9 @@ export default class ChatDrawer extends PureComponent {
anchor="right"
open={isOpen}
onClose={this.handleClose}
disableScrollLock
ModalProps={{
disableScrollLock: true,
disableEnforceFocus: true,
}}
sx={(theme) => ({

View File

@@ -44,6 +44,15 @@ html {
scrollbar-gutter: stable;
}
/* While the assistant chat drawer is open on the right edge of the screen,
the reserved scrollbar-gutter strip must read as white to seamlessly
continue the drawer surface. */
html.df-chat-open,
html.df-chat-open body,
html.df-chat-open #root {
background-color: #ffffff;
}
/* While any AppDialog is open, the reserved scrollbar-gutter strip must read
as fog, not app background. AppDialog toggles `df-dialog-open` on <html>
(ref-counted for stacked dialogs). The solid fog token is published by