diff --git a/backend/email/emails/new-search_alerts.tsx b/backend/email/emails/new-search_alerts.tsx index efedb529..27dadf83 100644 --- a/backend/email/emails/new-search_alerts.tsx +++ b/backend/email/emails/new-search_alerts.tsx @@ -1,8 +1,8 @@ import {Body, Container, Head, Html, Link, Preview, Section, Text} from '@react-email/components' import {DOMAIN} from 'common/envs/constants' import {FilterFields} from 'common/filters' +import {formatFilters, locationType} from 'common/filters-format' import {MatchesType} from 'common/profiles/bookmarked_searches' -import {formatFilters, locationType} from 'common/searches' import {type User} from 'common/user' import {container, content, Footer, main, paragraph} from 'email/utils' import React from 'react' diff --git a/common/messages/fr.json b/common/messages/fr.json index 173a08af..e52895b4 100644 --- a/common/messages/fr.json +++ b/common/messages/fr.json @@ -312,7 +312,7 @@ "filter.age.years": "ans", "filter.any": "Tous", "filter.any_causes": "Toute cause", - "filter.any_diet": "Tout régime", + "filter.any_diet": "Tout régime alimentaire", "filter.any_drinks": "Toute boisson", "filter.any_education": "Tous niveaux d'études", "filter.any_interests": "Tout intérêt", @@ -958,7 +958,7 @@ "filter.selected_plural": "filtres sélectionnés", "filter.clear_all": "Tout effacer", "filter.group.relationship": "Amour & Famille", - "filter.group.background": "Contexte", + "filter.group.background": "Milieu", "filter.group.lifestyle": "Mode de vie", "filter.group.values": "Valeurs & Croyances", "filter.group.personality": "Personnalité", diff --git a/common/src/searches.ts b/common/src/filters-format.ts similarity index 95% rename from common/src/searches.ts rename to common/src/filters-format.ts index 74ac75c7..74181d57 100644 --- a/common/src/searches.ts +++ b/common/src/filters-format.ts @@ -49,7 +49,7 @@ export type locationType = { radius: number } -const skippedKeys = [ +export const SKIPPED_FORMAT_FILTERS_KEYS = [ 'pref_age_min', 'pref_age_max', 'geodbCityIds', @@ -59,6 +59,9 @@ const skippedKeys = [ 'lat', 'lon', 'radius', + 'raised_in_lat', + 'raised_in_lon', + 'raised_in_radius', // Big Five min/max keys are handled separately 'big5_openness_min', 'big5_openness_max', @@ -101,7 +104,7 @@ export function formatFilters( if (ageMin) { text = `${text}-${ageMax}` } else { - text = `${text}${translate('filter.age.up_to', 'up to')} ${ageMax}` + text = `${text}${translate('filter.age.up_to', 'up to')} ${ageMax} ${translate('filter.age.years', 'years old')}` } } else { text = `${text}+` @@ -113,7 +116,7 @@ export function formatFilters( const typedKey = key as keyof FilterFields if (value === undefined || value === null) return - if (skippedKeys.includes(typedKey)) return + if (SKIPPED_FORMAT_FILTERS_KEYS.includes(typedKey)) return if (Array.isArray(value) && value.length === 0) return if (initialFilters[typedKey] === value) return @@ -241,13 +244,13 @@ export function formatFilters( if (drinksMin !== undefined && drinksMax !== undefined) { // Both min and max: "12-78" - drinksText = `${drinksMin}-${drinksMax}` + drinksText = drinksMin === drinksMax ? String(drinksMax) : `${drinksMin}-${drinksMax}` } else if (drinksMin !== undefined) { // Only min: "12+" drinksText = `${drinksMin}+` } else { // Only max: "up to 82" - drinksText = `${translate('filter.age.up_to', 'up to')} ${drinksMax}` + drinksText = drinksMax === 0 ? '0' : `${translate('filter.age.up_to', 'up to')} ${drinksMax}` } entries.push(`${drinksLabel}: ${drinksText} ${perMonth}`) diff --git a/common/src/filters.ts b/common/src/filters.ts index 81503f23..0ac6629c 100644 --- a/common/src/filters.ts +++ b/common/src/filters.ts @@ -3,12 +3,6 @@ import {Profile, ProfileRow} from 'common/profiles/profile' import {filterDefined} from 'common/util/array' import {cloneDeep} from 'lodash' -// export type TargetArea = { -// lat: number -// lon: number -// radius: number -// } - export type FilterFields = { orderBy: 'last_online_time' | 'created_time' | 'compatibility_score' last_active: string | undefined diff --git a/common/src/util/array.ts b/common/src/util/array.ts index e9407308..50dc6c89 100644 --- a/common/src/util/array.ts +++ b/common/src/util/array.ts @@ -33,6 +33,24 @@ export function groupConsecutive(xs: T[], key: (x: T) => U) { } export function nullifyEmpty(array: T[]) { + // Nullify a list if empty ([]) if (!Array.isArray(array)) return null return array.length > 0 ? array : null } + +export function nullifyDictValues(array: Record) { + // Nullify all the values of the dict + return Object.entries(array).reduce((acc, [key, _]) => { + return {...acc, [key]: null} + }, {}) +} + +export function sampleDictByPrefix(array: Record, prefix: string) { + // Extract the keys that start with the prefix + return Object.entries(array).reduce((acc, [key, value]) => { + if (key.startsWith(prefix)) { + return {...acc, [key]: value} + } + return acc + }, {}) +} diff --git a/common/src/wants-kids.ts b/common/src/wants-kids.ts index 287e0b0c..7c86002e 100644 --- a/common/src/wants-kids.ts +++ b/common/src/wants-kids.ts @@ -35,16 +35,16 @@ export const wantsKidsNames = Object.values(wantsKidsLabels).reduce= 0 && wantsKidsStrength < wantsKidsLabels.wants_kids.strength) { return hasKidsLabels.doesnt_have_kids.value } - return hasKidsLabels.no_preference.value + return null // hasKidsLabels.no_preference.value } export function wantsKidsDatabaseToWantsKidsFilter(wantsKidsStrength: wantsKidsDatabase) { // console.debug(wantsKidsStrength) if (wantsKidsStrength == wantsKidsLabels.no_preference.strength) { - return wantsKidsLabels.no_preference.strength + return null // wantsKidsLabels.no_preference.strength } if (wantsKidsStrength > wantsKidsLabels.wants_kids.strength) { return wantsKidsLabels.wants_kids.strength diff --git a/web/components/filters/filters.tsx b/web/components/filters/filters.tsx index c1fa13b2..fa9a3bbb 100644 --- a/web/components/filters/filters.tsx +++ b/web/components/filters/filters.tsx @@ -2,9 +2,11 @@ import {ChevronDownIcon, ChevronUpIcon} from '@heroicons/react/outline' import {XIcon} from '@heroicons/react/solid' import clsx from 'clsx' import {FilterFields} from 'common/filters' +import {formatFilters, SKIPPED_FORMAT_FILTERS_KEYS} from 'common/filters-format' import {Gender} from 'common/gender' import {OptionTableKey} from 'common/profiles/constants' import {Profile} from 'common/profiles/profile' +import {nullifyDictValues, sampleDictByPrefix} from 'common/util/array' import {removeNullOrUndefinedProps} from 'common/util/object' import {ReactNode, useState} from 'react' import { @@ -32,7 +34,8 @@ import {Col} from 'web/components/layout/col' import {Row} from 'web/components/layout/row' import {NewBadge} from 'web/components/new-badge' import {ResetFiltersButton} from 'web/components/searches/button' -import {useChoices} from 'web/hooks/use-choices' +import {useAllChoices, useChoices} from 'web/hooks/use-choices' +import {useMeasurementSystem} from 'web/hooks/use-measurement-system' import {useT} from 'web/lib/locale' import {DietType, RelationshipType, RomanticType} from 'web/lib/util/convert-types' @@ -51,9 +54,8 @@ function countActiveFilters( locationFilterProps: LocationFilterProps, raisedInLocationFilterProps: LocationFilterProps, ) { - let parsedFilters = Object.keys(removeNullOrUndefinedProps({...filters, orderBy: undefined})) - parsedFilters = parsedFilters.filter((key) => !key.startsWith('big5_')) - let count = parsedFilters.length + const keys = Object.keys(filters).filter((key) => !key.startsWith('big5_')) + let count = keys.length if (locationFilterProps.location) count = count - 2 if (raisedInLocationFilterProps.location) count = count - 2 if (filters.pref_age_min && filters.pref_age_max) count-- @@ -69,16 +71,40 @@ function SelectedFiltersSummary(props: { updateFilter: (newState: Partial) => void clearFilters: () => void }) { - const {filters, locationFilterProps, raisedInLocationFilterProps, updateFilter, clearFilters} = - props + const {locationFilterProps, raisedInLocationFilterProps, updateFilter, clearFilters} = props const t = useT() + const choicesIdsToLabels = useAllChoices() + const {measurementSystem} = useMeasurementSystem() + const filters = removeNullOrUndefinedProps({...props.filters, orderBy: undefined}) const filterCount = countActiveFilters(filters, locationFilterProps, raisedInLocationFilterProps) if (filterCount === 0) return null const selectedFilters: {label: string; onClear: () => void}[] = [] + function formatLabel(filters: any) { + return String( + formatFilters( + filters, + locationFilterProps.location as any, + choicesIdsToLabels, + measurementSystem, + t, + )?.join(' • ') || Object.values(filters)[0], + ) + } + + Object.entries(filters).forEach(([key, value]) => { + const typedKey = key as keyof FilterFields + if (value === undefined || value === null) return + if (SKIPPED_FORMAT_FILTERS_KEYS.includes(typedKey)) return + selectedFilters.push({ + label: formatLabel({[key]: value}), + onClear: () => updateFilter({[key]: undefined}), + }) + }) + if (locationFilterProps.location) { selectedFilters.push({ label: locationFilterProps.location.name || t('filter.location', 'Location'), @@ -90,8 +116,11 @@ function SelectedFiltersSummary(props: { } if (raisedInLocationFilterProps.location) { + let label = t('filter.raised_in', 'Grew up') + if (raisedInLocationFilterProps.location.name) + label = `${label}: ${raisedInLocationFilterProps.location.name}` selectedFilters.push({ - label: t('filter.raised_in', 'Grew up'), + label: label, onClear: () => { raisedInLocationFilterProps.setLocation(null) updateFilter({ @@ -103,30 +132,24 @@ function SelectedFiltersSummary(props: { }) } - 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}), - }) + function formatAggregatedFields(prefix: string) { + const aggFilters = sampleDictByPrefix(filters, prefix) + if (Object.keys(aggFilters).length > 0) { + selectedFilters.push({ + label: formatLabel(aggFilters), + onClear: () => updateFilter(nullifyDictValues(aggFilters)), + }) + } } - if (filters.genders?.length) { - selectedFilters.push({ - label: filters.genders.join(', '), - onClear: () => updateFilter({genders: undefined}), - }) - } + formatAggregatedFields('pref_age') + formatAggregatedFields('big5') + formatAggregatedFields('drink') - if (filters.pref_relation_styles?.length) { + if (filters.shortBio) { selectedFilters.push({ - label: filters.pref_relation_styles.join(', '), - onClear: () => updateFilter({pref_relation_styles: undefined}), + label: t('filter.short_bio_toggle', 'Include incomplete profiles'), + onClear: () => updateFilter({shortBio: undefined}), }) } @@ -147,7 +170,7 @@ function SelectedFiltersSummary(props: { {selectedFilters.map((filter, idx) => ( {filter.label}