From a4bb184e95553184a4c8773d7896e4b570508fe5 Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Sat, 25 Oct 2025 14:22:11 +0200 Subject: [PATCH] Add filter: religion --- backend/api/src/get-profiles.ts | 10 ++++ common/src/api/schema.ts | 1 + common/src/api/zod-types.ts | 1 + common/src/filters.ts | 2 + common/src/searches.ts | 1 + common/src/util/sorting.ts | 20 ++++++++ web/components/filters/desktop-filters.tsx | 36 ++++++++++++-- web/components/filters/education-filter.tsx | 17 ++----- web/components/filters/mobile-filters.tsx | 19 +++++++ web/components/filters/religion-filter.tsx | 55 +++++++++++++++++++++ web/components/filters/use-filters.ts | 2 + 11 files changed, 146 insertions(+), 18 deletions(-) create mode 100644 common/src/util/sorting.ts create mode 100644 web/components/filters/religion-filter.tsx diff --git a/backend/api/src/get-profiles.ts b/backend/api/src/get-profiles.ts index 3b2a7a12..823172e6 100644 --- a/backend/api/src/get-profiles.ts +++ b/backend/api/src/get-profiles.ts @@ -22,6 +22,7 @@ export type profileQueryType = { pref_romantic_styles?: String[] | undefined, diet?: String[] | undefined, political_beliefs?: String[] | undefined, + religion?: String[] | undefined, wants_kids_strength?: number | undefined, has_kids?: number | undefined, is_smoker?: boolean | undefined, @@ -57,6 +58,7 @@ export const loadProfiles = async (props: profileQueryType) => { pref_romantic_styles, diet, political_beliefs, + religion, wants_kids_strength, has_kids, is_smoker, @@ -102,6 +104,8 @@ export const loadProfiles = async (props: profileQueryType) => { intersection(diet, l.diet).length) && (!political_beliefs || intersection(political_beliefs, l.political_beliefs).length) && + (!religion || + intersection(religion, l.religion).length) && (!wants_kids_strength || wants_kids_strength == -1 || !l.wants_kids_strength || @@ -199,6 +203,12 @@ export const loadProfiles = async (props: profileQueryType) => { {political_beliefs} ), + religion?.length && + where( + `religion IS NULL OR religion = '{}' OR religion && $(religion)`, + {religion} + ), + !!wants_kids_strength && wants_kids_strength !== -1 && where( diff --git a/common/src/api/schema.ts b/common/src/api/schema.ts index e068cc68..f55ae9fc 100644 --- a/common/src/api/schema.ts +++ b/common/src/api/schema.ts @@ -402,6 +402,7 @@ export const API = (_apiTypeCheck = { pref_age_max: z.coerce.number().optional(), drinks_min: z.coerce.number().optional(), drinks_max: z.coerce.number().optional(), + religion: arraybeSchema.optional(), pref_relation_styles: arraybeSchema.optional(), pref_romantic_styles: arraybeSchema.optional(), diet: arraybeSchema.optional(), diff --git a/common/src/api/zod-types.ts b/common/src/api/zod-types.ts index 79ee611f..ba031fc6 100644 --- a/common/src/api/zod-types.ts +++ b/common/src/api/zod-types.ts @@ -77,6 +77,7 @@ const optionalProfilesSchema = z.object({ political_beliefs: z.array(z.string()).optional(), religious_belief_strength: z.number().optional(), religious_beliefs: z.string().optional(), + religion: z.array(z.string()).optional(), ethnicity: z.array(z.string()).optional(), born_in_location: z.string().optional(), height_in_inches: z.number().optional(), diff --git a/common/src/filters.ts b/common/src/filters.ts index d6124795..ebcfc5aa 100644 --- a/common/src/filters.ts +++ b/common/src/filters.ts @@ -32,6 +32,7 @@ export type FilterFields = { | 'pref_gender' | 'pref_age_min' | 'pref_age_max' + | 'religion' > export const orderProfiles = ( @@ -70,6 +71,7 @@ export const initialFilters: Partial = { pref_romantic_styles: undefined, diet: undefined, political_beliefs: undefined, + religion: undefined, pref_gender: undefined, shortBio: undefined, drinks_min: undefined, diff --git a/common/src/searches.ts b/common/src/searches.ts index ec301311..7f7ab0ca 100644 --- a/common/src/searches.ts +++ b/common/src/searches.ts @@ -17,6 +17,7 @@ const filterLabels: Record = { wants_kids_strength: "Kids", is_smoker: "", pref_relation_styles: "Seeking", + religion: "", pref_gender: "", orderBy: "", diet: "Diet", diff --git a/common/src/util/sorting.ts b/common/src/util/sorting.ts new file mode 100644 index 00000000..4eb6bb68 --- /dev/null +++ b/common/src/util/sorting.ts @@ -0,0 +1,20 @@ +import {MAX_INT} from "common/constants"; + +export function getSortedOptions(options: string[], order: string[] | Record) { + let parsedOrder: string[] + if (Array.isArray(order)) { + parsedOrder = order + } else { + parsedOrder = Object.keys(order) + } + return options + .slice() + .sort((a, b) => { + const ia = parsedOrder.indexOf(a as any) + const ib = parsedOrder.indexOf(b as any) + const sa = ia === -1 ? MAX_INT : ia + const sb = ib === -1 ? MAX_INT : ib + if (sa !== sb) return sa - sb + return String(a).localeCompare(String(b)) + }); +} \ No newline at end of file diff --git a/web/components/filters/desktop-filters.tsx b/web/components/filters/desktop-filters.tsx index 5a0239c7..c8046e19 100644 --- a/web/components/filters/desktop-filters.tsx +++ b/web/components/filters/desktop-filters.tsx @@ -26,11 +26,12 @@ import {PoliticalFilter, PoliticalFilterText} from "web/components/filters/polit import {GiFruitBowl} from "react-icons/gi"; import {RiScales3Line} from "react-icons/ri"; import {EducationFilter, EducationFilterText} from "web/components/filters/education-filter"; -import {LuGraduationCap} from "react-icons/lu"; +import {LuCigarette, LuGraduationCap} from "react-icons/lu"; import {DrinksFilter, DrinksFilterText} from "web/components/filters/drinks-filter"; import {MdLocalBar} from 'react-icons/md' -import {SmokerFilter, SmokerFilterText} from 'web/components/filters/smoker-filter' -import {LuCigarette} from 'react-icons/lu' +import {SmokerFilter, SmokerFilterText} from "web/components/filters/smoker-filter" +import {ReligionFilter, ReligionFilterText} from "web/components/filters/religion-filter"; +import {PiHandsPrayingBold} from "react-icons/pi"; export function DesktopFilters(props: { filters: Partial @@ -381,6 +382,33 @@ export function DesktopFilters(props: { menuWidth="w-50" /> + {/* RELIGION */} + ( + + + + + } + /> + )} + dropdownMenuContent={ + + } + popoverClassName="bg-canvas-50" + menuWidth="w-50" + /> + {/* SMOKER */} ( @@ -399,7 +427,7 @@ export function DesktopFilters(props: { )} dropdownMenuContent={ - + } popoverClassName="bg-canvas-50" diff --git a/web/components/filters/education-filter.tsx b/web/components/filters/education-filter.tsx index 0da4a776..d131aa9f 100644 --- a/web/components/filters/education-filter.tsx +++ b/web/components/filters/education-filter.tsx @@ -5,7 +5,7 @@ import {FilterFields} from "common/filters"; import {EDUCATION_CHOICES} from "web/components/filters/choices"; import {convertEducationTypes} from "web/lib/util/convert-types"; import stringOrStringArrayToText from "web/lib/util/string-or-string-array-to-text"; -import {MAX_INT} from "common/constants"; +import {getSortedOptions} from "common/util/sorting"; export function EducationFilterText(props: { options: string[] | undefined @@ -20,20 +20,9 @@ export function EducationFilterText(props: { ) } - const order = Object.values(EDUCATION_CHOICES) - const sortedOptions = options - .slice() - .sort((a, b) => { - const ia = order.indexOf(a as any) - const ib = order.indexOf(b as any) - const sa = ia === -1 ? MAX_INT : ia - const sb = ib === -1 ? MAX_INT : ib - if (sa !== sb) return sa - sb - return String(a).localeCompare(String(b)) - }) - + const sortedOptions = getSortedOptions(options, EDUCATION_CHOICES) const convertedTypes = sortedOptions.map((r) => convertEducationTypes(r as any)) - + return (
diff --git a/web/components/filters/mobile-filters.tsx b/web/components/filters/mobile-filters.tsx index 27b1e3d6..d1878b05 100644 --- a/web/components/filters/mobile-filters.tsx +++ b/web/components/filters/mobile-filters.tsx @@ -24,6 +24,7 @@ import {PoliticalFilter, PoliticalFilterText} from "web/components/filters/polit import {EducationFilter, EducationFilterText} from "web/components/filters/education-filter"; import {DrinksFilter, DrinksFilterText, getNoMinMaxDrinks} from "./drinks-filter"; import {SmokerFilter, SmokerFilterText} from "./smoker-filter"; +import {ReligionFilter, ReligionFilterText} from "web/components/filters/religion-filter"; function MobileFilters(props: { filters: Partial @@ -329,6 +330,24 @@ function MobileFilters(props: { + {/* RELIGION */} + + } + > + + + {/* EDUCATION */} Any religion + ) + } + + const sortedOptions = getSortedOptions(options, RELIGION_CHOICES) + const convertedTypes = sortedOptions.map((r) => convertReligionTypes(r as any)) + + return ( +
+ + {stringOrStringArrayToText({ + text: convertedTypes, + capitalizeFirstLetterOption: true, + })}{' '} + +
+ ) +} + +export function ReligionFilter(props: { + filters: Partial + updateFilter: (newState: Partial) => void +}) { + const {filters, updateFilter} = props + return ( + <> + { + updateFilter({religion: c}) + }} + /> + + ) +} diff --git a/web/components/filters/use-filters.ts b/web/components/filters/use-filters.ts index be33b13c..e6451559 100644 --- a/web/components/filters/use-filters.ts +++ b/web/components/filters/use-filters.ts @@ -73,6 +73,7 @@ export const useFilters = (you: Profile | undefined) => { pref_romantic_styles: you?.pref_romantic_styles?.length ? you.pref_romantic_styles : undefined, diet: you?.diet?.length ? you.diet : undefined, political_beliefs: you?.political_beliefs?.length ? you.political_beliefs : undefined, + religion: you?.religion?.length ? you.religion : undefined, wants_kids_strength: wantsKidsDatabaseToWantsKidsFilter( (you?.wants_kids_strength ?? 2) as wantsKidsDatabase ), @@ -93,6 +94,7 @@ export const useFilters = (you: Profile | undefined) => { && isEqual(new Set(filters.pref_relation_styles), new Set(you.pref_relation_styles)) && isEqual(new Set(filters.diet), new Set(you.diet)) && isEqual(new Set(filters.political_beliefs), new Set(you.political_beliefs)) + && isEqual(new Set(filters.religion), new Set(you.religion)) && filters.pref_age_max == yourFilters.pref_age_max && filters.pref_age_min == yourFilters.pref_age_min && filters.wants_kids_strength == yourFilters.wants_kids_strength