diff --git a/src/routes/artikel/marken.jsx b/src/routes/artikel/marken.jsx
index 61f8d78..ab8e8df 100644
--- a/src/routes/artikel/marken.jsx
+++ b/src/routes/artikel/marken.jsx
@@ -14,37 +14,69 @@ import { enqueueSnackbar } from 'notistack';
import {
BrandImageSearchDialog,
ArtikelMarke,
- ArtikelMarkenTable,
} from '../../components/brand';
import {
PURPLE_PANEL,
} from '../../colors';
-function BrandRowMeasureSample() {
- // Same component as real rows so the measured height matches exactly.
- return (
-
-
-
- );
+
+class ArtikelMarkeNew extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = { newName: '' };
+ }
+
+ async submit() {
+ const name = this.state.newName.trim();
+ if (!name) return;
+ await this.props.onCreate?.(name);
+ this.setState({ newName: '' });
+ }
+
+ render() {
+ const { rowRef } = this.props;
+ const { newName } = this.state;
+ return (
+
+ this.setState({ newName: next })}
+ onKeyDown={(event) => {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ void this.submit();
+ }
+ }}
+ sx={{ flex: 1 }}
+ />
+
+
+ );
+ }
}
class ArtikelMarkenPage extends PureComponent {
constructor(props) {
super(props);
this.state = {
- status: brandAutomat.state.status === 'succeeded' ? 'ready' : 'loading',
+ status: brandAutomat.state.status,
error: brandAutomat.state.error ? backendErrorFrom({ message: brandAutomat.state.error }) : null,
- brands: brandAutomat.state.brands || [],
- newName: '',
- busyKey: null,
+ brands: brandAutomat.state.brands,
searchBrand: null,
};
this._cancelled = false;
this.createRowRef = createRef();
this.handleCreate = this.handleCreate.bind(this);
this.handleRetry = this.handleRetry.bind(this);
- this.loadBrands = this.loadBrands.bind(this);
this.handleRename = this.handleRename.bind(this);
this.handleDelete = this.handleDelete.bind(this);
this.handleSearchLogo = this.handleSearchLogo.bind(this);
@@ -54,18 +86,13 @@ class ArtikelMarkenPage extends PureComponent {
setLastArticleSku(null);
this._cancelled = false;
- this.unsub = brandAutomat.subscribe((state) => {
- if (this._cancelled) return;
- if (state.status === 'succeeded') {
- this.applyBrands(state.brands);
- } else if (state.status === 'failed') {
- this.setState({ status: 'failed', error: backendErrorFrom({ message: state.error }) });
- }
- });
-
- if (brandAutomat.state.status !== 'succeeded') {
- void this.loadBrands();
- }
+ // Mirror the automat's brands slice directly — the automat owns
+ // fetching (auth/SSE), the component never triggers loads.
+ this.unsub = brandAutomat.subscribe(this, (state) => ({
+ brands: state.brands,
+ status: state.status,
+ error: state.error ? backendErrorFrom({ message: state.error }) : null,
+ }));
}
componentWillUnmount() {
@@ -73,88 +100,61 @@ class ArtikelMarkenPage extends PureComponent {
this.unsub?.();
}
- applyBrands(brands) {
- this.setState({ status: 'ready', error: null, brands });
- }
-
- async loadBrands() {
- if (brandAutomat.state.status !== 'succeeded') {
- this.setState({ status: 'loading', error: null });
- }
- try {
- const list = await brandAutomat.actions.load();
- if (this._cancelled) return;
- this.applyBrands(list);
- } catch (err) {
- if (this._cancelled) return;
- this.setState({ status: 'failed', error: backendErrorFrom(err) });
- }
- }
-
handleRetry() {
- void this.loadBrands();
+ // The automat owns fetching; just ask it to load again.
+ void brandAutomat.actions.load().catch((err) => notifyError(err));
}
handleSearchLogo(brand) {
this.setState({ searchBrand: brand });
}
- async handleCreate() {
- const name = this.state.newName.trim();
- if (!name || this.state.busyKey != null) return;
+ async handleCreate(name) {
+ const trimmed = String(name ?? '').trim();
+ if (!trimmed) return;
- this.setState({ busyKey: 'new' });
try {
- const row = await brandAutomat.actions.create({ name });
+ const row = await brandAutomat.actions.create({ name: trimmed });
if (this._cancelled) return;
- this.setState({ newName: '', busyKey: null });
- enqueueSnackbar(`Marke „${row.name || name}" angelegt.`, { variant: 'success' });
+ enqueueSnackbar(`Marke „${row.name || trimmed}" angelegt.`, { variant: 'success' });
} catch (err) {
if (this._cancelled) return;
notifyError(err);
- this.setState({ busyKey: null });
+ throw err;
}
}
async handleRename(brand, draft) {
const nextName = String(draft ?? '').trim();
- if (!nextName || nextName === brand.name || this.state.busyKey != null) return;
+ if (!nextName || nextName === brand.name) return;
- this.setState({ busyKey: brand.id });
try {
await brandAutomat.actions.rename(brand.id, { name: nextName });
if (this._cancelled) return;
- this.setState({ busyKey: null });
enqueueSnackbar('Marke gespeichert.', { variant: 'success' });
} catch (err) {
if (this._cancelled) return;
notifyError(err);
- this.setState({ busyKey: null });
}
}
async handleDelete(brand) {
- if (this.state.busyKey != null) return;
if (!window.confirm(`Marke „${brand.name}" wirklich löschen?`)) return;
- this.setState({ busyKey: brand.id });
try {
await brandAutomat.actions.remove(brand.id);
if (this._cancelled) return;
- this.setState({ busyKey: null });
enqueueSnackbar('Marke gelöscht.', { variant: 'success' });
} catch (err) {
if (this._cancelled) return;
notifyError(err);
- this.setState({ busyKey: null });
}
}
render() {
- const { status, error, brands, newName, busyKey } = this.state;
- const busy = busyKey != null;
+ const { status, error, brands } = this.state;
- if (status === 'loading') {
+ if (status === 'loading' || status === 'idle') {
return (
@@ -173,46 +173,20 @@ class ArtikelMarkenPage extends PureComponent {
aboveRef={[]}
belowRef={this.createRowRef}
reserveBottom={32}
- minPageSize={10}
- measureElement={}
+ minPageSize={8}
minHeight={0}
items={brands}
- renderPage={(pageBrands) => (
- (
+
)}
/>
-
- this.setState({ newName: next })}
- onKeyDown={(event) => {
- if (event.key === 'Enter') {
- event.preventDefault();
- void this.handleCreate();
- }
- }}
- sx={{ flex: 1 }}
- />
-
-
+