diff --git a/src/automats/articleIndexAutomat.js b/src/automats/articleIndexAutomat.js index 8ff9b4d..5feab7d 100644 --- a/src/automats/articleIndexAutomat.js +++ b/src/automats/articleIndexAutomat.js @@ -92,10 +92,6 @@ function scheduleRebuild() { /** * 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 {number|string|null} categoryId Category the article was loaded from. * @returns {ArticleIndexRecord} Index record. @@ -126,11 +122,15 @@ function toIndexRecord(article, categoryId) { const smallThumbBinary = article.smallThumbBinary ?? firstPhoto?.smallThumbBinary ?? null; const photos = article.photos ?? (fullHash ? [{ fullHash, thumbHash }] : []); + const descLength = Number(article.descLength ?? article.desclength ?? (article.desc ? String(article.desc).trim().length : 0)) || 0; + return { ...article, id: article.id, sku: article.sku, name: article.name, + descLength, + seo: article.seo ?? null, price, net: article.net ?? price, stock, @@ -143,6 +143,8 @@ function toIndexRecord(article, categoryId) { categories, isParent: Boolean(article.isParent ?? article.is_parent ?? (childCount > 0)), childCount, + parentArticleId: article.parentArticleId ?? article.parentarticle_id ?? null, + parentSku: article.parentSku ?? article.parent_sku ?? null, fullHash, thumbHash, photos, @@ -200,6 +202,24 @@ export function buildArticleIndex(views) { 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; + } } } diff --git a/src/automats/articleSearchAutomat.js b/src/automats/articleSearchAutomat.js index c8131ba..5df0a9b 100644 --- a/src/automats/articleSearchAutomat.js +++ b/src/automats/articleSearchAutomat.js @@ -67,6 +67,8 @@ const initialState = { filter: '', brand: '', supplier: '', + empty: '', + hierarchy: '', offset: 0, pageSize: null, loading: false, @@ -343,6 +345,8 @@ export function searchArticlesIndex(query, options = {}) { extraArticles = [], allowParent = false, categories = [], + hierarchy = '', + empty = '', offset = 0, pageSize = 10, limit, @@ -367,7 +371,7 @@ export function searchArticlesIndex(query, options = {}) { const excludedSet = new Set((excludeSkus ?? []).map((s) => String(s).toLowerCase())); let pool = rawMatches; - if (!allowParent) { + if (!allowParent && !hierarchy) { pool = pool.filter((a) => !a.isParent && !a.is_parent); } 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())); } + 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; if (categories && categories.length > 0) { @@ -669,6 +725,20 @@ function buildSupplierFacets(matchedArticles) { 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 @@ -689,6 +759,8 @@ const actions = { const filter = params.filter !== undefined ? String(params.filter ?? '') : currentState.filter; const brand = params.brand !== undefined ? String(params.brand ?? '') : (currentState.brand ?? ''); 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 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 totalCount = sortedArticles.length; @@ -781,6 +911,8 @@ const actions = { filter, brand, supplier, + empty, + hierarchy, offset, pageSize: safePageSize, loading: articleIndexAutomat.state.status === 'loading', diff --git a/src/components/article/artikelQuery.js b/src/components/article/artikelQuery.js index 62232db..ee27397 100644 --- a/src/components/article/artikelQuery.js +++ b/src/components/article/artikelQuery.js @@ -6,6 +6,8 @@ export function queryFromParams(searchParams) { filter: searchParams.get('filter') ?? '', brand: searchParams.get('brand') ?? '', supplier: searchParams.get('supplier') ?? '', + empty: searchParams.get('empty') ?? '', + hierarchy: searchParams.get('hierarchy') ?? '', 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 = {}) { let brand = ''; let supplier = ''; + let empty = ''; + let hierarchy = ''; if (typeof options === 'string') { brand = options; supplier = arguments[4] || ''; } else if (options && typeof options === 'object') { brand = options.brand || ''; supplier = options.supplier || ''; + empty = options.empty || ''; + hierarchy = options.hierarchy || ''; } const params = new URLSearchParams(); @@ -26,6 +32,8 @@ export function buildSearchUrl(target, filter = '', offset = 0, options = {}) { if (filter) params.set('filter', filter); if (brand) params.set('brand', brand); 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)); const qs = params.toString(); return qs ? `/artikel?${qs}` : '/artikel'; @@ -46,6 +54,8 @@ export function writeArtikelQuery({ offset = 0, brand = '', supplier = '', + empty = '', + hierarchy = '', }) { try { if (!target || target.trim().length < 3) { @@ -54,7 +64,7 @@ export function writeArtikelQuery({ } localStorage.setItem( ARTIKEL_QUERY_KEY, - JSON.stringify({ target, filter, offset, brand, supplier }), + JSON.stringify({ target, filter, offset, brand, supplier, empty, hierarchy }), ); } catch { // ignore quota / private mode @@ -76,5 +86,7 @@ export function artikelQueryPath() { return buildSearchUrl(query.target, query.filter ?? '', query.offset ?? 0, { brand: query.brand ?? '', supplier: query.supplier ?? '', + empty: query.empty ?? '', + hierarchy: query.hierarchy ?? '', }); } diff --git a/src/routes/artikel.jsx b/src/routes/artikel.jsx index 20986bf..3db5d25 100644 --- a/src/routes/artikel.jsx +++ b/src/routes/artikel.jsx @@ -11,11 +11,19 @@ import List from '@mui/material/List'; import ListItemButton from '@mui/material/ListItemButton'; import ListItemText from '@mui/material/ListItemText'; 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 Typography from '@mui/material/Typography'; import SearchIcon from '@mui/icons-material/Search'; import ClearIcon from '@mui/icons-material/Clear'; import FilterListIcon from '@mui/icons-material/FilterList'; +import FilterAltIcon from '@mui/icons-material/FilterAlt'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; @@ -265,6 +273,28 @@ class ResultRow extends Component { } // #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 function initialSearchState(searchParams) { const query = queryFromParams(searchParams); @@ -284,6 +314,7 @@ function initialSearchState(searchParams) { categoryFilterOpen: false, brandFilterOpen: false, supplierFilterOpen: false, + moreFilterAnchorEl: null, newArticleOpen: false, error: automatResult.error, 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 = () => { this.setState({ newArticleOpen: true }); }; @@ -380,14 +478,14 @@ class ArtikelSearchPage extends Component { parentId, name: list[0] ?? '', }); - void articleIndexAutomat.actions.reload().catch(() => {}); + void articleIndexAutomat.actions.reload().catch(() => { }); if (created) { this.props.navigate(artikelDetailPath(created)); } return; } const createdList = await categoryAutomat.actions.createArticles({ parentId, names: list }); - void articleIndexAutomat.actions.reload().catch(() => {}); + void articleIndexAutomat.actions.reload().catch(() => { }); if (createdList?.[0]) { this.props.navigate(artikelDetailPath(createdList[0])); } @@ -406,6 +504,8 @@ class ArtikelSearchPage extends Component { filter: query.filter, brand: query.brand, supplier: query.supplier, + empty: query.empty, + hierarchy: query.hierarchy, offset: query.offset, }); }; @@ -429,6 +529,8 @@ class ArtikelSearchPage extends Component { offset, brand: urlBrand, supplier: urlSupplier, + empty: urlEmpty, + hierarchy: urlHierarchy, } = queryFromParams(this.props.searchParams); const hasFilterInUrl = this.props.searchParams.has('filter'); const navCategory = navigationAutomat.state.lastCategoryId; @@ -439,6 +541,8 @@ class ArtikelSearchPage extends Component { const syncedUrl = buildSearchUrl(activeTarget, filter, offset, { brand: urlBrand, supplier: urlSupplier, + empty: urlEmpty, + hierarchy: urlHierarchy, }); setLastSearchTerm(activeTarget, syncedUrl); this.props.navigate(syncedUrl, { replace: true }); @@ -453,6 +557,8 @@ class ArtikelSearchPage extends Component { buildSearchUrl(activeTarget, filter, offset, { brand: urlBrand, supplier: urlSupplier, + empty: urlEmpty, + hierarchy: urlHierarchy, }), ); void articleSearchAutomat.actions.search({ @@ -460,8 +566,10 @@ class ArtikelSearchPage extends Component { filter, brand: urlBrand, supplier: urlSupplier, + empty: urlEmpty, + hierarchy: urlHierarchy, offset: 0, - }).catch(() => {}); + }).catch(() => { }); } componentWillUnmount() { @@ -478,6 +586,8 @@ class ArtikelSearchPage extends Component { buildSearchUrl(target, query.filter, query.offset, { brand: query.brand, 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('brand') !== prev.get('brand') || searchParams.get('supplier') !== prev.get('supplier') + || searchParams.get('empty') !== prev.get('empty') + || searchParams.get('hierarchy') !== prev.get('hierarchy') || searchParams.get('offset') !== prev.get('offset') ) { this.fetchResults(); @@ -512,17 +624,23 @@ class ArtikelSearchPage extends Component { nextOffset = 0, nextBrand = this.props.searchParams.get('brand') ?? '', nextSupplier = this.props.searchParams.get('supplier') ?? '', + nextEmpty = this.props.searchParams.get('empty') ?? '', + nextHierarchy = this.props.searchParams.get('hierarchy') ?? '', ) => { writeArtikelQuery({ target: nextTarget, filter: nextFilter, brand: nextBrand, supplier: nextSupplier, + empty: nextEmpty, + hierarchy: nextHierarchy, offset: nextOffset, }); const nextUrl = buildSearchUrl(nextTarget, nextFilter, nextOffset, { brand: nextBrand, supplier: nextSupplier, + empty: nextEmpty, + hierarchy: nextHierarchy, }); setLastSearchTerm(nextTarget, nextUrl); @@ -547,18 +665,20 @@ class ArtikelSearchPage extends Component { handleInputChange = (event) => { const value = event.target.value; this.setState({ input: value }); - const { filter, brand, supplier } = queryFromParams(this.props.searchParams); - const currentUrl = buildSearchUrl(value, filter, 0, { brand, supplier }); + const { filter, brand, supplier, empty, hierarchy } = queryFromParams(this.props.searchParams); + const currentUrl = buildSearchUrl(value, filter, 0, { brand, supplier, empty, hierarchy }); 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 }); void articleSearchAutomat.actions.search({ target: value, filter, brand, supplier, + empty, + hierarchy, offset: 0, - }).catch(() => {}); + }).catch(() => { }); }; handleClearInput = () => { @@ -566,17 +686,19 @@ class ArtikelSearchPage extends Component { }; 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({ target: target.trim(), filter, brand, supplier, + empty, + hierarchy, offset, - }).catch(() => {}); + }).catch(() => { }); }; // #endregion @@ -587,6 +709,7 @@ class ArtikelSearchPage extends Component { categoryFilterOpen, brandFilterOpen, supplierFilterOpen, + moreFilterAnchorEl, newArticleOpen, error, totalCount, @@ -598,10 +721,15 @@ class ArtikelSearchPage extends Component { 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 categories = parseFilter(filterParam); const selectedBrands = parseFilter(brandParam); const selectedSuppliers = parseFilter(supplierParam); + const selectedEmpty = parseFilter(emptyParam); + const activeMoreFilterCount = selectedEmpty.length + (hierarchyParam ? 1 : 0); + const isAnyMoreFilterActive = activeMoreFilterCount > 0; const rows = recordset ?? []; const categoriesMap = categoryAutomat.state?.categories; @@ -639,16 +767,16 @@ class ArtikelSearchPage extends Component { const brandFacetEntries = brandFacets ? Object.values(brandFacets).sort((a, b) => { - if (b.count !== a.count) return b.count - a.count; - return String(a.name).localeCompare(String(b.name), 'de'); - }) + if (b.count !== a.count) return b.count - a.count; + return String(a.name).localeCompare(String(b.name), 'de'); + }) : []; const supplierFacetEntries = supplierFacets ? Object.values(supplierFacets).sort((a, b) => { - if (b.count !== a.count) return b.count - a.count; - return String(a.name).localeCompare(String(b.name), 'de'); - }) + if (b.count !== a.count) return b.count - a.count; + return String(a.name).localeCompare(String(b.name), 'de'); + }) : []; // Per-render values the static `renderItem` row reads through the context. @@ -736,6 +864,22 @@ class ArtikelSearchPage extends Component { > Lieferanten + + + + + + )} + + + this.handleHierarchyChange(e.target.value)} + sx={{ mb: 1.5, mt: 0.5 }} + > + } + label={Alle Artikel} + /> + } + label={ + + Vater- oder Normalartikel + + } + /> + } + label={ + + Kind- oder Normalartikel + + } + /> + + + + + + Artikel ohne (leer): + + + {EMPTY_FILTER_OPTIONS.map(({ key, label }) => { + const checked = selectedEmpty.includes(key); + return ( + this.handleToggleEmptyFilter(key)} + /> + } + label={{label}} + sx={{ my: -0.25 }} + /> + ); + })} + + ); }