refactor: update pricing input handling to use PriceInput component
- Replaced raw input fields for pricing with PriceInput component across various forms and modals to ensure consistent formatting and accessibility. - Updated tests to reflect changes in pricing input handling, ensuring values are displayed in toman with proper formatting. - Enhanced accessibility by adding aria-labels and aria-invalid attributes to PriceInput components. - Adjusted UI elements to improve layout and user experience, particularly in forms related to service items and scheduling. - Changed labels from "نمایش در نوبتدهی" to "نمایش در نوبتدهی آنلاین" for clarity.
This commit is contained in:
@@ -160,7 +160,7 @@ export default function ServiceItemFormModal({ item, sectionUuid, onClose, onMan
|
||||
size="md"
|
||||
footer={
|
||||
<>
|
||||
<button type="button" className="btn" onClick={onClose}>انصراف</button>
|
||||
<button type="button" className="btn secondary" onClick={onClose}>انصراف</button>
|
||||
<button type="submit" form="service-item-form" className="btn primary" disabled={saving}>
|
||||
{saving ? 'در حال ذخیره...' : 'ذخیره سرویس'}
|
||||
</button>
|
||||
@@ -183,6 +183,8 @@ export default function ServiceItemFormModal({ item, sectionUuid, onClose, onMan
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* دو عددِ کوتاه کنار هم؛ پیش از این «قیمت» با «پرسنل مسئول» همردیف بود و
|
||||
با افزودن هر پرسنل، چیپها ستون را بلند میکردند و ردیف بههم میریخت. */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
|
||||
<div>
|
||||
<label className="field-label">قیمت پایه (تومان) *</label>
|
||||
@@ -200,16 +202,40 @@ export default function ServiceItemFormModal({ item, sectionUuid, onClose, onMan
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label className="field-label">پرسنل مسئول</label>
|
||||
<SearchableSelect
|
||||
options={staffOptions}
|
||||
value={''}
|
||||
onChange={(v) => { if (v != null) form.setValue('staff_uuids', [...selectedStaffUuids, String(v)]); }}
|
||||
placeholder="افزودن پرسنل (اختیاری)"
|
||||
noOptionsMessage="پرسنلی باقی نمانده"
|
||||
height={42}
|
||||
/>
|
||||
{selectedStaffUuids.length > 0 && (
|
||||
<label className="field-label">زمان متوسط (دقیقه)</label>
|
||||
<div className="field">
|
||||
<input {...numericField(form.register('duration_minutes'))} placeholder="مثلاً: 50" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="field-label">نوع خدمت *</label>
|
||||
<SearchableSelect
|
||||
options={categories.map((c) => ({ value: c.key, label: c.label }))}
|
||||
value={form.watch('service_category') || null}
|
||||
onChange={(v) => { if (v != null) form.setValue('service_category', String(v)); }}
|
||||
placeholder="انتخاب نوع خدمت"
|
||||
noOptionsMessage="نوعی تعریف نشده است"
|
||||
height={42}
|
||||
/>
|
||||
<span style={{ display: 'block', marginTop: 6, fontSize: 11.5, color: 'var(--text-3)' }}>
|
||||
درصد پوشش بیمه بر اساس همین نوع محاسبه میشود.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* تمامعرض: چیپهای پرسنل چند سطر میشوند و در نیمْستون میشکستند. */}
|
||||
<div>
|
||||
<label className="field-label">پرسنل مسئول</label>
|
||||
<SearchableSelect
|
||||
options={staffOptions}
|
||||
value={''}
|
||||
onChange={(v) => { if (v != null) form.setValue('staff_uuids', [...selectedStaffUuids, String(v)]); }}
|
||||
placeholder="افزودن پرسنل (اختیاری)"
|
||||
noOptionsMessage="پرسنلی باقی نمانده"
|
||||
height={42}
|
||||
/>
|
||||
{selectedStaffUuids.length > 0 && (
|
||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 8 }}>
|
||||
{selectedStaffUuids.map((uuid) => (
|
||||
<span key={uuid} style={{
|
||||
@@ -229,39 +255,20 @@ export default function ServiceItemFormModal({ item, sectionUuid, onClose, onMan
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, alignItems: 'end' }}>
|
||||
<div>
|
||||
<label className="field-label">زمان متوسط (دقیقه)</label>
|
||||
<div className="field">
|
||||
<input {...numericField(form.register('duration_minutes'))} placeholder="مثلاً: 50" />
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ padding: '9px 0' }}>
|
||||
<Switch
|
||||
inline
|
||||
checked={form.watch('bookable') ?? false}
|
||||
onChange={(v) => form.setValue('bookable', v, { shouldDirty: true })}
|
||||
label="نمایش در نوبتدهی"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="field-label">نوع خدمت *</label>
|
||||
<SearchableSelect
|
||||
options={categories.map((c) => ({ value: c.key, label: c.label }))}
|
||||
value={form.watch('service_category') || null}
|
||||
onChange={(v) => { if (v != null) form.setValue('service_category', String(v)); }}
|
||||
placeholder="انتخاب نوع خدمت"
|
||||
noOptionsMessage="نوعی تعریف نشده است"
|
||||
height={42}
|
||||
{/* تصمیمِ «آنلاین رزرو بشود یا نه» یک تنظیم است، نه یک فیلد فرم؛ پیش از این
|
||||
در نیمهٔ خالیِ ردیفِ «زمان متوسط» با پدینگ دستی همتراز شده بود. */}
|
||||
<div style={{
|
||||
padding: '12px 13px', borderRadius: 'var(--r-sm)',
|
||||
border: '1px solid var(--border)', background: 'var(--surface-2)',
|
||||
}}>
|
||||
<Switch
|
||||
checked={form.watch('bookable') ?? false}
|
||||
onChange={(v) => form.setValue('bookable', v, { shouldDirty: true })}
|
||||
label="نمایش در نوبتدهی آنلاین"
|
||||
hint="با فعال بودن این گزینه، بیمار میتواند این سرویس را بهصورت آنلاین رزرو کند."
|
||||
/>
|
||||
<span style={{ display: 'block', marginTop: 6, fontSize: 11.5, color: 'var(--text-3)' }}>
|
||||
درصد پوشش بیمه بر اساس همین نوع محاسبه میشود.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -330,7 +337,7 @@ export default function ServiceItemFormModal({ item, sectionUuid, onClose, onMan
|
||||
{editing && onManageInsurance && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn sm"
|
||||
className="btn secondary sm"
|
||||
style={{ flexShrink: 0 }}
|
||||
onClick={() => { const it = editing; onClose(); onManageInsurance(it); }}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user