This commit is contained in:
seb
2026-10-18 16:10:18 +02:00
parent 5644678b7d
commit cf85ec102d
3 changed files with 114 additions and 0 deletions

View File

@@ -17,6 +17,7 @@ import DeleteOutlineOutlinedIcon from '@mui/icons-material/DeleteOutlineOutlined
import EditOutlinedIcon from '@mui/icons-material/EditOutlined'; import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import GoogleIcon from '@mui/icons-material/Google';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import AppDialog from './AppDialog'; import AppDialog from './AppDialog';
import { thumb, article as articleApi } from '../api'; import { thumb, article as articleApi } from '../api';
@@ -29,6 +30,7 @@ import {
articleWithPhotoOrder, articleWithPhotoOrder,
articleWithUploadedPhoto, articleWithUploadedPhoto,
articleWithoutPhoto, articleWithoutPhoto,
buildGoogleSearchQuery,
formatPhotoDimensions, formatPhotoDimensions,
formatPhotoSizeKb, formatPhotoSizeKb,
sortedPhotos, sortedPhotos,
@@ -182,6 +184,22 @@ function resolveTitle(props, single) {
} }
return single ? 'Bild' : 'Bilder'; return single ? 'Bild' : 'Bilder';
} }
function resolveGoogleImagesQuery(props) {
if (props.article) {
return buildGoogleSearchQuery(props.article);
}
if (props.brand?.name) {
return String(props.brand.name).trim();
}
if (props.category?.name) {
return String(props.category.name).trim();
}
if (props.attributeValue) {
return String(props.attributeValue.value || props.attributeValue.name || '').trim();
}
return '';
}
// #endregion // #endregion
/** /**
@@ -400,6 +418,13 @@ export default class PicturesDialog extends PureComponent {
handleSearchClose = () => { handleSearchClose = () => {
this.setState({ searchOpen: false }); this.setState({ searchOpen: false });
}; };
handleOpenGoogleImages = () => {
const query = resolveGoogleImagesQuery(this.props);
if (!query) return;
const url = `https://www.google.com/search?tbm=isch&q=${encodeURIComponent(query)}`;
window.open(url, '_blank', 'noopener,noreferrer');
};
// #endregion // #endregion
// #region render helpers // #region render helpers
@@ -627,6 +652,7 @@ export default class PicturesDialog extends PureComponent {
const pics = resolvePictures(this.props, single); const pics = resolvePictures(this.props, single);
const title = resolveTitle(this.props, single); const title = resolveTitle(this.props, single);
const hasPic = Boolean(pics[0]?.fullHash || pics[0]?.thumbHash); const hasPic = Boolean(pics[0]?.fullHash || pics[0]?.thumbHash);
const googleQuery = resolveGoogleImagesQuery(this.props);
return ( return (
<> <>
@@ -781,6 +807,16 @@ export default class PicturesDialog extends PureComponent {
> >
Bild suchen Bild suchen
</Button> </Button>
<Button
size="small"
variant="outlined"
startIcon={<GoogleIcon />}
disabled={busy || disabled || !googleQuery}
onClick={this.handleOpenGoogleImages}
title={googleQuery ? `Google Bilder-Suche: ${googleQuery}` : 'Google'}
>
Google
</Button>
</Box> </Box>
<Button <Button
size="small" size="small"

View File

@@ -5,14 +5,24 @@ import Stack from '@mui/material/Stack';
import Tooltip from '@mui/material/Tooltip'; import Tooltip from '@mui/material/Tooltip';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
import CallMergeIcon from '@mui/icons-material/CallMerge'; import CallMergeIcon from '@mui/icons-material/CallMerge';
import GoogleIcon from '@mui/icons-material/Google';
import { enqueueSnackbar } from 'notistack'; import { enqueueSnackbar } from 'notistack';
import { article as articleApi } from '../../../api'; import { article as articleApi } from '../../../api';
import { artikelAutomat, getArticle } from '../../../automats'; import { artikelAutomat, getArticle } from '../../../automats';
import { notifyError } from '../../../utils/notify'; import { notifyError } from '../../../utils/notify';
import {
articleEans,
getArticleEan,
getArticleBrand,
getArticleName,
buildGoogleSearchQuery,
} from '../../../utils/articleDetail';
import ArticleSearch from '../ArticleSearch'; import ArticleSearch from '../ArticleSearch';
import ArticleUnifyDialog from '../ArticleUnifyDialog'; import ArticleUnifyDialog from '../ArticleUnifyDialog';
export { getArticleEan, getArticleBrand, getArticleName, buildGoogleSearchQuery };
export function isArticleFather(article) { export function isArticleFather(article) {
if (!article) return false; if (!article) return false;
return Boolean( return Boolean(
@@ -48,6 +58,15 @@ export default class ArticleSettingsTab extends Component {
this.handleDeleteArticle = this.handleDeleteArticle.bind(this); this.handleDeleteArticle = this.handleDeleteArticle.bind(this);
this.handleOpenSearch = this.handleOpenSearch.bind(this); this.handleOpenSearch = this.handleOpenSearch.bind(this);
this.handleSelectTargetArticle = this.handleSelectTargetArticle.bind(this); this.handleSelectTargetArticle = this.handleSelectTargetArticle.bind(this);
this.handleOpenGoogle = this.handleOpenGoogle.bind(this);
}
handleOpenGoogle() {
const { article: articleData } = this.props;
const query = buildGoogleSearchQuery(articleData);
if (!query) return;
const url = `https://www.google.com/search?q=${encodeURIComponent(query)}`;
window.open(url, '_blank', 'noopener,noreferrer');
} }
async handleDeleteArticle() { async handleDeleteArticle() {
@@ -128,6 +147,7 @@ export default class ArticleSettingsTab extends Component {
: isStueckliste : isStueckliste
? 'Ein Stücklisten-Artikel kann nicht vereint werden.' ? 'Ein Stücklisten-Artikel kann nicht vereint werden.'
: null; : null;
const googleQuery = buildGoogleSearchQuery(articleData);
return ( return (
<Box> <Box>
@@ -169,6 +189,18 @@ export default class ArticleSettingsTab extends Component {
Artikel vereinen Artikel vereinen
</Button> </Button>
)} )}
<Button
type="button"
color="primary"
variant="outlined"
startIcon={<GoogleIcon />}
disabled={busy || !googleQuery}
onClick={this.handleOpenGoogle}
title={googleQuery ? `Google-Suche: ${googleQuery}` : 'Google'}
>
Google
</Button>
</Stack> </Stack>
<ArticleSearch <ArticleSearch

View File

@@ -6,6 +6,7 @@ import {
netFromGross, netFromGross,
parsePriceInput, parsePriceInput,
} from './price'; } from './price';
import brandAutomat from '../automats/brandAutomat';
export function sortedPhotos(article) { export function sortedPhotos(article) {
if (!Array.isArray(article?.photos)) return []; if (!Array.isArray(article?.photos)) return [];
@@ -150,6 +151,51 @@ export function articleEans(article) {
return article.eans.filter(Boolean); return article.eans.filter(Boolean);
} }
export function getArticleEan(article) {
if (!article) return '';
const eans = articleEans(article);
if (eans.length > 0) {
const first = eans[0];
return String(typeof first === 'object' && first ? (first.ean ?? '') : first ?? '').trim();
}
if (article.ean) {
return String(typeof article.ean === 'object' && article.ean ? (article.ean.ean ?? '') : article.ean).trim();
}
return '';
}
export function getArticleBrand(article) {
if (!article) return '';
if (typeof article.brand === 'object' && article.brand?.name) {
return String(article.brand.name).trim();
}
if (typeof article.brand === 'string' && article.brand.trim()) {
return article.brand.trim();
}
if (article.brandName) {
return String(article.brandName).trim();
}
if (article.brandId != null) {
const brands = brandAutomat?.state?.brands || [];
const b = brands.find((x) => x.id === article.brandId);
if (b?.name) return String(b.name).trim();
}
return '';
}
export function getArticleName(article) {
if (!article) return '';
return String(article.name ?? article.sku ?? '').trim();
}
export function buildGoogleSearchQuery(article) {
if (!article) return '';
const ean = getArticleEan(article);
const brand = getArticleBrand(article);
const name = getArticleName(article);
return [ean, brand, name].filter(Boolean).join(' ');
}
export function formatPhotoSizeKb(fullSize) { export function formatPhotoSizeKb(fullSize) {
return `${Math.round(fullSize / 1024)} KB`; return `${Math.round(fullSize / 1024)} KB`;
} }