diff --git a/src/components/brand/ArtikelMarke.jsx b/src/components/brand/ArtikelMarke.jsx
new file mode 100644
index 0000000..cf8a0f0
--- /dev/null
+++ b/src/components/brand/ArtikelMarke.jsx
@@ -0,0 +1,124 @@
+import { PureComponent } from 'react';
+import IconButton from '@mui/material/IconButton';
+import ListItem from '@mui/material/ListItem';
+import Tooltip from '@mui/material/Tooltip';
+import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
+import CopyrightIcon from '@mui/icons-material/Copyright';
+import ImageSearchIcon from '@mui/icons-material/ImageSearch';
+import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
+import BrandThumb from './BrandThumb';
+
+class ArtikelMarke extends PureComponent {
+ constructor(props) {
+ super(props);
+ this.state = {
+ draft: props.brand?.name ?? '',
+ };
+ }
+
+ componentDidUpdate(prevProps) {
+ if (prevProps.brand?.name !== this.props.brand?.name) {
+ this.setState({ draft: this.props.brand?.name ?? '' });
+ } else if (prevProps.disabled && !this.props.disabled) {
+ if (this.state.draft !== this.props.brand?.name) {
+ this.setState({ draft: this.props.brand?.name ?? '' });
+ }
+ }
+ }
+
+ handleChange = (next) => {
+ this.setState({ draft: next });
+ this.props.onChange?.(next, this.props.brand);
+ };
+
+ handleBlur = () => {
+ const { onRename, brand } = this.props;
+ const draft = this.state.draft.trim();
+ if (!draft) {
+ this.setState({ draft: brand?.name ?? '' });
+ return;
+ }
+ if (draft !== brand?.name) {
+ onRename?.(brand, draft);
+ }
+ };
+
+ handleKeyDown = (event) => {
+ const { brand } = this.props;
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ event.target.blur();
+ }
+ if (event.key === 'Escape') {
+ event.preventDefault();
+ this.setState({ draft: brand?.name ?? '' });
+ event.target.blur();
+ }
+ };
+
+ handleSearchLogo = () => {
+ const { onSearchLogo, brand } = this.props;
+ onSearchLogo?.(brand);
+ };
+
+ handleDelete = () => {
+ const { onDelete, brand } = this.props;
+ onDelete?.(brand);
+ };
+
+ render() {
+ const { brand, disabled, sx } = this.props;
+ const { draft } = this.state;
+ const name = brand?.name ?? '';
+
+ return (
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default ArtikelMarke;
diff --git a/src/components/brand/ArtikelMarkenTable.jsx b/src/components/brand/ArtikelMarkenTable.jsx
new file mode 100644
index 0000000..5821859
--- /dev/null
+++ b/src/components/brand/ArtikelMarkenTable.jsx
@@ -0,0 +1,44 @@
+import { PureComponent } from 'react';
+import List from '@mui/material/List';
+import Typography from '@mui/material/Typography';
+import ArtikelMarke from './ArtikelMarke';
+
+class ArtikelMarkenTable extends PureComponent {
+ render() {
+ const {
+ brands = [],
+ busyKey,
+ disabled,
+ onRename,
+ onSearchLogo,
+ onDelete,
+ emptyMessage = 'Noch keine Marken angelegt.',
+ sx,
+ } = this.props;
+
+ const isBusy = disabled || busyKey != null;
+
+ return (
+
+ {brands.length === 0 ? (
+
+ {emptyMessage}
+
+ ) : (
+ brands.map((b) => (
+
+ ))
+ )}
+
+ );
+ }
+}
+
+export default ArtikelMarkenTable;
diff --git a/src/components/brand/index.js b/src/components/brand/index.js
index b265eaa..23375fa 100644
--- a/src/components/brand/index.js
+++ b/src/components/brand/index.js
@@ -1,3 +1,5 @@
export { default as BrandThumb, extractBrandThumbInfo } from './BrandThumb';
export { default as BrandLabel } from './BrandLabel';
export { default as BrandImageSearchDialog } from './BrandImageSearchDialog';
+export { default as ArtikelMarke } from './ArtikelMarke';
+export { default as ArtikelMarkenTable } from './ArtikelMarkenTable';
diff --git a/src/components/tiles/ArticleTile.jsx b/src/components/tiles/ArticleTile.jsx
index f887983..f2faf4f 100644
--- a/src/components/tiles/ArticleTile.jsx
+++ b/src/components/tiles/ArticleTile.jsx
@@ -22,7 +22,7 @@ import {
SCRIM_LABEL,
SCRIM_TILE,
TEXT_GLOW_DARK,
- VEIL_MUTED,
+ VEIL_SUBTLE,
WHITE,
} from '../../colors';
@@ -447,21 +447,19 @@ export class ArticleTile extends Tile {
px: 1,
py: '2px',
my: 0.25,
- bgcolor: VEIL_MUTED,
- backdropFilter: 'blur(4px)',
- WebkitBackdropFilter: 'blur(4px)',
+ bgcolor: VEIL_SUBTLE,
+ backdropFilter: 'blur(2px)',
+ WebkitBackdropFilter: 'blur(2px)',
}}
>
[b.id, b.name]));
-}
-
class ArtikelMarkenPage extends PureComponent {
constructor(props) {
super(props);
- const initialBrands = brandAutomat.state.brands || [];
this.state = {
status: brandAutomat.state.status === 'succeeded' ? 'ready' : 'loading',
error: brandAutomat.state.error ? backendErrorFrom({ message: brandAutomat.state.error }) : null,
- brands: initialBrands,
- drafts: draftsFrom(initialBrands),
+ brands: brandAutomat.state.brands || [],
newName: '',
busyKey: null,
searchBrand: null,
@@ -44,6 +34,9 @@ class ArtikelMarkenPage extends PureComponent {
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);
}
componentDidMount() {
@@ -70,20 +63,10 @@ class ArtikelMarkenPage extends PureComponent {
}
applyBrands(brands) {
- this.setState((prev) => {
- const currentDrafts = prev.drafts || {};
- const nextDrafts = { ...draftsFrom(brands) };
- for (const b of brands) {
- if (prev.busyKey !== b.id && currentDrafts[b.id] !== undefined) {
- nextDrafts[b.id] = currentDrafts[b.id];
- }
- }
- return {
- status: 'ready',
- error: null,
- brands,
- drafts: nextDrafts,
- };
+ this.setState({
+ status: 'ready',
+ error: null,
+ brands,
});
}
@@ -105,6 +88,10 @@ class ArtikelMarkenPage extends PureComponent {
void this.loadBrands();
}
+ handleSearchLogo(brand) {
+ this.setState({ searchBrand: brand });
+ }
+
async handleCreate() {
const name = this.state.newName.trim();
if (!name || this.state.busyKey != null) return;
@@ -122,29 +109,20 @@ class ArtikelMarkenPage extends PureComponent {
}
}
- async handleRename(brand) {
- const draft = String(this.state.drafts[brand.id] ?? '').trim();
- if (!draft) {
- this.setState((prev) => ({
- drafts: { ...prev.drafts, [brand.id]: brand.name },
- }));
- return;
- }
- if (draft === brand.name || this.state.busyKey != null) return;
+ async handleRename(brand, draft) {
+ const nextName = String(draft ?? '').trim();
+ if (!nextName || nextName === brand.name || this.state.busyKey != null) return;
this.setState({ busyKey: brand.id });
try {
- await brandAutomat.actions.upsert({ name: draft });
+ await brandAutomat.actions.upsert({ 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((prev) => ({
- busyKey: null,
- drafts: { ...prev.drafts, [brand.id]: brand.name },
- }));
+ this.setState({ busyKey: null });
}
}
@@ -166,7 +144,7 @@ class ArtikelMarkenPage extends PureComponent {
}
render() {
- const { status, error, brands, drafts, newName, busyKey } = this.state;
+ const { status, error, brands, newName, busyKey } = this.state;
const busy = busyKey != null;
if (status === 'loading') {
@@ -184,78 +162,13 @@ class ArtikelMarkenPage extends PureComponent {
return (
-
- {brands.length === 0 ? (
-
- Noch keine Marken angelegt.
-
- ) : (
- brands.map((b) => (
-
- }
- />
-
- this.setState((prev) => ({
- drafts: { ...prev.drafts, [b.id]: next },
- }))
- }
- onBlur={() => void this.handleRename(b)}
- onKeyDown={(event) => {
- if (event.key === 'Enter') {
- event.preventDefault();
- event.target.blur();
- }
- if (event.key === 'Escape') {
- event.preventDefault();
- this.setState((prev) => ({
- drafts: { ...prev.drafts, [b.id]: b.name },
- }));
- event.target.blur();
- }
- }}
- sx={{ flex: 1 }}
- />
-
-
- this.setState({ searchBrand: b })}
- >
-
-
-
-
-
-
- void this.handleDelete(b)}
- >
-
-
-
-
-
- ))
- )}
-
+