From 6920b8293d4fe1b5d84483eb387d7c5aeada3a2d Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Fri, 27 Feb 2026 21:34:44 +0100 Subject: [PATCH] Add filter sections --- common/messages/de.json | 9 + common/messages/fr.json | 9 + web/components/filters/big5-filter.tsx | 27 +- web/components/filters/filters.tsx | 847 +++++++++++-------- web/components/filters/has-kids-filter.tsx | 2 +- web/components/filters/wants-kids-filter.tsx | 2 +- web/styles/globals.css | 6 +- 7 files changed, 533 insertions(+), 369 deletions(-) diff --git a/common/messages/de.json b/common/messages/de.json index bee479f6..771aa2d4 100644 --- a/common/messages/de.json +++ b/common/messages/de.json @@ -954,6 +954,15 @@ "profiles.title": "Personen", "profiles.try_keyword_search": "Schlüsselwortsuche versuchen", "profiles.early_growth": "Compass befindet sich in seiner frühen Wachstumsphase — 500+ Mitglieder und ~100 neue Menschen jeden Monat. Erstellen Sie jetzt ein starkes Profil und werden Sie sichtbar, während die Community wächst.", + "filter.selected": "Filter ausgewählt", + "filter.selected_plural": "Filter ausgewählt", + "filter.clear_all": "Alle löschen", + "filter.group.relationship": "Beziehung", + "filter.group.background": "Hintergrund", + "filter.group.lifestyle": "Lebensstil", + "filter.group.values": "Werte & Überzeugungen", + "filter.group.personality": "Persönlichkeit", + "filter.group.advanced": "Erweitert", "referrals.title": "Lade jemanden ein, Compass beizutreten!", "register.agreement.and": " und ", "register.agreement.prefix": "Mit der Registrierung akzeptiere ich die ", diff --git a/common/messages/fr.json b/common/messages/fr.json index bf953433..0fd8379f 100644 --- a/common/messages/fr.json +++ b/common/messages/fr.json @@ -953,6 +953,15 @@ "profiles.title": "Personnes", "profiles.try_keyword_search": "Essayer une recherche par mot-clé", "profiles.early_growth": "Compass est dans sa phase de croissance initiale — 500+ membres et ~100 nouvelles personnes chaque mois. Construisez un profil solide maintenant et soyez visible alors que la communauté grandit.", + "filter.selected": "filtre sélectionné", + "filter.selected_plural": "filtres sélectionnés", + "filter.clear_all": "Tout effacer", + "filter.group.relationship": "Amour & Famille", + "filter.group.background": "Contexte", + "filter.group.lifestyle": "Mode de vie", + "filter.group.values": "Valeurs & Croyances", + "filter.group.personality": "Personnalité", + "filter.group.advanced": "Avancé", "referrals.title": "Invitez quelqu'un à rejoindre Compass !", "register.agreement.and": " et ", "register.agreement.prefix": "En vous inscrivant, j'accepte les ", diff --git a/web/components/filters/big5-filter.tsx b/web/components/filters/big5-filter.tsx index ab0003da..169a1446 100644 --- a/web/components/filters/big5-filter.tsx +++ b/web/components/filters/big5-filter.tsx @@ -25,19 +25,22 @@ export type Big5MinMaxKey = | 'big5_neuroticism_min' | 'big5_neuroticism_max' +const BIG5_TRAITS = [ + 'big5_openness', + 'big5_conscientiousness', + 'big5_extraversion', + 'big5_agreeableness', + 'big5_neuroticism', +] as const + +export function countBig5Filters(filters: Partial) { + return BIG5_TRAITS.filter( + (trait) => filters[`${trait}_min`] != null || filters[`${trait}_max`] != null, + ).length +} + export function hasAnyBig5Filter(filters: Partial) { - return ( - filters.big5_openness_min != null || - filters.big5_openness_max != null || - filters.big5_conscientiousness_min != null || - filters.big5_conscientiousness_max != null || - filters.big5_extraversion_min != null || - filters.big5_extraversion_max != null || - filters.big5_agreeableness_min != null || - filters.big5_agreeableness_max != null || - filters.big5_neuroticism_min != null || - filters.big5_neuroticism_max != null - ) + return countBig5Filters(filters) > 0 } export function Big5FilterText(props: {filters: Partial; highlightedClass?: string}) { diff --git a/web/components/filters/filters.tsx b/web/components/filters/filters.tsx index 414f0d48..266b4947 100644 --- a/web/components/filters/filters.tsx +++ b/web/components/filters/filters.tsx @@ -1,21 +1,18 @@ import {ChevronDownIcon, ChevronUpIcon} from '@heroicons/react/outline' +import {XIcon} from '@heroicons/react/solid' import clsx from 'clsx' import {FilterFields} from 'common/filters' import {Gender} from 'common/gender' -import {hasKidsLabels} from 'common/has-kids' import {OptionTableKey} from 'common/profiles/constants' import {Profile} from 'common/profiles/profile' -import {wantsKidsLabels} from 'common/wants-kids' +import {removeNullOrUndefinedProps} from 'common/util/object' import {ReactNode, useState} from 'react' -import {BsPersonHeart, BsPersonVcard} from 'react-icons/bs' -import {FaBriefcase, FaHandsHelping, FaHeart, FaStar} from 'react-icons/fa' -import {FaUserGroup} from 'react-icons/fa6' -import {GiFruitBowl} from 'react-icons/gi' -import {LuCigarette, LuGraduationCap} from 'react-icons/lu' -import {MdLanguage, MdLocalBar} from 'react-icons/md' -import {PiHandsPrayingBold} from 'react-icons/pi' -import {RiScales3Line} from 'react-icons/ri' -import {Big5Filters, Big5FilterText, hasAnyBig5Filter} from 'web/components/filters/big5-filter' +import { + Big5Filters, + Big5FilterText, + countBig5Filters, + hasAnyBig5Filter, +} from 'web/components/filters/big5-filter' import {DietFilter, DietFilterText} from 'web/components/filters/diet-filter' import {EducationFilter, EducationFilterText} from 'web/components/filters/education-filter' import {InterestFilter, InterestFilterText} from 'web/components/filters/interest-filter' @@ -49,6 +46,118 @@ import {MyMatchesToggle} from './my-matches-toggle' import {RelationshipFilter, RelationshipFilterText} from './relationship-filter' import {SmokerFilter, SmokerFilterText} from './smoker-filter' +function countActiveFilters( + filters: Partial, + locationFilterProps: LocationFilterProps, + raisedInLocationFilterProps: LocationFilterProps, +) { + let count = Object.keys(removeNullOrUndefinedProps({...filters, orderBy: undefined})).length + if (locationFilterProps.location) count = count - 2 + if (raisedInLocationFilterProps.location) count = count - 2 + if (filters.pref_age_min && filters.pref_age_max) count-- + const big5Count = countBig5Filters(filters) + if (big5Count > 1) count = count - (big5Count - 1) + return count +} + +function SelectedFiltersSummary(props: { + filters: Partial + locationFilterProps: LocationFilterProps + raisedInLocationFilterProps: LocationFilterProps + updateFilter: (newState: Partial) => void + clearFilters: () => void +}) { + const {filters, locationFilterProps, raisedInLocationFilterProps, updateFilter, clearFilters} = + props + const t = useT() + + const filterCount = countActiveFilters(filters, locationFilterProps, raisedInLocationFilterProps) + + if (filterCount === 0) return null + + const selectedFilters: {label: string; onClear: () => void}[] = [] + + if (locationFilterProps.location) { + selectedFilters.push({ + label: locationFilterProps.location.name || t('filter.location', 'Location'), + onClear: () => { + locationFilterProps.setLocation(null) + updateFilter({geodbCityIds: undefined, lat: undefined, lon: undefined, radius: undefined}) + }, + }) + } + + if (raisedInLocationFilterProps.location) { + selectedFilters.push({ + label: t('filter.raised_in', 'Grew up'), + onClear: () => { + raisedInLocationFilterProps.setLocation(null) + updateFilter({ + raised_in_lat: undefined, + raised_in_lon: undefined, + raised_in_radius: undefined, + }) + }, + }) + } + + if (filters.pref_age_min || filters.pref_age_max) { + const ageLabel = + filters.pref_age_min && filters.pref_age_max + ? `${filters.pref_age_min}-${filters.pref_age_max}` + : filters.pref_age_min + ? `${filters.pref_age_min}+` + : `< ${filters.pref_age_max}` + selectedFilters.push({ + label: `${t('filter.age.label', 'Age')}: ${ageLabel}`, + onClear: () => updateFilter({pref_age_min: undefined, pref_age_max: undefined}), + }) + } + + if (filters.genders?.length) { + selectedFilters.push({ + label: filters.genders.join(', '), + onClear: () => updateFilter({genders: undefined}), + }) + } + + if (filters.pref_relation_styles?.length) { + selectedFilters.push({ + label: filters.pref_relation_styles.join(', '), + onClear: () => updateFilter({pref_relation_styles: undefined}), + }) + } + + return ( + + + + {filterCount} + + {filterCount === 1 + ? t('filter.selected', 'filter selected') + : t('filter.selected_plural', 'filters selected')} + + + + + + {selectedFilters.map((filter, idx) => ( + + {filter.label} + + + ))} + + + ) +} + function Filters(props: { filters: Partial youProfile: Profile | undefined | null @@ -76,6 +185,7 @@ function Filters(props: { } = props const [openFilter, setOpenFilter] = useState(undefined) + const [openGroup, setOpenGroup] = useState(undefined) function hasAny(filterArray: any[] | undefined | null): boolean { return !!filterArray && filterArray.length > 0 @@ -85,6 +195,13 @@ function Filters(props: { return ( + @@ -96,7 +213,6 @@ function Filters(props: { hidden={!youProfile} /> - {/* Short Bios */} @@ -104,13 +220,15 @@ function Filters(props: {