Files
emaui/libs/ui/src/lib/input/BilingualInput.tsx
mihretue 84ff644ebb fix(question): show both language fields for MCQ options
Options editor used a single toggle-based bilingual field (BilingualInput)
that swapped English/Amharic in place via a tiny, easy-to-miss button.
Authors were filling English only and never noticing Amharic was empty.

- QuestionOptionsEditor: show separate always-visible English/Amharic
  TextInputs per option instead of the toggle field.
- BilingualInput (shared): for other callers still using the toggle,
  add a tooltip and a red dot indicator when the hidden language is
  empty, so the gap is visible without switching.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 08:26:21 +00:00

114 lines
3.1 KiB
TypeScript

import { useState } from 'react';
import {
Tooltip,
TextInput,
UnstyledButton,
rem,
type TextInputProps,
} from '@mantine/core';
export interface BilingualValue {
en: string;
am: string;
}
interface BilingualInputProps
extends Omit<TextInputProps, 'value' | 'onChange' | 'placeholder' | 'rightSection' | 'rightSectionWidth'> {
value: BilingualValue;
onChange: (value: BilingualValue) => void;
placeholder?: string | BilingualValue;
}
function resolvePlaceholder(placeholder: string | BilingualValue | undefined, lang: 'en' | 'am'): string {
if (!placeholder) {
return lang === 'en' ? 'Enter in English' : 'በአማርኛ ያስገቡ';
}
if (typeof placeholder === 'string') {
return placeholder;
}
return placeholder[lang];
}
export function BilingualInput({
label,
value,
onChange,
required,
placeholder,
...rest
}: BilingualInputProps) {
const [lang, setLang] = useState<'en' | 'am'>('en');
const otherLang = lang === 'en' ? 'am' : 'en';
const otherLangName = otherLang === 'en' ? 'English' : 'Amharic';
const otherIsEmpty = !value[otherLang]?.trim();
const toggle = () => setLang((l) => (l === 'en' ? 'am' : 'en'));
return (
<TextInput
label={label}
required={required}
placeholder={resolvePlaceholder(placeholder, lang)}
value={value[lang]}
onChange={(e) => onChange({ ...value, [lang]: e.currentTarget.value })}
rightSection={
<Tooltip
label={`Switch to ${otherLangName}${otherIsEmpty ? ' — empty' : ''}`}
withArrow
>
<UnstyledButton
onClick={toggle}
aria-label={`Switch to ${otherLangName}`}
style={{
position: 'relative',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: rem(32),
height: rem(24),
borderRadius: rem(4),
fontSize: rem(11),
fontWeight: 700,
letterSpacing: '0.05em',
background:
lang === 'en'
? 'var(--mantine-color-blue-1)'
: 'var(--mantine-color-teal-1)',
color:
lang === 'en'
? 'var(--mantine-color-blue-7)'
: 'var(--mantine-color-teal-7)',
cursor: 'pointer',
transition: 'background 150ms ease',
}}
>
{lang === 'en' ? 'EN' : 'AM'}
{otherIsEmpty && (
<span
aria-hidden
title={`${otherLangName} text is missing`}
style={{
position: 'absolute',
top: -2,
right: -2,
width: rem(7),
height: rem(7),
borderRadius: '50%',
background: 'var(--mantine-color-red-6)',
border: '1px solid var(--mantine-color-body)',
}}
/>
)}
</UnstyledButton>
</Tooltip>
}
styles={{
input: {
paddingRight: rem(46),
},
}}
{...rest}
/>
);
}