Skip to content

Commit 591798e

Browse files
committed
Add filter for education level
1 parent acdd82a commit 591798e

File tree

13 files changed

+168
-29
lines changed

13 files changed

+168
-29
lines changed

backend/api/src/get-profiles.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export type profileQueryType = {
1212
// Search and filter parameters
1313
name?: string | undefined,
1414
genders?: String[] | undefined,
15+
education_levels?: String[] | undefined,
1516
pref_gender?: String[] | undefined,
1617
pref_age_min?: number | undefined,
1718
pref_age_max?: number | undefined,
@@ -44,6 +45,7 @@ export const loadProfiles = async (props: profileQueryType) => {
4445
after,
4546
name,
4647
genders,
48+
education_levels,
4749
pref_gender,
4850
pref_age_min,
4951
pref_age_max,
@@ -82,6 +84,7 @@ export const loadProfiles = async (props: profileQueryType) => {
8284
(l) =>
8385
(!name || l.user.name.toLowerCase().includes(name.toLowerCase())) &&
8486
(!genders || genders.includes(l.gender)) &&
87+
(!education_levels || education_levels.includes(l.education_level ?? '')) &&
8588
(!pref_gender || intersection(pref_gender, l.pref_gender).length) &&
8689
(!pref_age_min || (l.age ?? MAX_INT) >= pref_age_min) &&
8790
(!pref_age_max || (l.age ?? MIN_INT) <= pref_age_max) &&
@@ -147,7 +150,9 @@ export const loadProfiles = async (props: profileQueryType) => {
147150
{word}
148151
)),
149152

150-
genders?.length && where(`gender = ANY($(gender))`, {gender: genders}),
153+
genders?.length && where(`gender = ANY($(genders))`, {genders}),
154+
155+
education_levels?.length && where(`education_level = ANY($(education_levels))`, {education_levels}),
151156

152157
pref_gender?.length &&
153158
where(`pref_gender is NULL or pref_gender = '{}' OR pref_gender && $(pref_gender)`, {pref_gender}),

common/src/api/schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ export const API = (_apiTypeCheck = {
345345
// Search and filter parameters
346346
name: z.string().optional(),
347347
genders: arraybeSchema.optional(),
348+
education_levels: arraybeSchema.optional(),
348349
pref_gender: arraybeSchema.optional(),
349350
pref_age_min: z.coerce.number().optional(),
350351
pref_age_max: z.coerce.number().optional(),

common/src/constants.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export const MAX_INT = 99999
2-
export const MIN_INT = -MAX_INT
1+
export const MIN_INT = Number.MIN_SAFE_INTEGER
2+
export const MAX_INT = Number.MAX_SAFE_INTEGER
33

44
export const supportEmail = '[email protected]';
55
// export const marketingEmail = '[email protected]';

common/src/filters.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type FilterFields = {
1515
lon: number | null
1616
radius: number | null
1717
genders: string[]
18+
education_levels: string[]
1819
name: string | undefined
1920
shortBio: boolean | undefined
2021
} & Pick<
@@ -57,6 +58,7 @@ export const initialFilters: Partial<FilterFields> = {
5758
radius: undefined,
5859
name: undefined,
5960
genders: undefined,
61+
education_levels: undefined,
6062
pref_age_max: undefined,
6163
pref_age_min: undefined,
6264
has_kids: undefined,

common/src/searches.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const filterLabels: Record<string, string> = {
88
location: "",
99
name: "Searching",
1010
genders: "",
11+
education_levels: "Education",
1112
pref_age_max: "Max age",
1213
pref_age_min: "Min age",
1314
has_kids: "",

web/components/filters/choices.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ export const DIET_CHOICES = {
3737
Other: 'other',
3838
}
3939

40+
export const EDUCATION_CHOICES = {
41+
None: 'none',
42+
'High school': 'high-school',
43+
'Some college': 'some-college',
44+
Bachelors: 'bachelors',
45+
Masters: 'masters',
46+
PhD: 'doctorate',
47+
}
48+
4049
export const REVERTED_RELATIONSHIP_CHOICES = Object.fromEntries(
4150
Object.entries(RELATIONSHIP_CHOICES).map(([key, value]) => [value, key])
4251
);
@@ -51,4 +60,8 @@ export const REVERTED_POLITICAL_CHOICES = Object.fromEntries(
5160

5261
export const REVERTED_DIET_CHOICES = Object.fromEntries(
5362
Object.entries(DIET_CHOICES).map(([key, value]) => [value, key])
63+
);
64+
65+
export const REVERTED_EDUCATION_CHOICES = Object.fromEntries(
66+
Object.entries(EDUCATION_CHOICES).map(([key, value]) => [value, key])
5467
);

web/components/filters/desktop-filters.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import {DietFilter, DietFilterText} from "web/components/filters/diet-filter";
2525
import {PoliticalFilter, PoliticalFilterText} from "web/components/filters/political-filter";
2626
import {GiFruitBowl} from "react-icons/gi";
2727
import {RiScales3Line} from "react-icons/ri";
28+
import {EducationFilter, EducationFilterText} from "web/components/filters/education-filter";
29+
import {LuGraduationCap} from "react-icons/lu";
2830

2931
export function DesktopFilters(props: {
3032
filters: Partial<FilterFields>
@@ -349,6 +351,30 @@ export function DesktopFilters(props: {
349351
menuWidth="w-50"
350352
/>
351353

354+
{/* EDUCATION */}
355+
<CustomizeableDropdown
356+
buttonContent={(open: boolean) => (
357+
<DropdownButton
358+
content={
359+
<Row className="items-center gap-1">
360+
<LuGraduationCap className="h-4 w-4"/>
361+
<EducationFilterText
362+
options={filters.education_levels as string[]}
363+
highlightedClass={open ? 'text-primary-500' : undefined}
364+
/>
365+
</Row>
366+
}
367+
open={open}
368+
/>
369+
)}
370+
dropdownMenuContent={
371+
<Col>
372+
<EducationFilter filters={filters} updateFilter={updateFilter}/>
373+
</Col>
374+
}
375+
popoverClassName="bg-canvas-50"
376+
/>
377+
352378
{/* Short Bios */}
353379
<ShortBioToggle
354380
updateFilter={updateFilter}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import clsx from 'clsx'
2+
import {MultiCheckbox} from 'web/components/multi-checkbox'
3+
4+
import {FilterFields} from "common/filters";
5+
import {EDUCATION_CHOICES} from "web/components/filters/choices";
6+
import {convertEducationTypes} from "web/lib/util/convert-types";
7+
import stringOrStringArrayToText from "web/lib/util/string-or-string-array-to-text";
8+
import {MAX_INT} from "common/constants";
9+
10+
export function EducationFilterText(props: {
11+
options: string[] | undefined
12+
highlightedClass?: string
13+
}) {
14+
const {options, highlightedClass} = props
15+
const length = (options ?? []).length
16+
17+
if (!options || length < 1) {
18+
return (
19+
<span className={clsx('text-semibold', highlightedClass)}>Any education</span>
20+
)
21+
}
22+
23+
const order = Object.values(EDUCATION_CHOICES)
24+
const sortedOptions = options
25+
.slice()
26+
.sort((a, b) => {
27+
const ia = order.indexOf(a as any)
28+
const ib = order.indexOf(b as any)
29+
const sa = ia === -1 ? MAX_INT : ia
30+
const sb = ib === -1 ? MAX_INT : ib
31+
if (sa !== sb) return sa - sb
32+
return String(a).localeCompare(String(b))
33+
})
34+
35+
const convertedTypes = sortedOptions.map((r) => convertEducationTypes(r as any))
36+
37+
return (
38+
<div>
39+
<span className={clsx('font-semibold', highlightedClass)}>
40+
{stringOrStringArrayToText({
41+
text: convertedTypes,
42+
capitalizeFirstLetterOption: true,
43+
})}{' '}
44+
</span>
45+
</div>
46+
)
47+
}
48+
49+
export function EducationFilter(props: {
50+
filters: Partial<FilterFields>
51+
updateFilter: (newState: Partial<FilterFields>) => void
52+
}) {
53+
const {filters, updateFilter} = props
54+
return (
55+
<>
56+
<MultiCheckbox
57+
selected={filters.education_levels ?? []}
58+
choices={EDUCATION_CHOICES}
59+
onChange={(c) => {
60+
updateFilter({education_levels: c})
61+
}}
62+
/>
63+
</>
64+
)
65+
}

web/components/filters/mobile-filters.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import {DietType, PoliticalType, RelationshipType, RomanticType} from 'web/lib/u
1414
import {FilterFields} from "common/filters";
1515
import {ShortBioToggle} from "web/components/filters/short-bio-toggle";
1616
import {PrefGenderFilter, PrefGenderFilterText} from "./pref-gender-filter"
17-
import {KidsLabel, WantsKidsFilter, WantsKidsIcon} from "web/components/filters/wants-kids-filter";
17+
import {KidsLabel, WantsKidsFilter} from "web/components/filters/wants-kids-filter";
1818
import {wantsKidsLabels} from "common/wants-kids";
19-
import {FaChild} from "react-icons/fa"
2019
import {HasKidsFilter, HasKidsLabel} from "./has-kids-filter"
2120
import {hasKidsLabels} from "common/has-kids";
2221
import {RomanticFilter, RomanticFilterText} from "web/components/filters/romantic-filter";
2322
import {DietFilter, DietFilterText} from "web/components/filters/diet-filter";
2423
import {PoliticalFilter, PoliticalFilterText} from "web/components/filters/political-filter";
24+
import {EducationFilter, EducationFilterText} from "web/components/filters/education-filter";
2525

2626
function MobileFilters(props: {
2727
filters: Partial<FilterFields>
@@ -198,7 +198,7 @@ function MobileFilters(props: {
198198
filters.wants_kids_strength != null &&
199199
filters.wants_kids_strength !== -1
200200
}
201-
// icon={<WantsKidsIcon strength={filters.wants_kids_strength ?? -1}/>}
201+
// icon={<WantsKidsIcon strength={filters.wants_kids_strength ?? -1}/>}
202202
selection={
203203
<KidsLabel
204204
strength={filters.wants_kids_strength ?? -1}
@@ -221,7 +221,7 @@ function MobileFilters(props: {
221221
openFilter={openFilter}
222222
setOpenFilter={setOpenFilter}
223223
isActive={filters.has_kids != null && filters.has_kids !== -1}
224-
// icon={<FaChild className="text-ink-900 h-4 w-4"/>}
224+
// icon={<FaChild className="text-ink-900 h-4 w-4"/>}
225225
selection={
226226
<HasKidsLabel
227227
has_kids={filters.has_kids ?? -1}
@@ -279,6 +279,24 @@ function MobileFilters(props: {
279279
<PoliticalFilter filters={filters} updateFilter={updateFilter}/>
280280
</MobileFilterSection>
281281

282+
{/* EDUCATION */}
283+
<MobileFilterSection
284+
title="Education"
285+
openFilter={openFilter}
286+
setOpenFilter={setOpenFilter}
287+
isActive={hasAny(filters.education_levels)}
288+
selection={
289+
<EducationFilterText
290+
options={filters.education_levels as string[]}
291+
highlightedClass={
292+
hasAny(filters.education_levels) ? 'text-primary-600' : 'text-ink-900'
293+
}
294+
/>
295+
}
296+
>
297+
<EducationFilter filters={filters} updateFilter={updateFilter}/>
298+
</MobileFilterSection>
299+
282300
{/* Short Bios */}
283301
<Col className="p-4 pb-2">
284302
<ShortBioToggle

web/components/filters/use-filters.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,9 @@ export const useFilters = (you: Profile | undefined) => {
7878
}
7979

8080
const yourFilters: Partial<FilterFields> = {
81-
genders: you?.pref_gender?.length ? you.pref_gender : undefined,
8281
pref_gender: you?.gender?.length ? [you.gender] : undefined,
82+
genders: you?.pref_gender?.length ? you.pref_gender : undefined,
83+
education_levels: you?.education_level ? [you.education_level] : undefined,
8384
pref_age_max: (you?.pref_age_max ?? MAX_INT) < 100 ? you?.pref_age_max : undefined,
8485
pref_age_min: (you?.pref_age_min ?? MIN_INT) > 18 ? you?.pref_age_min : undefined,
8586
pref_relation_styles: you?.pref_relation_styles.length ? you.pref_relation_styles : undefined,
@@ -99,6 +100,7 @@ export const useFilters = (you: Profile | undefined) => {
99100
!!you
100101
&& (!location || location.id === you.geodb_city_id)
101102
&& isEqual(filters.genders?.length ? filters.genders : undefined, yourFilters.genders?.length ? yourFilters.genders : undefined)
103+
&& isEqual(new Set(filters.education_levels), new Set([you.education_level]))
102104
&& (!you.gender || filters.pref_gender?.length == 1 && isEqual(filters.pref_gender?.length ? filters.pref_gender[0] : undefined, you.gender))
103105
&& isEqual(new Set(filters.pref_romantic_styles), new Set(you.pref_romantic_styles))
104106
&& isEqual(new Set(filters.pref_relation_styles), new Set(you.pref_relation_styles))

0 commit comments

Comments
 (0)