feat(schedule): enhance time selection with separate hour and minute dropdowns

This commit is contained in:
hamed
2026-06-24 02:43:05 +03:30
parent fbfe25c3c6
commit fc87e4a04e
+31 -19
View File
@@ -294,7 +294,7 @@ const SCHEDULE_DAYS = [
{ key: '5', label: 'پنجشنبه' },
{ key: '6', label: 'جمعه' },
];
const DURATION_OPTS = [10, 15, 20, 30, 45, 60];
const DURATION_OPTS = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60];
const DEFAULT_SESSION: SessionConfig = {
active: true, location_id: null,
start_time: '09:00', end_time: '13:00',
@@ -1020,6 +1020,32 @@ function AddressCard({ addr, onEdit, onDelete }: {
// ── Schedule components ────────────────────────────────────────────────────
// انتخاب ساعت و دقیقه به صورت دو منوی جدا (24 ساعته، دقیقه با گام 5)
const HOUR_VALUES = Array.from({ length: 24 }, (_, h) => h.toString().padStart(2, '0'));
const MINUTE_VALUES = Array.from({ length: 12 }, (_, i) => (i * 5).toString().padStart(2, '0'));
function TimeSelect({ value, onChange }: { value: string; onChange: (v: string) => void }) {
const [hRaw, mRaw] = (value || '00:00').split(':');
const h = (hRaw ?? '00').padStart(2, '0');
// دقیقه را به نزدیک‌ترین گام ۵ گرد کن تا همیشه در لیست موجود باشد
const mNum = Math.round((Number(mRaw) || 0) / 5) * 5;
const m = (mNum >= 60 ? 55 : mNum).toString().padStart(2, '0');
return (
<div className="cp-time-select" style={{ display: 'flex', alignItems: 'center', gap: 6 }} dir="ltr">
<select className="cp-input" style={{ height: 38, width: 72, textAlign: 'center' }}
value={h} onChange={e => onChange(`${e.target.value}:${m}`)}>
{HOUR_VALUES.map(hv => <option key={hv} value={hv}>{hv}</option>)}
</select>
<span style={{ fontWeight: 700, color: 'var(--text-2)' }}>:</span>
<select className="cp-input" style={{ height: 38, width: 72, textAlign: 'center' }}
value={m} onChange={e => onChange(`${h}:${e.target.value}`)}>
{MINUTE_VALUES.map(mv => <option key={mv} value={mv}>{mv}</option>)}
</select>
</div>
);
}
function SlotEditor({ slots, onChange }: {
slots: SlotConfig[];
onChange: (slots: SlotConfig[]) => void;
@@ -1078,25 +1104,19 @@ function SessionEditor({ session, onChange, onRemove, addresses }: {
<div style={{ padding: '14px 16px 12px', display: 'grid', gridTemplateColumns: '1fr 1fr auto', gap: 12, alignItems: 'end' }}>
<div>
<div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>از ساعت</div>
<div className="field">
<input type="time" dir="ltr" value={session.start_time}
onChange={e => upd('start_time', e.target.value)} />
</div>
<TimeSelect value={session.start_time} onChange={v => upd('start_time', v)} />
</div>
<div>
<div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>تا ساعت</div>
<div className="field">
<input type="time" dir="ltr" value={session.end_time}
onChange={e => upd('end_time', e.target.value)} />
</div>
<TimeSelect value={session.end_time} onChange={v => upd('end_time', v)} />
</div>
<button type="button" className="mini-btn danger" onClick={onRemove} style={{ marginBottom: 1 }}>
<TrashIcon style={{ width: 15, height: 15 }} />
</button>
</div>
{/* Duration + Location + Limit */}
<div style={{ padding: '0 16px 14px', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 12 }}>
{/* Duration + Location */}
<div style={{ padding: '0 16px 14px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
<div>
<div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>بازه هر نوبت</div>
<GlobalSearchableSelect
@@ -1124,14 +1144,6 @@ function SessionEditor({ session, onChange, onRemove, addresses }: {
isClearable
/>
</div>
<div>
<div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 5 }}>حداکثر نوبت</div>
<div className="field">
<input type="number" min={1} placeholder="—"
value={session.patient_limit ?? ''}
onChange={e => upd('patient_limit', e.target.value ? Number(e.target.value) : null)} />
</div>
</div>
</div>
{/* Rest toggle */}