Replace all native <select> elements in the admin panel with SearchableSelect for a consistent UI experience. This change enhances accessibility, supports RTL and dark mode, and improves the overall design by utilizing a common component. The updates include adjustments to state management and event handling to ensure seamless integration with existing functionality across various pages and components.

This commit is contained in:
hamed
2026-07-16 08:56:59 +03:30
parent 7b8a5c2775
commit b1b99903ec
21 changed files with 571 additions and 288 deletions
+44 -16
View File
@@ -7,6 +7,7 @@ import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api';
import PersianDateInput from '../components/ui/PersianDateInput';
import PriceInput from '../components/ui/PriceInput';
import SearchableSelect from '../components/ui/SearchableSelect';
import { WalletChargeLink } from '../components/AppointmentActions';
interface Option { uuid: string; name?: string; full_name?: string }
@@ -100,7 +101,6 @@ export default function AppointmentEditPage() {
});
const label = { fontSize: 12.5, color: 'var(--text-3)' } as const;
const sel = { width: '100%', height: 38, borderRadius: 'var(--r-sm)', border: '1px solid var(--border)', background: 'var(--surface)', fontSize: 13, fontFamily: 'inherit', padding: '0 10px' } as const;
if (isLoading || !a) return <div style={{ padding: 24, color: 'var(--text-3)', fontSize: 13 }}>در حال بارگذاری...</div>;
@@ -122,24 +122,46 @@ export default function AppointmentEditPage() {
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 12, marginBottom: 18 }}>
<div>
<label style={label}>بخش</label>
<select aria-label="بخش" style={{ ...sel, marginTop: 6 }} value={sectionUuid} onChange={e => { setSectionUuid(e.target.value); setItemUuid(''); }}>
<option value="">انتخاب بخش</option>
{(sectionsQ.data?.data ?? []).map(o => <option key={o.uuid} value={o.uuid}>{o.name}</option>)}
</select>
<div style={{ marginTop: 6 }}>
<SearchableSelect
options={(sectionsQ.data?.data ?? []).map(o => ({ value: o.uuid, label: o.name ?? '' }))}
value={sectionUuid || null}
onChange={v => { setSectionUuid(v ? String(v) : ''); setItemUuid(''); }}
placeholder="انتخاب بخش"
isLoading={sectionsQ.isLoading}
isClearable
height={38}
/>
</div>
</div>
<div>
<label style={label}>سرویس</label>
<select aria-label="سرویس" style={{ ...sel, marginTop: 6 }} value={itemUuid} onChange={e => setItemUuid(e.target.value)} disabled={!sectionUuid}>
<option value="">انتخاب سرویس</option>
{(itemsQ.data?.data ?? []).map(o => <option key={o.uuid} value={o.uuid}>{o.name}</option>)}
</select>
<div style={{ marginTop: 6 }}>
<SearchableSelect
options={(itemsQ.data?.data ?? []).map(o => ({ value: o.uuid, label: o.name ?? '' }))}
value={itemUuid || null}
onChange={v => setItemUuid(v ? String(v) : '')}
placeholder="انتخاب سرویس"
isDisabled={!sectionUuid}
isLoading={itemsQ.isLoading}
isClearable
height={38}
/>
</div>
</div>
<div>
<label style={label}>پرسنل</label>
<select aria-label="پرسنل" style={{ ...sel, marginTop: 6 }} value={staffUuid} onChange={e => setStaffUuid(e.target.value)}>
<option value="">انتخاب پرسنل</option>
{(staffQ.data?.data ?? []).map(o => <option key={o.uuid} value={o.uuid}>{o.full_name}</option>)}
</select>
<div style={{ marginTop: 6 }}>
<SearchableSelect
options={(staffQ.data?.data ?? []).map(o => ({ value: o.uuid, label: o.full_name ?? '' }))}
value={staffUuid || null}
onChange={v => setStaffUuid(v ? String(v) : '')}
placeholder="انتخاب پرسنل"
isLoading={staffQ.isLoading}
isClearable
height={38}
/>
</div>
</div>
</div>
@@ -178,9 +200,15 @@ export default function AppointmentEditPage() {
<div style={{ maxWidth: 320, marginBottom: 18 }}>
<label style={label}>انتخاب وضعیت</label>
<select aria-label="وضعیت" style={{ ...sel, marginTop: 6 }} value={status} onChange={e => setStatus(e.target.value)}>
{statusOptions.map(([v, l]) => <option key={v} value={v}>{l}</option>)}
</select>
<div style={{ marginTop: 6 }}>
<SearchableSelect
options={statusOptions.map(([v, l]) => ({ value: v, label: l }))}
value={status || null}
onChange={v => setStatus(v ? String(v) : '')}
placeholder="انتخاب وضعیت"
height={38}
/>
</div>
</div>
<label style={label}>توضیحات</label>