feat: toggle new-patient form on appointment create page
Show name/phone/national-code fields only after clicking 'مراجعه کننده جدید'; hide the existing-patient search while in new-patient mode, with a 'انتخاب از لیست موجود' link to switch back. Update tests to click the toggle before filling the fields. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ describe('AppointmentCreatePage — افزودن نوبت', () => {
|
||||
it('posts a new appointment with the entered patient (happy path)', async () => {
|
||||
renderWithProviders(<AppointmentCreatePage />, { route: '/admin/appointments/new' });
|
||||
|
||||
fireEvent.click(screen.getByText('مراجعه کننده جدید'));
|
||||
fireEvent.change(screen.getByPlaceholderText('نام و نام خانوادگی مراجعه کننده'), { target: { value: 'علی محمدی' } });
|
||||
fireEvent.change(screen.getByPlaceholderText('شماره تماس مراجعه کننده'), { target: { value: '09121234567' } });
|
||||
fireEvent.change(screen.getByPlaceholderText('کد ملی مراجعه کننده'), { target: { value: '1234567891' } });
|
||||
@@ -49,6 +50,7 @@ describe('AppointmentCreatePage — افزودن نوبت', () => {
|
||||
|
||||
renderWithProviders(<AppointmentCreatePage />, { route: '/admin/appointments/new' });
|
||||
|
||||
fireEvent.click(screen.getByText('مراجعه کننده جدید'));
|
||||
fireEvent.change(screen.getByPlaceholderText('نام و نام خانوادگی مراجعه کننده'), { target: { value: 'علی محمدی' } });
|
||||
fireEvent.change(screen.getByPlaceholderText('شماره تماس مراجعه کننده'), { target: { value: '09121234567' } });
|
||||
fireEvent.change(screen.getByPlaceholderText('کد ملی مراجعه کننده'), { target: { value: '1234567891' } });
|
||||
@@ -70,6 +72,10 @@ describe('AppointmentCreatePage — افزودن نوبت', () => {
|
||||
const btn = screen.getByText('ثبت اطلاعات') as HTMLButtonElement;
|
||||
expect(btn).toBeDisabled();
|
||||
|
||||
// فیلدهای مراجعهکنندهٔ جدید تا کلیک روی دکمه پنهاناند
|
||||
expect(screen.queryByPlaceholderText('نام و نام خانوادگی مراجعه کننده')).toBeNull();
|
||||
fireEvent.click(screen.getByText('مراجعه کننده جدید'));
|
||||
|
||||
// شماره ناقص → همچنان غیرفعال
|
||||
fireEvent.change(screen.getByPlaceholderText('نام و نام خانوادگی مراجعه کننده'), { target: { value: 'ب' } });
|
||||
fireEvent.change(screen.getByPlaceholderText('شماره تماس مراجعه کننده'), { target: { value: '0912' } });
|
||||
|
||||
@@ -55,6 +55,7 @@ export default function AppointmentCreatePage() {
|
||||
// ── بیمار: جستجوی رکورد موجود یا ورود شخص جدید
|
||||
const [patientSearch, setPatientSearch] = useState('');
|
||||
const [picked, setPicked] = useState<PatientRow | null>(null);
|
||||
const [newPatient, setNewPatient] = useState(false); // فرم «مراجعه کننده جدید» فعال است؟
|
||||
const [name, setName] = useState('');
|
||||
const [mobile, setMobile] = useState('');
|
||||
const [nationalCode, setNationalCode] = useState('');
|
||||
@@ -172,53 +173,71 @@ export default function AppointmentCreatePage() {
|
||||
|
||||
{/* مراجعه کننده */}
|
||||
<div style={{ ...sectionTitle, marginTop: isDoctor ? 0 : 18 }}>اطلاعات مراجعه کننده :</div>
|
||||
<label style={label}>انتخاب مراجعه کننده</label>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 12, margin: '6px 0 8px' }}>
|
||||
<div className="field" style={{ width: 400, maxWidth: '100%' }}>
|
||||
<MagnifyingGlassIcon style={{ width: 18, height: 18, color: 'var(--text-3)', flexShrink: 0 }} />
|
||||
<input value={picked ? `${picked.user_name ?? ''} — ${picked.user_mobile ?? ''}` : patientSearch}
|
||||
onChange={e => { setPicked(null); setPatientSearch(e.target.value); }}
|
||||
placeholder="جستجوی نام، شماره تماس، شماره پرونده..." />
|
||||
</div>
|
||||
<button type="button" className="btn outline" onClick={() => { setPicked(null); setPatientSearch(''); }}
|
||||
style={{ height: 44, minWidth: 161, gap: 6 }}>
|
||||
<PlusIcon style={{ width: 16, height: 16 }} /> مراجعه کننده جدید
|
||||
</button>
|
||||
</div>
|
||||
{!picked && patients.length > 0 && (
|
||||
<div style={{ border: '1px solid var(--border)', borderRadius: 'var(--r-sm)', marginBottom: 10, overflow: 'hidden', maxWidth: 400 }}>
|
||||
{patients.map(p => (
|
||||
<button key={p.uuid} onClick={() => setPicked(p)} style={{
|
||||
display: 'block', width: '100%', padding: '8px 10px', fontSize: 13, textAlign: 'right',
|
||||
background: 'transparent', border: 'none', cursor: 'pointer', fontFamily: 'inherit',
|
||||
}}>
|
||||
{p.user_name} <span style={{ color: 'var(--text-3)', direction: 'ltr' }}>{p.user_mobile}</span>
|
||||
|
||||
{/* حالت جستجوی مراجعهکنندهٔ موجود */}
|
||||
{!newPatient && (
|
||||
<>
|
||||
<label style={label}>انتخاب مراجعه کننده</label>
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 12, margin: '6px 0 8px' }}>
|
||||
<div className="field" style={{ width: 400, maxWidth: '100%' }}>
|
||||
<MagnifyingGlassIcon style={{ width: 18, height: 18, color: 'var(--text-3)', flexShrink: 0 }} />
|
||||
<input value={picked ? `${picked.user_name ?? ''} — ${picked.user_mobile ?? ''}` : patientSearch}
|
||||
onChange={e => { setPicked(null); setPatientSearch(e.target.value); }}
|
||||
placeholder="جستجوی نام، شماره تماس، شماره پرونده..." />
|
||||
</div>
|
||||
<button type="button" className="btn outline"
|
||||
onClick={() => { setPicked(null); setPatientSearch(''); setNewPatient(true); }}
|
||||
style={{ height: 44, minWidth: 161, gap: 6 }}>
|
||||
<PlusIcon style={{ width: 16, height: 16 }} /> مراجعه کننده جدید
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{!picked && patients.length > 0 && (
|
||||
<div style={{ border: '1px solid var(--border)', borderRadius: 'var(--r-sm)', marginBottom: 10, overflow: 'hidden', maxWidth: 400 }}>
|
||||
{patients.map(p => (
|
||||
<button key={p.uuid} onClick={() => setPicked(p)} style={{
|
||||
display: 'block', width: '100%', padding: '8px 10px', fontSize: 13, textAlign: 'right',
|
||||
background: 'transparent', border: 'none', cursor: 'pointer', fontFamily: 'inherit',
|
||||
}}>
|
||||
{p.user_name} <span style={{ color: 'var(--text-3)', direction: 'ltr' }}>{p.user_mobile}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{picked === null && (
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16, marginTop: 8 }}>
|
||||
<div>
|
||||
<label style={label}>نام و نام خانوادگی مراجعه کننده</label>
|
||||
<div className="field" style={{ marginTop: 6, height: 44 }}>
|
||||
<input value={name} onChange={e => setName(e.target.value)} placeholder="نام و نام خانوادگی مراجعه کننده" />
|
||||
|
||||
{/* حالت ثبت مراجعهکنندهٔ جدید */}
|
||||
{newPatient && (
|
||||
<>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', margin: '2px 0 6px' }}>
|
||||
<span style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-2)' }}>مراجعه کننده جدید</span>
|
||||
<button type="button" className="btn sm ghost"
|
||||
onClick={() => { setName(''); setMobile(''); setNationalCode(''); setNewPatient(false); }}>
|
||||
انتخاب از لیست موجود
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 16, marginTop: 4 }}>
|
||||
<div>
|
||||
<label style={label}>نام و نام خانوادگی مراجعه کننده</label>
|
||||
<div className="field" style={{ marginTop: 6, height: 44 }}>
|
||||
<input value={name} onChange={e => setName(e.target.value)} placeholder="نام و نام خانوادگی مراجعه کننده" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label style={label}>شماره تماس</label>
|
||||
<div className="field" style={{ marginTop: 6, height: 44 }}>
|
||||
<input value={mobile} onChange={e => setMobile(e.target.value)} placeholder="شماره تماس مراجعه کننده" dir="ltr" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label style={label}>کد ملی</label>
|
||||
<div className="field" style={{ marginTop: 6, height: 44 }}>
|
||||
<input value={nationalCode} onChange={e => setNationalCode(e.target.value.replace(/\D/g, '').slice(0, 10))}
|
||||
placeholder="کد ملی مراجعه کننده" dir="ltr" inputMode="numeric" maxLength={10} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label style={label}>شماره تماس</label>
|
||||
<div className="field" style={{ marginTop: 6, height: 44 }}>
|
||||
<input value={mobile} onChange={e => setMobile(e.target.value)} placeholder="شماره تماس مراجعه کننده" dir="ltr" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label style={label}>کد ملی</label>
|
||||
<div className="field" style={{ marginTop: 6, height: 44 }}>
|
||||
<input value={nationalCode} onChange={e => setNationalCode(e.target.value.replace(/\D/g, '').slice(0, 10))}
|
||||
placeholder="کد ملی مراجعه کننده" dir="ltr" inputMode="numeric" maxLength={10} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* مشخصات سرویس */}
|
||||
|
||||
Reference in New Issue
Block a user