# معماری — تسک ۱۲: تکمیل منشی ## فایل‌های موجود که تغییر می‌کنند ``` src/Secretary/Controller/SecretaryController.php ← inject SubscriptionService + limit check src/Shared/Constant/ErrorCodes.php ← ثابت جدید ERR_SECRETARY_LIMIT_REACHED assets/admin/pages/SecretariesPage.tsx ← Modal permissions checkbox matrix ``` ## تغییرات Backend ### SecretaryController — constructor injection ```php public function __construct( private readonly EntityManagerInterface $em, private readonly DoctorSecretaryRepository $secretaryRepo, private readonly DoctorRepository $doctorRepo, private readonly SubscriptionService $subscriptionService, // ← جدید ) {} ``` ### SecretaryRepository — متد جدید countActive ```php // src/Secretary/Repository/DoctorSecretaryRepository.php public function countActive(int $doctorId): int { return (int) $this->createQueryBuilder('s') ->select('COUNT(s.id)') ->where('s.doctor = :doctor') ->andWhere('s.active = true') ->setParameter('doctor', $doctorId) ->getQuery() ->getSingleScalarResult(); } ``` ## تغییرات Frontend ### SecretariesPage.tsx — ساختار Modal permissions ```tsx // types جدید interface PermissionsMatrix { version: number; resources: { appointments?: { view: boolean; create: boolean; cancel: boolean; update_status: boolean }; addresses?: { view: boolean; create: boolean; update: boolean; delete: boolean }; clinic_info?: { view: boolean; update: boolean }; insurances?: { view: boolean; create: boolean; update: boolean; delete: boolean }; }; } // component function PermissionsEditor({ value, onChange }: { value: PermissionsMatrix; onChange: (v: PermissionsMatrix) => void; }) { // رندر جدول checkbox // هر ردیف: نام بخش + چک‌باکس‌های action // onChange ساختار را update می‌کند } ``` ### نکته: مدیریت خطای 403 در فرم ایجاد منشی ```tsx // وقتی POST /api/v1/secretary با 403 برمی‌گردد: // نمایش toast: "سقف منشی پنل رسیده است. برای افزایش به صفحه اشتراک بروید" // دکمه "ارتقاء پنل" → navigate('/admin/subscription') ```