94 lines
4.4 KiB
TypeScript
94 lines
4.4 KiB
TypeScript
|
|
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>
|
||
|
|
);
|
||
|
|
}
|