# رفع ویرایش بلاگ در پنل ادمین + سازگاری Dark Mode + Webhook سینک فرانت ## پروژه `clinicpro` (بک‌اند Symfony + پنل ادمین React) پرامپت همتا (cross-repo): `nobat724_front/.claude/prompt/blog-frontend-sync-and-default-cover.md` — **اول این پرامپت اجرا شود**، چون اندپوینت جدید و webhook در همین‌جا تعریف می‌شوند. ## زمینه صفحهٔ `/admin/blogs/{uuid}/edit` برای مقالهٔ `56fd9a20-9594-4aa1-a651-346fa86720bd` کاملاً خالی بالا می‌آید (اسکرین‌شات کاربر: همهٔ فیلدها placeholder، ادیتور خالی، بدون هیچ پیام خطا). این مقاله در دیتابیس لوکال وجود دارد: ``` uuid: 56fd9a20-9594-4aa1-a651-346fa86720bd title: rtertert status: draft ← کلید ماجرا review_status: NULL image_url: NULL city_id: NULL ``` همچنین ادیتور CKEditor در دارک‌مود سفید می‌ماند (اسکرین‌شات: نوار ابزار و بدنهٔ ادیتور روشن، بقیهٔ صفحه تیره). ## مشکل / هدف ### مشکل ۱ — فرم ویرایش خالی است (باگ اصلی) `BlogFormPage` دادهٔ ویرایش را از **اندپوینت عمومی** می‌گیرد: `clinicpro/assets/admin/pages/BlogFormPage.tsx:48-53` ```tsx const { data, isLoading } = useQuery({ queryKey: ['blog', uuid], queryFn: () => api.get>(`/api/v1/blog/${uuid}`), enabled: isEdit, }); const blog = data?.data?.data; ``` و اندپوینت عمومی هر پستِ غیرمنتشر را ۴۰۴ می‌کند: `clinicpro/src/Blog/Controller/BlogController.php:147-152` ```php public function detail(string $slug, Request $request): JsonResponse { $blog = $this->blogRepo->findBySlug($slug) ?? $this->blogRepo->findByUuid($slug); if ($blog === null || $blog->getStatus() !== Blog::STATUS_PUBLISHED) { return $this->error(ErrorCodes::ERR_NOT_FOUND_001, 'مقاله یافت نشد', 404); } ``` نتیجه: **هر پیش‌نویس (و هر پست آرشیو) اصلاً قابل ویرایش نیست.** چون `isError` در کامپوننت مصرف نمی‌شود، کاربر فقط یک فرم خالی می‌بیند؛ اگر روی «بروزرسانی» بزند، `PATCH` با فیلدهای خالی ارسال می‌شود و **محتوای واقعی مقاله را پاک می‌کند** (چون `update()` با `array_key_exists` هر کلید ارسال‌شده را می‌نویسد). این خطرِ از دست رفتن داده است، نه فقط یک باگ نمایشی. خروجی `ddev exec php bin/console debug:router | grep blog` تأیید می‌کند هیچ route ادمینی برای «جزئیات یک بلاگ» وجود ندارد: ``` app_blog_blog_list GET /api/v1/blogs app_blog_blog_detail GET /api/v1/blog/{slug} ← فقط published app_blog_blog_adminlist GET /api/v1/admin/blogs app_blog_blog_create POST /api/v1/blog app_blog_blog_update PATCH /api/v1/blog/{uuid} ... ``` **دو راه‌حل بررسی شد:** | راه‌حل | مزیت | ریسک | تصمیم | |---|---|---|---| | A) شل کردن شرط در `detail()` عمومی: اگر `isGranted('ROLE_ADMIN')` بود، پیش‌نویس هم برگردان | بدون اندپوینت جدید | اندپوینت عمومی cache-able است (سایت با `next: { revalidate: 3600 }` می‌خواندش) و پاسخش وابسته به نقش می‌شود → نشت پیش‌نویس به کش CDN/ISR؛ نقض اصل «یک اندپوینت یک قرارداد» | ❌ رد | | B) اندپوینت ادمین جدا: `GET /api/v1/admin/blog/{uuid}` با `#[IsGranted('ROLE_ADMIN')]`، بدون فیلتر status | قرارداد عمومی دست‌نخورده می‌ماند؛ آینهٔ دقیقِ `GET /api/v1/admin/blogs` که همین الآن هم برای لیست وجود دارد | یک route اضافه | ✅ انتخاب | قاعدهٔ پروژه («اول بگرد، بعد توسعه بده، در آخر بساز») رعایت شده: اندپوینت موجودی که پیش‌نویس بدهد وجود ندارد و توسعهٔ اندپوینت عمومی ریسک نشت دارد. ### مشکل ۲ — Dark mode در ادیتور و فرم بلاگ - در `assets/admin/styles.css` **هیچ** قاعده‌ای برای CKEditor نیست (`grep '\-\-ck-\|ck-content\|ck-editor'` روی کل `assets/` صفر نتیجه دارد). CKEditor 5 تمام رنگ‌هایش را از متغیرهای `--ck-color-*` می‌گیرد که پیش‌فرضشان روشن است → در `[data-theme="dark"]` نوار ابزار و بدنهٔ ادیتور سفید می‌ماند. - کلاس `ck-rtl` در `BlogFormPage.tsx:159` و `RepresentationBlogFormPage.tsx:105` استفاده شده ولی **در هیچ CSS ای تعریف نشده** — یک کلاس مرده است. - ناهماهنگی کلاس: در همان فرم بعضی فیلدها `cp-input` دارند و بعضی `input` (`BlogFormPage.tsx:150`, `BlogSeoFields.tsx:27`)، و `