This commit is contained in:
seb
2026-10-04 21:04:40 +02:00
parent 669954b496
commit f765b87336
59 changed files with 1491 additions and 2045 deletions

View File

@@ -4,8 +4,9 @@ import TextInput from '../../TextInput';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import { EditablePanelTile } from 'dashgui/panels';
import { StorageManagerContext } from '../../../api/storageManagerContext';
import { requireDefined, requireObject } from '../../../utils/apiShape';
import brandAutomat from '../../../automats/brandAutomat';
import artikelAutomat from '../../../automats/artikelAutomat';
import { requireObject } from '../../../utils/apiShape';
import { notifyError } from '../../../utils/notify';
import InputAdornment from '@mui/material/InputAdornment';
import { BrandLabel, BrandThumb } from '../../brand';
@@ -111,37 +112,29 @@ class BrandEdit extends Component {
}
export default class ArticleViewBrand extends PureComponent {
static contextType = StorageManagerContext;
constructor(props) {
super(props);
const brands = this.context?.brands;
this.state = {
brands: brands ?? [],
brands: brandAutomat.state.brands ?? [],
};
this._cancelled = false;
this._brandsReady = brands != null;
}
componentDidMount() {
this._cancelled = false;
if (this._brandsReady) return;
this.context.brandPromise
.then((list) => {
if (!this._cancelled) this.setState({ brands: list });
})
.catch((err) => {
if (!this._cancelled) notifyError(err);
});
this.unsub = brandAutomat.subscribe(this, (state) => ({
brands: state.brands ?? [],
}));
}
componentWillUnmount() {
this._cancelled = true;
this.unsub?.();
this.unsub = null;
}
handleSave = async (draft) => {
const { article: articleData, onArticleUpdated } = this.props;
const store = this.context;
const { brands } = this.state;
const name = String(draft.name ?? '').trim();
if (!name) {
@@ -156,16 +149,16 @@ export default class ArticleViewBrand extends PureComponent {
if (existing) {
brandId = existing.id;
} else {
const createdRes = await store.brand.upsert({ name });
requireObject(createdRes, 'PUT /api/brand');
requireObject(createdRes.brand, 'brand');
requireDefined(createdRes.brand.id, 'brand.id');
brandId = createdRes.brand.id;
const nextBrands = await store.updateBrand();
await brandAutomat.actions.upsert({ name });
const nextBrands = await brandAutomat.actions.load();
this.setState({ brands: nextBrands });
brandId = nextBrands.find(
(row) => String(row.name ?? '').toLowerCase() === name.toLowerCase(),
)?.id;
if (brandId == null) throw new Error('Marke konnte nicht gespeichert werden.');
}
const res = await store.article.setBrand(articleData.sku, { brandId });
const res = await artikelAutomat.actions.setBrand(articleData.sku, { brandId });
requireObject(res, 'PUT /api/article/:sku/brand');
requireObject(res.article, 'article');
onArticleUpdated?.(res.article);

View File

@@ -4,7 +4,7 @@ import TextInput from '../../TextInput';
import Typography from '@mui/material/Typography';
import Box from '@mui/material/Box';
import { EditablePanelTile } from 'dashgui/panels';
import { StorageManagerContext } from '../../../api/storageManagerContext';
import artikelAutomat from '../../../automats/artikelAutomat';
import { requireObject } from '../../../utils/apiShape';
import { articleEans } from '../../../utils/articleDetail';
import { notifyError } from '../../../utils/notify';
@@ -131,14 +131,12 @@ class EansEdit extends Component {
}
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 });
const res = await artikelAutomat.actions.setEans(articleData.sku, eans);
requireObject(res, 'PUT /api/article/:sku/eans');
requireObject(res.article, 'article');
onArticleUpdated?.(res.article);

View File

@@ -35,11 +35,9 @@ import { article as articleApi } from '../../../api';
import attributeAutomat, { selectAttributes } from '../../../automats/attributeAutomat';
import artikelAutomat, { getArticle } from '../../../automats/artikelAutomat';
import { notifyError } from '../../../utils/notify';
import { StorageManagerContext } from '../../../api/storageManagerContext';
import articleIndexAutomat from '../../../automats/articleIndexAutomat';
export default class ArticleViewLists extends PureComponent {
static contextType = StorageManagerContext;
constructor(props) {
super(props);
const initialAttributes = selectAttributes() || [];
@@ -167,13 +165,12 @@ export default class ArticleViewLists extends PureComponent {
}
getApi() {
return this.context?.article || articleApi;
return articleApi;
}
async loadAllArticles() {
try {
const data = await this.context?.articleIndexPromise;
const articles = Array.isArray(data) ? data : (data?.articles || []);
const articles = await articleIndexAutomat.actions.ensureLoaded();
if (!this._cancelled) {
this.setState({ allArticles: articles });
}
@@ -1316,7 +1313,7 @@ export default class ArticleViewLists extends PureComponent {
}
renderStandardView(selectableAttributes, assignedAttributeIds) {
const { busy, activeCreatorMode, selectedAttributeId, attributes } = this.state;
const { busy, activeCreatorMode, selectedAttributeId } = this.state;
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{activeCreatorMode === 'father' ? (
@@ -1422,7 +1419,6 @@ export default class ArticleViewLists extends PureComponent {
allArticles,
busy,
activeCreatorMode,
activeListTab,
selectedChildValueId,
newChildText,
isCustomChildValue,

View File

@@ -1,7 +1,8 @@
import { Component, PureComponent } from 'react';
import { EditablePanelTile } from 'dashgui/panels';
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
import { StorageManagerContext } from '../../../api/storageManagerContext';
import { getCategoryState } from '../../../automats/categoryAutomat';
import artikelAutomat from '../../../automats/artikelAutomat';
import { getCategoryBreadcrumbs, categoryDisplayName } from '../../../utils/categoryUtils';
import { Box, Chip, IconButton } from '@mui/material';
import AddIcon from '@mui/icons-material/Add';
@@ -88,8 +89,6 @@ function categoryLabels(categoryIds, categories, rootCategoryId) {
}
class CategoryPills extends Component {
static contextType = StorageManagerContext;
constructor(props) {
super(props);
this.state = { pickerOpen: false, targetPillId: null };
@@ -133,7 +132,7 @@ class CategoryPills extends Component {
render() {
const { categoryIds, editMode } = this.props;
const { pickerOpen, targetPillId } = this.state;
const { categories, rootCategoryId } = this.context.getCategoryState() ?? {};
const { categories, rootCategoryId } = getCategoryState() ?? {};
const labels = categoryLabels(categoryIds, categories, rootCategoryId);
const targetNode = categories && targetPillId != null ? categories[String(targetPillId)] : null;
@@ -273,8 +272,6 @@ class NameEdit extends Component {
}
export default class ArticleViewName extends PureComponent {
static contextType = StorageManagerContext;
constructor(props) {
super(props);
this.state = {
@@ -294,7 +291,6 @@ export default class ArticleViewName extends PureComponent {
handleSave = async () => {
const { article: articleData, onArticleUpdated } = this.props;
const { nameDraft, categoryIdsDraft } = this.state;
const store = this.context;
const name = nameDraft.trim();
const categoryIds = categoryIdsDraft;
const nameChanged = name !== String(articleData.name ?? '').trim();
@@ -303,14 +299,12 @@ export default class ArticleViewName extends PureComponent {
let article = null;
if (nameChanged) {
console.log('storing name update', name, articleData.name);
const nameRes = await store.article.update(articleData.sku, { name });
const nameRes = await artikelAutomat.actions.update(articleData.sku, { name });
article = nameRes.article;
}
if (categoriesChanged) {
console.log('storing categories update', categoryIds, articleData.categoryIds);
const catRes = await store.article.setCategories(articleData.sku, { categoryIds });
const catRes = await artikelAutomat.actions.setCategories(articleData.sku, categoryIds);
article = catRes.article;
}

View File

@@ -6,7 +6,9 @@ import Typography from '@mui/material/Typography';
import { EditablePanelTile, PanelTile } from 'dashgui/panels';
import ArticlePriceTiersDialog from '../ArticlePriceTiersDialog';
import ArticleUnitEditorDialog from '../ArticleUnitEditorDialog';
import { StorageManagerContext } from '../../../api/storageManagerContext';
import unitAutomat from '../../../automats/unitAutomat';
import companyAutomat from '../../../automats/companyAutomat';
import artikelAutomat from '../../../automats/artikelAutomat';
import { requireDefined, requireObject } from '../../../utils/apiShape';
import {
buildPosPricesPutBody,
@@ -169,9 +171,8 @@ function priceDraftValid(draft) {
return true;
}
function companyFromStore(store) {
if (!store) return null;
return store.company ?? null;
function companyFromStore() {
return companyAutomat.state.current ?? null;
}
class PriceAmountField extends Component {
@@ -361,13 +362,10 @@ class PriceEdit extends Component {
}
export default class ArticleViewPrice extends PureComponent {
static contextType = StorageManagerContext;
constructor(props) {
super(props);
const store = this.context;
const company = companyFromStore(store);
const units = store?.units;
const company = companyFromStore();
const units = unitAutomat.state.units;
this.state = {
tiersDialogOpen: false,
unitEditorOpen: false,
@@ -375,37 +373,28 @@ export default class ArticleViewPrice extends PureComponent {
company,
};
this._cancelled = false;
this._ready = company != null && units != null;
}
componentDidMount() {
this._cancelled = false;
if (this._ready) return;
void this.load();
this.unsubUnits = unitAutomat.subscribe(this, (state) => ({
units: state.units ?? [],
}));
this.unsubCompany = companyAutomat.subscribe(this, (state) => ({
company: state.current ?? null,
}));
}
componentWillUnmount() {
this._cancelled = true;
}
async load() {
const store = this.context;
try {
const [unitList, company] = await Promise.all([
store.unitPromise,
store.companyPromise,
]);
if (!this._cancelled) {
this.setState({ units: unitList, company: company ?? null });
}
} catch (err) {
if (!this._cancelled) notifyError(err);
}
this.unsubUnits?.();
this.unsubUnits = null;
this.unsubCompany?.();
this.unsubCompany = null;
}
handleSave = async (draft) => {
const { article: articleData, onArticleUpdated } = this.props;
const store = this.context;
const { company } = this.state;
const saved = priceDraftValue(articleData, company);
let nextArticle = articleData;
@@ -426,12 +415,12 @@ export default class ArticleViewPrice extends PureComponent {
if (isNoPackUnit(unitSi, unitamount)) {
throw new Error('Einheit pc × 1 ist keine Packungseinheit');
}
const res = await store.article.update(articleData.sku, { unitamount, unitSi });
const res = await artikelAutomat.actions.update(articleData.sku, { unitamount, unitSi });
requireObject(res, 'PATCH /api/article/:sku');
requireObject(res.article, 'article');
nextArticle = res.article;
} else {
const res = await store.article.update(articleData.sku, {
const res = await artikelAutomat.actions.update(articleData.sku, {
unitamount: 1,
unitSi: 'pc',
});
@@ -456,7 +445,7 @@ export default class ArticleViewPrice extends PureComponent {
taxRate,
tiers: draft.tiers ?? [],
});
const res = await store.article.setPrices(articleData.sku, { prices });
const res = await artikelAutomat.actions.setPrices(articleData.sku, prices);
requireObject(res, 'PUT /api/article/:sku/prices');
requireObject(res.article, 'article');
nextArticle = res.article;
@@ -480,7 +469,8 @@ export default class ArticleViewPrice extends PureComponent {
}
refreshUnits = async () => {
this.setState({ units: await this.context.unitPromise });
const units = await unitAutomat.actions.load();
this.setState({ units });
};
render() {