GdzieWjade/src/components/ProfileView.tsx

94 lines
4.4 KiB
TypeScript
Raw Normal View History

2026-10-03 13:33:50 +02:00
import { useState, type FormEvent } from 'react';
import { useI18n } from '../lib/i18n';
import { DEFAULT_PROFILE } from '../lib/profile';
import type { Profile, ProfileKind } from '../lib/types';
import { Icon } from './ui';
interface Props {
initial: Profile | null;
onSave: (p: Profile) => void;
}
export function ProfileView({ initial, onSave }: Props) {
const { t } = useI18n();
const [p, setP] = useState<Profile>(initial ?? DEFAULT_PROFILE.wheelchair);
const set = <K extends keyof Profile>(k: K, v: Profile[K]) => setP((cur) => ({ ...cur, [k]: v }));
const submit = (e: FormEvent) => { e.preventDefault(); onSave(p); };
const pickKind = (kind: ProfileKind) => setP(DEFAULT_PROFILE[kind]);
return (
<div className="grid-container padding-y-4 kbb-narrow">
<h1 className="margin-top-0">{t('profile.title')}</h1>
<p className="usa-intro">{t('profile.intro')}</p>
<form className="usa-form usa-form--large" onSubmit={submit}>
<fieldset className="usa-fieldset">
<legend className="usa-legend usa-legend--large">{t('profile.kind')}</legend>
{(['wheelchair', 'stroller'] as const).map((k) => (
<div className="usa-radio" key={k}>
<input
className="usa-radio__input usa-radio__input--tile" id={`kind-${k}`} type="radio" name="kind"
checked={p.kind === k} onChange={() => pickKind(k)}
/>
<label className="usa-radio__label" htmlFor={`kind-${k}`}>
{t(`profile.${k}`)}
<span className="usa-checkbox__label-description">{t(`profile.${k}.hint`)}</span>
</label>
</div>
))}
</fieldset>
<fieldset className="usa-fieldset margin-top-4">
<legend className="usa-legend usa-legend--large">{t('profile.limits')}</legend>
<Range id="kerb" label={t('profile.kerb')} min={0} max={10} step={1} value={p.maxKerbCm} unit={t('unit.cm')} onChange={(v) => set('maxKerbCm', v)} />
<Range id="width" label={t('profile.width')} min={60} max={120} step={5} value={Math.round(p.minWidthM * 100)} unit={t('unit.cm')} onChange={(v) => set('minWidthM', v / 100)} />
<Range id="incline" label={t('profile.incline')} min={3} max={12} step={1} value={p.maxInclinePct} unit={t('unit.pct')} onChange={(v) => set('maxInclinePct', v)} />
</fieldset>
<fieldset className="usa-fieldset margin-top-4">
<legend className="usa-legend usa-legend--large">{t('profile.prefs')}</legend>
<Check id="cobbles" label={t('profile.cobbles')} checked={p.avoidCobbles} onChange={(v) => set('avoidCobbles', v)} />
<Check id="toilet" label={t('profile.toilet')} checked={p.needToilet} onChange={(v) => set('needToilet', v)} />
<Check id="rest" label={t('profile.rest')} checked={p.needRest} onChange={(v) => set('needRest', v)} />
{p.kind === 'stroller' && (
<Check id="changing" label={t('profile.changing')} checked={p.needChanging} onChange={(v) => set('needChanging', v)} />
)}
</fieldset>
<button type="submit" className="usa-button usa-button--big margin-top-4">
{t('profile.save')}
<Icon name="arrow_forward" size={3} className="margin-left-1" />
</button>
<p className="font-sans-xs text-base-dark margin-top-2">
<Icon name="lock" size={2} className="margin-right-05" />
{t('profile.privacy')}
</p>
</form>
</div>
);
}
function Range({ id, label, min, max, step, value, unit, onChange }: {
id: string; label: string; min: number; max: number; step: number; value: number; unit: string; onChange: (v: number) => void;
}) {
return (
<div className="margin-top-2">
<label className="usa-label" htmlFor={`range-${id}`}>
{label}: <strong>{value} {unit}</strong>
</label>
<input
id={`range-${id}`} className="usa-range" type="range" min={min} max={max} step={step} value={value}
onChange={(e) => onChange(Number(e.target.value))}
/>
</div>
);
}
function Check({ id, label, checked, onChange }: { id: string; label: string; checked: boolean; onChange: (v: boolean) => void }) {
return (
<div className="usa-checkbox">
<input className="usa-checkbox__input" id={`chk-${id}`} type="checkbox" checked={checked} onChange={(e) => onChange(e.target.checked)} />
<label className="usa-checkbox__label" htmlFor={`chk-${id}`}>{label}</label>
</div>
);
}