feat: replace checkboxes with Switch component for better UI consistency
- Updated DoctorDetailPage, MySecretariesPage, RecordNumberSettingsPage, RepresentationsPage, ResourcePoolsPage, ResourceTypesPage, SecretariesPage, SecretaryDetailPage, SettingsPage, SkillsPage, SmsWalletPage, and TagsSettingsPage to use the new Switch component instead of native checkboxes. - Enhanced accessibility by ensuring the Switch component uses appropriate roles and labels. - Added tests for the new Switch component to ensure functionality and accessibility compliance. - Updated styles to accommodate the new Switch component design.
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { XMarkIcon } from '@heroicons/react/24/outline';
|
||||
import Modal from '../ui/Modal';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import SearchableSelect from '../ui/SearchableSelect';
|
||||
import Field from '../ui/Field';
|
||||
import Input from '../ui/Input';
|
||||
import Switch from '../ui/Switch';
|
||||
import { api, type ApiResponse } from '../../lib/api';
|
||||
import type { ClinicResource, ResourcePayload, ResourceType } from '../../types';
|
||||
import { useResourceDetail } from '../../hooks/useResources';
|
||||
@@ -98,8 +102,14 @@ export default function ResourceFormModal({
|
||||
return (
|
||||
<Modal open={open} onClose={onClose} title={isEdit ? 'ویرایش منبع' : 'افزودن منبع'} size="lg">
|
||||
<div style={{ display: 'grid', gap: 14 }}>
|
||||
<Field label="نام منبع">
|
||||
<input className="field" value={name} onChange={(e) => setName(e.target.value)} placeholder="لیزر آلکساندرایت ۱" />
|
||||
<Field label="نام منبع" htmlFor="resource-name">
|
||||
<Input
|
||||
id="resource-name"
|
||||
value={name}
|
||||
autoFocus
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="لیزر آلکساندرایت ۱"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: 12 }}>
|
||||
@@ -134,14 +144,17 @@ export default function ResourceFormModal({
|
||||
)}
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: 12 }}>
|
||||
<Field label="ظرفیت همزمان">
|
||||
<input className="field" type="number" min={1} value={capacity} onChange={(e) => setCapacity(e.target.value)} />
|
||||
{/* `numeric` بهجای `type="number"`: ورودیِ عددیِ فارسی در `type="number"`
|
||||
نامعتبر است و مرورگر رشتهٔ خالی میدهد — یعنی «۳» تایپشده به ۰ میرسید.
|
||||
`Input numeric` ارقام را زنده به لاتین برمیگرداند. */}
|
||||
<Field label="ظرفیت همزمان" htmlFor="resource-capacity">
|
||||
<Input id="resource-capacity" numeric value={capacity} onChange={(e) => setCapacity(e.target.value)} />
|
||||
</Field>
|
||||
<Field label="آمادهسازی (دقیقه)">
|
||||
<input className="field" type="number" min={0} value={setupMinutes} onChange={(e) => setSetupMinutes(e.target.value)} />
|
||||
<Field label="آمادهسازی (دقیقه)" htmlFor="resource-setup">
|
||||
<Input id="resource-setup" numeric value={setupMinutes} onChange={(e) => setSetupMinutes(e.target.value)} />
|
||||
</Field>
|
||||
<Field label="تمیزکاری (دقیقه)">
|
||||
<input className="field" type="number" min={0} value={cleanupMinutes} onChange={(e) => setCleanupMinutes(e.target.value)} />
|
||||
<Field label="تمیزکاری (دقیقه)" htmlFor="resource-cleanup">
|
||||
<Input id="resource-cleanup" numeric value={cleanupMinutes} onChange={(e) => setCleanupMinutes(e.target.value)} />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@@ -152,7 +165,7 @@ export default function ResourceFormModal({
|
||||
|
||||
<div style={{ display: 'grid', gap: 8 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<label style={{ fontSize: 12, color: 'var(--text-2)' }}>ویژگیها (اختیاری)</label>
|
||||
<span className="cp-label">ویژگیها (اختیاری)</span>
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary sm"
|
||||
@@ -164,32 +177,33 @@ export default function ResourceFormModal({
|
||||
|
||||
{attributes.map((row, index) => (
|
||||
<div key={index} style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
|
||||
<input
|
||||
className="field"
|
||||
<Input
|
||||
list="resource-attribute-keys"
|
||||
value={row.key}
|
||||
placeholder="gender"
|
||||
aria-label={`کلید ویژگی ${formatNumber(index + 1)}`}
|
||||
onChange={(e) =>
|
||||
setAttributes((a) => a.map((r, i) => (i === index ? { ...r, key: e.target.value } : r)))
|
||||
}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<input
|
||||
className="field"
|
||||
<Input
|
||||
value={row.value}
|
||||
placeholder="female"
|
||||
aria-label={`مقدار ویژگی ${formatNumber(index + 1)}`}
|
||||
onChange={(e) =>
|
||||
setAttributes((a) => a.map((r, i) => (i === index ? { ...r, value: e.target.value } : r)))
|
||||
}
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
{/* دکمهٔ فقط-آیکون → `mini-btn`، نه `btn secondary sm` با یک ✕ متنی */}
|
||||
<button
|
||||
type="button"
|
||||
className="btn secondary sm"
|
||||
className="mini-btn danger"
|
||||
onClick={() => setAttributes((a) => a.filter((_, i) => i !== index))}
|
||||
aria-label="حذف ویژگی"
|
||||
aria-label={`حذف ویژگی ${formatNumber(index + 1)}`}
|
||||
>
|
||||
✕
|
||||
<XMarkIcon style={{ width: 16 }} />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
@@ -199,10 +213,13 @@ export default function ResourceFormModal({
|
||||
</datalist>
|
||||
</div>
|
||||
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>
|
||||
<input type="checkbox" checked={active} onChange={(e) => setActive(e.target.checked)} />
|
||||
منبع فعال است
|
||||
</label>
|
||||
<Switch
|
||||
id="resource-active"
|
||||
checked={active}
|
||||
onChange={setActive}
|
||||
label="منبع فعال است"
|
||||
hint="منبع غیرفعال در جستجوی وقت و رزرو نوبت نمیآید."
|
||||
/>
|
||||
|
||||
{/* غیرفعالکردن نوبتهای ثبتشده را لغو نمیکند؛ فقط از جستجوی وقتِ بعدی حذف
|
||||
میشود. پس این هشدار است نه مانع — ولی اپراتور باید بداند چند بیمار روی
|
||||
@@ -233,12 +250,3 @@ export default function ResourceFormModal({
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
function Field({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div style={{ display: 'grid', gap: 6 }}>
|
||||
<label style={{ fontSize: 12, color: 'var(--text-2)' }}>{label}</label>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user