mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-04-04 14:53:33 -04:00
Add diet
This commit is contained in:
@@ -17,6 +17,7 @@ export type profileQueryType = {
|
||||
pref_age_max?: number | undefined,
|
||||
pref_relation_styles?: String[] | undefined,
|
||||
pref_romantic_styles?: String[] | undefined,
|
||||
diet?: String[] | undefined,
|
||||
wants_kids_strength?: number | undefined,
|
||||
has_kids?: number | undefined,
|
||||
is_smoker?: boolean | undefined,
|
||||
@@ -47,6 +48,7 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
pref_age_max,
|
||||
pref_relation_styles,
|
||||
pref_romantic_styles,
|
||||
diet,
|
||||
wants_kids_strength,
|
||||
has_kids,
|
||||
is_smoker,
|
||||
@@ -85,6 +87,8 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
intersection(pref_relation_styles, l.pref_relation_styles).length) &&
|
||||
(!pref_romantic_styles ||
|
||||
intersection(pref_romantic_styles, l.pref_romantic_styles).length) &&
|
||||
(!diet ||
|
||||
intersection(diet, l.diet).length) &&
|
||||
(!wants_kids_strength ||
|
||||
wants_kids_strength == -1 ||
|
||||
!l.wants_kids_strength ||
|
||||
@@ -162,6 +166,12 @@ export const loadProfiles = async (props: profileQueryType) => {
|
||||
{pref_romantic_styles}
|
||||
),
|
||||
|
||||
diet?.length &&
|
||||
where(
|
||||
`diet IS NULL OR diet = '{}' OR diet && $(diet)`,
|
||||
{diet}
|
||||
),
|
||||
|
||||
!!wants_kids_strength &&
|
||||
wants_kids_strength !== -1 &&
|
||||
where(
|
||||
|
||||
@@ -46,7 +46,7 @@ export const sinclairProfile: ProfileRow = {
|
||||
has_kids: 0,
|
||||
is_smoker: false,
|
||||
drinks_per_month: 0,
|
||||
is_vegetarian_or_vegan: null,
|
||||
diet: null,
|
||||
political_beliefs: ['e/acc', 'libertarian'],
|
||||
religious_belief_strength: null,
|
||||
religious_beliefs: null,
|
||||
@@ -147,7 +147,7 @@ export const jamesProfile: ProfileRow = {
|
||||
has_kids: 0,
|
||||
is_smoker: false,
|
||||
drinks_per_month: 5,
|
||||
is_vegetarian_or_vegan: null,
|
||||
diet: null,
|
||||
political_beliefs: ['libertarian'],
|
||||
religious_belief_strength: null,
|
||||
religious_beliefs: '',
|
||||
|
||||
@@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS profiles (
|
||||
height_in_inches INTEGER,
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY NOT NULL,
|
||||
is_smoker BOOLEAN,
|
||||
is_vegetarian_or_vegan BOOLEAN,
|
||||
diet TEXT[],
|
||||
last_modification_time TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||||
looking_for_matches BOOLEAN DEFAULT TRUE NOT NULL,
|
||||
messaging_status TEXT DEFAULT 'open'::TEXT NOT NULL,
|
||||
|
||||
@@ -350,6 +350,7 @@ export const API = (_apiTypeCheck = {
|
||||
pref_age_max: z.coerce.number().optional(),
|
||||
pref_relation_styles: arraybeSchema.optional(),
|
||||
pref_romantic_styles: arraybeSchema.optional(),
|
||||
diet: arraybeSchema.optional(),
|
||||
wants_kids_strength: z.coerce.number().optional(),
|
||||
has_kids: z.coerce.number().optional(),
|
||||
is_smoker: z.coerce.boolean().optional(),
|
||||
|
||||
@@ -80,7 +80,7 @@ const optionalProfilesSchema = z.object({
|
||||
education_level: z.string().optional(),
|
||||
is_smoker: z.boolean().optional(),
|
||||
drinks_per_month: z.number().min(0).optional(),
|
||||
is_vegetarian_or_vegan: z.boolean().optional(),
|
||||
diet: z.array(z.string()).optional(),
|
||||
has_kids: z.number().min(0).optional(),
|
||||
university: z.string().optional(),
|
||||
occupation_title: z.string().optional(),
|
||||
|
||||
@@ -22,6 +22,7 @@ export type FilterFields = {
|
||||
| 'wants_kids_strength'
|
||||
| 'pref_relation_styles'
|
||||
| 'pref_romantic_styles'
|
||||
| 'diet'
|
||||
| 'is_smoker'
|
||||
| 'has_kids'
|
||||
| 'pref_gender'
|
||||
@@ -62,6 +63,7 @@ export const initialFilters: Partial<FilterFields> = {
|
||||
is_smoker: undefined,
|
||||
pref_relation_styles: undefined,
|
||||
pref_romantic_styles: undefined,
|
||||
diet: undefined,
|
||||
pref_gender: undefined,
|
||||
shortBio: undefined,
|
||||
orderBy: 'created_time',
|
||||
|
||||
@@ -454,7 +454,7 @@ export type Database = {
|
||||
height_in_inches: number | null
|
||||
id: number
|
||||
is_smoker: boolean | null
|
||||
is_vegetarian_or_vegan: boolean | null
|
||||
diet: string[] | null
|
||||
last_modification_time: string
|
||||
looking_for_matches: boolean
|
||||
messaging_status: string
|
||||
@@ -502,7 +502,7 @@ export type Database = {
|
||||
height_in_inches?: number | null
|
||||
id?: number
|
||||
is_smoker?: boolean | null
|
||||
is_vegetarian_or_vegan?: boolean | null
|
||||
diet?: string[] | null
|
||||
last_modification_time?: string
|
||||
looking_for_matches?: boolean
|
||||
messaging_status?: string
|
||||
@@ -550,7 +550,7 @@ export type Database = {
|
||||
height_in_inches?: number | null
|
||||
id?: number
|
||||
is_smoker?: boolean | null
|
||||
is_vegetarian_or_vegan?: boolean | null
|
||||
diet?: string[] | null
|
||||
last_modification_time?: string
|
||||
looking_for_matches?: boolean
|
||||
messaging_status?: string
|
||||
|
||||
@@ -27,6 +27,16 @@ export const POLITICAL_CHOICES = {
|
||||
'Independent / Other': 'other',
|
||||
}
|
||||
|
||||
export const DIET_CHOICES = {
|
||||
Omnivore: 'omnivore',
|
||||
Vegetarian: 'veg',
|
||||
Vegan: 'vegan',
|
||||
Keto: 'keto',
|
||||
Paleo: 'paleo',
|
||||
Pescetarian: 'pescetarian',
|
||||
Other: 'other',
|
||||
}
|
||||
|
||||
export const REVERTED_RELATIONSHIP_CHOICES = Object.fromEntries(
|
||||
Object.entries(RELATIONSHIP_CHOICES).map(([key, value]) => [value, key])
|
||||
);
|
||||
@@ -37,4 +47,8 @@ export const REVERTED_ROMANTIC_CHOICES = Object.fromEntries(
|
||||
|
||||
export const REVERTED_POLITICAL_CHOICES = Object.fromEntries(
|
||||
Object.entries(POLITICAL_CHOICES).map(([key, value]) => [value, key])
|
||||
);
|
||||
|
||||
export const REVERTED_DIET_CHOICES = Object.fromEntries(
|
||||
Object.entries(DIET_CHOICES).map(([key, value]) => [value, key])
|
||||
);
|
||||
@@ -1,5 +1,5 @@
|
||||
import {ChevronDownIcon, ChevronUpIcon} from '@heroicons/react/outline'
|
||||
import {RelationshipType, RomanticType} from 'web/lib/util/convert-relationship-type'
|
||||
import {DietType, RelationshipType, RomanticType} from 'web/lib/util/convert-types'
|
||||
import {ReactNode} from 'react'
|
||||
import {FaUserGroup} from 'react-icons/fa6'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
@@ -21,6 +21,7 @@ import {hasKidsLabels} from "common/has-kids";
|
||||
import {HasKidsLabel} from "web/components/filters/has-kids-filter";
|
||||
import {RomanticFilter, RomanticFilterText} from "web/components/filters/romantic-filter";
|
||||
import {FaHeart} from "react-icons/fa";
|
||||
import {DietFilter, DietFilterText} from "web/components/filters/diet-filter";
|
||||
|
||||
export function DesktopFilters(props: {
|
||||
filters: Partial<FilterFields>
|
||||
@@ -171,7 +172,7 @@ export function DesktopFilters(props: {
|
||||
|
||||
{includeRelationshipFilters && <>
|
||||
|
||||
{/* CONNECTION */}
|
||||
{/* RELATIONSHIP STYLE */}
|
||||
<CustomizeableDropdown
|
||||
buttonContent={(open) => (
|
||||
<DropdownButton
|
||||
@@ -291,6 +292,32 @@ export function DesktopFilters(props: {
|
||||
</>
|
||||
}
|
||||
|
||||
{/* DIET */}
|
||||
<CustomizeableDropdown
|
||||
buttonContent={(open) => (
|
||||
<DropdownButton
|
||||
open={open}
|
||||
content={
|
||||
<Row className="items-center gap-1">
|
||||
<DietFilterText
|
||||
options={
|
||||
filters.diet as
|
||||
| DietType[]
|
||||
| undefined
|
||||
}
|
||||
highlightedClass={open ? 'text-primary-500' : undefined}
|
||||
/>
|
||||
</Row>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
dropdownMenuContent={
|
||||
<DietFilter filters={filters} updateFilter={updateFilter}/>
|
||||
}
|
||||
popoverClassName="bg-canvas-50"
|
||||
menuWidth="w-50"
|
||||
/>
|
||||
|
||||
{/* Short Bios */}
|
||||
<ShortBioToggle
|
||||
updateFilter={updateFilter}
|
||||
|
||||
61
web/components/filters/diet-filter.tsx
Normal file
61
web/components/filters/diet-filter.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import clsx from 'clsx'
|
||||
import {convertDietTypes, DietType,} from 'web/lib/util/convert-types'
|
||||
import stringOrStringArrayToText from 'web/lib/util/string-or-string-array-to-text'
|
||||
import {MultiCheckbox} from 'web/components/multi-checkbox'
|
||||
|
||||
import {DIET_CHOICES} from "web/components/filters/choices";
|
||||
import {FilterFields} from "common/filters";
|
||||
|
||||
export function DietFilterText(props: {
|
||||
options: DietType[] | undefined
|
||||
highlightedClass?: string
|
||||
}) {
|
||||
const {options, highlightedClass} = props
|
||||
const length = (options ?? []).length
|
||||
|
||||
if (!options || length < 1) {
|
||||
return (
|
||||
<span className={clsx('text-semibold', highlightedClass)}>Any diet</span>
|
||||
)
|
||||
}
|
||||
|
||||
const convertedTypes = options.map((r) =>
|
||||
convertDietTypes(r)
|
||||
)
|
||||
|
||||
if (length > 1) {
|
||||
return (
|
||||
<span>
|
||||
<span className={clsx('font-semibold', highlightedClass)}>
|
||||
Multiple
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<span className={clsx('font-semibold', highlightedClass)}>
|
||||
{stringOrStringArrayToText({
|
||||
text: convertedTypes,
|
||||
capitalizeFirstLetterOption: true,
|
||||
})}{' '}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function DietFilter(props: {
|
||||
filters: Partial<FilterFields>
|
||||
updateFilter: (newState: Partial<FilterFields>) => void
|
||||
}) {
|
||||
const {filters, updateFilter} = props
|
||||
return (
|
||||
<MultiCheckbox
|
||||
selected={filters.diet ?? []}
|
||||
choices={DIET_CHOICES as any}
|
||||
onChange={(c) => {
|
||||
updateFilter({diet: c})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import {RelationshipFilter, RelationshipFilterText,} from './relationship-filter
|
||||
import {MyMatchesToggle} from './my-matches-toggle'
|
||||
import {Profile} from 'common/profiles/profile'
|
||||
import {Gender} from 'common/gender'
|
||||
import {RelationshipType, RomanticType} from 'web/lib/util/convert-relationship-type'
|
||||
import {DietType, RelationshipType, RomanticType} from 'web/lib/util/convert-types'
|
||||
import {FilterFields} from "common/filters";
|
||||
import {ShortBioToggle} from "web/components/filters/short-bio-toggle";
|
||||
import {PrefGenderFilter, PrefGenderFilterText} from "./pref-gender-filter"
|
||||
@@ -20,6 +20,7 @@ import {FaChild} from "react-icons/fa"
|
||||
import {HasKidsFilter, HasKidsLabel} from "./has-kids-filter"
|
||||
import {hasKidsLabels} from "common/has-kids";
|
||||
import {RomanticFilter, RomanticFilterText} from "web/components/filters/romantic-filter";
|
||||
import {DietFilter, DietFilterText} from "web/components/filters/diet-filter";
|
||||
|
||||
function MobileFilters(props: {
|
||||
filters: Partial<FilterFields>
|
||||
@@ -237,6 +238,26 @@ function MobileFilters(props: {
|
||||
|
||||
</>}
|
||||
|
||||
{/* DIET */}
|
||||
<MobileFilterSection
|
||||
title="Diet"
|
||||
openFilter={openFilter}
|
||||
setOpenFilter={setOpenFilter}
|
||||
isActive={hasAny(filters.diet || undefined)}
|
||||
selection={
|
||||
<DietFilterText
|
||||
options={filters.diet as DietType[]}
|
||||
highlightedClass={
|
||||
hasAny(filters.diet || undefined)
|
||||
? 'text-primary-600'
|
||||
: 'text-ink-900'
|
||||
}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DietFilter filters={filters} updateFilter={updateFilter}/>
|
||||
</MobileFilterSection>
|
||||
|
||||
{/* Short Bios */}
|
||||
<Col className="p-4 pb-2">
|
||||
<ShortBioToggle
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx'
|
||||
import {convertRelationshipType, RelationshipType,} from 'web/lib/util/convert-relationship-type'
|
||||
import {convertRelationshipType, RelationshipType,} from 'web/lib/util/convert-types'
|
||||
import stringOrStringArrayToText from 'web/lib/util/string-or-string-array-to-text'
|
||||
import {MultiCheckbox} from 'web/components/multi-checkbox'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx'
|
||||
import {convertRomanticTypes, RomanticType,} from 'web/lib/util/convert-relationship-type'
|
||||
import {convertRomanticTypes, RomanticType,} from 'web/lib/util/convert-types'
|
||||
import stringOrStringArrayToText from 'web/lib/util/string-or-string-array-to-text'
|
||||
import {MultiCheckbox} from 'web/components/multi-checkbox'
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ export const useFilters = (you: Profile | undefined) => {
|
||||
pref_age_min: (you?.pref_age_min ?? MIN_INT) > 18 ? you?.pref_age_min : undefined,
|
||||
pref_relation_styles: you?.pref_relation_styles.length ? you.pref_relation_styles : undefined,
|
||||
pref_romantic_styles: you?.pref_romantic_styles?.length ? you.pref_romantic_styles : undefined,
|
||||
diet: you?.diet?.length ? you.diet : undefined,
|
||||
wants_kids_strength: wantsKidsDatabaseToWantsKidsFilter(
|
||||
(you?.wants_kids_strength ?? 2) as wantsKidsDatabase
|
||||
),
|
||||
@@ -100,6 +101,7 @@ export const useFilters = (you: Profile | undefined) => {
|
||||
filters.pref_gender?.length == 1 && isEqual(filters.pref_gender?.length ? filters.pref_gender[0] : undefined, you.gender) &&
|
||||
isEqual(new Set(filters.pref_romantic_styles), new Set(you.pref_romantic_styles)) &&
|
||||
isEqual(new Set(filters.pref_relation_styles), new Set(you.pref_relation_styles)) &&
|
||||
isEqual(new Set(filters.diet), new Set(you.diet)) &&
|
||||
filters.pref_age_max == yourFilters.pref_age_max &&
|
||||
filters.pref_age_min == yourFilters.pref_age_min &&
|
||||
filters.wants_kids_strength == yourFilters.wants_kids_strength
|
||||
|
||||
@@ -28,7 +28,7 @@ import {City, CityRow, profileToCity, useCitySearch} from "web/components/search
|
||||
import {AddPhotosWidget} from './widgets/add-photos'
|
||||
import {RadioToggleGroup} from "web/components/widgets/radio-toggle-group";
|
||||
import {MultipleChoiceOptions} from "common/profiles/multiple-choice";
|
||||
import {POLITICAL_CHOICES, RELATIONSHIP_CHOICES, ROMANTIC_CHOICES} from "web/components/filters/choices";
|
||||
import {DIET_CHOICES, POLITICAL_CHOICES, RELATIONSHIP_CHOICES, ROMANTIC_CHOICES} from "web/components/filters/choices";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export const OptionalProfileUserForm = (props: {
|
||||
@@ -409,6 +409,15 @@ export const OptionalProfileUserForm = (props: {
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col className={clsx(colClassName)}>
|
||||
<label className={clsx(labelClassName)}>Diet</label>
|
||||
<MultiCheckbox
|
||||
choices={DIET_CHOICES}
|
||||
selected={profile['diet'] ?? []}
|
||||
onChange={(selected) => setProfile('diet', selected)}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col className={clsx(colClassName)}>
|
||||
<label className={clsx(labelClassName)}>Do you smoke?</label>
|
||||
<ChoicesToggleGroup
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import clsx from 'clsx'
|
||||
import {convertRelationshipType, type RelationshipType,} from 'web/lib/util/convert-relationship-type'
|
||||
import {convertRelationshipType, type RelationshipType,} from 'web/lib/util/convert-types'
|
||||
import stringOrStringArrayToText from 'web/lib/util/string-or-string-array-to-text'
|
||||
import {ReactNode} from 'react'
|
||||
import {REVERTED_POLITICAL_CHOICES, REVERTED_ROMANTIC_CHOICES} from 'web/components/filters/choices'
|
||||
import {
|
||||
REVERTED_DIET_CHOICES,
|
||||
REVERTED_POLITICAL_CHOICES,
|
||||
REVERTED_ROMANTIC_CHOICES
|
||||
} from 'web/components/filters/choices'
|
||||
import {BiSolidDrink} from 'react-icons/bi'
|
||||
import {BsPersonHeart} from 'react-icons/bs'
|
||||
import {FaChild} from 'react-icons/fa6'
|
||||
import {LuBriefcase, LuCigarette, LuCigaretteOff, LuGraduationCap,} from 'react-icons/lu'
|
||||
import {MdNoDrinks, MdOutlineChildFriendly} from 'react-icons/md'
|
||||
import {PiHandsPrayingBold, PiMagnifyingGlassBold, PiPlantBold,} from 'react-icons/pi'
|
||||
import {PiHandsPrayingBold, PiMagnifyingGlassBold,} from 'react-icons/pi'
|
||||
import {RiScales3Line} from 'react-icons/ri'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
@@ -21,6 +25,7 @@ import {Profile} from 'common/profiles/profile'
|
||||
import {UserActivity} from "common/user";
|
||||
import {ClockIcon} from "@heroicons/react/solid";
|
||||
import {MAX_INT, MIN_INT} from "common/constants";
|
||||
import {GiFruitBowl} from "react-icons/gi";
|
||||
|
||||
export function AboutRow(props: {
|
||||
icon: ReactNode
|
||||
@@ -77,8 +82,8 @@ export default function ProfileAbout(props: {
|
||||
<Smoker profile={profile}/>
|
||||
<Drinks profile={profile}/>
|
||||
<AboutRow
|
||||
icon={<PiPlantBold className="h-5 w-5"/>}
|
||||
text={profile.is_vegetarian_or_vegan ? 'Vegetarian/Vegan' : null}
|
||||
icon={<GiFruitBowl className="h-5 w-5"/>}
|
||||
text={profile.diet?.map(e => REVERTED_DIET_CHOICES[e])}
|
||||
/>
|
||||
<HasKids profile={profile}/>
|
||||
<WantsKids profile={profile}/>
|
||||
@@ -296,7 +301,7 @@ export const formatProfileValue = (key: string, value: any) => {
|
||||
case 'last_online_time':
|
||||
return fromNow(new Date(value).valueOf())
|
||||
case 'is_smoker':
|
||||
case 'is_vegetarian_or_vegan':
|
||||
case 'diet':
|
||||
case 'has_pets':
|
||||
return value ? 'Yes' : 'No'
|
||||
case 'height_in_inches':
|
||||
|
||||
@@ -50,11 +50,13 @@ export function ProfilesHome() {
|
||||
if (!user) return;
|
||||
setIsReloading(true);
|
||||
const current = ++id.current;
|
||||
api('get-profiles', removeNullOrUndefinedProps({
|
||||
const args = removeNullOrUndefinedProps({
|
||||
limit: 20,
|
||||
compatibleWithUserId: user?.id,
|
||||
...filters
|
||||
}) as any)
|
||||
});
|
||||
console.debug('Refreshing profiles, filters:', args);
|
||||
api('get-profiles', args as any)
|
||||
.then(({profiles}) => {
|
||||
if (current === id.current) setProfiles(profiles);
|
||||
})
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import {REVERTED_RELATIONSHIP_CHOICES, REVERTED_ROMANTIC_CHOICES} from "web/components/filters/choices";
|
||||
import {
|
||||
REVERTED_DIET_CHOICES,
|
||||
REVERTED_RELATIONSHIP_CHOICES,
|
||||
REVERTED_ROMANTIC_CHOICES
|
||||
} from "web/components/filters/choices";
|
||||
|
||||
export type RelationshipType = keyof typeof REVERTED_RELATIONSHIP_CHOICES
|
||||
export type RomanticType = keyof typeof REVERTED_ROMANTIC_CHOICES
|
||||
export type DietType = keyof typeof REVERTED_DIET_CHOICES
|
||||
|
||||
export function convertRelationshipType(relationshipType: RelationshipType) {
|
||||
return REVERTED_RELATIONSHIP_CHOICES[relationshipType]
|
||||
@@ -10,3 +15,7 @@ export function convertRelationshipType(relationshipType: RelationshipType) {
|
||||
export function convertRomanticTypes(romanticType: RomanticType) {
|
||||
return REVERTED_ROMANTIC_CHOICES[romanticType]
|
||||
}
|
||||
|
||||
export function convertDietTypes(dietType: DietType) {
|
||||
return REVERTED_DIET_CHOICES[dietType]
|
||||
}
|
||||
Reference in New Issue
Block a user