The filtering system enables users to narrow down camera options by multiple criteria simultaneously. It uses Redux actions and dynamic URL routing to maintain filter state.
Filter Categories
The application supports three main filter categories:
Design Interior or Exterior installation
Resolution 2mp, 4mp, or 5mp quality
Camera Type Domo or Bullet style
Filter State Management
The sidebar component dynamically generates filter options based on available cameras:
components/navbarAside.js:119-133
function filterAside ({ camaras }){
let filtro_diseno = { "diseño" : {}}
let filtro_resolucion = { "resolucion" : {}}
let filtro_tipo_camara = { "tipo_de_camara" : {}}
camaras . map ( m => {
updateDict ( filtro_diseno , m . diseño )
updateDict ( filtro_resolucion , m . resolucion )
updateDict ( filtro_tipo_camara , m . tipo_de_camara )
})
return [ filtro_diseno , filtro_resolucion , filtro_tipo_camara ]
}
This function counts occurrences of each filter value to show users how many cameras match each criterion.
Dictionary Updates
The updateDict function tracks filter option counts:
components/navbarAside.js:135-140
function updateDict ( dict , key ){
const [ valueDict ] = Object . keys ( dict )
Object . keys ( dict [ valueDict ]). length === 0 || ! ( Object . keys ( dict [ valueDict ]). includes ( key )) ?
dict [ valueDict ][ key ] = 1 : dict [ valueDict ][ key ] += 1
}
Redux Filter Actions
The application provides several Redux actions for filtering:
Filter by Multiple Criteria
The primary filtering action handles multiple simultaneous filters:
store/camarasSlice.js:163-182
filterCamarasAside :( state , action ) => {
const value = action . payload
let new_state = []
for ( let cam of state ){
Object . values ( cam ). forEach (( value_state ) => {
if ( capitalize ( value [ 0 ]) === value_state ) new_state . push ( current ( cam ))
})
}
if ( value . length > 1 ){
//son mas filtros
new_state = new_state . filter (( element , index , array ) =>
//cheackeamos que cada uno cumpla la condicion
value . every ( filtro => {
return Object . values ( element ). includes ( capitalize ( filtro ))
})
)
}
return new_state
},
When multiple filters are applied, cameras must match ALL criteria (AND logic), not just one.
Filter by ID
store/camarasSlice.js:155-158
filterCamara :( state , action ) => {
const id = action . payload
return state . filter (( camara ) => camara . id === id )
},
Filter by Model
store/camarasSlice.js:159-162
filterModelo :( state , action ) => {
const modelo = action . payload
return state . filter (( camara ) => camara . modelo === modelo )
},
Reset Filters
store/camarasSlice.js:187-189
backToInitialState :( state , action ) => {
return [ ... initialState ]
}
URL-Based Filtering
Filters are encoded in the URL for shareable and bookmarkable filter states:
utilities/otherFunctions.js:13-24
const urlFiltro = ( cadena , location ) => {
let filtro = cadena . toLowerCase ()
let url = ''
let prevRute = location . pathname . split ( "/" )
if ( hasWhiteSpace ( filtro )) { filtro = createSlug ( cadena )}
prevRute = prevRute . at ( - 1 )
url = prevRute === 'productos' ? filtro : prevRute . toLowerCase () + '+' + filtro
return url
}
Filters use this URL pattern:
/productos/camaras/filtro/interior+4_mp+domo
Multiple filters separated by +
Spaces replaced with _
All lowercase
Filter Component
The FilterCamara component coordinates filtering:
components/filtercamaras.js:10-15
let filtros_aplicados = filterArray ( filter )
useEffect (() => {
dispatch ( filterCamarasAside ( filtros_aplicados ))
setFiltrado ( true )
}, [ location . pathname ])
It re-filters whenever the URL changes, ensuring the UI stays synchronized with the current filter state.
Filter Array Parsing
The filterArray utility converts URL filters back to an array:
utilities/otherFunctions.js:30
const filterArray = ( filter ) => filter . split ( "+" ). map ( filtro => deleteSlug ( filtro ))
Active Filter Display
Users see applied filters as removable badges:
components/navbarAside.js:47-65
const filtros = filter !== undefined ? filterArray ( filter ). map (( filtro , index ) => {
return (
< li key = { filtro + index . toString () } className = "nav-item" >
< div id = { filtro } className = "btn p-2 text-nowrap text-white mt-2 mb-2 ml-1 mr-1 filtro" >
{ filtro }
< span className = "badge badge-light filtro" style = { { "cursor" : "pointer" } }
onClick = { () => {
let resultado = createSlug ( createUrlFilter ( filter , filtro ))
if ( ! resultado ){ navigate ( '/productos' , { state: { text: [ 'reset' ]}})}
else {
navigate ( `/productos/camaras/filtro/ ${ resultado } ` , { replace: true })
dispatch ( backToInitialState ([ filtro ]))
}
} }
> X </ span >
</ div >
</ li >
)
})
Clicking the “X” removes that specific filter and updates the URL.
Filter Interaction Flow
User Selects Filter
User clicks a filter option in the sidebar (e.g., “Interior”)
URL Updates
Application navigates to /productos/camaras/filtro/interior
Redux Action Dispatched
filterCamarasAside action filters the camera array
UI Re-renders
Component displays only matching cameras with result count
String Utilities
The system uses helper functions for text processing:
utilities/otherFunctions.js:2-11
const normalizeString = ( string ) => string . normalize ( "NFD" ). replace ( / [ \u0300 - \u036f ] / g , "" )
const hasWhiteSpace = ( s ) => ( / \s / ). test ( s )
const createSlug = ( s ) => s . split ( " " ). join ( "_" )
const deleteSlug = ( s ) => s . split ( "_" ). join ( " " )
The normalizeString function removes accents, ensuring filter comparisons work correctly with Spanish text.
The filter sidebar is responsive and collapsible on mobile devices:
components/navbarAside.js:102-107
< button type = "button" className = "navbar-toggler border-0 shadow-none"
data-bs-toggle = "collapse" data-bs-target = "#nav" aria-controls = "nav"
aria-expanded = "false" aria-label = "Toggle-navigation" id = "filtro" >
Filtrar
< svg xmlns = "http://www.w3.org/2000/svg" width = "22" height = "22" viewBox = "0 0 24 24" ...>
</button>
The filtering system only shows filter options that have more than one available value. If all cameras share the same value for a property, that filter won’t appear.