Filtro de marcas
Implementa: api, web, app
Consume: api, web, app
Estado: activa
Última revisión: 2026-05-21T19:38:35Z
# Implementa: api, web, app
# Consume: api, web, app
# Estado: activa
# Última revisión: 2026-05-21T19:38:35Z
# Position: 2
Feature: Filtro de marcas
El usuario puede filtrar las gasolineras por marca. El estado inicial
es sin selección — sin filtro activo, se muestran todas las marcas disponibles.
La lista es dinámica: se recarga al abrir la app, al soltar el slider de radio
y al cambiar el tipo de combustible. Cada marca incluye el conteo de estaciones
disponibles con el combustible seleccionado en el radio actual.
# Contrato: GET /v2/filters → { brands: [{ name, count }] }
# Swagger: https://tanko-a4q2.onrender.com/tanko_docs_ad0df4baad113557
El back devuelve las marcas ordenadas por conteo descendente.
El front maqueta el formato del conteo.
El default de marcas es siempre sin selección — no viene del API.
El badge del botón de filtros suma +1 cuando hay al menos una marca seleccionada.
El badge cuenta solo marcas y "Abierto ahora" — radio y combustible no suman.
La preferencia de marcas seleccionadas se guarda de forma independiente al resto
de filtros (radio, combustible, abierto ahora). Esta capa de persistencia
sobrevive a cerrar y reabrir la app. La clave de almacenamiento es tanko_brands_pref.
Al abrir el panel de filtros, la preferencia guardada se cruza con las marcas
disponibles en el radio actual. Las marcas guardadas que no estén disponibles en
ese radio no se muestran como seleccionadas, pero siguen guardadas. Si el radio
se amplía y la marca vuelve a aparecer, se muestra automáticamente como seleccionada.
La preferencia solo se borra cuando el usuario pulsa "Reiniciar" o "Limpiar",
o deselecciona la última marca manualmente. Aplicar los filtros (botón CTA) no borra
la preferencia, aunque en ese momento la selección activa esté vacía porque ninguna
marca guardada estaba disponible en el radio actual.
El badge del botón de filtros refleja estados distintos según si el panel está abierto
o cerrado: con el panel abierto refleja el borrador (draft) — las marcas marcadas en
ese instante; con el panel cerrado refleja el estado confirmado — los filtros activos
que afectan a los resultados visibles.
Background:
Given la lista de marcas se carga desde el endpoint /filters
And las marcas están ordenadas por número de estaciones descendente
Scenario: Estado inicial sin preferencia guardada
Given el usuario nunca ha seleccionado marcas
When abre el panel de filtros
Then no hay ninguna marca seleccionada
And el label "Limpiar" no aparece
Scenario: Seleccionar una marca activa el label Limpiar
Given no hay ninguna marca seleccionada
When el usuario selecciona una marca
Then la marca aparece marcada
And el label "Limpiar" aparece junto al título del bloque
Scenario: Limpiar elimina toda la selección y borra la preferencia
Given el usuario tiene marcas seleccionadas
When pulsa "Limpiar"
Then todas las marcas se desmarcan
And el label "Limpiar" desaparece
And se borra la persistencia de marcas
Scenario: Deseleccionar la última marca elimina el label Limpiar
Given el usuario tiene una sola marca seleccionada
When la deselecciona
Then no hay ninguna marca seleccionada
And el label "Limpiar" desaparece
Scenario: La lista se recarga al cambiar el radio
Given el usuario tiene el radio en 10 km
When arrastra el slider a 5 km y lo suelta
Then la lista de marcas se recarga con las disponibles en 5 km
And el conteo de cada marca refleja el nuevo radio
Scenario: La lista se recarga al cambiar el tipo de combustible
Given el usuario cambia de "Gasolina 95" a "Diésel"
Then la lista de marcas se recarga
And el conteo de cada marca refleja las estaciones con Diésel en el radio actual
Scenario: Marca seleccionada desaparece al reducir el radio
Given el usuario tiene "Repsol" seleccionada
And al reducir el radio "Repsol" deja de tener estaciones en el área
When suelta el slider
Then "Repsol" desaparece de la lista silenciosamente
And se elimina de la selección activa sin borrar la persistencia
Scenario: Marca que desapareció vuelve al ampliar el radio
Given "Repsol" fue eliminada silenciosamente al reducir el radio
When el usuario amplía el radio y "Repsol" vuelve a tener estaciones en el área
Then "Repsol" aparece de nuevo en la lista seleccionada
Scenario: Fallo en la recarga de marcas
Given el usuario suelta el slider de radio
And la llamada al API falla
Then la lista mantiene las marcas del estado anterior
And el CTA sigue siendo accionable
Scenario: La lista se recarga al abrir la app con nueva ubicación
Given el usuario abre la app
Then la lista de marcas se carga con las disponibles en la ubicación actual
And las marcas persistidas se cruzan con la nueva lista
And solo se marcan las que siguen disponibles
Scenario: Persistencia entre sesiones — la preferencia es independiente de otros filtros
Given el usuario tiene "Repsol" y "BP" seleccionadas con radio 10 km
When cierra y vuelve a abrir la app
Then la preferencia de marcas se conserva de forma independiente al radio y combustible
And al abrir el panel de filtros la preferencia se cruza con las marcas disponibles en el radio actual
And solo se muestran como seleccionadas las marcas guardadas que siguen disponibles
Scenario: Aplicar con selección vacía no borra la preferencia guardada
Given el usuario seleccionó "Repsol" con radio 10 km y aplicó los filtros
When reabre el panel de filtros y reduce el radio a 1 km
Then "Repsol" no aparece en la lista porque no hay estaciones a 1 km
When pulsa el CTA con 0 marcas activas
Then la pantalla muestra "sin resultados"
And la preferencia de "Repsol" sigue guardada
When amplía el radio a 10 km y reabre el panel de filtros
Then "Repsol" aparece seleccionado automáticamente
Scenario: Reiniciar limpia la selección de marcas y borra la preferencia
Given el usuario tiene marcas seleccionadas
When pulsa "Reiniciar"
Then todas las marcas se desmarcan
And se borra la persistencia de marcas
Scenario: La barra de subtítulo refleja las marcas seleccionadas
Given el usuario tiene 2 marcas seleccionadas
Then la barra de subtítulo incluye "2 marcas"
Scenario: La barra de subtítulo no menciona marcas cuando no hay filtro activo
Given no hay ninguna marca seleccionada
Then la barra de subtítulo no incluye ninguna referencia a marcas
# ---------------------------------------------------------------------------
# Corner cases: comportamiento avanzado documentado explícitamente
# ---------------------------------------------------------------------------
Rule: La preferencia se conserva durante la sesión aunque la marca desaparezca del listado
# Flujo: el usuario selecciona una marca y luego mueve el slider hasta un radio
# donde esa marca no tiene estaciones. La marca desaparece visualmente pero la
# preferencia sigue guardada internamente hasta que el usuario la deseleccione
# de forma explícita.
Scenario: El badge baja a 0 cuando la marca preferida desaparece al reducir el radio con el panel abierto
Given el usuario tiene "Combus" seleccionada en el panel con radio 10 km
And el badge muestra 1 en el botón de filtros
When arrastra el slider a 2 km y "Combus" deja de tener estaciones disponibles
Then "Combus" desaparece silenciosamente del listado del panel
And el badge baja a 0 reflejando el borrador actual (ninguna marca visible marcada)
And la preferencia de "Combus" sigue guardada en tanko_brands_pref
Scenario: El badge se restaura al ampliar el radio hasta que la marca vuelve a estar disponible
Given el usuario tiene el panel abierto con radio 2 km
And "Combus" no aparece en el listado porque no tiene estaciones en ese radio
And hay una preferencia de "Combus" guardada en tanko_brands_pref
When arrastra el slider a 10 km y "Combus" vuelve a tener estaciones
Then "Combus" aparece en el listado marcada con checkmark
And el badge sube a 1 reflejando el borrador restaurado
Rule: La preferencia de marca se guarda en localStorage bajo la clave tanko_brands_pref
# Flujo: la preferencia persiste entre sesiones. Al abrir el panel, la preferencia
# guardada se cruza con las marcas disponibles en el radio actual.
# Si la marca no está disponible, no aparece pero la preferencia se conserva.
Scenario: Al abrir el panel, la preferencia guardada aparece marcada si la marca está disponible
Given el usuario tiene "Combus" guardada en tanko_brands_pref
And en el radio actual "Combus" tiene estaciones disponibles
When abre el panel de filtros (incluso en otra sesión)
Then "Combus" aparece marcada automáticamente sin ninguna acción del usuario
And el badge muestra 1 al abrir el panel
Scenario: La preferencia guardada se conserva aunque la marca no esté disponible en el radio actual
Given el usuario tiene "Combus" guardada en tanko_brands_pref
And en el radio actual "Combus" no tiene estaciones disponibles
When abre el panel de filtros
Then "Combus" no aparece en el listado
And el badge muestra 0 porque no hay marcas visibles marcadas
And la preferencia sigue guardada en tanko_brands_pref para cuando el radio la incluya
Scenario: La preferencia solo se limpia con acción explícita del usuario
Given el usuario tiene "Combus" guardada en tanko_brands_pref
When reduce el radio hasta que "Combus" desaparece del listado
Then tanko_brands_pref sigue conteniendo "Combus"
When vuelve a ampliar el radio y abre el panel
Then "Combus" aparece marcada automáticamente
Rule: El badge refleja el borrador cuando el panel está abierto y el estado confirmado cuando está cerrado
# Expected behavior:
# - Panel abierto → badge = número de marcas marcadas en el draft en ese instante
# - Panel cerrado → badge = número de marcas en el estado confirmado (filtros aplicados)
# El badge nunca debe mostrar 1 si no hay ninguna marca efectivamente activa
# en el estado relevante.
Scenario: Badge refleja el borrador mientras el panel está abierto
Given el usuario tiene "Repsol" aplicada como filtro activo con el panel cerrado
And el badge muestra 1
When abre el panel de filtros y deselecciona "Repsol" sin pulsar "Aplicar"
Then el badge baja a 0 mientras el panel está abierto (refleja el borrador sin marcas)
Scenario: Badge vuelve al estado confirmado si el panel se cierra sin aplicar
Given el usuario tiene "Repsol" aplicada como filtro activo (badge = 1)
And abre el panel y deselecciona "Repsol" sin aplicar (badge en panel = 0)
When cierra el panel sin pulsar "Aplicar"
Then el badge vuelve a 1 porque el estado confirmado sigue teniendo "Repsol"
Scenario: Badge con panel cerrado refleja solo los filtros aplicados
Given el usuario tiene "BP" aplicada como filtro activo
Then el badge muestra 1 con el panel cerrado
When abre el panel, ve "BP" marcada, y cierra sin modificar nada
Then el badge sigue mostrando 1
Rule: Aplicar con marca no disponible genera confirmed vacío pero conserva la preferencia
# Flujo: el usuario abre el panel, mueve el slider a un radio donde su marca
# preferida no aparece, y pulsa "Aplicar". El resultado confirmado tiene brands: []
# pero localStorage conserva la preferencia para uso futuro.
Scenario: Aplicar con 0 marcas activas produce brands vacío en el estado confirmado
Given el usuario tiene "Combus" guardada en tanko_brands_pref
And abre el panel de filtros con radio 2 km donde "Combus" no está disponible
And el badge muestra 0 en ese borrador
When pulsa "Aplicar"
Then el estado confirmado tiene brands: []
And el badge con el panel cerrado muestra 0
And los resultados se buscan sin filtro de marca
And tanko_brands_pref sigue conteniendo "Combus"
Scenario: Aplicar con brands vacío en radio muy pequeño puede derivar en "Nada cerca"
Given el usuario aplica con brands: [] en un radio donde no hay estaciones de ningún tipo
Then la pantalla muestra "Nada cerca"
And tanko_brands_pref conserva la preferencia guardada previamente
Rule: Ampliar radio desde "Nada cerca" restaura la preferencia de marca guardada
# Flujo: el usuario llegó a "Nada cerca" habiendo aplicado con brands: [] (porque
# su marca preferida no estaba disponible al pulsar Aplicar). Al pulsar
# "Ampliar a X km", si hay una preferencia guardada en localStorage, se restaura
# automáticamente como filtro activo en el nuevo radio.
Scenario: Ampliar radio restaura la marca preferida si hay preferencia guardada
Given el usuario tiene "Combus" guardada en tanko_brands_pref
And el estado confirmado tiene brands: [] porque "Combus" no estaba disponible al aplicar
And la pantalla muestra "Nada cerca"
When pulsa el botón "Ampliar a X km"
Then la preferencia de "Combus" se restaura como filtro activo en el nuevo radio
And el badge sube a 1
And la búsqueda se lanza con "Combus" como filtro en el nuevo radio
Scenario: Si la marca restaurada tampoco tiene estaciones en el radio ampliado, aparece Nada cerca con filtro activo
Given el usuario tiene "Combus" guardada en tanko_brands_pref
And la pantalla muestra "Nada cerca" con brands: [] confirmado
When pulsa "Ampliar a X km" y "Combus" tampoco tiene estaciones en ese radio
Then la preferencia se restaura como filtro activo (badge = 1)
And la pantalla muestra "Nada cerca" con "Combus" como filtro activo
And este estado es correcto y esperado: el usuario ve que el filtro está activo
Scenario: Ampliar radio sin preferencia guardada solo cambia el radio
Given el usuario limpió su preferencia de marcas explícitamente (tanko_brands_pref vacío)
And la pantalla muestra "Nada cerca"
When pulsa "Ampliar a X km"
Then solo se cambia el radio sin restaurar ningún filtro de marca
And el badge sigue en 0
Rule: El panel relanza la petición a /filters si el caché de configuración está desactualizado
# El caché de /filters se obtiene con el radio en que fue llamado. Si al abrir
# el panel el radio confirmado difiere del radio del caché, el caché se considera
# desactualizado (stale) y el panel lanza una nueva petición antes de mostrar
# el listado de marcas.
Scenario: El panel detecta caché desactualizado y lanza petición fresca al abrirse
Given el caché de la respuesta /filters corresponde al radio 10 km
And el radio confirmado actual es 5 km
When el usuario abre el panel de filtros
Then el panel detecta que el caché no corresponde al radio actual
And lanza una nueva petición a /filters con el radio correcto (5 km)
And muestra un estado de carga mientras espera la respuesta
Scenario: La preferencia guardada se restaura cuando llega la respuesta fresca
Given el panel lanzó una petición fresca a /filters por caché desactualizado
And el usuario tiene "Repsol" guardada en tanko_brands_pref
When llega la respuesta con el listado actualizado al radio correcto
Then la preferencia de "Repsol" se cruza con el listado nuevo
And "Repsol" aparece seleccionada si está disponible en el radio actual
And si no está disponible, no aparece pero tanko_brands_pref la conserva
Rule: Invariantes de la preferencia de marca y el badge
# Estas propiedades deben mantenerse en cualquier flujo de la feature.
Scenario: La preferencia solo se borra con acción explícita del usuario
Given el usuario tiene una preferencia guardada en tanko_brands_pref
When realiza cualquier acción que no sea toggle-off explícito, "Limpiar" o "Reiniciar"
# Ejemplos de acciones que NO borran la preferencia:
# - Reducir el radio hasta que la marca desaparece
# - Aplicar filtros con brands: [] porque la marca no estaba disponible
# - Cerrar el panel sin aplicar
# - Ampliar el radio desde "Nada cerca"
Then tanko_brands_pref sigue conteniendo la preferencia
Scenario: Reducir el radio oculta la marca visualmente pero no borra la preferencia
Given el usuario tiene "BP" guardada en tanko_brands_pref con radio 15 km
When reduce el radio a 1 km y "BP" desaparece del listado
Then tanko_brands_pref sigue conteniendo "BP"
When amplía el radio a 15 km de nuevo
Then "BP" reaparece en el listado marcada automáticamente
Scenario: El badge nunca muestra un valor positivo si no hay ninguna marca filtrando
Given el estado relevante no tiene ninguna marca activa
# (draft si panel abierto, confirmado si panel cerrado)
Then el badge muestra 0
And no muestra ningún número positivo