- List endpoint returned camelCase keys (getArrayResult) so the edit modal read empty discount_type/target_*; return toArray() (snake_case) instead. - Portal the Persian date-picker calendar to body (fixed) so it no longer renders under the modal's overflow-scroll body. - Style the PriceInput/number fields with cp-input in the rule modal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
67 lines
2.6 KiB
TypeScript
67 lines
2.6 KiB
TypeScript
import { useRef, useState } from 'react';
|
|
import { createPortal } from 'react-dom';
|
|
import { CalendarDaysIcon, XMarkIcon } from '@heroicons/react/24/outline';
|
|
import { formatDate } from '../../lib/utils';
|
|
import PersianCalendar from './PersianCalendar';
|
|
|
|
interface Props {
|
|
value: string; // YYYY-MM-DD میلادی
|
|
onChange: (v: string) => void;
|
|
placeholder?: string;
|
|
height?: number;
|
|
minWidth?: number;
|
|
/** انتخاب سال→ماه→روز را فعال میکند (پیشفرض خاموش). */
|
|
enableYearPicker?: boolean;
|
|
}
|
|
|
|
export default function PersianDatePicker({ value, onChange, placeholder = 'انتخاب تاریخ', height = 38, minWidth = 148, enableYearPicker = false }: Props) {
|
|
const [open, setOpen] = useState(false);
|
|
const triggerRef = useRef<HTMLDivElement>(null);
|
|
const [pos, setPos] = useState<{ top: number; right: number } | null>(null);
|
|
|
|
// تقویم را به body پورت میکنیم تا داخل مودالهای overflow-scroll زیر کادر نرود.
|
|
const toggle = () => {
|
|
const r = triggerRef.current?.getBoundingClientRect();
|
|
if (r) setPos({ top: r.bottom, right: window.innerWidth - r.right });
|
|
setOpen((o) => !o);
|
|
};
|
|
|
|
return (
|
|
<div ref={triggerRef} style={{ position: 'relative', display: 'inline-block' }}>
|
|
<div
|
|
onClick={toggle}
|
|
style={{
|
|
display: 'flex', alignItems: 'center', gap: 7,
|
|
height, padding: '0 10px', borderRadius: 'var(--r-sm)',
|
|
border: '1px solid var(--border)', background: 'var(--surface)',
|
|
cursor: 'pointer', fontSize: 13, color: value ? 'var(--text)' : 'var(--text-3)',
|
|
userSelect: 'none', minWidth, whiteSpace: 'nowrap',
|
|
}}
|
|
>
|
|
<CalendarDaysIcon style={{ width: 15, height: 15, color: 'var(--text-3)', flexShrink: 0 }} />
|
|
<span style={{ flex: 1 }}>{value ? formatDate(value) : placeholder}</span>
|
|
{value && (
|
|
<span
|
|
onClick={(e) => { e.stopPropagation(); onChange(''); }}
|
|
style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', color: 'var(--text-3)' }}
|
|
>
|
|
<XMarkIcon style={{ width: 13, height: 13 }} />
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{open && pos && createPortal(
|
|
<div style={{ position: 'fixed', top: pos.top, right: pos.right, zIndex: 3000 }}>
|
|
<PersianCalendar
|
|
value={value}
|
|
onChange={(v) => { onChange(v); setOpen(false); }}
|
|
onClose={() => setOpen(false)}
|
|
enableYearPicker={enableYearPicker}
|
|
/>
|
|
</div>,
|
|
document.body,
|
|
)}
|
|
</div>
|
|
);
|
|
}
|