u
This commit is contained in:
@@ -92,10 +92,6 @@ function scheduleRebuild() {
|
|||||||
/**
|
/**
|
||||||
* Normalizes a category-view article row into an index record.
|
* Normalizes a category-view article row into an index record.
|
||||||
*
|
*
|
||||||
* NOTE: `eans` is not part of the `GET /api/category/:id/articles` payload, so the
|
|
||||||
* field stays empty until that endpoint exposes EANs. EAN matching in
|
|
||||||
* `articleSearchAutomat` is therefore not functional yet.
|
|
||||||
*
|
|
||||||
* @param {object} article Raw category-view article row.
|
* @param {object} article Raw category-view article row.
|
||||||
* @param {number|string|null} categoryId Category the article was loaded from.
|
* @param {number|string|null} categoryId Category the article was loaded from.
|
||||||
* @returns {ArticleIndexRecord} Index record.
|
* @returns {ArticleIndexRecord} Index record.
|
||||||
@@ -126,11 +122,15 @@ function toIndexRecord(article, categoryId) {
|
|||||||
const smallThumbBinary = article.smallThumbBinary ?? firstPhoto?.smallThumbBinary ?? null;
|
const smallThumbBinary = article.smallThumbBinary ?? firstPhoto?.smallThumbBinary ?? null;
|
||||||
const photos = article.photos ?? (fullHash ? [{ fullHash, thumbHash }] : []);
|
const photos = article.photos ?? (fullHash ? [{ fullHash, thumbHash }] : []);
|
||||||
|
|
||||||
|
const descLength = Number(article.descLength ?? article.desclength ?? (article.desc ? String(article.desc).trim().length : 0)) || 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...article,
|
...article,
|
||||||
id: article.id,
|
id: article.id,
|
||||||
sku: article.sku,
|
sku: article.sku,
|
||||||
name: article.name,
|
name: article.name,
|
||||||
|
descLength,
|
||||||
|
seo: article.seo ?? null,
|
||||||
price,
|
price,
|
||||||
net: article.net ?? price,
|
net: article.net ?? price,
|
||||||
stock,
|
stock,
|
||||||
@@ -143,6 +143,8 @@ function toIndexRecord(article, categoryId) {
|
|||||||
categories,
|
categories,
|
||||||
isParent: Boolean(article.isParent ?? article.is_parent ?? (childCount > 0)),
|
isParent: Boolean(article.isParent ?? article.is_parent ?? (childCount > 0)),
|
||||||
childCount,
|
childCount,
|
||||||
|
parentArticleId: article.parentArticleId ?? article.parentarticle_id ?? null,
|
||||||
|
parentSku: article.parentSku ?? article.parent_sku ?? null,
|
||||||
fullHash,
|
fullHash,
|
||||||
thumbHash,
|
thumbHash,
|
||||||
photos,
|
photos,
|
||||||
@@ -200,6 +202,24 @@ export function buildArticleIndex(views) {
|
|||||||
existing.articlewarehousesummary = rec.articlewarehousesummary;
|
existing.articlewarehousesummary = rec.articlewarehousesummary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const incomingDescLen = Number(article.descLength ?? article.desclength ?? (article.desc ? String(article.desc).trim().length : 0)) || 0;
|
||||||
|
if ((existing.descLength == null || existing.descLength === 0) && incomingDescLen > 0) {
|
||||||
|
existing.descLength = incomingDescLen;
|
||||||
|
}
|
||||||
|
if ((!existing.seo || existing.seo.trim() === '') && article.seo) {
|
||||||
|
existing.seo = article.seo;
|
||||||
|
}
|
||||||
|
if (Array.isArray(article.eans) && article.eans.length > 0) {
|
||||||
|
const mergedEans = new Set(existing.eans ?? []);
|
||||||
|
for (const e of article.eans) if (e) mergedEans.add(e);
|
||||||
|
existing.eans = Array.from(mergedEans);
|
||||||
|
}
|
||||||
|
if (!existing.parentArticleId && (article.parentArticleId || article.parentarticle_id)) {
|
||||||
|
existing.parentArticleId = article.parentArticleId ?? article.parentarticle_id;
|
||||||
|
}
|
||||||
|
if (!existing.parentSku && (article.parentSku || article.parent_sku)) {
|
||||||
|
existing.parentSku = article.parentSku ?? article.parent_sku;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ const initialState = {
|
|||||||
filter: '',
|
filter: '',
|
||||||
brand: '',
|
brand: '',
|
||||||
supplier: '',
|
supplier: '',
|
||||||
|
empty: '',
|
||||||
|
hierarchy: '',
|
||||||
offset: 0,
|
offset: 0,
|
||||||
pageSize: null,
|
pageSize: null,
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -343,6 +345,8 @@ export function searchArticlesIndex(query, options = {}) {
|
|||||||
extraArticles = [],
|
extraArticles = [],
|
||||||
allowParent = false,
|
allowParent = false,
|
||||||
categories = [],
|
categories = [],
|
||||||
|
hierarchy = '',
|
||||||
|
empty = '',
|
||||||
offset = 0,
|
offset = 0,
|
||||||
pageSize = 10,
|
pageSize = 10,
|
||||||
limit,
|
limit,
|
||||||
@@ -367,7 +371,7 @@ export function searchArticlesIndex(query, options = {}) {
|
|||||||
const excludedSet = new Set((excludeSkus ?? []).map((s) => String(s).toLowerCase()));
|
const excludedSet = new Set((excludeSkus ?? []).map((s) => String(s).toLowerCase()));
|
||||||
|
|
||||||
let pool = rawMatches;
|
let pool = rawMatches;
|
||||||
if (!allowParent) {
|
if (!allowParent && !hierarchy) {
|
||||||
pool = pool.filter((a) => !a.isParent && !a.is_parent);
|
pool = pool.filter((a) => !a.isParent && !a.is_parent);
|
||||||
}
|
}
|
||||||
if (excludedSet.size > 0) {
|
if (excludedSet.size > 0) {
|
||||||
@@ -378,6 +382,58 @@ export function searchArticlesIndex(query, options = {}) {
|
|||||||
pool = pool.filter((a) => a.sku && includedSet.has(String(a.sku).toLowerCase()));
|
pool = pool.filter((a) => a.sku && includedSet.has(String(a.sku).toLowerCase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hierarchy === 'father') {
|
||||||
|
pool = pool.filter((a) => !a.parentArticleId && !a.parentSku);
|
||||||
|
} else if (hierarchy === 'child') {
|
||||||
|
pool = pool.filter((a) => !a.isParent && !a.is_parent && Number(a.childCount || 0) === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emptyFilters = parseFilter(empty);
|
||||||
|
if (emptyFilters.length > 0) {
|
||||||
|
const skusWithSupplier = emptyFilters.includes('supplier') ? getSkusWithSupplier() : null;
|
||||||
|
pool = pool.filter((row) => {
|
||||||
|
for (const f of emptyFilters) {
|
||||||
|
if (f === 'ean') {
|
||||||
|
const hasEan = (Array.isArray(row.eans) && row.eans.some((e) => e && String(e).trim())) ||
|
||||||
|
(row.ean && String(row.ean).trim());
|
||||||
|
if (hasEan) return false;
|
||||||
|
} else if (f === 'photo') {
|
||||||
|
const firstPhoto = Array.isArray(row.photos) && row.photos.length > 0 ? row.photos[0] : null;
|
||||||
|
const hasPhoto = Boolean(
|
||||||
|
row.fullHash ||
|
||||||
|
row.thumbHash ||
|
||||||
|
row.smallThumbBinary ||
|
||||||
|
firstPhoto?.fullHash ||
|
||||||
|
firstPhoto?.thumbHash ||
|
||||||
|
firstPhoto?.smallThumbBinary
|
||||||
|
);
|
||||||
|
if (hasPhoto) return false;
|
||||||
|
} else if (f === 'price') {
|
||||||
|
const p = row.price ?? row.net;
|
||||||
|
const hasPrice = p != null && Number(p) > 0;
|
||||||
|
if (hasPrice) return false;
|
||||||
|
} else if (f === 'brand') {
|
||||||
|
const hasBrand = Boolean(row.brand?.id || row.brand?.name || row.brandId || row.brandName);
|
||||||
|
if (hasBrand) return false;
|
||||||
|
} else if (f === 'supplier') {
|
||||||
|
const hasSupplier = Boolean(
|
||||||
|
(row.suppliers && row.suppliers.length > 0) ||
|
||||||
|
row.supplierId != null ||
|
||||||
|
(skusWithSupplier && skusWithSupplier.has(String(row.sku ?? '').toLowerCase()))
|
||||||
|
);
|
||||||
|
if (hasSupplier) return false;
|
||||||
|
} else if (f === 'seo') {
|
||||||
|
const hasSeo = Boolean(row.seo && String(row.seo).trim());
|
||||||
|
if (hasSeo) return false;
|
||||||
|
} else if (f === 'desc') {
|
||||||
|
const hasDesc = (Number(row.descLength) > 0) || Boolean((row.desc && String(row.desc).trim()) || (row.description && String(row.description).trim()));
|
||||||
|
if (hasDesc) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let matchesList = pool;
|
let matchesList = pool;
|
||||||
|
|
||||||
if (categories && categories.length > 0) {
|
if (categories && categories.length > 0) {
|
||||||
@@ -669,6 +725,20 @@ function buildSupplierFacets(matchedArticles) {
|
|||||||
|
|
||||||
return facets;
|
return facets;
|
||||||
}
|
}
|
||||||
|
function getSkusWithSupplier() {
|
||||||
|
const articlesBySupplier = companyAutomat.state?.articlesBySupplier ?? {};
|
||||||
|
const set = new Set();
|
||||||
|
for (const list of Object.values(articlesBySupplier)) {
|
||||||
|
if (Array.isArray(list)) {
|
||||||
|
for (const item of list) {
|
||||||
|
if (item.article?.sku) set.add(String(item.article.sku).toLowerCase());
|
||||||
|
if (item.sku) set.add(String(item.sku).toLowerCase());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
// #endregion
|
// #endregion
|
||||||
@@ -689,6 +759,8 @@ const actions = {
|
|||||||
const filter = params.filter !== undefined ? String(params.filter ?? '') : currentState.filter;
|
const filter = params.filter !== undefined ? String(params.filter ?? '') : currentState.filter;
|
||||||
const brand = params.brand !== undefined ? String(params.brand ?? '') : (currentState.brand ?? '');
|
const brand = params.brand !== undefined ? String(params.brand ?? '') : (currentState.brand ?? '');
|
||||||
const supplier = params.supplier !== undefined ? String(params.supplier ?? '') : (currentState.supplier ?? '');
|
const supplier = params.supplier !== undefined ? String(params.supplier ?? '') : (currentState.supplier ?? '');
|
||||||
|
const empty = params.empty !== undefined ? String(params.empty ?? '') : (currentState.empty ?? '');
|
||||||
|
const hierarchy = params.hierarchy !== undefined ? String(params.hierarchy ?? '') : (currentState.hierarchy ?? '');
|
||||||
const pageSize = params.pageSize !== undefined ? params.pageSize : currentState.pageSize;
|
const pageSize = params.pageSize !== undefined ? params.pageSize : currentState.pageSize;
|
||||||
const offset = params.offset !== undefined ? Math.max(0, Number(params.offset) || 0) : currentState.offset;
|
const offset = params.offset !== undefined ? Math.max(0, Number(params.offset) || 0) : currentState.offset;
|
||||||
|
|
||||||
@@ -752,6 +824,64 @@ const actions = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply hierarchy filter:
|
||||||
|
// - "father": "father or normal article" -> brand editable, exclude child variants
|
||||||
|
// - "child": "child or normal article" -> price editable, exclude parents
|
||||||
|
if (hierarchy === 'father') {
|
||||||
|
filteredArticles = filteredArticles.filter((row) => !row.parentArticleId && !row.parentSku);
|
||||||
|
} else if (hierarchy === 'child') {
|
||||||
|
filteredArticles = filteredArticles.filter(
|
||||||
|
(row) => !row.isParent && !row.is_parent && Number(row.childCount || 0) === 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply empty filters: articles missing ean, photo, price, brand, supplier, seo, desc
|
||||||
|
const emptyFilters = parseFilter(empty);
|
||||||
|
if (emptyFilters.length > 0) {
|
||||||
|
const skusWithSupplier = emptyFilters.includes('supplier') ? getSkusWithSupplier() : null;
|
||||||
|
filteredArticles = filteredArticles.filter((row) => {
|
||||||
|
for (const f of emptyFilters) {
|
||||||
|
if (f === 'ean') {
|
||||||
|
const hasEan = (Array.isArray(row.eans) && row.eans.some((e) => e && String(e).trim())) ||
|
||||||
|
(row.ean && String(row.ean).trim());
|
||||||
|
if (hasEan) return false;
|
||||||
|
} else if (f === 'photo') {
|
||||||
|
const firstPhoto = Array.isArray(row.photos) && row.photos.length > 0 ? row.photos[0] : null;
|
||||||
|
const hasPhoto = Boolean(
|
||||||
|
row.fullHash ||
|
||||||
|
row.thumbHash ||
|
||||||
|
row.smallThumbBinary ||
|
||||||
|
firstPhoto?.fullHash ||
|
||||||
|
firstPhoto?.thumbHash ||
|
||||||
|
firstPhoto?.smallThumbBinary
|
||||||
|
);
|
||||||
|
if (hasPhoto) return false;
|
||||||
|
} else if (f === 'price') {
|
||||||
|
const p = row.price ?? row.net;
|
||||||
|
const hasPrice = p != null && Number(p) > 0;
|
||||||
|
if (hasPrice) return false;
|
||||||
|
} else if (f === 'brand') {
|
||||||
|
const hasBrand = Boolean(row.brand?.id || row.brand?.name || row.brandId || row.brandName);
|
||||||
|
if (hasBrand) return false;
|
||||||
|
} else if (f === 'supplier') {
|
||||||
|
const hasSupplier = Boolean(
|
||||||
|
(row.suppliers && row.suppliers.length > 0) ||
|
||||||
|
row.supplierId != null ||
|
||||||
|
(skusWithSupplier && skusWithSupplier.has(String(row.sku ?? '').toLowerCase()))
|
||||||
|
);
|
||||||
|
if (hasSupplier) return false;
|
||||||
|
} else if (f === 'seo') {
|
||||||
|
const hasSeo = Boolean(row.seo && String(row.seo).trim());
|
||||||
|
if (hasSeo) return false;
|
||||||
|
} else if (f === 'desc') {
|
||||||
|
const hasDesc = (Number(row.descLength) > 0) || Boolean((row.desc && String(row.desc).trim()) || (row.description && String(row.description).trim()));
|
||||||
|
if (hasDesc) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const sortedArticles = sortArticlesByRelevance(filteredArticles, query);
|
const sortedArticles = sortArticlesByRelevance(filteredArticles, query);
|
||||||
|
|
||||||
const totalCount = sortedArticles.length;
|
const totalCount = sortedArticles.length;
|
||||||
@@ -781,6 +911,8 @@ const actions = {
|
|||||||
filter,
|
filter,
|
||||||
brand,
|
brand,
|
||||||
supplier,
|
supplier,
|
||||||
|
empty,
|
||||||
|
hierarchy,
|
||||||
offset,
|
offset,
|
||||||
pageSize: safePageSize,
|
pageSize: safePageSize,
|
||||||
loading: articleIndexAutomat.state.status === 'loading',
|
loading: articleIndexAutomat.state.status === 'loading',
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ export function queryFromParams(searchParams) {
|
|||||||
filter: searchParams.get('filter') ?? '',
|
filter: searchParams.get('filter') ?? '',
|
||||||
brand: searchParams.get('brand') ?? '',
|
brand: searchParams.get('brand') ?? '',
|
||||||
supplier: searchParams.get('supplier') ?? '',
|
supplier: searchParams.get('supplier') ?? '',
|
||||||
|
empty: searchParams.get('empty') ?? '',
|
||||||
|
hierarchy: searchParams.get('hierarchy') ?? '',
|
||||||
offset: Math.max(0, Number(searchParams.get('offset')) || 0),
|
offset: Math.max(0, Number(searchParams.get('offset')) || 0),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -13,12 +15,16 @@ export function queryFromParams(searchParams) {
|
|||||||
export function buildSearchUrl(target, filter = '', offset = 0, options = {}) {
|
export function buildSearchUrl(target, filter = '', offset = 0, options = {}) {
|
||||||
let brand = '';
|
let brand = '';
|
||||||
let supplier = '';
|
let supplier = '';
|
||||||
|
let empty = '';
|
||||||
|
let hierarchy = '';
|
||||||
if (typeof options === 'string') {
|
if (typeof options === 'string') {
|
||||||
brand = options;
|
brand = options;
|
||||||
supplier = arguments[4] || '';
|
supplier = arguments[4] || '';
|
||||||
} else if (options && typeof options === 'object') {
|
} else if (options && typeof options === 'object') {
|
||||||
brand = options.brand || '';
|
brand = options.brand || '';
|
||||||
supplier = options.supplier || '';
|
supplier = options.supplier || '';
|
||||||
|
empty = options.empty || '';
|
||||||
|
hierarchy = options.hierarchy || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
@@ -26,6 +32,8 @@ export function buildSearchUrl(target, filter = '', offset = 0, options = {}) {
|
|||||||
if (filter) params.set('filter', filter);
|
if (filter) params.set('filter', filter);
|
||||||
if (brand) params.set('brand', brand);
|
if (brand) params.set('brand', brand);
|
||||||
if (supplier) params.set('supplier', supplier);
|
if (supplier) params.set('supplier', supplier);
|
||||||
|
if (empty) params.set('empty', empty);
|
||||||
|
if (hierarchy) params.set('hierarchy', hierarchy);
|
||||||
if (offset > 0) params.set('offset', String(offset));
|
if (offset > 0) params.set('offset', String(offset));
|
||||||
const qs = params.toString();
|
const qs = params.toString();
|
||||||
return qs ? `/artikel?${qs}` : '/artikel';
|
return qs ? `/artikel?${qs}` : '/artikel';
|
||||||
@@ -46,6 +54,8 @@ export function writeArtikelQuery({
|
|||||||
offset = 0,
|
offset = 0,
|
||||||
brand = '',
|
brand = '',
|
||||||
supplier = '',
|
supplier = '',
|
||||||
|
empty = '',
|
||||||
|
hierarchy = '',
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
if (!target || target.trim().length < 3) {
|
if (!target || target.trim().length < 3) {
|
||||||
@@ -54,7 +64,7 @@ export function writeArtikelQuery({
|
|||||||
}
|
}
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
ARTIKEL_QUERY_KEY,
|
ARTIKEL_QUERY_KEY,
|
||||||
JSON.stringify({ target, filter, offset, brand, supplier }),
|
JSON.stringify({ target, filter, offset, brand, supplier, empty, hierarchy }),
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
// ignore quota / private mode
|
// ignore quota / private mode
|
||||||
@@ -76,5 +86,7 @@ export function artikelQueryPath() {
|
|||||||
return buildSearchUrl(query.target, query.filter ?? '', query.offset ?? 0, {
|
return buildSearchUrl(query.target, query.filter ?? '', query.offset ?? 0, {
|
||||||
brand: query.brand ?? '',
|
brand: query.brand ?? '',
|
||||||
supplier: query.supplier ?? '',
|
supplier: query.supplier ?? '',
|
||||||
|
empty: query.empty ?? '',
|
||||||
|
hierarchy: query.hierarchy ?? '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,19 @@ import List from '@mui/material/List';
|
|||||||
import ListItemButton from '@mui/material/ListItemButton';
|
import ListItemButton from '@mui/material/ListItemButton';
|
||||||
import ListItemText from '@mui/material/ListItemText';
|
import ListItemText from '@mui/material/ListItemText';
|
||||||
import Paper from '@mui/material/Paper';
|
import Paper from '@mui/material/Paper';
|
||||||
|
import Popover from '@mui/material/Popover';
|
||||||
|
import Badge from '@mui/material/Badge';
|
||||||
|
import Checkbox from '@mui/material/Checkbox';
|
||||||
|
import Radio from '@mui/material/Radio';
|
||||||
|
import RadioGroup from '@mui/material/RadioGroup';
|
||||||
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
|
import Divider from '@mui/material/Divider';
|
||||||
import TextField from '@mui/material/TextField';
|
import TextField from '@mui/material/TextField';
|
||||||
import Typography from '@mui/material/Typography';
|
import Typography from '@mui/material/Typography';
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
import ClearIcon from '@mui/icons-material/Clear';
|
import ClearIcon from '@mui/icons-material/Clear';
|
||||||
import FilterListIcon from '@mui/icons-material/FilterList';
|
import FilterListIcon from '@mui/icons-material/FilterList';
|
||||||
|
import FilterAltIcon from '@mui/icons-material/FilterAlt';
|
||||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
|
||||||
@@ -265,6 +273,28 @@ class ResultRow extends Component {
|
|||||||
}
|
}
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
|
// #region Constants
|
||||||
|
const EMPTY_FILTER_OPTIONS = [
|
||||||
|
{ key: 'ean', label: 'EAN' },
|
||||||
|
{ key: 'photo', label: 'Foto' },
|
||||||
|
{ key: 'price', label: 'Preis' },
|
||||||
|
{ key: 'brand', label: 'Marke' },
|
||||||
|
{ key: 'supplier', label: 'Lieferant' },
|
||||||
|
{ key: 'seo', label: 'SEO' },
|
||||||
|
{ key: 'desc', label: 'Beschreibung' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const EMPTY_FILTER_LABELS = {
|
||||||
|
ean: 'Ohne EAN',
|
||||||
|
photo: 'Ohne Foto',
|
||||||
|
price: 'Ohne Preis',
|
||||||
|
brand: 'Ohne Marke',
|
||||||
|
supplier: 'Ohne Lieferant',
|
||||||
|
seo: 'Ohne SEO',
|
||||||
|
desc: 'Ohne Beschreibung',
|
||||||
|
};
|
||||||
|
// #endregion
|
||||||
|
|
||||||
// #region Helpers
|
// #region Helpers
|
||||||
function initialSearchState(searchParams) {
|
function initialSearchState(searchParams) {
|
||||||
const query = queryFromParams(searchParams);
|
const query = queryFromParams(searchParams);
|
||||||
@@ -284,6 +314,7 @@ function initialSearchState(searchParams) {
|
|||||||
categoryFilterOpen: false,
|
categoryFilterOpen: false,
|
||||||
brandFilterOpen: false,
|
brandFilterOpen: false,
|
||||||
supplierFilterOpen: false,
|
supplierFilterOpen: false,
|
||||||
|
moreFilterAnchorEl: null,
|
||||||
newArticleOpen: false,
|
newArticleOpen: false,
|
||||||
error: automatResult.error,
|
error: automatResult.error,
|
||||||
categoryFacets: automatResult.categoryFacets,
|
categoryFacets: automatResult.categoryFacets,
|
||||||
@@ -357,6 +388,73 @@ class ArtikelSearchPage extends Component {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleOpenMoreFilters = (event) => {
|
||||||
|
this.setState({ moreFilterAnchorEl: event.currentTarget });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCloseMoreFilters = () => {
|
||||||
|
this.setState({ moreFilterAnchorEl: null });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleToggleEmptyFilter = (emptyKey) => {
|
||||||
|
const { input } = this.state;
|
||||||
|
const filterParam = this.props.searchParams.get('filter') ?? '';
|
||||||
|
const brandParam = this.props.searchParams.get('brand') ?? '';
|
||||||
|
const supplierParam = this.props.searchParams.get('supplier') ?? '';
|
||||||
|
const emptyParam = this.props.searchParams.get('empty') ?? '';
|
||||||
|
const hierarchyParam = this.props.searchParams.get('hierarchy') ?? '';
|
||||||
|
const selectedEmpty = parseFilter(emptyParam);
|
||||||
|
const nextEmpty = selectedEmpty.includes(emptyKey)
|
||||||
|
? selectedEmpty.filter((k) => k !== emptyKey)
|
||||||
|
: [...selectedEmpty, emptyKey];
|
||||||
|
|
||||||
|
this.updateParams(
|
||||||
|
input,
|
||||||
|
filterParam,
|
||||||
|
0,
|
||||||
|
brandParam,
|
||||||
|
supplierParam,
|
||||||
|
buildFilter(nextEmpty),
|
||||||
|
hierarchyParam,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleHierarchyChange = (nextHierarchy) => {
|
||||||
|
const { input } = this.state;
|
||||||
|
const filterParam = this.props.searchParams.get('filter') ?? '';
|
||||||
|
const brandParam = this.props.searchParams.get('brand') ?? '';
|
||||||
|
const supplierParam = this.props.searchParams.get('supplier') ?? '';
|
||||||
|
const emptyParam = this.props.searchParams.get('empty') ?? '';
|
||||||
|
const hierarchy = nextHierarchy === 'all' ? '' : nextHierarchy;
|
||||||
|
|
||||||
|
this.updateParams(
|
||||||
|
input,
|
||||||
|
filterParam,
|
||||||
|
0,
|
||||||
|
brandParam,
|
||||||
|
supplierParam,
|
||||||
|
emptyParam,
|
||||||
|
hierarchy,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClearAllMoreFilters = () => {
|
||||||
|
const { input } = this.state;
|
||||||
|
const filterParam = this.props.searchParams.get('filter') ?? '';
|
||||||
|
const brandParam = this.props.searchParams.get('brand') ?? '';
|
||||||
|
const supplierParam = this.props.searchParams.get('supplier') ?? '';
|
||||||
|
|
||||||
|
this.updateParams(
|
||||||
|
input,
|
||||||
|
filterParam,
|
||||||
|
0,
|
||||||
|
brandParam,
|
||||||
|
supplierParam,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
handleOpenNewArticle = () => {
|
handleOpenNewArticle = () => {
|
||||||
this.setState({ newArticleOpen: true });
|
this.setState({ newArticleOpen: true });
|
||||||
};
|
};
|
||||||
@@ -380,14 +478,14 @@ class ArtikelSearchPage extends Component {
|
|||||||
parentId,
|
parentId,
|
||||||
name: list[0] ?? '',
|
name: list[0] ?? '',
|
||||||
});
|
});
|
||||||
void articleIndexAutomat.actions.reload().catch(() => {});
|
void articleIndexAutomat.actions.reload().catch(() => { });
|
||||||
if (created) {
|
if (created) {
|
||||||
this.props.navigate(artikelDetailPath(created));
|
this.props.navigate(artikelDetailPath(created));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const createdList = await categoryAutomat.actions.createArticles({ parentId, names: list });
|
const createdList = await categoryAutomat.actions.createArticles({ parentId, names: list });
|
||||||
void articleIndexAutomat.actions.reload().catch(() => {});
|
void articleIndexAutomat.actions.reload().catch(() => { });
|
||||||
if (createdList?.[0]) {
|
if (createdList?.[0]) {
|
||||||
this.props.navigate(artikelDetailPath(createdList[0]));
|
this.props.navigate(artikelDetailPath(createdList[0]));
|
||||||
}
|
}
|
||||||
@@ -406,6 +504,8 @@ class ArtikelSearchPage extends Component {
|
|||||||
filter: query.filter,
|
filter: query.filter,
|
||||||
brand: query.brand,
|
brand: query.brand,
|
||||||
supplier: query.supplier,
|
supplier: query.supplier,
|
||||||
|
empty: query.empty,
|
||||||
|
hierarchy: query.hierarchy,
|
||||||
offset: query.offset,
|
offset: query.offset,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -429,6 +529,8 @@ class ArtikelSearchPage extends Component {
|
|||||||
offset,
|
offset,
|
||||||
brand: urlBrand,
|
brand: urlBrand,
|
||||||
supplier: urlSupplier,
|
supplier: urlSupplier,
|
||||||
|
empty: urlEmpty,
|
||||||
|
hierarchy: urlHierarchy,
|
||||||
} = queryFromParams(this.props.searchParams);
|
} = queryFromParams(this.props.searchParams);
|
||||||
const hasFilterInUrl = this.props.searchParams.has('filter');
|
const hasFilterInUrl = this.props.searchParams.has('filter');
|
||||||
const navCategory = navigationAutomat.state.lastCategoryId;
|
const navCategory = navigationAutomat.state.lastCategoryId;
|
||||||
@@ -439,6 +541,8 @@ class ArtikelSearchPage extends Component {
|
|||||||
const syncedUrl = buildSearchUrl(activeTarget, filter, offset, {
|
const syncedUrl = buildSearchUrl(activeTarget, filter, offset, {
|
||||||
brand: urlBrand,
|
brand: urlBrand,
|
||||||
supplier: urlSupplier,
|
supplier: urlSupplier,
|
||||||
|
empty: urlEmpty,
|
||||||
|
hierarchy: urlHierarchy,
|
||||||
});
|
});
|
||||||
setLastSearchTerm(activeTarget, syncedUrl);
|
setLastSearchTerm(activeTarget, syncedUrl);
|
||||||
this.props.navigate(syncedUrl, { replace: true });
|
this.props.navigate(syncedUrl, { replace: true });
|
||||||
@@ -453,6 +557,8 @@ class ArtikelSearchPage extends Component {
|
|||||||
buildSearchUrl(activeTarget, filter, offset, {
|
buildSearchUrl(activeTarget, filter, offset, {
|
||||||
brand: urlBrand,
|
brand: urlBrand,
|
||||||
supplier: urlSupplier,
|
supplier: urlSupplier,
|
||||||
|
empty: urlEmpty,
|
||||||
|
hierarchy: urlHierarchy,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
void articleSearchAutomat.actions.search({
|
void articleSearchAutomat.actions.search({
|
||||||
@@ -460,8 +566,10 @@ class ArtikelSearchPage extends Component {
|
|||||||
filter,
|
filter,
|
||||||
brand: urlBrand,
|
brand: urlBrand,
|
||||||
supplier: urlSupplier,
|
supplier: urlSupplier,
|
||||||
|
empty: urlEmpty,
|
||||||
|
hierarchy: urlHierarchy,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}).catch(() => {});
|
}).catch(() => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
@@ -478,6 +586,8 @@ class ArtikelSearchPage extends Component {
|
|||||||
buildSearchUrl(target, query.filter, query.offset, {
|
buildSearchUrl(target, query.filter, query.offset, {
|
||||||
brand: query.brand,
|
brand: query.brand,
|
||||||
supplier: query.supplier,
|
supplier: query.supplier,
|
||||||
|
empty: query.empty,
|
||||||
|
hierarchy: query.hierarchy,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -498,6 +608,8 @@ class ArtikelSearchPage extends Component {
|
|||||||
searchParams.get('filter') !== prev.get('filter')
|
searchParams.get('filter') !== prev.get('filter')
|
||||||
|| searchParams.get('brand') !== prev.get('brand')
|
|| searchParams.get('brand') !== prev.get('brand')
|
||||||
|| searchParams.get('supplier') !== prev.get('supplier')
|
|| searchParams.get('supplier') !== prev.get('supplier')
|
||||||
|
|| searchParams.get('empty') !== prev.get('empty')
|
||||||
|
|| searchParams.get('hierarchy') !== prev.get('hierarchy')
|
||||||
|| searchParams.get('offset') !== prev.get('offset')
|
|| searchParams.get('offset') !== prev.get('offset')
|
||||||
) {
|
) {
|
||||||
this.fetchResults();
|
this.fetchResults();
|
||||||
@@ -512,17 +624,23 @@ class ArtikelSearchPage extends Component {
|
|||||||
nextOffset = 0,
|
nextOffset = 0,
|
||||||
nextBrand = this.props.searchParams.get('brand') ?? '',
|
nextBrand = this.props.searchParams.get('brand') ?? '',
|
||||||
nextSupplier = this.props.searchParams.get('supplier') ?? '',
|
nextSupplier = this.props.searchParams.get('supplier') ?? '',
|
||||||
|
nextEmpty = this.props.searchParams.get('empty') ?? '',
|
||||||
|
nextHierarchy = this.props.searchParams.get('hierarchy') ?? '',
|
||||||
) => {
|
) => {
|
||||||
writeArtikelQuery({
|
writeArtikelQuery({
|
||||||
target: nextTarget,
|
target: nextTarget,
|
||||||
filter: nextFilter,
|
filter: nextFilter,
|
||||||
brand: nextBrand,
|
brand: nextBrand,
|
||||||
supplier: nextSupplier,
|
supplier: nextSupplier,
|
||||||
|
empty: nextEmpty,
|
||||||
|
hierarchy: nextHierarchy,
|
||||||
offset: nextOffset,
|
offset: nextOffset,
|
||||||
});
|
});
|
||||||
const nextUrl = buildSearchUrl(nextTarget, nextFilter, nextOffset, {
|
const nextUrl = buildSearchUrl(nextTarget, nextFilter, nextOffset, {
|
||||||
brand: nextBrand,
|
brand: nextBrand,
|
||||||
supplier: nextSupplier,
|
supplier: nextSupplier,
|
||||||
|
empty: nextEmpty,
|
||||||
|
hierarchy: nextHierarchy,
|
||||||
});
|
});
|
||||||
setLastSearchTerm(nextTarget, nextUrl);
|
setLastSearchTerm(nextTarget, nextUrl);
|
||||||
|
|
||||||
@@ -547,18 +665,20 @@ class ArtikelSearchPage extends Component {
|
|||||||
handleInputChange = (event) => {
|
handleInputChange = (event) => {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
this.setState({ input: value });
|
this.setState({ input: value });
|
||||||
const { filter, brand, supplier } = queryFromParams(this.props.searchParams);
|
const { filter, brand, supplier, empty, hierarchy } = queryFromParams(this.props.searchParams);
|
||||||
const currentUrl = buildSearchUrl(value, filter, 0, { brand, supplier });
|
const currentUrl = buildSearchUrl(value, filter, 0, { brand, supplier, empty, hierarchy });
|
||||||
setLastSearchTerm(value, currentUrl);
|
setLastSearchTerm(value, currentUrl);
|
||||||
writeArtikelQuery({ target: value, filter, brand, supplier, offset: 0 });
|
writeArtikelQuery({ target: value, filter, brand, supplier, empty, hierarchy, offset: 0 });
|
||||||
this.props.navigate(currentUrl, { replace: true });
|
this.props.navigate(currentUrl, { replace: true });
|
||||||
void articleSearchAutomat.actions.search({
|
void articleSearchAutomat.actions.search({
|
||||||
target: value,
|
target: value,
|
||||||
filter,
|
filter,
|
||||||
brand,
|
brand,
|
||||||
supplier,
|
supplier,
|
||||||
|
empty,
|
||||||
|
hierarchy,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
}).catch(() => {});
|
}).catch(() => { });
|
||||||
};
|
};
|
||||||
|
|
||||||
handleClearInput = () => {
|
handleClearInput = () => {
|
||||||
@@ -566,17 +686,19 @@ class ArtikelSearchPage extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fetchResults = () => {
|
fetchResults = () => {
|
||||||
const { target, filter, brand, supplier, offset } = queryFromParams(this.props.searchParams);
|
const { target, filter, brand, supplier, empty, hierarchy, offset } = queryFromParams(this.props.searchParams);
|
||||||
|
|
||||||
writeArtikelQuery({ target, filter, brand, supplier, offset });
|
writeArtikelQuery({ target, filter, brand, supplier, empty, hierarchy, offset });
|
||||||
|
|
||||||
void articleSearchAutomat.actions.search({
|
void articleSearchAutomat.actions.search({
|
||||||
target: target.trim(),
|
target: target.trim(),
|
||||||
filter,
|
filter,
|
||||||
brand,
|
brand,
|
||||||
supplier,
|
supplier,
|
||||||
|
empty,
|
||||||
|
hierarchy,
|
||||||
offset,
|
offset,
|
||||||
}).catch(() => {});
|
}).catch(() => { });
|
||||||
};
|
};
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
@@ -587,6 +709,7 @@ class ArtikelSearchPage extends Component {
|
|||||||
categoryFilterOpen,
|
categoryFilterOpen,
|
||||||
brandFilterOpen,
|
brandFilterOpen,
|
||||||
supplierFilterOpen,
|
supplierFilterOpen,
|
||||||
|
moreFilterAnchorEl,
|
||||||
newArticleOpen,
|
newArticleOpen,
|
||||||
error,
|
error,
|
||||||
totalCount,
|
totalCount,
|
||||||
@@ -598,10 +721,15 @@ class ArtikelSearchPage extends Component {
|
|||||||
const filterParam = this.props.searchParams.get('filter') ?? '';
|
const filterParam = this.props.searchParams.get('filter') ?? '';
|
||||||
const brandParam = this.props.searchParams.get('brand') ?? '';
|
const brandParam = this.props.searchParams.get('brand') ?? '';
|
||||||
const supplierParam = this.props.searchParams.get('supplier') ?? '';
|
const supplierParam = this.props.searchParams.get('supplier') ?? '';
|
||||||
|
const emptyParam = this.props.searchParams.get('empty') ?? '';
|
||||||
|
const hierarchyParam = this.props.searchParams.get('hierarchy') ?? '';
|
||||||
|
|
||||||
const categories = parseFilter(filterParam);
|
const categories = parseFilter(filterParam);
|
||||||
const selectedBrands = parseFilter(brandParam);
|
const selectedBrands = parseFilter(brandParam);
|
||||||
const selectedSuppliers = parseFilter(supplierParam);
|
const selectedSuppliers = parseFilter(supplierParam);
|
||||||
|
const selectedEmpty = parseFilter(emptyParam);
|
||||||
|
const activeMoreFilterCount = selectedEmpty.length + (hierarchyParam ? 1 : 0);
|
||||||
|
const isAnyMoreFilterActive = activeMoreFilterCount > 0;
|
||||||
const rows = recordset ?? [];
|
const rows = recordset ?? [];
|
||||||
|
|
||||||
const categoriesMap = categoryAutomat.state?.categories;
|
const categoriesMap = categoryAutomat.state?.categories;
|
||||||
@@ -639,16 +767,16 @@ class ArtikelSearchPage extends Component {
|
|||||||
|
|
||||||
const brandFacetEntries = brandFacets
|
const brandFacetEntries = brandFacets
|
||||||
? Object.values(brandFacets).sort((a, b) => {
|
? Object.values(brandFacets).sort((a, b) => {
|
||||||
if (b.count !== a.count) return b.count - a.count;
|
if (b.count !== a.count) return b.count - a.count;
|
||||||
return String(a.name).localeCompare(String(b.name), 'de');
|
return String(a.name).localeCompare(String(b.name), 'de');
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const supplierFacetEntries = supplierFacets
|
const supplierFacetEntries = supplierFacets
|
||||||
? Object.values(supplierFacets).sort((a, b) => {
|
? Object.values(supplierFacets).sort((a, b) => {
|
||||||
if (b.count !== a.count) return b.count - a.count;
|
if (b.count !== a.count) return b.count - a.count;
|
||||||
return String(a.name).localeCompare(String(b.name), 'de');
|
return String(a.name).localeCompare(String(b.name), 'de');
|
||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
// Per-render values the static `renderItem` row reads through the context.
|
// Per-render values the static `renderItem` row reads through the context.
|
||||||
@@ -736,6 +864,22 @@ class ArtikelSearchPage extends Component {
|
|||||||
>
|
>
|
||||||
Lieferanten
|
Lieferanten
|
||||||
</Button>
|
</Button>
|
||||||
|
<IconButton
|
||||||
|
onClick={this.handleOpenMoreFilters}
|
||||||
|
color={isAnyMoreFilterActive ? 'primary' : 'default'}
|
||||||
|
sx={{
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
border: '1px solid',
|
||||||
|
borderColor: isAnyMoreFilterActive ? 'primary.main' : 'rgba(0, 0, 0, 0.23)',
|
||||||
|
borderRadius: 1,
|
||||||
|
flexShrink: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Badge badgeContent={activeMoreFilterCount} color="primary">
|
||||||
|
<FilterAltIcon fontSize="small" />
|
||||||
|
</Badge>
|
||||||
|
</IconButton>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
onClick={this.handleOpenNewArticle}
|
onClick={this.handleOpenNewArticle}
|
||||||
@@ -765,6 +909,8 @@ class ArtikelSearchPage extends Component {
|
|||||||
0,
|
0,
|
||||||
buildFilter(selectedBrands),
|
buildFilter(selectedBrands),
|
||||||
buildFilter(selectedSuppliers),
|
buildFilter(selectedSuppliers),
|
||||||
|
emptyParam,
|
||||||
|
hierarchyParam,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
||||||
@@ -791,6 +937,8 @@ class ArtikelSearchPage extends Component {
|
|||||||
0,
|
0,
|
||||||
buildFilter(next),
|
buildFilter(next),
|
||||||
buildFilter(selectedSuppliers),
|
buildFilter(selectedSuppliers),
|
||||||
|
emptyParam,
|
||||||
|
hierarchyParam,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
||||||
@@ -817,12 +965,78 @@ class ArtikelSearchPage extends Component {
|
|||||||
0,
|
0,
|
||||||
buildFilter(selectedBrands),
|
buildFilter(selectedBrands),
|
||||||
buildFilter(next),
|
buildFilter(next),
|
||||||
|
emptyParam,
|
||||||
|
hierarchyParam,
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{hierarchyParam === 'father' && (
|
||||||
|
<Chip
|
||||||
|
key="hierarchy-father"
|
||||||
|
label="Typ: Vater / Normal"
|
||||||
|
color="primary"
|
||||||
|
variant="outlined"
|
||||||
|
onDelete={() => {
|
||||||
|
this.updateParams(
|
||||||
|
input,
|
||||||
|
buildFilter(categories),
|
||||||
|
0,
|
||||||
|
buildFilter(selectedBrands),
|
||||||
|
buildFilter(selectedSuppliers),
|
||||||
|
emptyParam,
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hierarchyParam === 'child' && (
|
||||||
|
<Chip
|
||||||
|
key="hierarchy-child"
|
||||||
|
label="Typ: Kind / Normal"
|
||||||
|
color="primary"
|
||||||
|
variant="outlined"
|
||||||
|
onDelete={() => {
|
||||||
|
this.updateParams(
|
||||||
|
input,
|
||||||
|
buildFilter(categories),
|
||||||
|
0,
|
||||||
|
buildFilter(selectedBrands),
|
||||||
|
buildFilter(selectedSuppliers),
|
||||||
|
emptyParam,
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{selectedEmpty.map((emptyKey) => (
|
||||||
|
<Chip
|
||||||
|
key={`empty-${emptyKey}`}
|
||||||
|
label={EMPTY_FILTER_LABELS[emptyKey] || emptyKey}
|
||||||
|
color="primary"
|
||||||
|
variant="outlined"
|
||||||
|
onDelete={() => {
|
||||||
|
const next = selectedEmpty.filter((k) => k !== emptyKey);
|
||||||
|
this.updateParams(
|
||||||
|
input,
|
||||||
|
buildFilter(categories),
|
||||||
|
0,
|
||||||
|
buildFilter(selectedBrands),
|
||||||
|
buildFilter(selectedSuppliers),
|
||||||
|
buildFilter(next),
|
||||||
|
hierarchyParam,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
sx={{ height: 40, borderRadius: 1.5, fontWeight: 500, flexShrink: 0 }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
@@ -1100,6 +1314,91 @@ class ArtikelSearchPage extends Component {
|
|||||||
onClose={this.handleCloseNewArticle}
|
onClose={this.handleCloseNewArticle}
|
||||||
onSubmit={this.handleCreateArticle}
|
onSubmit={this.handleCreateArticle}
|
||||||
/>
|
/>
|
||||||
|
<Popover
|
||||||
|
open={Boolean(moreFilterAnchorEl)}
|
||||||
|
anchorEl={moreFilterAnchorEl}
|
||||||
|
onClose={this.handleCloseMoreFilters}
|
||||||
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
||||||
|
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
||||||
|
slotProps={{
|
||||||
|
paper: {
|
||||||
|
sx: {
|
||||||
|
width: 320,
|
||||||
|
p: 2,
|
||||||
|
borderRadius: 2,
|
||||||
|
boxShadow: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1.5 }}>
|
||||||
|
{isAnyMoreFilterActive && (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
color="secondary"
|
||||||
|
onClick={this.handleClearAllMoreFilters}
|
||||||
|
sx={{ py: 0.25, px: 1, minWidth: 0, textTransform: 'none', fontSize: 13 }}
|
||||||
|
>
|
||||||
|
Zurücksetzen
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
value={hierarchyParam || 'all'}
|
||||||
|
onChange={(e) => this.handleHierarchyChange(e.target.value)}
|
||||||
|
sx={{ mb: 1.5, mt: 0.5 }}
|
||||||
|
>
|
||||||
|
<FormControlLabel
|
||||||
|
value="all"
|
||||||
|
control={<Radio size="small" />}
|
||||||
|
label={<Typography variant="body2">Alle Artikel</Typography>}
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
value="father"
|
||||||
|
control={<Radio size="small" />}
|
||||||
|
label={
|
||||||
|
<Box>
|
||||||
|
<Typography variant="body2">Vater- oder Normalartikel</Typography>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
value="child"
|
||||||
|
control={<Radio size="small" />}
|
||||||
|
label={
|
||||||
|
<Box>
|
||||||
|
<Typography variant="body2">Kind- oder Normalartikel</Typography>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
<Divider sx={{ my: 1.5 }} />
|
||||||
|
|
||||||
|
<Typography variant="caption" sx={{ fontWeight: 600, color: 'text.secondary', textTransform: 'uppercase', letterSpacing: 0.5 }}>
|
||||||
|
Artikel ohne (leer):
|
||||||
|
</Typography>
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', mt: 0.5 }}>
|
||||||
|
{EMPTY_FILTER_OPTIONS.map(({ key, label }) => {
|
||||||
|
const checked = selectedEmpty.includes(key);
|
||||||
|
return (
|
||||||
|
<FormControlLabel
|
||||||
|
key={key}
|
||||||
|
control={
|
||||||
|
<Checkbox
|
||||||
|
size="small"
|
||||||
|
checked={checked}
|
||||||
|
onChange={() => this.handleToggleEmptyFilter(key)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={<Typography variant="body2">{label}</Typography>}
|
||||||
|
sx={{ my: -0.25 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
</Popover>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user