Translate filters

This commit is contained in:
MartinBraquet
2026-01-04 12:42:53 +02:00
parent 21db5cb481
commit bb423ba0e4
29 changed files with 1090 additions and 924 deletions

View File

@@ -1,3 +1,3 @@
export const toKey = (str: string) => {
return str.replace(/ /g, '_').toLowerCase()
export const toKey = (str: string | number | boolean) => {
return String(str).replace(/ /g, '_').toLowerCase()
}

View File

@@ -146,7 +146,7 @@ const LoadedAboutSettings = (props: {
const handleCopy = async () => {
if (!diagnostics) return
await navigator.clipboard.writeText(diagnosticsToText(diagnostics))
setCopyFeedback(t('aboutsettings.copied', 'Copied!'))
setCopyFeedback(t('about.settings.copied', 'Copied!'))
setTimeout(() => {
setCopyFeedback('')
}, 2000)
@@ -161,7 +161,7 @@ const LoadedAboutSettings = (props: {
onClick={handleCopy}
className="w-fit mt-4"
>
{copyFeedback || t('aboutsettings.copy_info', 'Copy Info')}
{copyFeedback || t('about.settings.copy_info', 'Copy Info')}
</Button>
</Col>
}

View File

@@ -1,6 +1,7 @@
import clsx from 'clsx'
import { RangeSlider } from 'web/components/widgets/slider'
import {FilterFields} from "common/filters";
import {RangeSlider} from 'web/components/widgets/slider'
import {FilterFields} from "common/filters"
import {useT} from 'web/lib/locale'
export const PREF_AGE_MIN = 18
export const PREF_AGE_MAX = 100
@@ -22,11 +23,12 @@ export function AgeFilterText(props: {
const { pref_age_min, pref_age_max, highlightedClass } = props
const [noMinAge, noMaxAge] = getNoMinMaxAge(pref_age_min, pref_age_max)
const t = useT()
if (noMinAge && noMaxAge) {
return (
<span>
<span className={clsx('text-semibold', highlightedClass)}>Any</span>{' '}
<span className="hidden sm:inline">age</span>
<span className={clsx('text-semibold', highlightedClass)}>{t('filter.age.any', 'Any')}</span>{' '}
<span className="hidden sm:inline">{t('filter.age.age', 'age')}</span>
</span>
)
}
@@ -37,7 +39,7 @@ export function AgeFilterText(props: {
{'<'}
{pref_age_max}
</span>{' '}
years
{t('filter.age.years', 'years')}
</span>
)
}
@@ -48,7 +50,7 @@ export function AgeFilterText(props: {
{'>'}
{pref_age_min}
</span>{' '}
years
{t('filter.age.years', 'years')}
</span>
)
}
@@ -59,7 +61,7 @@ export function AgeFilterText(props: {
{' - '}
{pref_age_max}
</span>{' '}
years
{t('filter.age.years', 'years')}
</span>
)
}

View File

@@ -225,6 +225,12 @@ export const GENDERS = {
Other: 'other',
}
export const GENDERS_PLURAL = {
Women: 'female',
Men: 'male',
Other: 'other',
}
export const INVERTED_RELATIONSHIP_CHOICES = invert(RELATIONSHIP_CHOICES)
export const INVERTED_RELATIONSHIP_STATUS_CHOICES = invert(RELATIONSHIP_STATUS_CHOICES)
export const INVERTED_ROMANTIC_CHOICES = invert(ROMANTIC_CHOICES)

View File

@@ -16,7 +16,7 @@ import {FilterFields} from "common/filters";
import {ShortBioToggle} from "web/components/filters/short-bio-toggle";
import {PrefGenderFilter, PrefGenderFilterText} from "web/components/filters/pref-gender-filter";
import DropdownMenu from "web/components/comments/dropdown-menu";
import {KidsLabel, wantsKidsLabelsWithIcon} from "web/components/filters/wants-kids-filter";
import {KidsLabel, useWantsKidsLabelsWithIcon} from "web/components/filters/wants-kids-filter";
import {hasKidsLabels} from "common/has-kids";
import {HasKidsLabel} from "web/components/filters/has-kids-filter";
import {RomanticFilter, RomanticFilterText} from "web/components/filters/romantic-filter";
@@ -27,7 +27,7 @@ import {GiFruitBowl} from "react-icons/gi";
import {RiScales3Line} from "react-icons/ri";
import {EducationFilter, EducationFilterText} from "web/components/filters/education-filter";
import {MbtiFilter, MbtiFilterText} from "web/components/filters/mbti-filter";
import {BsPersonVcard} from "react-icons/bs";
import {BsPersonHeart, BsPersonVcard} from "react-icons/bs";
import {LuCigarette, LuGraduationCap} from "react-icons/lu";
import {DrinksFilter, DrinksFilterText} from "web/components/filters/drinks-filter";
import {MdLanguage, MdLocalBar} from 'react-icons/md'
@@ -39,9 +39,9 @@ import {
RelationshipStatusFilter,
RelationshipStatusFilterText
} from "web/components/filters/relationship-status-filter";
import {BsPersonHeart} from "react-icons/bs";
import {InterestFilter, InterestFilterText} from "web/components/filters/interest-filter";
import {OptionTableKey} from "common/profiles/constants";
import {useT} from "web/lib/locale";
export function DesktopFilters(props: {
filters: Partial<FilterFields>
@@ -66,6 +66,9 @@ export function DesktopFilters(props: {
choices,
} = props
const t = useT()
const wantsKidsLabelsWithIcon = useWantsKidsLabelsWithIcon()
return (
<>
<MyMatchesToggle
@@ -116,7 +119,7 @@ export function DesktopFilters(props: {
| string[]
| undefined
}
defaultLabel={'Any relationship status'}
defaultLabel={t('filter.relationship_status.any', 'Any relationship status')}
highlightedClass={open ? 'text-primary-500' : undefined}
/>
</Row>
@@ -439,7 +442,7 @@ export function DesktopFilters(props: {
| undefined
}
highlightedClass={open ? 'text-primary-500' : undefined}
label={'interest'}
label={'interests'}
/>
</Row>
}
@@ -474,7 +477,7 @@ export function DesktopFilters(props: {
| undefined
}
highlightedClass={open ? 'text-primary-500' : undefined}
label={'cause'}
label={'causes'}
/>
</Row>
}
@@ -596,7 +599,7 @@ export function DesktopFilters(props: {
<MbtiFilterText
options={filters.mbti as string[] | undefined}
highlightedClass={open ? 'text-primary-500' : undefined}
defaultLabel={'Any MBTI'}
defaultLabel={t('filter.any_mbti', 'Any MBTI')}
/>
</Row>
}
@@ -656,6 +659,7 @@ export function DesktopFilters(props: {
</Col>
}
popoverClassName="bg-canvas-50"
menuWidth="w-50"
/>
{/* Short Bios */}
@@ -669,7 +673,7 @@ export function DesktopFilters(props: {
className="text-ink-900 hover:text-primary-500 underline"
onClick={clearFilters}
>
Reset filters
{t('filter.reset', 'Reset filters')}
</button>
</>
)

View File

@@ -3,8 +3,9 @@ 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";
import {DIET_CHOICES} from 'web/components/filters/choices'
import {FilterFields} from 'common/filters'
import {useT} from 'web/lib/locale'
export function DietFilterText(props: {
options: DietType[] | undefined
@@ -13,9 +14,13 @@ export function DietFilterText(props: {
const {options, highlightedClass} = props
const length = (options ?? []).length
const t = useT()
if (!options || length < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any diet</span>
<span className={clsx('text-semibold', highlightedClass)}>
{t('filter.any_diet', 'Any diet')}
</span>
)
}
@@ -27,7 +32,7 @@ export function DietFilterText(props: {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
@@ -53,6 +58,7 @@ export function DietFilter(props: {
<MultiCheckbox
selected={filters.diet ?? []}
choices={DIET_CHOICES as any}
translationPrefix={'profile.diet'}
onChange={(c) => {
updateFilter({diet: c})
}}

View File

@@ -1,6 +1,7 @@
import clsx from 'clsx'
import {RangeSlider} from 'web/components/widgets/slider'
import {FilterFields} from 'common/filters'
import {useT} from 'web/lib/locale'
export const DRINKS_MIN = 0
export const DRINKS_MAX = 20
@@ -22,12 +23,13 @@ export function DrinksFilterText(props: {
}) {
const {drinks_min, drinks_max, highlightedClass} = props
const [noMin, noMax] = getNoMinMaxDrinks(drinks_min, drinks_max)
const t = useT()
if (drinks_max === DRINKS_MIN) {
return (
<span className="font-semibold">
<span className={clsx(highlightedClass)}>{drinks_max}</span> / mo
<span className={clsx(highlightedClass)}>{drinks_max}</span> {t('common.per_month', '/ mo')}
</span>
)
}
@@ -35,8 +37,7 @@ export function DrinksFilterText(props: {
if (noMin && noMax) {
return (
<span>
<span className={clsx('text-semibold', highlightedClass)}>Any</span>{' '}
<span className="hidden sm:inline">drinks</span>
<span className="">{t('filter.any_drinks', 'Any drinks')}</span>
</span>
)
}
@@ -47,7 +48,7 @@ export function DrinksFilterText(props: {
{' < '}
{drinks_max}
</span>{' '}
/ mo
{t('common.per_month', '/ mo')}
</span>
)
}
@@ -58,14 +59,14 @@ export function DrinksFilterText(props: {
{' > '}
{drinks_min}
</span>{' '}
/ mo
{t('common.per_month', '/ mo')}
</span>
)
}
if (drinks_min === drinks_max) {
return (
<span className="font-semibold">
<span className={clsx(highlightedClass)}>{drinks_min}</span> / mo
<span className={clsx(highlightedClass)}>{drinks_min}</span> {t('common.per_month', '/ mo')}
</span>
)
}
@@ -76,7 +77,7 @@ export function DrinksFilterText(props: {
{' - '}
{drinks_max}
</span>{' '}
/mo
{t('common.per_month', '/ mo')}
</span>
)
}

View File

@@ -5,7 +5,9 @@ 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 {getSortedOptions} from "common/util/sorting";
import {getSortedOptions} from 'common/util/sorting'
import {useT} from 'web/lib/locale'
import {toKey} from 'common/parsing'
export function EducationFilterText(props: {
options: string[] | undefined
@@ -14,14 +16,20 @@ export function EducationFilterText(props: {
const {options, highlightedClass} = props
const length = (options ?? []).length
const t = useT()
if (!options || length < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any education</span>
<span className={clsx('text-semibold', highlightedClass)}>
{t('filter.any_education', 'Any education')}
</span>
)
}
const sortedOptions = getSortedOptions(options, EDUCATION_CHOICES)
const convertedTypes = sortedOptions.map((r) => convertEducationTypes(r as any))
const convertedTypes = sortedOptions.map((r) =>
t(`profile.education.${toKey(r)}`, convertEducationTypes(r as any))
)
return (
<div>
@@ -45,6 +53,7 @@ export function EducationFilter(props: {
<MultiCheckbox
selected={filters.education_levels ?? []}
choices={EDUCATION_CHOICES}
translationPrefix={'profile.education'}
onChange={(c) => {
updateFilter({education_levels: c})
}}

View File

@@ -1,10 +1,11 @@
import clsx from 'clsx'
import GenderIcon from '../gender-icon'
import { Gender } from 'common/gender'
import { Row } from 'web/components/layout/row'
import { MultiCheckbox } from 'web/components/multi-checkbox'
import {Gender} from 'common/gender'
import {Row} from 'web/components/layout/row'
import {MultiCheckbox} from 'web/components/multi-checkbox'
import {useT} from 'web/lib/locale'
import {FilterFields} from "common/filters";
import {GENDERS_PLURAL} from "web/components/filters/choices";
export function GenderFilterText(props: {
gender: Gender[] | undefined
@@ -12,11 +13,12 @@ export function GenderFilterText(props: {
}) {
const { gender, highlightedClass } = props
const t = useT()
if (!gender || gender.length < 1) {
return (
<span>
<span className={clsx('text-semibold', highlightedClass)}>Any</span>{' '}
<span className="hidden sm:inline">gender</span>
<span className={clsx('text-semibold', highlightedClass)}>{t('filter.gender.any', 'Any')}</span>{' '}
<span className="hidden sm:inline">{t('filter.gender.gender', 'gender')}</span>
</span>
)
}
@@ -28,7 +30,7 @@ export function GenderFilterText(props: {
)
})}{' '}
<span className="hidden sm:inline">
gender{gender.length > 1 ? 's' : ''}
{gender.length > 1 ? t('filter.gender.genders', 'genders') : t('filter.gender.gender', 'gender')}
</span>
</Row>
)
@@ -43,11 +45,8 @@ export function GenderFilter(props: {
<>
<MultiCheckbox
selected={filters.genders ?? []}
choices={{
Women: 'female',
Men: 'male',
'Other': 'other',
}}
choices={GENDERS_PLURAL}
translationPrefix={'profile.gender.plural'}
onChange={(c) => {
updateFilter({ genders: c })
}}

View File

@@ -2,8 +2,9 @@ import clsx from 'clsx'
import {Row} from 'web/components/layout/row'
import {ChoicesToggleGroup} from 'web/components/widgets/choices-toggle-group'
import {FaChild} from 'react-icons/fa6'
import {FilterFields} from "common/filters";
import {generateChoicesMap, hasKidsLabels} from "common/has-kids";
import {FilterFields} from 'common/filters'
import {generateChoicesMap, hasKidsLabels} from 'common/has-kids'
import {useT} from 'web/lib/locale'
export function HasKidsLabel(props: {
@@ -12,23 +13,26 @@ export function HasKidsLabel(props: {
mobile?: boolean
}) {
const {has_kids, highlightedClass, mobile} = props
const t = useT()
// Get the appropriate label based on has_kids value
let labelKey = 'no_preference'
let labelValue = hasKidsLabels.no_preference.name
if (has_kids === hasKidsLabels.has_kids.value) {
labelKey = 'has_kids'
labelValue = mobile ? hasKidsLabels.has_kids.shortName : hasKidsLabels.has_kids.name
} else if (has_kids === hasKidsLabels.doesnt_have_kids.value) {
labelKey = 'doesnt_have_kids'
labelValue = mobile ? hasKidsLabels.doesnt_have_kids.shortName : hasKidsLabels.doesnt_have_kids.name
}
return (
<Row className="items-center gap-0.5">
<FaChild className="hidden h-4 w-4 sm:inline"/>
<span
className={clsx(highlightedClass, has_kids !== -1 && 'font-semibold')}
>
{has_kids == hasKidsLabels.has_kids.value
? mobile
? hasKidsLabels.has_kids.shortName
: hasKidsLabels.has_kids.name
: has_kids == hasKidsLabels.doesnt_have_kids.value
? mobile
? hasKidsLabels.doesnt_have_kids.shortName
: hasKidsLabels.doesnt_have_kids.name
: mobile
? hasKidsLabels.no_preference.shortName
: hasKidsLabels.no_preference.name}
{t(`profile.has_kids.${labelKey}`, labelValue)}
</span>
</Row>
)
@@ -43,6 +47,7 @@ export function HasKidsFilter(props: {
<ChoicesToggleGroup
currentChoice={filters.has_kids ?? 0}
choicesMap={generateChoicesMap(hasKidsLabels)}
translationPrefix="profile.has_kids"
setChoice={(c) => updateFilter({has_kids: Number(c)})}
toggleClassName="w-1/3 justify-center"
/>

View File

@@ -2,19 +2,24 @@ import clsx from 'clsx'
import stringOrStringArrayToText from 'web/lib/util/string-or-string-array-to-text'
import {MultiCheckbox} from 'web/components/multi-checkbox'
import {FilterFields} from "common/filters";
import {OptionTableKey} from "common/profiles/constants";
import {OptionTableKey} from 'common/profiles/constants'
import {useT} from 'web/lib/locale'
import {toKey} from "common/parsing";
export function InterestFilterText(props: {
options: string[] | undefined
highlightedClass?: string
label: string
label: OptionTableKey
}) {
const {options, highlightedClass, label} = props
const t = useT()
const length = (options ?? []).length
if (!options || length < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any {label}</span>
<span className={clsx('text-semibold', highlightedClass)}>
{t(`filter.any_${label}`, `Any ${label}`)}
</span>
)
}
@@ -22,7 +27,7 @@ export function InterestFilterText(props: {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
@@ -32,7 +37,7 @@ export function InterestFilterText(props: {
<div>
<span className={clsx('font-semibold', highlightedClass)}>
{stringOrStringArrayToText({
text: options,
text: options.map((o) => t(`profile.${label}.${toKey(o)}`, o)),
capitalizeFirstLetterOption: true,
})}{' '}
</span>
@@ -51,6 +56,7 @@ export function InterestFilter(props: {
<MultiCheckbox
selected={filters[label] ?? []}
choices={choices as any}
translationPrefix={`profile.${label}`}
onChange={(c) => {
updateFilter({[label]: c})
}}

View File

@@ -3,9 +3,10 @@ import {convertLanguageTypes,} 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 {LANGUAGE_CHOICES} from "web/components/filters/choices";
import {FilterFields} from "common/filters";
import {getSortedOptions} from "common/util/sorting";
import {LANGUAGE_CHOICES} from 'web/components/filters/choices'
import {FilterFields} from 'common/filters'
import {getSortedOptions} from 'common/util/sorting'
import {useT} from 'web/lib/locale'
export function LanguageFilterText(props: {
options: string[] | undefined
@@ -14,9 +15,13 @@ export function LanguageFilterText(props: {
const {options, highlightedClass} = props
const length = (options ?? []).length
const t = useT()
if (!options || length < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any language</span>
<span className={clsx('text-semibold', highlightedClass)}>
{t('filter.any_language', 'Any language')}
</span>
)
}
@@ -24,14 +29,16 @@ export function LanguageFilterText(props: {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
}
const sortedOptions = getSortedOptions(options, LANGUAGE_CHOICES)
const convertedTypes = sortedOptions.map((r) => convertLanguageTypes(r as any))
const convertedTypes = sortedOptions.map((r) =>
t(`profile.language.${r}`, convertLanguageTypes(r as any))
)
return (
<div>
@@ -54,6 +61,7 @@ export function LanguageFilter(props: {
<MultiCheckbox
selected={filters.languages ?? []}
choices={LANGUAGE_CHOICES as any}
translationPrefix={'profile.language'}
onChange={(c) => {
updateFilter({languages: c})
}}

View File

@@ -3,10 +3,11 @@ import {Col} from 'web/components/layout/col'
import {Slider} from 'web/components/widgets/slider'
import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-state'
import {Row} from 'web/components/layout/row'
import {City, CityRow, profileToCity, originToCity, useCitySearch,} from '../search-location'
import {City, CityRow, originToCity, profileToCity, useCitySearch,} from '../search-location'
import {Profile} from 'common/profiles/profile'
import {useEffect, useState} from 'react'
import {Input} from 'web/components/widgets/input'
import {useT} from 'web/lib/locale'
import {XIcon} from '@heroicons/react/solid'
import {uniqBy} from 'lodash'
import {buildArray} from 'common/util/array'
@@ -20,11 +21,12 @@ export function LocationFilterText(props: {
}) {
const { location, radius, highlightedClass } = props
const t = useT()
if (!location) {
return (
<span>
<span className={clsx('text-semibold', highlightedClass)}>Any</span>
<span className="hidden sm:inline"> location</span>
<span className={clsx('text-semibold', highlightedClass)}>{t('filter.location.any', 'Any')}</span>
<span className="hidden sm:inline"> {t('filter.location', 'location')}</span>
</span>
)
}
@@ -66,6 +68,8 @@ export function LocationFilter(props: {
const youCity = youProfile && profileToCity(youProfile)
const t = useT()
const [lastCity, setLastCity] = usePersistentInMemoryState<City>(
location ? originToCity(location) : youCity || DEFAULT_LAST_CITY,
'last-used-city'
@@ -94,7 +98,7 @@ export function LocationFilter(props: {
<Input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder={'Search city...'}
placeholder={t('filter.location.search_city', 'Search city...')}
className="h-8 w-full rounded-none border-0 bg-transparent px-1 focus:border-b focus:ring-0 focus:ring-transparent"
autoFocus
// onBlur // TODO
@@ -169,6 +173,7 @@ function LocationResults(props: {
}
}, [loading])
const t = useT()
return (
<Col className={className}>
{showAny && (
@@ -177,8 +182,8 @@ function LocationResults(props: {
className="hover:bg-primary-200 hover:text-ink-950 cursor-pointer px-4 py-2 transition-colors"
>
<Row className="items-center gap-2">
<XIcon className="h-4 w-4" />
<span>Set to Any City</span>
<XIcon className="h-4 w-4 text-ink-400" aria-label={t('common.close', 'Close')}/>
<span>{t('filter.location.set_any_city', 'Set to Any City')}</span>
</Row>
</button>
)}

View File

@@ -4,6 +4,7 @@ import {FilterFields} from 'common/filters'
import {getSortedOptions} from 'common/util/sorting'
import {MultiCheckbox} from 'web/components/multi-checkbox'
import stringOrStringArrayToText from 'web/lib/util/string-or-string-array-to-text'
import {useT} from 'web/lib/locale'
export function MbtiFilterText(props: {
options: string[] | undefined
@@ -11,9 +12,10 @@ export function MbtiFilterText(props: {
defaultLabel?: string
}) {
const {options, highlightedClass, defaultLabel} = props
const t = useT()
const length = (options ?? []).length
const label = defaultLabel || 'Any'
const label = defaultLabel || t('filter.any', 'Any')
if (!options || length < 1) {
return (
@@ -25,7 +27,7 @@ export function MbtiFilterText(props: {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
@@ -57,6 +59,7 @@ export function MbtiFilter(props: {
className={'grid grid-cols-2 xs:grid-cols-4'}
selected={filters.mbti ?? []}
choices={MBTI_CHOICES as any}
translationPrefix={'profile.mbti'}
onChange={(c) => {
updateFilter({mbti: c})
}}

View File

@@ -34,6 +34,7 @@ import {MbtiFilter, MbtiFilterText} from "web/components/filters/mbti-filter";
import {InterestFilter, InterestFilterText} from "web/components/filters/interest-filter";
import {OptionTableKey} from "common/profiles/constants";
import {NewBadge} from "web/components/new-badge";
import {useT} from "web/lib/locale";
function MobileFilters(props: {
filters: Partial<FilterFields>
@@ -46,6 +47,7 @@ function MobileFilters(props: {
includeRelationshipFilters: boolean | undefined
choices: Record<OptionTableKey, Record<string, string[]>>
}) {
const t = useT()
const {
filters,
youProfile,
@@ -88,16 +90,16 @@ function MobileFilters(props: {
/>
</Col>
<button
className="text-ink-500 hover:text-primary-500 underline mt-4 mb-8 right absolute right-4"
className="text-ink-900 hover:text-primary-500 underline"
onClick={clearFilters}
>
Reset filters
{t('filter.reset', 'Reset filters')}
</button>
</Row>
{/* RELATIONSHIP STYLE */}
<MobileFilterSection
title="Seeking"
title={t('profile.seeking', "Seeking")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.pref_relation_styles)}
@@ -117,7 +119,7 @@ function MobileFilters(props: {
{/* Relationship Status */}
<MobileFilterSection
title="Relationship Status"
title={t('profile.optional.relationship_status', 'Relationship status')}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.relationship_status || undefined)}
@@ -137,7 +139,7 @@ function MobileFilters(props: {
{/* LOCATION */}
<MobileFilterSection
title="Location"
title={t("profile.optional.location", "Location")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={!!locationFilterProps.location}
@@ -162,7 +164,7 @@ function MobileFilters(props: {
{/* AGE RANGE */}
<MobileFilterSection
title="Age"
title={t("profile.optional.age", "Age")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
childrenClassName={'pb-6'}
@@ -182,7 +184,7 @@ function MobileFilters(props: {
{/* GENDER */}
<MobileFilterSection
title="Gender"
title={t("profile.optional.gender", "Gender")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.genders)}
@@ -200,7 +202,7 @@ function MobileFilters(props: {
{/* PREFERRED GENDER */}
<MobileFilterSection
title="Gender they seek"
title={t('filter.gender.they_seek', 'Gender they seek')}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.pref_gender)}
@@ -220,7 +222,7 @@ function MobileFilters(props: {
{/* ROMANTIC STYLE */}
<MobileFilterSection
title="Style"
title={t("profile.romantic.style", "Style")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.pref_romantic_styles || undefined)}
@@ -240,7 +242,7 @@ function MobileFilters(props: {
{/* WANTS KIDS */}
<MobileFilterSection
title="Wants kids"
title={t('filter.wants_kids.wants_kids', 'Wants kids')}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={
@@ -266,7 +268,7 @@ function MobileFilters(props: {
{/* HAS KIDS */}
<MobileFilterSection
title="Has kids"
title={t("profile.has_kids", "Has kids")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={filters.has_kids != null && filters.has_kids !== -1}
@@ -290,7 +292,7 @@ function MobileFilters(props: {
{/* DIET */}
<MobileFilterSection
title="Diet"
title={t("profile.optional.diet", "Diet")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.diet || undefined)}
@@ -310,7 +312,7 @@ function MobileFilters(props: {
{/* DRINKS PER MONTH */}
<MobileFilterSection
title="Drinks"
title={t("profile.drinks", "Drinks")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={(() => {
@@ -333,7 +335,7 @@ function MobileFilters(props: {
{/* SMOKER */}
<MobileFilterSection
title="Smoker"
title={t("profile.smokes", "Smoker")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={filters.is_smoker != null}
@@ -350,7 +352,7 @@ function MobileFilters(props: {
{/* LANGUAGES */}
<MobileFilterSection
title="Languages"
title={t("profile.optional.languages", "Languages")}
// className="col-span-full max-h-80 overflow-y-auto"
openFilter={openFilter}
setOpenFilter={setOpenFilter}
@@ -373,7 +375,7 @@ function MobileFilters(props: {
<MobileFilterSection
showNewBadge
newBadgeClassName={"-top-0 -left-0"}
title="Interests"
title={t("profile.optional.interests", "Interests")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.interests || undefined)}
@@ -401,7 +403,7 @@ function MobileFilters(props: {
<MobileFilterSection
showNewBadge
newBadgeClassName={"-top-0 -left-0"}
title="Causes"
title={t("profile.optional.causes", "Causes")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.causes || undefined)}
@@ -429,7 +431,7 @@ function MobileFilters(props: {
<MobileFilterSection
showNewBadge
newBadgeClassName={"-top-0 -left-0"}
title="Work"
title={t("profile.optional.work", "Work")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.work || undefined)}
@@ -455,7 +457,7 @@ function MobileFilters(props: {
{/* POLITICS */}
<MobileFilterSection
title="Politics"
title={t("profile.optional.political_beliefs", "Politics")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.political_beliefs || undefined)}
@@ -475,7 +477,7 @@ function MobileFilters(props: {
{/* RELIGION */}
<MobileFilterSection
title="Religion"
title={t("profile.optional.religious_beliefs", "Religion")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.religion || undefined)}
@@ -511,7 +513,7 @@ function MobileFilters(props: {
{/* EDUCATION */}
<MobileFilterSection
title="Education"
title={t("profile.education.short_name", "Education")}
openFilter={openFilter}
setOpenFilter={setOpenFilter}
isActive={hasAny(filters.education_levels)}

View File

@@ -1,6 +1,7 @@
import { Row } from 'web/components/layout/row'
import {Row} from 'web/components/layout/row'
import clsx from 'clsx'
import { Profile } from 'common/profiles/profile'
import {Profile} from 'common/profiles/profile'
import {useT} from "web/lib/locale";
export function MyMatchesToggle(props: {
setYourFilters: (checked: boolean) => void
@@ -13,7 +14,8 @@ export function MyMatchesToggle(props: {
return <></>
}
const label = 'Your filters'
const t = useT()
const label = t('filter.mine_toggle', 'Your filters')
return (
<Row className={clsx('mr-2 items-center', on && 'font-semibold')}>

View File

@@ -3,9 +3,11 @@ import {convertPoliticalTypes,} 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 {POLITICAL_CHOICES} from "web/components/filters/choices";
import {FilterFields} from "common/filters";
import {getSortedOptions} from "common/util/sorting";
import {POLITICAL_CHOICES} from 'web/components/filters/choices'
import {FilterFields} from 'common/filters'
import {getSortedOptions} from 'common/util/sorting'
import {useT} from 'web/lib/locale'
import {toKey} from 'common/parsing'
export function PoliticalFilterText(props: {
options: string[] | undefined
@@ -14,9 +16,13 @@ export function PoliticalFilterText(props: {
const {options, highlightedClass} = props
const length = (options ?? []).length
const t = useT()
if (!options || length < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any politics</span>
<span className={clsx('text-semibold', highlightedClass)}>
{t('filter.any_politics', 'Any politics')}
</span>
)
}
@@ -24,14 +30,16 @@ export function PoliticalFilterText(props: {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
}
const sortedOptions = getSortedOptions(options, POLITICAL_CHOICES)
const convertedTypes = sortedOptions.map((r) => convertPoliticalTypes(r as any))
const convertedTypes = sortedOptions.map((r) =>
t(`profile.political.${toKey(r)}`, convertPoliticalTypes(r as any))
)
return (
<div>
@@ -54,6 +62,7 @@ export function PoliticalFilter(props: {
<MultiCheckbox
selected={filters.political_beliefs ?? []}
choices={POLITICAL_CHOICES as any}
translationPrefix={'profile.political'}
onChange={(c) => {
updateFilter({political_beliefs: c})
}}

View File

@@ -1,35 +1,37 @@
import clsx from 'clsx'
import GenderIcon from '../gender-icon'
import { Gender } from 'common/gender'
import { Row } from 'web/components/layout/row'
import { MultiCheckbox } from 'web/components/multi-checkbox'
import {FilterFields} from "common/filters";
import {Gender} from 'common/gender'
import {Row} from 'web/components/layout/row'
import {MultiCheckbox} from 'web/components/multi-checkbox'
import {FilterFields} from 'common/filters'
import {useT} from 'web/lib/locale'
import {GENDERS_PLURAL} from "web/components/filters/choices";
export function PrefGenderFilterText(props: {
pref_gender: Gender[] | undefined
highlightedClass?: string
}) {
const { pref_gender, highlightedClass } = props
const {pref_gender, highlightedClass} = props
const t = useT()
if (!pref_gender || pref_gender.length < 1) {
return (
<span>
<span className="hidden sm:inline">Gender they seek: </span>
<span className="hidden sm:inline">{t('filter.gender.they_seek', 'Gender they seek')}: </span>
<span
className={clsx(
'text-semibold capitalize sm:normal-case',
highlightedClass
)}
>
any
{t('filter.any', 'any')}
</span>
</span>
)
}
return (
<Row className="items-center gap-0.5 font-semibold">
<span className="hidden sm:inline">Gender they seek: </span>
<span className="hidden sm:inline">{t('filter.gender.they_seek', 'Gender they seek')}: </span>
{pref_gender.map((gender) => {
return (
<GenderIcon
@@ -48,17 +50,14 @@ export function PrefGenderFilter(props: {
filters: Partial<FilterFields>
updateFilter: (newState: Partial<FilterFields>) => void
}) {
const { filters, updateFilter } = props
const {filters, updateFilter} = props
return (
<MultiCheckbox
selected={filters.pref_gender ?? []}
choices={{
Women: 'female',
Men: 'male',
Other: 'other',
}}
choices={GENDERS_PLURAL}
translationPrefix={'profile.gender.plural'}
onChange={(c) => {
updateFilter({ pref_gender: c })
updateFilter({pref_gender: c})
}}
/>
)

View File

@@ -2,6 +2,7 @@ import clsx from 'clsx'
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'
import {useT} from 'web/lib/locale'
import {RELATIONSHIP_CHOICES} from "web/components/filters/choices";
import {FilterFields} from "common/filters";
@@ -13,21 +14,24 @@ export function RelationshipFilterText(props: {
const {relationship, highlightedClass} = props
const relationshipLength = (relationship ?? []).length
const t = useT()
if (!relationship || relationshipLength < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any connection</span>
<span className={clsx('text-semibold', highlightedClass)}>
{t('filter.relationship.any_connection', 'Any connection')}
</span>
)
}
const convertedRelationships = relationship.map((r) =>
convertRelationshipType(r)
t(`profile.relationship.${r}`, convertRelationshipType(r))
)
if (relationshipLength > 1) {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
@@ -53,6 +57,7 @@ export function RelationshipFilter(props: {
<MultiCheckbox
selected={filters.pref_relation_styles ?? []}
choices={RELATIONSHIP_CHOICES as any}
translationPrefix={'profile.relationship'}
onChange={(c) => {
updateFilter({pref_relation_styles: c})
}}

View File

@@ -6,6 +6,7 @@ import {MultiCheckbox} from 'web/components/multi-checkbox'
import {RELATIONSHIP_STATUS_CHOICES} from "web/components/filters/choices"
import {FilterFields} from "common/filters"
import {getSortedOptions} from "common/util/sorting"
import {useT} from 'web/lib/locale'
export function RelationshipStatusFilterText(props: {
options: string[] | undefined
@@ -13,9 +14,10 @@ export function RelationshipStatusFilterText(props: {
defaultLabel?: string
}) {
const {options, highlightedClass, defaultLabel} = props
const t = useT()
const length = (options ?? []).length
const label = defaultLabel || 'Any'
const label = defaultLabel || t('filter.any', 'Any')
if (!options || length < 1) {
return (
@@ -27,14 +29,16 @@ export function RelationshipStatusFilterText(props: {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
}
const sortedOptions = getSortedOptions(options, RELATIONSHIP_STATUS_CHOICES)
const convertedTypes = sortedOptions.map((r) => convertRelationshipStatusTypes(r as any))
const convertedTypes = sortedOptions.map((r) =>
t(`profile.relationship_status.${r}`, convertRelationshipStatusTypes(r))
)
return (
<div>
@@ -57,6 +61,7 @@ export function RelationshipStatusFilter(props: {
<MultiCheckbox
selected={filters.relationship_status ?? []}
choices={RELATIONSHIP_STATUS_CHOICES as any}
translationPrefix={'profile.relationship_status'}
onChange={(c) => {
updateFilter({relationship_status: c})
}}

View File

@@ -1,11 +1,13 @@
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";
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'
import {useT} from 'web/lib/locale'
import {toKey} from 'common/parsing'
export function ReligionFilterText(props: {
@@ -15,9 +17,13 @@ export function ReligionFilterText(props: {
const {options, highlightedClass} = props
const length = (options ?? []).length
const t = useT()
if (!options || length < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any religion</span>
<span className={clsx('text-semibold', highlightedClass)}>
{t('filter.any_religion', 'Any religion')}
</span>
)
}
@@ -25,14 +31,16 @@ export function ReligionFilterText(props: {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
}
const sortedOptions = getSortedOptions(options, RELIGION_CHOICES)
const convertedTypes = sortedOptions.map((r) => convertReligionTypes(r as any))
const convertedTypes = sortedOptions.map((r) =>
t(`profile.religion.${toKey(r)}`, convertReligionTypes(r as any))
)
return (
<div>
@@ -57,6 +65,7 @@ export function ReligionFilter(props: {
<MultiCheckbox
selected={filters.religion ?? []}
choices={RELIGION_CHOICES}
translationPrefix={'profile.religion'}
onChange={(c) => {
updateFilter({religion: c})
}}

View File

@@ -3,31 +3,36 @@ 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'
import {ROMANTIC_CHOICES} from "web/components/filters/choices";
import {FilterFields} from "common/filters";
import {ROMANTIC_CHOICES} from 'web/components/filters/choices'
import {FilterFields} from 'common/filters'
import {useT} from 'web/lib/locale'
import {toKey} from "common/parsing";
export function RomanticFilterText(props: {
relationship: RomanticType[] | undefined
highlightedClass?: string
}) {
const {relationship, highlightedClass} = props
const t = useT()
const length = (relationship ?? []).length
if (!relationship || length < 1) {
return (
<span className={clsx('text-semibold', highlightedClass)}>Any relationship</span>
<span className={clsx('text-semibold', highlightedClass)}>
{t('filter.any_relationship', 'Any relationship')}
</span>
)
}
const convertedTypes = relationship.map((r) =>
convertRomanticTypes(r)
t(`profile.romantic.${toKey(r)}`, convertRomanticTypes(r))
)
if (length > 1) {
return (
<span>
<span className={clsx('font-semibold', highlightedClass)}>
Multiple
{t('filter.multiple', 'Multiple')}
</span>
</span>
)
@@ -53,6 +58,7 @@ export function RomanticFilter(props: {
<MultiCheckbox
selected={filters.pref_romantic_styles ?? []}
choices={ROMANTIC_CHOICES as any}
translationPrefix={'profile.romantic'}
onChange={(c) => {
updateFilter({pref_romantic_styles: c})
}}

View File

@@ -1,6 +1,7 @@
import {Row} from 'web/components/layout/row'
import clsx from 'clsx'
import {FilterFields} from "common/filters";
import {useT} from "web/lib/locale";
export function ShortBioToggle(props: {
filters: Partial<FilterFields>
@@ -12,7 +13,8 @@ export function ShortBioToggle(props: {
return <></>
}
const label = 'Include Short Bios'
const t = useT()
const label = t('filter.short_bio_toggle', 'Include Short Bios')
const on = filters.shortBio || false

View File

@@ -2,6 +2,7 @@ import clsx from 'clsx'
import {Row} from 'web/components/layout/row'
import {ChoicesToggleGroup} from 'web/components/widgets/choices-toggle-group'
import {FilterFields} from 'common/filters'
import {useT} from 'web/lib/locale'
export function SmokerFilterText(props: {
is_smoker: boolean | null | undefined
@@ -9,14 +10,15 @@ export function SmokerFilterText(props: {
mobile?: boolean
}) {
const {is_smoker, highlightedClass, mobile} = props
const t = useT()
return (
<Row className="items-center gap-0.5">
<span className={clsx(highlightedClass, is_smoker != null && 'font-semibold')}>
{is_smoker == null
? mobile ? 'Either' : 'Either'
? t('common.either', 'Either')
: is_smoker
? mobile ? 'Yes' : 'Smoker'
: mobile ? 'No' : "Non-smoker"}
? mobile ? t('common.yes', 'Yes') : t('profile.smoker.yes', 'Smoker')
: mobile ? t('common.no', 'No') : t('profile.smoker.no', 'Non-smoker')}
</span>
</Row>
)
@@ -34,16 +36,16 @@ export function SmokerFilter(props: {
No: 'no',
} as const
const currentChoice =
filters.is_smoker == null ? choicesMap.Either : filters.is_smoker ? choicesMap.Yes : choicesMap.No
const currentChoice = filters.is_smoker == null ? 'either' : filters.is_smoker ? 'yes' : 'no'
return (
<ChoicesToggleGroup
currentChoice={currentChoice}
choicesMap={choicesMap}
translationPrefix={'profile.smoker'}
setChoice={(c) => {
if (c === choicesMap.Either) updateFilter({is_smoker: undefined})
else if (c === choicesMap.Yes) updateFilter({is_smoker: true})
if (c === 'either') updateFilter({is_smoker: undefined})
else if (c === 'yes') updateFilter({is_smoker: true})
else updateFilter({is_smoker: false})
}}
toggleClassName="w-1/3 justify-center"

View File

@@ -6,6 +6,7 @@ import clsx from 'clsx'
import {FilterFields} from "common/filters";
import {generateChoicesMap, KidLabel, wantsKidsLabels} from "common/wants-kids"
import {useT} from "web/lib/locale";
interface KidLabelWithIcon extends KidLabel {
icon: ReactNode
@@ -15,25 +16,35 @@ interface KidsLabelsMapWithIcon {
[key: string]: KidLabelWithIcon
}
export const wantsKidsLabelsWithIcon: KidsLabelsMapWithIcon = {
...wantsKidsLabels,
no_preference: {
...wantsKidsLabels.no_preference,
icon: <MdOutlineStroller className="h-4 w-4"/>,
},
wants_kids: {
...wantsKidsLabels.wants_kids,
icon: <MdStroller className="h-4 w-4"/>,
},
doesnt_want_kids: {
...wantsKidsLabels.doesnt_want_kids,
icon: <MdNoStroller className="h-4 w-4"/>,
},
export const useWantsKidsLabelsWithIcon = () => {
const t = useT()
return {
no_preference: {
...wantsKidsLabels.no_preference,
name: t('filter.wants_kids.any_preference', 'Any preference'),
shortName: t('filter.wants_kids.either', 'Either'),
icon: <MdOutlineStroller className="h-4 w-4"/>,
},
wants_kids: {
...wantsKidsLabels.wants_kids,
name: t('filter.wants_kids.wants_kids', 'Wants kids'),
shortName: t('common.yes', 'Yes'),
icon: <MdStroller className="h-4 w-4"/>,
},
doesnt_want_kids: {
...wantsKidsLabels.doesnt_want_kids,
name: t('filter.wants_kids.doesnt_want_kids', "Doesn't want kids"),
shortName: t('common.no', 'No'),
icon: <MdNoStroller className="h-4 w-4"/>,
},
} as KidsLabelsMapWithIcon
}
export function WantsKidsIcon(props: { strength: number; className?: string }) {
const {strength, className} = props
const wantsKidsLabelsWithIcon = useWantsKidsLabelsWithIcon()
return (
<span className={className}>
{strength == wantsKidsLabelsWithIcon.no_preference.strength
@@ -51,6 +62,7 @@ export function KidsLabel(props: {
mobile?: boolean
}) {
const {strength, highlightedClass, mobile} = props
const wantsKidsLabelsWithIcon = useWantsKidsLabelsWithIcon()
return (
<Row className="items-center gap-0.5">
@@ -82,6 +94,7 @@ export function WantsKidsFilter(props: {
updateFilter: (newState: Partial<FilterFields>) => void
}) {
const {filters, updateFilter} = props
const wantsKidsLabelsWithIcon = useWantsKidsLabelsWithIcon()
return (
<ChoicesToggleGroup

View File

@@ -1,12 +1,12 @@
import { Row } from 'web/components/layout/row'
import { Checkbox } from 'web/components/widgets/checkbox'
import { Input } from 'web/components/widgets/input'
import { Button } from 'web/components/buttons/button'
import {Row} from 'web/components/layout/row'
import {Checkbox} from 'web/components/widgets/checkbox'
import {Input} from 'web/components/widgets/input'
import {Button} from 'web/components/buttons/button'
import clsx from 'clsx'
import { useEffect, useMemo, useState } from 'react'
import {useEffect, useMemo, useState} from 'react'
import {useT} from "web/lib/locale";
import {toKey} from "common/parsing";
export const MultiCheckbox = (props: {
// Map of label -> value
choices: { [key: string]: string }
@@ -119,7 +119,7 @@ export const MultiCheckbox = (props: {
{filteredEntries.map(([key, value]) => (
<Checkbox
key={key}
label={t(`${translationPrefix}.${toKey(value)}`, key)}
label={translationPrefix ? t(`${translationPrefix}.${toKey(value)}`, key) : key}
checked={selected.includes(value)}
toggle={(checked: boolean) => {
if (checked) {

View File

@@ -12,18 +12,18 @@ import {useT} from 'web/lib/locale'
export function DeleteYourselfButton() {
const [deleteAccountConfirmation, setDeleteAccountConfirmation] = useState('')
const t = useT()
const confirmPhrase = t('deleteyourself.confirm_phrase', 'delete my account')
const confirmPhrase = t('delete_yourself.confirm_phrase', 'delete my account')
return (
<ConfirmationButton
openModalBtn={{
className: 'p-2',
label: t('deleteyourself.open_label', 'Permanently delete this account'),
label: t('delete_yourself.open_label', 'Permanently delete this account'),
icon: <TrashIcon className="mr-1 h-5 w-5"/>,
color: 'red',
}}
submitBtn={{
label: t('deleteyourself.submit', 'Delete account'),
label: t('delete_yourself.submit', 'Delete account'),
color:
deleteAccountConfirmation == confirmPhrase ? 'red' : 'gray',
}}
@@ -31,13 +31,13 @@ export function DeleteYourselfButton() {
if (deleteAccountConfirmation == confirmPhrase) {
toast
.promise(deleteAccount(), {
loading: t('deleteyourself.toast.loading', 'Deleting account...'),
loading: t('delete_yourself.toast.loading', 'Deleting account...'),
success: () => {
router.push('/')
return t('deleteyourself.toast.success', 'Your account has been deleted.')
return t('delete_yourself.toast.success', 'Your account has been deleted.')
},
error: () => {
return t('deleteyourself.toast.error', 'Failed to delete account.')
return t('delete_yourself.toast.error', 'Failed to delete account.')
},
})
.then(() => {
@@ -51,16 +51,16 @@ export function DeleteYourselfButton() {
}}
>
<Col>
<Title>{t('deleteyourself.title', 'Are you sure?')}</Title>
<Title>{t('delete_yourself.title', 'Are you sure?')}</Title>
<div>
{t(
'deleteyourself.description',
'delete_yourself.description',
'Deleting your account means you will no longer be able to use your account. You will lose access to all of your data.'
)}
</div>
<Input
type="text"
placeholder={t('deleteyourself.input_placeholder', "Type 'delete my account' to confirm")}
placeholder={t('delete_yourself.input_placeholder', "Type 'delete my account' to confirm")}
className="w-full"
value={deleteAccountConfirmation}
onChange={(e) => setDeleteAccountConfirmation(e.target.value)}

View File

@@ -1,5 +1,7 @@
import { RadioGroup } from '@headlessui/react'
import {RadioGroup} from '@headlessui/react'
import clsx from 'clsx'
import {toKey} from "common/parsing";
import {useT} from "web/lib/locale";
const colorClasses = {
'indigo-dark':
@@ -15,16 +17,17 @@ export type ColorType = keyof typeof colorClasses
export function ChoicesToggleGroup<T extends Record<string, string | number | boolean>>(
props: {
currentChoice: T[keyof T] | undefined | null
choicesMap: T
disabled?: boolean
disabledOptions?: Array<T[keyof T]>
setChoice: (val: T[keyof T]) => void
color?: ColorType
className?: string
toggleClassName?: string
children?: React.ReactNode
}) {
currentChoice: T[keyof T] | undefined | null
choicesMap: T
disabled?: boolean
disabledOptions?: Array<T[keyof T]>
setChoice: (val: T[keyof T]) => void
color?: ColorType
className?: string
toggleClassName?: string
children?: React.ReactNode
translationPrefix?: string
}) {
const {
currentChoice,
setChoice,
@@ -35,7 +38,12 @@ export function ChoicesToggleGroup<T extends Record<string, string | number | bo
className,
children,
toggleClassName,
translationPrefix
} = props
const t = useT()
// Object.entries(choicesMap).map(([choiceKey, choice]) => (
// console.log(`${translationPrefix}.${toKey(String(choice))}`)
// ))
return (
<RadioGroup
className={clsx(
@@ -52,7 +60,7 @@ export function ChoicesToggleGroup<T extends Record<string, string | number | bo
key={choiceKey}
value={choice}
disabled={disabledOptions?.includes(choice as any)}
className={({ disabled }) =>
className={({disabled}) =>
clsx(
disabled
? 'text-ink-300 aria-checked:bg-ink-300 aria-checked:text-ink-0 cursor-not-allowed'
@@ -62,7 +70,8 @@ export function ChoicesToggleGroup<T extends Record<string, string | number | bo
)
}
>
<RadioGroup.Label as="span">{choiceKey}</RadioGroup.Label>
<RadioGroup.Label
as="span">{translationPrefix ? t(`${translationPrefix}.${toKey(String(choice))}`, choiceKey) : choiceKey}</RadioGroup.Label>
</RadioGroup.Option>
))}
{children}

View File

File diff suppressed because it is too large Load Diff