feat(doctors): add city and province filters for doctor listing

This commit is contained in:
hamed
2026-07-12 14:59:02 +03:30
parent bb5884d6bd
commit d2372d4d21
3 changed files with 64 additions and 2 deletions
+51 -2
View File
@@ -107,6 +107,8 @@ export default function DoctorsPage() {
const [status, setStatus] = useState('');
const [ownerStatus, setOwnerStatus] = useState('');
const [specialtyId, setSpecialty] = useState('');
const [provinceId, setProvinceId] = useState('');
const [cityId, setCityId] = useState('');
const [view, setView] = useState<'table' | 'grid'>('table');
const [deleteTarget, setDeleteTarget] = useState<AdminDoctor | null>(null);
@@ -115,7 +117,7 @@ export default function DoctorsPage() {
return () => clearTimeout(t);
}, [searchInput]);
useEffect(() => { setPage(1); }, [status, ownerStatus, specialtyId]);
useEffect(() => { setPage(1); }, [status, ownerStatus, specialtyId, provinceId, cityId]);
// ── Queries ──
@@ -136,14 +138,29 @@ export default function DoctorsPage() {
staleTime: 300_000,
});
const provincesQ = useQuery({
queryKey: ['provinces-list'],
queryFn: () => api.get<ApiResponse<{ id: number; name: string }[]>>('/api/v1/provinces'),
staleTime: 600_000,
enabled: !isRepresentation,
});
const citiesQ = useQuery({
queryKey: ['cities-list', provinceId],
queryFn: () => api.get<ApiResponse<{ id: number; name: string }[]>>(`/api/v1/cities${provinceId ? `?province_id=${provinceId}` : ''}`),
staleTime: 600_000,
enabled: !isRepresentation && !!provinceId,
});
const doctorsQ = useQuery({
queryKey: ['admin-doctors', page, limit, search, status, ownerStatus, specialtyId],
queryKey: ['admin-doctors', page, limit, search, status, ownerStatus, specialtyId, provinceId, cityId],
queryFn: () => {
const p = new URLSearchParams({ page: String(page), limit: String(limit) });
if (search) p.set('search', search);
if (status) p.set('status', status);
if (ownerStatus && !isRepresentation) p.set('owner_status', ownerStatus);
if (specialtyId) p.set('specialty_id', specialtyId);
if (provinceId && !isRepresentation) p.set('state_id', provinceId);
if (cityId && !isRepresentation) p.set('city_id', cityId);
const base = isRepresentation ? '/api/v1/representation/doctors' : '/api/v1/admin/doctors';
return api.get<PaginatedResponse<AdminDoctor>>(`${base}?${p}`);
},
@@ -159,6 +176,14 @@ export default function DoctorsPage() {
() => (specialtiesQ.data?.data as any)?.data ?? specialtiesQ.data?.data ?? [],
[specialtiesQ.data]
);
const provinceOptions = useMemo(
() => ((provincesQ.data?.data as any)?.data ?? provincesQ.data?.data ?? []).map((p: { id: number; name: string }) => ({ value: String(p.id), label: p.name })),
[provincesQ.data]
);
const cityOptions = useMemo(
() => ((citiesQ.data?.data as any)?.data ?? citiesQ.data?.data ?? []).map((c: { id: number; name: string }) => ({ value: String(c.id), label: c.name })),
[citiesQ.data]
);
// ── Mutations ──
@@ -259,6 +284,30 @@ export default function DoctorsPage() {
/>
</div>
)}
{!isRepresentation && (
<div style={{ minWidth: 170 }}>
<SearchableSelect
options={provinceOptions}
value={provinceId || null}
onChange={(v) => { setProvinceId(v ? String(v) : ''); setCityId(''); }}
placeholder="همه استان‌ها"
isClearable
height={36}
/>
</div>
)}
{!isRepresentation && provinceId && (
<div style={{ minWidth: 170 }}>
<SearchableSelect
options={cityOptions}
value={cityId || null}
onChange={(v) => setCityId(v ? String(v) : '')}
placeholder="همه شهرها"
isClearable
height={36}
/>
</div>
)}
<div className="seg">
<button className={!status ? 'on' : ''} onClick={() => setStatus('')}>همه</button>
<button className={status === 'active' ? 'on' : ''} onClick={() => setStatus('active')}>فعال</button>