Enhance article photo handling and API documentation.

- Updated API documentation to reflect changes in article data structure.
- Improved ArticlePhoto component to support dynamic sizing and object fit options.
- Added performance logging for image upload in ArticlePhotosDialog and ArticlePhotoTile components.
- Enhanced ArticleTile to conditionally render photos and adjusted styling for better layout.
- Optimized image preparation process with configurable AVIF encoding speed.
This commit is contained in:
seb
2026-08-01 04:32:49 +02:00
parent 701487f525
commit 09fb1da335
10 changed files with 368 additions and 36 deletions

View File

@@ -1,10 +1,18 @@
import Inventory2OutlinedIcon from '@mui/icons-material/Inventory2Outlined';
import Box from '@mui/material/Box';
import ButtonBase from '@mui/material/ButtonBase';
import Typography from '@mui/material/Typography';
import { Tile } from './Tile';
import { Tile, tileBaseSx } from './Tile';
import { formatPrice } from '../../utils/price';
import { articleSku } from '../../pages/artikel/articleRoute';
import ArticlePhoto from '../article/ArticlePhoto';
export class ArticleTile extends Tile {
hasPhoto() {
const { article } = this.props;
return article?.thumbHash != null || article?.fullHash != null;
}
renderIcon() {
return <Inventory2OutlinedIcon color="action" sx={{ fontSize: 36 }} />;
}
@@ -28,4 +36,70 @@ export class ArticleTile extends Tile {
</>
);
}
render() {
if (!this.hasPhoto()) {
return super.render();
}
const { article, label, sx, disabled } = this.props;
const sku = articleSku(article);
return (
<ButtonBase
component="button"
type="button"
disabled={disabled}
onClick={this.handleClick}
sx={{
...tileBaseSx,
p: 0,
gap: 0,
position: 'relative',
...(disabled ? { opacity: 0.5, pointerEvents: 'none' } : {}),
...sx,
}}
>
<Box sx={{ position: 'absolute', inset: 0 }}>
<ArticlePhoto
fill
photo={{
fullHash: article.fullHash,
thumbW: article.thumbW,
thumbH: article.thumbH,
thumbHash: article.thumbHash,
}}
/>
</Box>
<Box
sx={{
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
px: 1,
py: 0.75,
background: 'linear-gradient(transparent, rgba(0,0,0,0.65))',
textAlign: 'left',
}}
>
{label ? (
<Typography
variant="body2"
fontWeight={600}
noWrap
sx={{ color: '#fff', lineHeight: 1.2 }}
>
{label}
</Typography>
) : null}
{sku ? (
<Typography variant="caption" noWrap sx={{ color: 'rgba(255,255,255,0.85)' }}>
{sku}
</Typography>
) : null}
</Box>
</ButtonBase>
);
}
}

View File

@@ -4,6 +4,9 @@ import Typography from '@mui/material/Typography';
export const TILE_SIZE = 140;
/** Matches panel tile rounding (theme.shape.borderRadius × 4 → typically 16px). */
export const TILE_BORDER_RADIUS = 4;
export const tileBaseSx = {
aspectRatio: '1 / 1',
width: '100%',
@@ -16,10 +19,15 @@ export const tileBaseSx = {
cursor: 'pointer',
userSelect: 'none',
textAlign: 'center',
transition: 'border-color 0.15s, background-color 0.15s',
border: 'none',
borderRadius: TILE_BORDER_RADIUS,
overflow: 'hidden',
bgcolor: 'background.paper',
boxShadow: 3,
transition: 'box-shadow 0.15s, background-color 0.15s',
'&:hover': {
bgcolor: 'action.hover',
borderColor: 'primary.main',
boxShadow: 6,
},
};
@@ -62,11 +70,6 @@ export class Tile extends Component {
onClick={this.handleClick}
sx={{
...tileBaseSx,
border: '1px solid',
borderColor: 'divider',
bgcolor: 'background.paper',
borderRadius: 1,
overflow: 'hidden',
...(disabled
? { opacity: 0.5, pointerEvents: 'none' }
: {}),