feat(stepper): implement multi-step wizard for appointment confirmation process

This commit is contained in:
hamed
2026-08-09 09:12:00 +03:30
parent 948f59827a
commit 3ffab2bbd0
7 changed files with 544 additions and 81 deletions
@@ -11,6 +11,7 @@ import Modal from '../ui/Modal';
import PersianDateInput from '../ui/PersianDateInput';
import PriceInput from '../ui/PriceInput';
import SearchableSelect from '../ui/SearchableSelect';
import Stepper from '../ui/Stepper';
import ServiceSlotPicker from './ServiceSlotPicker';
import type { ServicePick } from './ServiceSlotPicker';
import type { BookingService } from '../../hooks/useDoctorBookingServices';
@@ -379,7 +380,11 @@ export default function NewAppointmentModal({
</div>
{stepKeys.length > 1 && (
<Stepper keys={stepKeys} current={currentStep} />
<Stepper
steps={stepKeys.map(key => ({ key, title: STEP_TITLES[key] }))}
current={currentStep}
ariaLabel="مراحل ثبت نوبت"
/>
)}
{currentStep === 'service' && (
@@ -685,57 +690,3 @@ function Step({ title, children }: { title: string; children: ReactNode }) {
);
}
/**
* نوار مراحل — «کجای کارم و چقدر مانده».
*
* فرم قبلاً همهٔ مراحل را با هم نشان می‌داد و مودال از ارتفاع صفحه بلندتر می‌شد؛
* حالا هر بار یک مرحله دیده می‌شود و این نوار تنها چیزی است که کلِ مسیر را می‌گوید.
*/
function Stepper({ keys, current }: { keys: StepKey[]; current: StepKey }) {
const activeIdx = keys.indexOf(current);
return (
<ol
aria-label="مراحل ثبت نوبت"
style={{
display: 'flex', alignItems: 'center', gap: 8, listStyle: 'none',
margin: '0 0 18px', padding: 0,
}}
>
{keys.map((key, idx) => {
const done = idx < activeIdx;
const active = idx === activeIdx;
return (
<li
key={key}
aria-current={active ? 'step' : undefined}
style={{ display: 'flex', alignItems: 'center', gap: 8, flex: idx === keys.length - 1 ? '0 0 auto' : 1 }}
>
<span style={{
display: 'grid', placeItems: 'center', width: 22, height: 22, borderRadius: 999,
flexShrink: 0, fontSize: 11.5, fontWeight: 700,
background: active || done ? 'var(--primary)' : 'var(--surface-3)',
color: active || done ? 'var(--on-primary)' : 'var(--text-3)',
}}>
{idx + 1}
</span>
<span style={{
fontSize: 12.5, whiteSpace: 'nowrap',
fontWeight: active ? 700 : 500,
color: active ? 'var(--text)' : 'var(--text-3)',
}}>
{STEP_TITLES[key]}
</span>
{idx < keys.length - 1 && (
<span style={{
flex: 1, height: 1, minWidth: 12,
background: done ? 'var(--primary)' : 'var(--border)',
}} />
)}
</li>
);
})}
</ol>
);
}