From aeb801c0bae0e5a9cefdc463fe979471aaec7ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Wed, 24 Jun 2026 11:18:27 -0300 Subject: [PATCH] Allow searching sellers in client filters --- backend/services/analyticsService.js | 8 ++++--- backend/test/analyticsService.test.js | 14 ++++++++++-- src/pages/Clients.tsx | 31 +++++++++++++++++++++------ 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/backend/services/analyticsService.js b/backend/services/analyticsService.js index e7452b2..c13ccc6 100644 --- a/backend/services/analyticsService.js +++ b/backend/services/analyticsService.js @@ -215,11 +215,13 @@ const appendOrderMetadataFilters = (params, filters, range = {}) => { params.push(seller.value); filters.push(`NULLIF(TRIM(id_vendedor), '') = $${params.length}`); } else if (seller?.type === 'name') { - params.push(seller.value); - filters.push(`NULLIF(TRIM(nome_vendedor), '') = $${params.length}`); + params.push(`%${seller.value}%`); + filters.push(`NULLIF(TRIM(nome_vendedor), '') ILIKE $${params.length}`); } else if (seller?.type === 'any') { params.push(seller.value); - filters.push(`(NULLIF(TRIM(id_vendedor), '') = $${params.length} OR NULLIF(TRIM(nome_vendedor), '') = $${params.length})`); + const idParamIndex = params.length; + params.push(`%${seller.value}%`); + filters.push(`(NULLIF(TRIM(id_vendedor), '') = $${idParamIndex} OR NULLIF(TRIM(nome_vendedor), '') ILIKE $${params.length})`); } }; diff --git a/backend/test/analyticsService.test.js b/backend/test/analyticsService.test.js index 1d776a0..40a1760 100644 --- a/backend/test/analyticsService.test.js +++ b/backend/test/analyticsService.test.js @@ -112,10 +112,20 @@ test('buildDateFilter composes client metadata predicates after date predicates' test('buildDateFilter can filter seller by display name when no seller id is selected', () => { const filter = buildDateFilter({ seller: 'name:Maria' }); - assert.deepEqual(filter.params, ['Maria']); + assert.deepEqual(filter.params, ['%Maria%']); assert.equal( filter.whereClause, - "WHERE data_pedido_date IS NOT NULL AND NULLIF(TRIM(nome_vendedor), '') = $1" + "WHERE data_pedido_date IS NOT NULL AND NULLIF(TRIM(nome_vendedor), '') ILIKE $1" + ); +}); + +test('buildDateFilter can search seller names from typed text', () => { + const filter = buildDateFilter({ seller: 'MARCUS' }); + + assert.deepEqual(filter.params, ['MARCUS', '%MARCUS%']); + assert.equal( + filter.whereClause, + "WHERE data_pedido_date IS NOT NULL AND (NULLIF(TRIM(id_vendedor), '') = $1 OR NULLIF(TRIM(nome_vendedor), '') ILIKE $2)" ); }); diff --git a/src/pages/Clients.tsx b/src/pages/Clients.tsx index 6f1ca6b..b274d07 100644 --- a/src/pages/Clients.tsx +++ b/src/pages/Clients.tsx @@ -104,6 +104,7 @@ const dateFilterPresets = [ ]; const filterSelectClassName = "w-full h-9 bg-dark-input border border-dark-border text-dark-text text-sm rounded-lg px-3 focus:outline-none focus:border-brand-primary transition-colors cursor-pointer"; +const filterInputClassName = "w-full h-9 bg-dark-input border border-dark-border text-dark-text text-sm rounded-lg px-3 focus:outline-none focus:border-brand-primary transition-colors"; const Clients = () => { const { dateRange, setDateRange } = useOutletContext<{ @@ -350,6 +351,19 @@ const Clients = () => { return [fallbackSeller, ...filterOptions.sellers]; }, [filterOptions.sellers, metadataFilters.seller]); + const sellerSuggestions = useMemo(() => { + const suggestions = new Set(); + + sellerOptions.forEach(seller => { + const label = getSellerOptionLabel(seller); + if (seller.name) suggestions.add(seller.name); + if (seller.id) suggestions.add(seller.id); + if (label) suggestions.add(label); + }); + + return [...suggestions].filter(Boolean); + }, [sellerOptions]); + const activeMetadataFilters = useMemo(() => { const filters = []; const selectedSeller = sellerOptions.find(seller => seller.value === metadataFilters.seller); @@ -515,16 +529,19 @@ const Clients = () => {