diff --git a/assets/admin/components/DiscountTab.tsx b/assets/admin/components/DiscountTab.tsx index 931e0382..08c48833 100644 --- a/assets/admin/components/DiscountTab.tsx +++ b/assets/admin/components/DiscountTab.tsx @@ -239,8 +239,8 @@ function RuleModal({ initial, onClose, onSaved }: { initial: DiscountRule | null
{f.discount_type === 'percent' - ? set('value', Number(e.target.value) || 0)} /> - : set('value', v)} />} + ? set('value', Number(e.target.value) || 0)} /> + : set('value', v)} />}
@@ -262,7 +262,7 @@ function RuleModal({ initial, onClose, onSaved }: { initial: DiscountRule | null {f.type === 'invoice_amount' && (
- set('min_amount_toman', v)} /> + set('min_amount_toman', v)} />
)} {f.type === 'specific_patient' && ( diff --git a/assets/admin/components/ui/PersianDatePicker.tsx b/assets/admin/components/ui/PersianDatePicker.tsx index 75972f74..82a5614b 100644 --- a/assets/admin/components/ui/PersianDatePicker.tsx +++ b/assets/admin/components/ui/PersianDatePicker.tsx @@ -1,4 +1,5 @@ -import { useState } from 'react'; +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'; @@ -15,11 +16,20 @@ interface Props { export default function PersianDatePicker({ value, onChange, placeholder = 'انتخاب تاریخ', height = 38, minWidth = 148, enableYearPicker = false }: Props) { const [open, setOpen] = useState(false); + const triggerRef = useRef(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 ( -
+
setOpen((o) => !o)} + onClick={toggle} style={{ display: 'flex', alignItems: 'center', gap: 7, height, padding: '0 10px', borderRadius: 'var(--r-sm)', @@ -40,13 +50,16 @@ export default function PersianDatePicker({ value, onChange, placeholder = 'ان )}
- {open && ( - { onChange(v); setOpen(false); }} - onClose={() => setOpen(false)} - enableYearPicker={enableYearPicker} - /> + {open && pos && createPortal( +
+ { onChange(v); setOpen(false); }} + onClose={() => setOpen(false)} + enableYearPicker={enableYearPicker} + /> +
, + document.body, )}
); diff --git a/src/Discount/Controller/DiscountController.php b/src/Discount/Controller/DiscountController.php index 3ae76bbd..95e8451f 100644 --- a/src/Discount/Controller/DiscountController.php +++ b/src/Discount/Controller/DiscountController.php @@ -49,7 +49,8 @@ class DiscountController extends BaseController if ($ownerId === null) { return $this->error(ErrorCodes::ERR_AUTH_006, 'دسترسی ممنوع', 403); } - return $this->success(['data' => $this->ruleRepo->listForOwner($ownerType, $ownerId)]); + $items = array_map(fn(DiscountRule $r) => $r->toArray(), $this->ruleRepo->findAllForOwner($ownerType, $ownerId)); + return $this->success(['data' => $items]); } #[Route('/api/v1/admin/discount-rules', methods: ['POST'])] diff --git a/src/Discount/Repository/DiscountRuleRepository.php b/src/Discount/Repository/DiscountRuleRepository.php index 8169c3fc..d54a312e 100644 --- a/src/Discount/Repository/DiscountRuleRepository.php +++ b/src/Discount/Repository/DiscountRuleRepository.php @@ -37,13 +37,17 @@ class DiscountRuleRepository extends ServiceEntityRepository return $this->findBy(['ownerType' => $ownerType, 'ownerId' => $ownerId, 'active' => true], ['priority' => 'DESC']); } - /** لیست ادمین به‌صورت آرایه (array hydration). */ - public function listForOwner(string $ownerType, int $ownerId): array + /** + * لیست کامل قوانین یک owner (شامل غیرفعال‌ها) برای ادمین، مرتب بر priority. + * موجودیت‌ها برمی‌گردند تا کنترلر با toArray() قرارداد snake_case فرانت را بدهد. + * @return DiscountRule[] + */ + public function findAllForOwner(string $ownerType, int $ownerId): array { return $this->createQueryBuilder('r') ->where('r.ownerType = :t')->setParameter('t', $ownerType) ->andWhere('r.ownerId = :i')->setParameter('i', $ownerId) ->orderBy('r.priority', 'DESC')->addOrderBy('r.id', 'DESC') - ->getQuery()->getArrayResult(); + ->getQuery()->getResult(); } }