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';