Fix options in multiple languages and filter language mismatch

This commit is contained in:
MartinBraquet
2026-01-18 23:32:42 +01:00
parent 274ea45cb8
commit 94298e4609
3 changed files with 40 additions and 18 deletions

View File

@@ -23,7 +23,7 @@ export const MultiCheckbox = (props: {
addPlaceholder?: string
translationPrefix?: string
}) => {
const { choices, selected, onChange, className, addOption, addPlaceholder, translationPrefix } = props
const {choices, selected, onChange, className, addOption, addPlaceholder, translationPrefix} = props
// Keep a local merged copy to allow optimistic adds while remaining in sync with props
const [localChoices, setLocalChoices] = useState<{ [key: string]: string }>(choices)
@@ -31,7 +31,7 @@ export const MultiCheckbox = (props: {
setLocalChoices((prev) => {
// If incoming choices changed, merge them with any locally added that still don't collide
// Props should be source of truth on conflicts
return { ...prev, ...choices }
return {...prev, ...choices}
})
}, [choices])
@@ -44,12 +44,18 @@ export const MultiCheckbox = (props: {
const t = useT()
const translateOption = (key: string, value: string) => {
if (!translationPrefix) return key
return t(`${translationPrefix}.${toKey(value)}`, key)
}
// Filter visible options while typing a new option (case-insensitive label match)
const filteredEntries = useMemo(() => {
if (!addOption) return entries
const q = newLabel.trim().toLowerCase()
let q = newLabel.trim()
q = translateOption(q, q).toLowerCase()
if (!q) return entries
return entries.filter(([key]) => key.toLowerCase().includes(q))
return entries.filter(([key, value]) => translateOption(key, value).toLowerCase().includes(q))
}, [addOption, entries, newLabel])
const submitAdd = async () => {
@@ -57,33 +63,37 @@ export const MultiCheckbox = (props: {
const label = newLabel.trim()
setError(null)
if (!label) {
setError('Please enter a value.')
setError(t('multi-checkbox.enter_value', 'Please enter a value.'))
return
}
// prevent duplicate by label or by value already selected
const lowerCaseChoices = Object.keys(localChoices).map((k: string) => k.toLowerCase())
if (lowerCaseChoices.includes(label.toLowerCase())) {
setError('That option already exists.')
// const key = Object.keys(lowerCaseChoices).find((k) => k.toLowerCase() === label.toLowerCase())
// if (!key) return
// setProfile('interests', [...(profile['interests'] ?? []), key])
const existingEntry = Object.entries(localChoices).find(([key, value]) =>
translateOption(key, value).toLowerCase() === translateOption(label, label).toLowerCase()
)
if (existingEntry) {
const [_, existingValue] = existingEntry
if (!selected.includes(existingValue)) {
onChange([...selected, existingValue])
}
setNewLabel('')
return
}
setAdding(true)
try {
const result = addOption(label)
if (!result) {
setError('Could not add option.')
setError(t('multi-checkbox.could_not_add', 'Could not add option.'))
setAdding(false)
return
}
const { key, value } = typeof result === 'string' ? { key: label, value: result } : result
setLocalChoices((prev) => ({ ...prev, [key]: value }))
const {key, value} = typeof result === 'string' ? {key: label, value: result} : result
setLocalChoices((prev) => ({...prev, [key]: value}))
// auto-select newly added option if not already selected
if (!selected.includes(value)) onChange([...selected, value])
setNewLabel('')
} catch (e) {
setError('Failed to add option.')
setError(t('multi-checkbox.add_failed', 'Failed to add option.'))
} finally {
setAdding(false)
}
@@ -119,7 +129,7 @@ export const MultiCheckbox = (props: {
{filteredEntries.map(([key, value]) => (
<Checkbox
key={key}
label={translationPrefix ? t(`${translationPrefix}.${toKey(value)}`, key) : key}
label={translateOption(key, value)}
checked={selected.includes(value)}
toggle={(checked: boolean) => {
if (checked) {
@@ -132,7 +142,9 @@ export const MultiCheckbox = (props: {
))}
</Row>
{addOption && newLabel.trim() && filteredEntries.length === 0 && (
<div className="px-2 text-sm text-ink-500">No matching options, feel free to add it.</div>
<div className="px-2 text-sm text-ink-500">
{t('multi-checkbox.no_matching_options', 'No matching options, feel free to add it.')}
</div>
)}
</div>
)

View File

@@ -283,7 +283,11 @@
"messages.toast.muting_forever.success": "Benachrichtigungen dauerhaft stummgeschaltet",
"messages.toast.send_failed": "Senden der Nachricht fehlgeschlagen. Bitte versuchen Sie es später erneut oder kontaktieren Sie den Support, wenn das Problem weiterhin besteht.",
"messages.you_prefix": "Sie: ",
"multi-checkbox.enter_value": "Bitte geben Sie einen Wert ein.",
"multi-checkbox.could_not_add": "Option konnte nicht hinzugefügt werden.",
"multi-checkbox.add_failed": "Hinzufügen der Option fehlgeschlagen.",
"multi-checkbox.search_or_add": "Suchen oder hinzufügen",
"multi-checkbox.no_matching_options": "Keine passenden Optionen, Sie können gerne eine hinzufügen.",
"nav.about": "Über uns",
"nav.contact": "Kontakt",
"nav.faq": "FAQ",
@@ -734,6 +738,7 @@
"register.error.all_fields_required": "Alle Felder sind erforderlich",
"register.error.email_in_use": "Diese E-Mail ist bereits registriert",
"register.error.unknown": "Registrierung fehlgeschlagen",
"error.option_exists": "Diese Option existiert bereits",
"register.get_started": "Loslegen",
"register.link_signin": "Anmelden",
"register.or_sign_up_with": "Oder registrieren mit",

View File

@@ -283,7 +283,11 @@
"messages.toast.muting_forever.success": "Notifs coupées définitivement",
"messages.toast.send_failed": "Échec de l'envoi du message. Veuillez réessayer plus tard ou contacter le support si le problème persiste.",
"messages.you_prefix": "Vous : ",
"multi-checkbox.search_or_add": "Chercher ou ajouter",
"multi-checkbox.enter_value": "Veuillez saisir une valeur.",
"multi-checkbox.could_not_add": "Impossible d'ajouter l'option.",
"multi-checkbox.add_failed": "Échec de l'ajout de l'option.",
"multi-checkbox.search_or_add": "Rechercher ou ajouter",
"multi-checkbox.no_matching_options": "Aucune option correspondante, n'hésitez pas à l'ajouter.",
"nav.about": "À propos",
"nav.contact": "Contact",
"nav.faq": "FAQ",
@@ -734,6 +738,7 @@
"register.error.all_fields_required": "Tous les champs sont requis",
"register.error.email_in_use": "Cet email est déjà enregistré",
"register.error.unknown": "Échec de l'inscription",
"error.option_exists": "Cette option existe déjà",
"register.get_started": "Commencer",
"register.link_signin": "Se connecter",
"register.or_sign_up_with": "Ou inscrivez-vous avec",