diff --git a/src/features/orders/components/SearchBar.tsx b/src/features/orders/components/SearchBar.tsx index 940b2f3..ed5832b 100644 --- a/src/features/orders/components/SearchBar.tsx +++ b/src/features/orders/components/SearchBar.tsx @@ -12,9 +12,13 @@ import { Autocomplete, Paper, Tooltip, - Collapse, CircularProgress, Badge, + Drawer, + IconButton, + Typography, + Divider, + Stack, type AutocompleteRenderInputParams, } from '@mui/material'; import { useStores } from '../store/useStores'; @@ -28,8 +32,8 @@ import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment'; import { Search as SearchIcon, RestartAlt as ResetIcon, - ExpandLess as ExpandLessIcon, - ExpandMore as ExpandMoreIcon, + Tune as TuneIcon, + Close as CloseIcon, } from '@mui/icons-material'; import moment from 'moment'; import 'moment/locale/pt-br'; @@ -186,8 +190,10 @@ export const SearchBar = () => { touchedFields.createDateIni && !localFilters.createDateIni; const showDateEndError = touchedFields.createDateEnd && dateError; - // Contador de filtros avançados ativos - const advancedFiltersCount = [ + // Contador de filtros ativos no Drawer + const activeDrawerFiltersCount = [ + localFilters.status, + localFilters.customerId, localFilters.store?.length, localFilters.stockId?.length, localFilters.sellerId, @@ -195,10 +201,21 @@ export const SearchBar = () => { localFilters.productId, ].filter(Boolean).length; + const toggleDrawer = (open: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => { + if ( + event.type === 'keydown' && + ((event as React.KeyboardEvent).key === 'Tab' || + (event as React.KeyboardEvent).key === 'Shift') + ) { + return; + } + setShowAdvancedFilters(open); + }; + return ( { elevation={0} onKeyDown={handleKeyDown} > - - {/* --- Primary Filters (Always Visible) --- */} - - {/* Campo de Texto Simples (Nº Pedido) */} + + {/* Nº do Pedido */} { /> - {/* Select do MUI (Situação) */} - - { - const value = e.target.value || null; - updateLocalFilter('status', value); - }} - > - Todos - Pendente - Faturado - Cancelado - - - - {/* Autocomplete do MUI para Cliente */} - - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={ - customers.options.find( - (option) => localFilters.customerId === option.customer?.id - ) || null - } - onChange={(_, newValue) => { - if (!newValue) { - updateLocalFilter('customerName', null); - updateLocalFilter('customerId', null); - setCustomerSearchTerm(''); - return; - } - - updateLocalFilter('customerId', newValue.customer?.id || null); - updateLocalFilter( - 'customerName', - newValue.customer?.name || null - ); - }} - onInputChange={(_, newInputValue, reason) => { - if (reason === 'clear') { - updateLocalFilter('customerName', null); - updateLocalFilter('customerId', null); - setCustomerSearchTerm(''); - return; - } - - if (reason === 'input') { - setCustomerSearchTerm(newInputValue); - if (!newInputValue) { - updateLocalFilter('customerName', null); - updateLocalFilter('customerId', null); - setCustomerSearchTerm(''); - } - } - }} - loading={customers.isLoading} - renderInput={(params: AutocompleteRenderInputParams) => ( - + + + { + setTouchedFields(prev => ({ ...prev, createDateIni: true })); + updateLocalFilter('createDateIni', date ? date.format('YYYY-MM-DD') : null); + }} + format="DD/MM/YYYY" + slotProps={{ + textField: { + size: 'small', + fullWidth: true, + required: true, + error: showDateIniError, + helperText: showDateIniError ? 'Obrigatório' : '', + } + }} /> - )} - noOptionsText={ - customerSearchTerm.length < 2 - ? 'Digite pelo menos 2 caracteres' - : 'Nenhum cliente encontrado' - } - loadingText="Buscando clientes..." - filterOptions={(x) => x} - clearOnBlur={false} - selectOnFocus - handleHomeEndKeys - /> - - - {/* Campos de Data */} - - - - - { + setTouchedFields(prev => ({ ...prev, createDateEnd: true })); + updateLocalFilter('createDateEnd', date ? date.format('YYYY-MM-DD') : null); + }} + format="DD/MM/YYYY" + slotProps={{ + textField: { + size: 'small', + fullWidth: true, + error: !!showDateEndError, + helperText: showDateEndError || '', } - onChange={(date: moment.Moment | null) => { - setTouchedFields((prev) => ({ - ...prev, - createDateIni: true, - })); - updateLocalFilter( - 'createDateIni', - date ? date.format('YYYY-MM-DD') : null - ); - }} - format="DD/MM/YYYY" - maxDate={ - localFilters.createDateEnd - ? moment(localFilters.createDateEnd, 'YYYY-MM-DD') - : undefined - } - slotProps={{ - textField: { - size: 'small', - fullWidth: true, - required: true, - error: showDateIniError, - helperText: showDateIniError - ? 'Data inicial é obrigatória' - : '', - onBlur: () => - setTouchedFields((prev) => ({ - ...prev, - createDateIni: true, - })), - inputProps: { - 'aria-required': true, - }, - }, - }} - /> - - - { - setTouchedFields((prev) => ({ - ...prev, - createDateEnd: true, - })); - updateLocalFilter( - 'createDateEnd', - date ? date.format('YYYY-MM-DD') : null - ); - }} - format="DD/MM/YYYY" - minDate={ - localFilters.createDateIni - ? moment(localFilters.createDateIni, 'YYYY-MM-DD') - : undefined - } - slotProps={{ - textField: { - size: 'small', - fullWidth: true, - error: !!showDateEndError, - helperText: showDateEndError || '', - onBlur: () => - setTouchedFields((prev) => ({ - ...prev, - createDateEnd: true, - })), - inputProps: { - placeholder: 'Opcional', - }, - }, - }} - /> - + }} + /> - {/* Botão Mais Filtros - inline com filtros primários */} - - + + + - - {/* Botões de Ação - nova linha abaixo */} - - - - - - - - - - - - - - - - {/* --- Advanced Filters (Collapsible) --- */} - - - - {/* Autocomplete do MUI para Múltiplas Filiais (codfilial) */} - - option.label} - isOptionEqualToValue={(option, value) => - option.id === value.id - } - value={stores.options.filter((option) => - localFilters.store?.includes(option.value) - )} - onChange={(_, newValue) => { - updateLocalFilter( - 'store', - newValue.map((option) => option.value) - ); - }} - loading={stores.isLoading} - renderInput={(params: AutocompleteRenderInputParams) => ( - - )} - /> - - - {/* Autocomplete do MUI para Filial de Estoque */} - - option.label} - isOptionEqualToValue={(option, value) => - option.id === value.id - } - value={stores.options.filter((option) => - localFilters.stockId?.includes(option.value) - )} - onChange={(_, newValue) => { - updateLocalFilter( - 'stockId', - newValue.map((option) => option.value) - ); - }} - loading={stores.isLoading} - renderInput={(params: AutocompleteRenderInputParams) => ( - - )} - /> - - - {/* Autocomplete do MUI para Vendedor */} - - option.label} - isOptionEqualToValue={(option, value) => - option.id === value.id - } - value={ - sellers.options.find( - (option) => - localFilters.sellerId === option.seller.id.toString() - ) || null - } - onChange={(_, newValue) => { - updateLocalFilter( - 'sellerId', - newValue?.seller.id.toString() || null - ); - updateLocalFilter( - 'sellerName', - newValue?.seller.name || null - ); - }} - loading={sellers.isLoading} - renderInput={(params) => ( - - )} - renderOption={(props, option) => { - const { key, ...otherProps } = props; - return ( -
  • - {option.label} -
  • - ); - }} - /> -
    - - {/* Autocomplete do MUI para Parceiro */} - - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={ - partners.options.find( - (option) => - localFilters.partnerId === - option.partner?.id?.toString() - ) || null - } - onChange={(_, newValue) => { - if (!newValue) { - updateLocalFilter('partnerName', null); - updateLocalFilter('partnerId', null); - setPartnerSearchTerm(''); - return; - } - - updateLocalFilter( - 'partnerId', - newValue.partner?.id?.toString() || null - ); - updateLocalFilter( - 'partnerName', - newValue.partner?.nome || null - ); - }} - onInputChange={(_, newInputValue, reason) => { - if (reason === 'clear') { - updateLocalFilter('partnerName', null); - updateLocalFilter('partnerId', null); - setPartnerSearchTerm(''); - return; - } - - if (reason === 'input') { - setPartnerSearchTerm(newInputValue); - if (!newInputValue) { - updateLocalFilter('partnerName', null); - updateLocalFilter('partnerId', null); - setPartnerSearchTerm(''); - } - } - }} - loading={partners.isLoading} - renderInput={(params: AutocompleteRenderInputParams) => ( - - )} - noOptionsText={ - partnerSearchTerm.length < 2 - ? 'Digite pelo menos 2 caracteres' - : 'Nenhum parceiro encontrado' - } - loadingText="Buscando parceiros..." - filterOptions={(x) => x} - clearOnBlur={false} - selectOnFocus - handleHomeEndKeys - /> - - - {/* Autocomplete do MUI para Produto */} - - option.label} - isOptionEqualToValue={(option, value) => option.id === value.id} - value={ - products.options.find( - (option) => localFilters.productId === option.product?.id - ) || null - } - onChange={(_, newValue) => { - if (!newValue) { - updateLocalFilter('productName', null); - updateLocalFilter('productId', null); - setProductSearchTerm(''); - return; - } - - updateLocalFilter('productId', newValue.product?.id || null); - updateLocalFilter( - 'productName', - newValue.product?.description || null - ); - }} - onInputChange={(_, newInputValue, reason) => { - if (reason === 'clear') { - updateLocalFilter('productName', null); - updateLocalFilter('productId', null); - setProductSearchTerm(''); - return; - } - - if (reason === 'input') { - setProductSearchTerm(newInputValue); - if (!newInputValue) { - updateLocalFilter('productName', null); - updateLocalFilter('productId', null); - setProductSearchTerm(''); - } - } - }} - loading={products.isLoading} - renderInput={(params: AutocompleteRenderInputParams) => ( - - )} - noOptionsText={ - productSearchTerm.length < 2 - ? 'Digite pelo menos 2 caracteres' - : 'Nenhum produto encontrado' - } - loadingText="Buscando produtos..." - filterOptions={(x) => x} - clearOnBlur={false} - selectOnFocus - handleHomeEndKeys - /> - -
    -
    +
    + + {/* Sidebar de Filtros */} + + + {/* Header */} + + Todos os Filtros + + + + + + {/* Filtros Content */} + + + {/* Situação */} + updateLocalFilter('status', e.target.value || null)} + > + Todos + Pendente + Faturado + Cancelado + + + {/* Cliente */} + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={customers.options.find(opt => localFilters.customerId === opt.customer?.id) || null} + onChange={(_, newValue) => { + updateLocalFilter('customerId', newValue?.customer?.id || null); + updateLocalFilter('customerName', newValue?.customer?.name || null); + }} + onInputChange={(_, val) => setCustomerSearchTerm(val)} + loading={customers.isLoading} + renderInput={(params) => } + noOptionsText={customerSearchTerm.length < 2 ? 'Digite 2+ caracteres' : 'Nenhum cliente'} + filterOptions={(x) => x} + /> + + {/* Filiais */} + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={stores.options.filter((opt) => localFilters.store?.includes(opt.value))} + onChange={(_, newValue) => updateLocalFilter('store', newValue.map((opt) => opt.value))} + loading={stores.isLoading} + renderInput={(params) => } + /> + + {/* Filial de Estoque */} + option.label} + isOptionEqualToValue={(option, value) => option.id === value.id} + value={stores.options.filter((opt) => localFilters.stockId?.includes(opt.value))} + onChange={(_, newValue) => updateLocalFilter('stockId', newValue.map((opt) => opt.value))} + loading={stores.isLoading} + renderInput={(params) => } + /> + + {/* Vendedor */} + option.label} + value={sellers.options.find(opt => localFilters.sellerId === opt.seller.id.toString()) || null} + onChange={(_, newValue) => { + updateLocalFilter('sellerId', newValue?.seller.id.toString() || null); + updateLocalFilter('sellerName', newValue?.seller.name || null); + }} + loading={sellers.isLoading} + renderInput={(params) => } + /> + + {/* Parceiro */} + option.label} + value={partners.options.find(opt => localFilters.partnerId === opt.partner?.id?.toString()) || null} + onChange={(_, newValue) => { + updateLocalFilter('partnerId', newValue?.partner?.id?.toString() || null); + updateLocalFilter('partnerName', newValue?.partner?.nome || null); + }} + onInputChange={(_, val) => setPartnerSearchTerm(val)} + loading={partners.isLoading} + renderInput={(params) => } + noOptionsText={partnerSearchTerm.length < 2 ? 'Digite 2+ caracteres' : 'Nenhum parceiro'} + filterOptions={(x) => x} + /> + + {/* Produto */} + option.label} + value={products.options.find(opt => localFilters.productId === opt.product?.id) || null} + onChange={(_, newValue) => { + updateLocalFilter('productId', newValue?.product?.id || null); + updateLocalFilter('productName', newValue?.product?.description || null); + }} + onInputChange={(_, val) => setProductSearchTerm(val)} + loading={products.isLoading} + renderInput={(params) => } + noOptionsText={productSearchTerm.length < 2 ? 'Digite 2+ caracteres' : 'Nenhum produto'} + filterOptions={(x) => x} + /> + + + + + {/* Footer */} + + + + + +
    ); };