Add filter: religion

This commit is contained in:
MartinBraquet
2025-10-25 14:22:11 +02:00
parent 940c1f5692
commit a4bb184e95
11 changed files with 146 additions and 18 deletions

View File

@@ -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(

View File

@@ -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(),

View File

@@ -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(),

View File

@@ -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<FilterFields> = {
pref_romantic_styles: undefined,
diet: undefined,
political_beliefs: undefined,
religion: undefined,
pref_gender: undefined,
shortBio: undefined,
drinks_min: undefined,

View File

@@ -17,6 +17,7 @@ const filterLabels: Record<string, string> = {
wants_kids_strength: "Kids",
is_smoker: "",
pref_relation_styles: "Seeking",
religion: "",
pref_gender: "",
orderBy: "",
diet: "Diet",

View File

@@ -0,0 +1,20 @@
import {MAX_INT} from "common/constants";
export function getSortedOptions(options: string[], order: string[] | Record<string, string>) {
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))
});
}

View File

@@ -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<FilterFields>
@@ -381,6 +382,33 @@ export function DesktopFilters(props: {
menuWidth="w-50"
/>
{/* RELIGION */}
<CustomizeableDropdown
buttonContent={(open) => (
<DropdownButton
open={open}
content={
<Row className="items-center gap-1">
<PiHandsPrayingBold className="h-4 w-4"/>
<ReligionFilterText
options={
filters.religion as
| string[]
| undefined
}
highlightedClass={open ? 'text-primary-500' : undefined}
/>
</Row>
}
/>
)}
dropdownMenuContent={
<ReligionFilter filters={filters} updateFilter={updateFilter}/>
}
popoverClassName="bg-canvas-50"
menuWidth="w-50"
/>
{/* SMOKER */}
<CustomizeableDropdown
buttonContent={(open) => (
@@ -399,7 +427,7 @@ export function DesktopFilters(props: {
)}
dropdownMenuContent={
<Col className="mx-2 mb-4">
<SmokerFilter filters={filters} updateFilter={updateFilter} />
<SmokerFilter filters={filters} updateFilter={updateFilter}/>
</Col>
}
popoverClassName="bg-canvas-50"

View File

@@ -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 (
<div>
<span className={clsx('font-semibold', highlightedClass)}>

View File

@@ -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<FilterFields>
@@ -329,6 +330,24 @@ function MobileFilters(props: {
<PoliticalFilter filters={filters} updateFilter={updateFilter}/>
</MobileFilterSection>
{/* RELIGION */}
<MobileFilterSection
title="Religion"
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.religion || undefined)}
selection={
<ReligionFilterText
options={filters.religion as string[]}
highlightedClass={
hasAny(filters.religion || undefined) ? 'text-primary-600' : 'text-ink-900'
}
/>
}
>
<ReligionFilter filters={filters} updateFilter={updateFilter}/>
</MobileFilterSection>
{/* EDUCATION */}
<MobileFilterSection
title="Education"

View File

@@ -0,0 +1,55 @@
import clsx from 'clsx'
import {MultiCheckbox} from 'web/components/multi-checkbox'
import {FilterFields} from "common/filters";
import {RELIGION_CHOICES} from "web/components/filters/choices";
import {convertReligionTypes} from "web/lib/util/convert-types";
import stringOrStringArrayToText from "web/lib/util/string-or-string-array-to-text";
import {getSortedOptions} from "common/util/sorting";
export function ReligionFilterText(props: {
options: string[] | undefined
highlightedClass?: string
}) {
const {options, highlightedClass} = props
const length = (options ?? []).length
if (!options || length < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any religion</span>
)
}
const sortedOptions = getSortedOptions(options, RELIGION_CHOICES)
const convertedTypes = sortedOptions.map((r) => convertReligionTypes(r as any))
return (
<div>
<span className={clsx('font-semibold', highlightedClass)}>
{stringOrStringArrayToText({
text: convertedTypes,
capitalizeFirstLetterOption: true,
})}{' '}
</span>
</div>
)
}
export function ReligionFilter(props: {
filters: Partial<FilterFields>
updateFilter: (newState: Partial<FilterFields>) => void
}) {
const {filters, updateFilter} = props
return (
<>
<MultiCheckbox
selected={filters.religion ?? []}
choices={RELIGION_CHOICES}
onChange={(c) => {
updateFilter({religion: c})
}}
/>
</>
)
}

View File

@@ -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