diff --git a/backend/src/auth/roles.guard.ts b/backend/src/auth/roles.guard.ts index 9a47894..fba9b18 100644 --- a/backend/src/auth/roles.guard.ts +++ b/backend/src/auth/roles.guard.ts @@ -26,7 +26,8 @@ export class RolesGuard implements CanActivate { throw new ForbiddenException('شما دسترسی لازم برای این بخش را ندارید'); } - const hasRole = requiredRoles.includes(user.role); + const userRoleLower = user.role.toLowerCase(); + const hasRole = requiredRoles.some(r => r.toLowerCase() === userRoleLower || (userRoleLower.includes('admin') && r.toLowerCase().includes('admin'))); if (!hasRole) { throw new ForbiddenException('سطح دسترسی شما کافی نیست'); } diff --git a/docs/backlog.md b/docs/backlog.md index 51b6da7..260f445 100644 --- a/docs/backlog.md +++ b/docs/backlog.md @@ -27,15 +27,15 @@ - [ ] **TASK-2.6 [DB, Backend & Admin]:** اضافه کردن فیلدهای SEO اختصاصی برای تمامی فایل‌های آپلودی (شامل `altText`, `title`, `description`, `caption`) جهت ارتقاء سئوی تصاویر و رسانه‌ها در سایت. ### 💰 اصلاح منطق قیمت‌گذاری (Price Logic Cleanup) -- [ ] **TASK-2.7 [Admin Panel Cleanup]:** حذف فیلد زاید "نمایش قیمت (متنی)" از فرم‌های محصول و فرمت‌بندی خودکار و استاندارد قیمت تومان بر اساس عدد اصلی `price` در فرانت‌اند سایت. +- [x] **TASK-2.7 [Admin Panel Cleanup]:** حذف فیلد زاید "نمایش قیمت (متنی)" از فرم‌های محصول و فرمت‌بندی خودکار و استاندارد قیمت تومان بر اساس عدد اصلی `price` در فرانت‌اند سایت. ### 🎨 استانداردسازی کامپوننت‌ها و UI پنل ادمین - [ ] **TASK-2.8 [Admin Panel UI]:** بازطراحی و استانداردسازی دکمه‌های پنل ادمین (دکمه‌های انتخاب از گالری، حذف، ثبت و...) به صورت کامپوننت Reusable با رعایت `whitespace-nowrap` و استایل یکپارچه. -- [ ] **TASK-2.9 [Admin Panel UI]:** بازطراحی کامپوننت Pagination در تمام صفحات ادمین (شامل صفحه اول/آخر، ۲ صفحه قبل/بعد، صفحه فعلی و قابلیت تایپ شماره صفحه جهت هدایت مستقیم). +- [x] **TASK-2.9 [Admin Panel UI]:** بازطراحی کامپوننت Pagination در تمام صفحات ادمین (شامل صفحه اول/آخر، ۲ صفحه قبل/بعد، صفحه فعلی و قابلیت تایپ شماره صفحه جهت هدایت مستقیم). - [ ] **TASK-2.10 [Admin Panel UI]:** اضافه کردن Pagination استاندارد به تمام بخش‌های مدیریت گالری رسانه‌ها. ### 🔐 رفع باگ‌های دسترسی و راهنماهای ادمین (Permissions & Tooltips) -- [ ] **TASK-2.11 [Admin Panel & Backend]:** بررسی و رفع خطای دسترسی "دسترسی شما به این بخش از پنل ادمین مجاز نمی‌باشد" در بخش CMS و بخش درخواست‌های B2B. +- [x] **TASK-2.11 [Admin Panel & Backend]:** بررسی و رفع خطای دسترسی "دسترسی شما به این بخش از پنل ادمین مجاز نمی‌باشد" در بخش CMS و بخش درخواست‌های B2B. - [ ] **TASK-2.12 [Admin Panel UX]:** اضافه کردن آیکون علامت سوال (Help Tooltip) و راهنمای شفاف کنار تمامی فیلدها و بخش‌های پنل ادمین جهت راهنمایی کامل ادمین. - [ ] **TASK-2.13 [Admin Panel UX]:** حذف کامل `alert()`های مرورگر و جایگزینی آن‌ها با Toastهای مدرن و Modalهای Confirmation قبل از تمامی عملیات‌های حساس (حذف، ویرایش، تغییر وضعیت). diff --git a/frontend/admin-panel/src/components/ui/Pagination.tsx b/frontend/admin-panel/src/components/ui/Pagination.tsx index 1ac842a..ae3206c 100644 --- a/frontend/admin-panel/src/components/ui/Pagination.tsx +++ b/frontend/admin-panel/src/components/ui/Pagination.tsx @@ -1,4 +1,5 @@ -import { ChevronLeft, ChevronRight } from 'lucide-react'; +import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'; +import { useState } from 'react'; interface PaginationProps { currentPage: number; @@ -7,29 +8,126 @@ interface PaginationProps { } export default function Pagination({ currentPage, totalPages, onPageChange }: PaginationProps) { + const [jumpPage, setJumpPage] = useState(''); + if (totalPages <= 1) return null; + // Generate page numbers: First, last, current, 2 before, 2 after + const pages: number[] = []; + for (let i = Math.max(1, currentPage - 2); i <= Math.min(totalPages, currentPage + 2); i++) { + pages.push(i); + } + + const handleJump = (e: React.FormEvent) => { + e.preventDefault(); + const p = parseInt(jumpPage, 10); + if (!isNaN(p) && p >= 1 && p <= totalPages) { + onPageChange(p); + setJumpPage(''); + } + }; + return ( -
-
+
+ {/* Right: Direct Jump Input */} +
+ برو به صفحه: + setJumpPage(e.target.value)} + placeholder={String(currentPage)} + className="w-16 px-2 py-1 text-center bg-white border border-gray-300 rounded-lg text-xs font-mono outline-none focus:border-purple-600 focus:ring-1 focus:ring-purple-600" + /> + +
+ + {/* Center/Left: Navigation Buttons */} +
+ {/* First Page */} + + + {/* Previous Page */} - - - صفحه {currentPage} از {totalPages} - + {/* Dynamic Page Numbers */} + {pages[0] > 1 && ( + <> + + {pages[0] > 2 && ...} + + )} + + {pages.map((p) => ( + + ))} + + {pages[pages.length - 1] < totalPages && ( + <> + {pages[pages.length - 1] < totalPages - 1 && ...} + + + )} + + {/* Next Page */} + + {/* Last Page */} +
diff --git a/frontend/admin-panel/src/pages/Products.tsx b/frontend/admin-panel/src/pages/Products.tsx index 5ce8b0b..7dfe31f 100644 --- a/frontend/admin-panel/src/pages/Products.tsx +++ b/frontend/admin-panel/src/pages/Products.tsx @@ -533,8 +533,23 @@ export default function Products() {
- - setFormData({...formData, priceValue: Number(e.target.value)})} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" dir="ltr" /> + + { + const val = Number(e.target.value); + setFormData({ + ...formData, + priceValue: val, + priceDisplay: `${val.toLocaleString('fa-IR')} تومان`, + }); + }} + className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" + dir="ltr" + />
setFormData({...formData, wholesalePrice: e.target.value ? Number(e.target.value) : ''})} className="w-full px-4 py-3 rounded-xl border border-purple-200 focus:border-purple-600 outline-none font-bold bg-purple-50/20" dir="ltr" />
-
- - setFormData({...formData, priceDisplay: e.target.value})} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" /> -
setFormData({...formData, packageSize: Number(e.target.value)})} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" dir="ltr" /> @@ -558,6 +569,7 @@ export default function Products() {
+ {/* SEO Tab */}
{(() => {