diff --git a/.agents/skills/backend/SKILL.md b/.agents/skills/backend/SKILL.md
index 01d29bc..ee6b62b 100644
--- a/.agents/skills/backend/SKILL.md
+++ b/.agents/skills/backend/SKILL.md
@@ -7,7 +7,7 @@ description: >-
# Dash API Routes Overview (All)
-Overview of all 205 API endpoints across all modules.
+Overview of all 206 API endpoints across all modules.
### Notation
@@ -620,6 +620,9 @@ _none_
#### Create package for a shipping — `POST /shipping/{id}/package`
- Params: `id: int, body: { entityaddressId: int, type: str, state: str, details?: str }`
- Returns: `201 { package: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null } }`
+#### Small thumb image by hash — `GET /smallthumb/{fullHash}`
+- Params: `fullHash: str`
+- Returns: `200 avif`
#### Adjust stock manually — `POST /stock/adjust`
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T15[] }`
diff --git a/src/components/brand/BrandThumb.jsx b/src/components/brand/BrandThumb.jsx
index 6a20225..1243dde 100644
--- a/src/components/brand/BrandThumb.jsx
+++ b/src/components/brand/BrandThumb.jsx
@@ -1,119 +1,82 @@
+import { PureComponent } from 'react';
import Box from '@mui/material/Box';
-import ThumbHashImage, { getThumbHashDimensions } from '../article/ThumbHashImage';
-import brandAutomat from '../../automats/brandAutomat';
+import ThumbHashImage from '../article/ThumbHashImage';
import {
SHADOW_THUMB_RING,
TINT_ROW_HOVER,
} from '../../colors';
-/**
- * Extracts thumbHash from various brand representations:
- * - Direct thumbHash / thumbhash property
- * - Brand with pics array (e.g. brand.pics[0].thumbHash)
- * - Brand by name or id looked up in brandAutomat.state.brands
- */
-export function extractBrandThumbInfo(brand, thumbHashOverride) {
- if (thumbHashOverride) {
- const dims = getThumbHashDimensions(thumbHashOverride);
- return {
- thumbHash: thumbHashOverride,
- thumbW: dims?.w ?? null,
- thumbH: dims?.h ?? null,
- };
- }
+export default class BrandThumb extends PureComponent {
+ render() {
+ const {
+ brand,
+ size = 20,
+ fallback = null,
+ sx,
+ } = this.props;
+ const pic = brand?.pics?.[0];
- if (!brand) return null;
+ if (!pic?.thumbHash) {
+ return fallback;
+ }
- // Direct thumbHash on brand object
- let hash = brand.thumbHash ?? brand.thumbhash;
- let thumbW = brand.thumbW ?? brand.thumbw;
- let thumbH = brand.thumbH ?? brand.thumbh;
-
- // Check pics array
- if (!hash && Array.isArray(brand.pics) && brand.pics.length > 0) {
- const firstPic = brand.pics[0];
- hash = firstPic?.thumbHash ?? firstPic?.thumbhash;
- thumbW = firstPic?.thumbW ?? firstPic?.thumbw;
- thumbH = firstPic?.thumbH ?? firstPic?.thumbh;
- }
-
- // Fallback: look up in brandAutomat
- if (!hash) {
- const brands = brandAutomat.state?.brands;
- if (Array.isArray(brands)) {
- const id = typeof brand === 'object' ? brand.id : (typeof brand === 'number' ? brand : null);
- const name = typeof brand === 'object' ? brand.name : (typeof brand === 'string' ? brand : null);
- const found = brands.find(
- (b) =>
- (id != null && b.id === id) ||
- (name && String(b.name || '').trim().toLowerCase() === String(name).trim().toLowerCase())
- );
- if (found) {
- hash = found.thumbHash ?? found.thumbhash ?? found.pics?.[0]?.thumbHash ?? found.pics?.[0]?.thumbhash;
- thumbW = found.thumbW ?? found.thumbw ?? found.pics?.[0]?.thumbW ?? found.pics?.[0]?.thumbw;
- thumbH = found.thumbH ?? found.thumbh ?? found.pics?.[0]?.thumbH ?? found.pics?.[0]?.thumbh;
+ const { thumbHash, thumbW, thumbH } = pic;
+ const ratio = thumbW && thumbH ? thumbW / thumbH : 1;
+ // Contain-fit inside a square `size x size` box, preserving aspect ratio.
+ let innerW = size;
+ let innerH = size;
+ if (Number.isFinite(ratio) && ratio > 0) {
+ if (ratio >= 1) {
+ innerW = size;
+ innerH = Math.max(1, Math.round(size / ratio));
+ } else {
+ innerH = size;
+ innerW = Math.max(1, Math.round(size * ratio));
}
}
+
+ return (
+
+ = 24 ? '4px' : '3px',
+ overflow: 'hidden',
+ flexShrink: 0,
+ lineHeight: 0,
+ boxSizing: 'border-box',
+ bgcolor: TINT_ROW_HOVER,
+ boxShadow: SHADOW_THUMB_RING,
+ }}
+ >
+
+
+
+ );
}
-
- if (!hash) return null;
-
- if (thumbW == null || thumbH == null) {
- const dims = getThumbHashDimensions(hash);
- if (dims) {
- thumbW = dims.w;
- thumbH = dims.h;
- }
- }
-
- return { thumbHash: hash, thumbW, thumbH };
-}
-
-export default function BrandThumb({
- brand,
- thumbHash: thumbHashProp,
- size = 20,
- fallback = null,
- sx,
-}) {
- const info = extractBrandThumbInfo(brand, thumbHashProp);
-
- if (!info?.thumbHash) {
- return fallback;
- }
-
- const { thumbHash, thumbW, thumbH } = info;
- const ratio = (thumbW && thumbH) ? (thumbW / thumbH) : 1;
- const calcWidth = Math.round(size * Math.max(0.6, Math.min(ratio, 2.5)));
-
- return (
- = 24 ? '4px' : '3px',
- overflow: 'hidden',
- flexShrink: 0,
- verticalAlign: 'middle',
- lineHeight: 0,
- my: 0,
- boxSizing: 'border-box',
- bgcolor: TINT_ROW_HOVER,
- boxShadow: SHADOW_THUMB_RING,
- ...sx,
- }}
- >
-
-
- );
}
diff --git a/src/components/brand/index.js b/src/components/brand/index.js
index d819fee..3739128 100644
--- a/src/components/brand/index.js
+++ b/src/components/brand/index.js
@@ -1,4 +1,4 @@
-export { default as BrandThumb, extractBrandThumbInfo } from './BrandThumb';
+export { default as BrandThumb } from './BrandThumb';
export { default as BrandLabel } from './BrandLabel';
export { default as BrandImageSearchDialog } from './BrandImageSearchDialog';
export { default as ArtikelMarke } from './ArtikelMarke';