From a92cd7fc856fbe222219feeb8730b0e2034eaead Mon Sep 17 00:00:00 2001 From: parsa aghaei Date: Sun, 23 Aug 2026 10:07:35 +0330 Subject: [PATCH] feat(admin): standardize Buttons and Modals across CMS, Catalog, Payment, and FAQ managers --- .../admin-panel/src/components/ui/Button.tsx | 19 +- .../admin-panel/src/pages/BannersManager.tsx | 283 ++++++++++-------- frontend/admin-panel/src/pages/Blogs.tsx | 166 +++++----- frontend/admin-panel/src/pages/Categories.tsx | 218 +++++++------- frontend/admin-panel/src/pages/Coupons.tsx | 11 +- frontend/admin-panel/src/pages/FAQManager.tsx | 155 +++++----- .../admin-panel/src/pages/MenuManager.tsx | 91 +++--- .../src/pages/PaymentGatewaysPage.tsx | 17 +- frontend/admin-panel/src/pages/Products.tsx | 34 ++- frontend/admin-panel/src/pages/Reports.tsx | 14 +- frontend/admin-panel/src/pages/Reviews.tsx | 63 ++-- frontend/admin-panel/src/pages/Tickets.tsx | 100 ++++--- frontend/admin-panel/src/pages/Users.tsx | 10 +- frontend/admin-panel/src/pages/Videos.tsx | 95 +++--- frontend/admin-panel/src/pages/Wiki.tsx | 192 ++++++------ .../admin-panel/src/pages/ZibalPortalPage.tsx | 177 ++++++----- 16 files changed, 896 insertions(+), 749 deletions(-) diff --git a/frontend/admin-panel/src/components/ui/Button.tsx b/frontend/admin-panel/src/components/ui/Button.tsx index f7be1e6..bf7a1fd 100644 --- a/frontend/admin-panel/src/components/ui/Button.tsx +++ b/frontend/admin-panel/src/components/ui/Button.tsx @@ -42,32 +42,33 @@ export default function Button({ const baseClasses = 'inline-flex items-center justify-center font-bold whitespace-nowrap transition-all select-none rounded-xl cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none focus:outline-none focus:ring-2 focus:ring-offset-2'; - // Size styling + // Size styling - crisp, modern and compact const sizeClasses: Record = { xs: 'px-2.5 py-1 text-[11px] gap-1.5', - sm: 'px-3.5 py-1.5 text-xs gap-2', - md: 'px-4 py-2.5 text-xs sm:text-sm gap-2', - lg: 'px-6 py-3 text-sm sm:text-base gap-2.5 shadow-md', + sm: 'px-3 py-1.5 text-xs gap-1.5', + md: 'px-3.5 py-2 text-xs sm:text-sm gap-2', + lg: 'px-5 py-2.5 text-sm sm:text-base gap-2 shadow-sm', }; // Variant styling const variantClasses: Record = { primary: - 'bg-indigo-600 hover:bg-indigo-700 text-white shadow-sm shadow-indigo-200 focus:ring-indigo-500 border border-indigo-600', + 'bg-purple-600 hover:bg-purple-700 text-white shadow-xs focus:ring-purple-500 border border-purple-600', secondary: 'bg-slate-100 hover:bg-slate-200 text-slate-700 focus:ring-slate-400 border border-slate-200', danger: - 'bg-rose-600 hover:bg-rose-700 text-white shadow-sm shadow-rose-200 focus:ring-rose-500 border border-rose-600', + 'bg-rose-600 hover:bg-rose-700 text-white shadow-xs focus:ring-rose-500 border border-rose-600', success: - 'bg-emerald-600 hover:bg-emerald-700 text-white shadow-sm shadow-emerald-200 focus:ring-emerald-500 border border-emerald-600', + 'bg-emerald-600 hover:bg-emerald-700 text-white shadow-xs focus:ring-emerald-500 border border-emerald-600', warning: - 'bg-amber-500 hover:bg-amber-600 text-white shadow-sm shadow-amber-200 focus:ring-amber-400 border border-amber-500', + 'bg-amber-500 hover:bg-amber-600 text-white shadow-xs focus:ring-amber-400 border border-amber-500', outline: - 'bg-white hover:bg-gray-50 text-gray-700 border border-gray-300 focus:ring-indigo-500', + 'bg-white hover:bg-gray-50 text-gray-700 border border-gray-300 focus:ring-purple-500', ghost: 'bg-transparent hover:bg-gray-100 text-gray-600 focus:ring-gray-300 border border-transparent', }; + const iconSizes: Record = { xs: 'w-3 h-3', sm: 'w-3.5 h-3.5', diff --git a/frontend/admin-panel/src/pages/BannersManager.tsx b/frontend/admin-panel/src/pages/BannersManager.tsx index 4c6db6a..37b0022 100644 --- a/frontend/admin-panel/src/pages/BannersManager.tsx +++ b/frontend/admin-panel/src/pages/BannersManager.tsx @@ -5,8 +5,11 @@ import api, { BASE_DOMAIN } from '../services/api'; import Spinner from '../components/ui/Spinner'; import ConfirmModal from '../components/ui/ConfirmModal'; import MediaSelector from '../components/ui/MediaSelector'; +import Button from '../components/ui/Button'; +import Modal from '../components/ui/Modal'; import type { Banner } from '../types/admin'; + export default function BannersManager() { const [banners, setBanners] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -166,13 +169,14 @@ export default function BannersManager() {

مدیریت اسلایدر صفحه اصلی و تبلیغات بنری

- +
@@ -198,7 +202,7 @@ export default function BannersManager() { ) : banners.length === 0 ? ( - هیچ بنری ثبت نشده است + هیچ بنری یافت نشد ) : ( banners.map((banner, index) => ( @@ -207,71 +211,79 @@ export default function BannersManager() {
- {index + 1}
- -
- {banner.imageUrl ? ( - {banner.title} - ) : ( - - )} -
- -
- {banner.title} - {banner.subtitle && {banner.subtitle}} -
- - - - {banner.position === 'home_hero' - ? 'اسلایدر هیرو اصلی' - : banner.position === 'home_middle' - ? 'بنر میانی صفحه نخست' - : banner.position === 'home_bottom' - ? 'بنر عریض پایین خانه' - : banner.position === 'shop_top' - ? 'بالای فروشگاه و کاتالوگ' - : banner.position === 'category_top' - ? 'بالای دسته‌بندی' - : 'سایدبار'} - - - - {banner.linkUrl || '-'} - - - {banner.isActive ? ( - - فعال - + {banner.imageUrl ? ( + {banner.title ) : ( - - غیرفعال - +
+ +
)} + +
{banner.title || '-'}
+
{banner.subtitle || '-'}
+ + + + {banner.position === 'home_hero' ? 'اسلایدر اصلی' : banner.position === 'home_promo' ? 'بنر تبلیغاتی میانی' : banner.position} + + + + {banner.linkUrl || '-'} + + + +
- -
@@ -284,96 +296,122 @@ export default function BannersManager() {
- {/* Form Modal */} + {/* Modal */} {isModalOpen && ( -
-
-
-

- {editingBanner ? 'ویرایش بنر' : 'افزودن بنر جدید'} -

- + setIsModalOpen(false)} + title={editingBanner ? 'ویرایش بنر' : 'افزودن بنر جدید'} + icon={ImageIcon} + maxWidth="2xl" + footer={ +
+ +
- -
+ } + > +
+
- + setFormData({ ...formData, title: e.target.value })} - placeholder="مثال: تخصص دارویی از آلمان" + placeholder="مثال: تخفیف ویژه مکمل‌های غذایی" className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm" />
- + setFormData({ ...formData, subtitle: e.target.value })} - placeholder="مثال: بیش از ۴۰ سال تجربه" + placeholder="مثال: تا ۳۰٪ تخفیف برای مدت محدود" className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm" />
-
- -
- setFormData({ ...formData, imageUrl: e.target.value })} - placeholder="https://... یا /uploads/..." - className="flex-1 px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-xs font-mono" - dir="ltr" - /> - -
-
- -
- - setFormData({ ...formData, linkUrl: e.target.value })} - placeholder="مثال: /products/joints" - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-xs font-mono" - dir="ltr" - /> -
- -
+
- + + setFormData({ ...formData, linkUrl: e.target.value })} + placeholder="/shop یا https://..." + dir="ltr" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm font-mono" + /> +
+
+ +
+ +
+ setFormData({ ...formData, imageUrl: e.target.value })} + placeholder="آدرس تصویر یا انتخاب از رسانه‌ها..." + dir="ltr" + className="flex-1 px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm font-mono" + /> + +
+ {formData.imageUrl && ( +
+ Preview +
+ )} +
+ +
+
+
- -
- - -
-
+
)} + {/* Delete Confirmation Modal */} {deleteTargetId && (

مدیریت محتوای آموزشی و مقالات علمی سایت

- +
@@ -196,8 +201,8 @@ export default function Blogs() {
- - + +
@@ -214,82 +219,99 @@ export default function Blogs() {
{isModalOpen && ( -
-
-
-

- {editingBlog ? 'ویرایش مقاله' : 'افزودن مقاله جدید'} -

- + setIsModalOpen(false)} + title={editingBlog ? 'ویرایش مقاله' : 'افزودن مقاله جدید'} + icon={FileText} + maxWidth="4xl" + footer={ +
+ +
- -
-
-
-
- - setFormData({ ...formData, title: e.target.value })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" /> -
-
- - setFormData({ ...formData, slug: e.target.value })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-mono text-sm" /> -
-
- + } + > +
+ +
- - + + setFormData({ ...formData, title: e.target.value })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" /> +
+
+ + setFormData({ ...formData, slug: e.target.value })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-mono text-sm" /> +
+
+ +
+ + +
+ +
+
+ +
+ {formData.imageUrl ? ( + preview + ) : ( +
+ +
+ )} + +
+
-
-
- -
- {formData.imageUrl ? ( - preview - ) : ( -
- -
- )} - -
- +
+

تنظیمات سئو (SEO)

+
+ + setFormData({ ...formData, metaTitle: e.target.value })} className="w-full px-3 py-2 rounded-lg border border-gray-200 focus:border-purple-500 outline-none text-sm" />
- -
-

تنظیمات سئو (SEO)

-
- - setFormData({ ...formData, metaTitle: e.target.value })} className="w-full px-3 py-2 rounded-lg border border-gray-200 focus:border-purple-500 outline-none text-sm" /> -
-
- - setFormData({ ...formData, keywords: e.target.value })} className="w-full px-3 py-2 rounded-lg border border-gray-200 focus:border-purple-500 outline-none text-sm" /> -
-
- - -
+
+ + setFormData({ ...formData, keywords: e.target.value })} className="w-full px-3 py-2 rounded-lg border border-gray-200 focus:border-purple-500 outline-none text-sm" /> +
+
+ +
- - -
- -
- - -
+
+
-
+ )} + setIsMediaSelectorOpen(false)} onSelect={(url) => setFormData({ ...formData, imageUrl: url })} />

ایجاد دسته‌بندی محصولات و تنظیمات سئو (SEO)

- +
+ {/* Filters */}
@@ -197,10 +203,10 @@ export default function Categories() {
- -
@@ -220,120 +226,120 @@ export default function Categories() { {/* Modal */} {isModalOpen && ( -
-
-
-

- {editingCategory ? 'ویرایش دسته‌بندی' : 'افزودن دسته‌بندی جدید'} -

- + setIsModalOpen(false)} + title={editingCategory ? 'ویرایش دسته‌بندی' : 'افزودن دسته‌بندی جدید'} + icon={FolderTree} + maxWidth="2xl" + footer={ +
+ +
- -
-
-
+ } + > +
+ +
+
+ + setFormData({...formData, name: e.target.value})} + className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none transition-all" + /> +
+
+ + setFormData({...formData, slug: e.target.value})} + className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none transition-all font-mono" + /> +
+
+ +
+ +
+ {formData.imageUrl ? ( + preview + ) : ( +
+ +
+ )} + +
+
+ +
+ + +
+ +
+

تنظیمات سئو (SEO)

+
- + setFormData({...formData, name: e.target.value})} + value={formData.metaTitle} + onChange={(e) => setFormData({...formData, metaTitle: e.target.value})} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none transition-all" />
- - setFormData({...formData, slug: e.target.value})} - className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none transition-all font-mono" - /> + +
- -
- -
- {formData.imageUrl ? ( - preview - ) : ( -
- -
- )} - -
-
- -
- - -
- -
-

تنظیمات سئو (SEO)

-
-
- - setFormData({...formData, metaTitle: e.target.value})} - className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none transition-all" - /> -
-
- - -
-
-
- -
- -
- - -
+
+
-
+
)} {/* Media Selector Modal */} + setIsMediaSelectorOpen(false)} diff --git a/frontend/admin-panel/src/pages/Coupons.tsx b/frontend/admin-panel/src/pages/Coupons.tsx index 053cba1..7fd47df 100644 --- a/frontend/admin-panel/src/pages/Coupons.tsx +++ b/frontend/admin-panel/src/pages/Coupons.tsx @@ -165,12 +165,17 @@ export default function Coupons() {

مدیریت کدهای تخفیف با اهداف چندگانه (کاربران، حیوانات، محصولات و ...)

- +
+
diff --git a/frontend/admin-panel/src/pages/FAQManager.tsx b/frontend/admin-panel/src/pages/FAQManager.tsx index ebf9921..751567e 100644 --- a/frontend/admin-panel/src/pages/FAQManager.tsx +++ b/frontend/admin-panel/src/pages/FAQManager.tsx @@ -4,6 +4,9 @@ import { toast } from 'react-hot-toast'; import api from '../services/api'; import Spinner from '../components/ui/Spinner'; import ConfirmModal from '../components/ui/ConfirmModal'; +import Button from '../components/ui/Button'; +import Modal from '../components/ui/Modal'; + interface FAQ { id: string; @@ -129,13 +132,14 @@ export default function FAQManager() { سوالات و پاسخ‌هایی که در صفحه اصلی سایت نمایش داده می‌شوند — با Schema.org FAQPage برای SEO

- +
{/* Info Box */} @@ -147,7 +151,7 @@ export default function FAQManager() {
{isLoading ? (
- +
) : faqs.length === 0 ? (
@@ -155,56 +159,71 @@ export default function FAQManager() {

هیچ سوالی ثبت نشده است

) : ( - faqs.map((faq, idx) => ( + faqs.map((faq) => (
-
-
- - {idx + 1} -
-
-
- {faq.question} - {!faq.isActive && ( - - غیرفعال - - )} +
+
+
+ +
+
+
+ + ترتیب: {faq.order} + + + {faq.isActive ? 'فعال' : 'غیرفعال'} + +
+

{faq.question}

+

+ {faq.answer} +

-

{faq.answer}

-
+ +
@@ -215,31 +234,49 @@ export default function FAQManager() { )}
- {/* Form Modal */} + {/* Modal */} {isModalOpen && ( -
-
-
-

- {editingFaq ? 'ویرایش سوال متداول' : 'افزودن سوال جدید'} -

- + setIsModalOpen(false)} + title={editingFaq ? 'ویرایش سوال متداول' : 'افزودن سوال جدید'} + icon={HelpCircle} + maxWidth="2xl" + footer={ +
+ +
- -
+ } + > +
+
setFormData({ ...formData, question: e.target.value })} placeholder="مثال: آیا محصولات کنینا برای سگ‌های حساس مناسب است؟" - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-orange-400 outline-none text-sm" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm" />
@@ -253,7 +290,7 @@ export default function FAQManager() { value={formData.answer} onChange={(e) => setFormData({ ...formData, answer: e.target.value })} placeholder="پاسخ کامل، واضح و مفید..." - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-orange-400 outline-none text-sm resize-none" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm resize-none" />
@@ -264,44 +301,28 @@ export default function FAQManager() { type="number" value={formData.order} onChange={(e) => setFormData({ ...formData, order: Number(e.target.value) })} - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-orange-400 outline-none text-sm" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm" />
-
- -
- - -
-
+ )} + + {deleteTargetId && (
- +
+ {/* Tabs */}
{MENU_TABS.map((tab) => ( @@ -292,27 +298,45 @@ export default function MenuManager() {
)} - {/* Form Modal */} + {/* Modal */} {isModalOpen && ( -
-
-
-

- {editingItem ? 'ویرایش آیتم منو' : 'افزودن آیتم جدید'} -

- + setIsModalOpen(false)} + title={editingItem ? 'ویرایش آیتم منو' : 'افزودن آیتم جدید'} + icon={Menu} + maxWidth="xl" + footer={ +
+ +
- -
+ } + > +
+
setFormData({ ...formData, parentId: e.target.value || null })} - className="w-full px-3 py-2.5 rounded-xl border border-gray-200 focus:border-blue-400 outline-none text-sm bg-white" + className="w-full px-3 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm bg-white" > {parentItems.map((p) => ( @@ -342,7 +366,7 @@ export default function MenuManager() { value={formData.label} onChange={(e) => setFormData({ ...formData, label: e.target.value })} placeholder="مثال: فروشگاه تخصصی" - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-blue-400 outline-none text-sm" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm" />
@@ -355,7 +379,7 @@ export default function MenuManager() { onChange={(e) => setFormData({ ...formData, href: e.target.value })} placeholder="مثال: /shop یا https://..." dir="ltr" - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-blue-400 outline-none text-sm font-mono" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm font-mono" />
@@ -367,7 +391,7 @@ export default function MenuManager() { value={formData.icon} onChange={(e) => setFormData({ ...formData, icon: e.target.value })} placeholder="🐕 یا نام آیکون" - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-blue-400 outline-none text-sm" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm" />
@@ -377,7 +401,7 @@ export default function MenuManager() { value={formData.badge} onChange={(e) => setFormData({ ...formData, badge: e.target.value })} placeholder="مثال: جدید" - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-blue-400 outline-none text-sm" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm" />
@@ -389,7 +413,7 @@ export default function MenuManager() { type="number" value={formData.order} onChange={(e) => setFormData({ ...formData, order: Number(e.target.value) })} - className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-blue-400 outline-none text-sm" + className="w-full px-4 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none text-sm" />
- -
- - -
-
+ )} + {deleteTargetId && ( ('gateways'); @@ -490,18 +493,20 @@ export default function PaymentGatewaysPage() {
- + ذخیره کلیه تنظیمات درگاه زیبال +
)} + {activeTab === 'wallets' && (
{/* Wallets Overview */} diff --git a/frontend/admin-panel/src/pages/Products.tsx b/frontend/admin-panel/src/pages/Products.tsx index fff609b..e721ae0 100644 --- a/frontend/admin-panel/src/pages/Products.tsx +++ b/frontend/admin-panel/src/pages/Products.tsx @@ -8,6 +8,9 @@ import Pagination from '../components/ui/Pagination'; import MediaSelector from '../components/ui/MediaSelector'; import ConfirmModal from '../components/ui/ConfirmModal'; import PriceInput from '../components/ui/PriceInput'; +import Button from '../components/ui/Button'; + + export interface Category { id: string; @@ -435,15 +438,17 @@ export default function Products() {

افزودن و ویرایش پیشرفته محصولات با تنظیمات سئو

- +
+ {/* Filters Bar */}
@@ -1650,18 +1655,29 @@ export default function Products() {
-
- - + +
)} + {/* Media Selector Modal */}

آمار فروش، رفتار کاربران و عملکرد تخفیف‌ها

- +
+ {/* Overview Cards */}
diff --git a/frontend/admin-panel/src/pages/Reviews.tsx b/frontend/admin-panel/src/pages/Reviews.tsx index e2f7e08..b44392b 100644 --- a/frontend/admin-panel/src/pages/Reviews.tsx +++ b/frontend/admin-panel/src/pages/Reviews.tsx @@ -18,6 +18,10 @@ import { import { toast } from 'react-hot-toast'; import api from '../services/api'; import Spinner from '../components/ui/Spinner'; +import Button from '../components/ui/Button'; +import Modal from '../components/ui/Modal'; + + interface ProductReview { id: string; @@ -434,21 +438,34 @@ export default function Reviews() { {/* Reply Modal */} {replyingReview && ( -
-
-
-

- - پاسخ به دیدگاه {replyingReview.userName} -

- + انصراف + +
- + } + > +
متن دیدگاه خریدار:

{replyingReview.comment}

@@ -466,28 +483,10 @@ export default function Reviews() { className="w-full p-3 border border-gray-200 rounded-xl text-sm font-medium outline-none focus:ring-2 focus:ring-purple-500 resize-none leading-relaxed" />
- -
- - -
-
+ )}
); } + diff --git a/frontend/admin-panel/src/pages/Tickets.tsx b/frontend/admin-panel/src/pages/Tickets.tsx index 615d80f..6296fca 100644 --- a/frontend/admin-panel/src/pages/Tickets.tsx +++ b/frontend/admin-panel/src/pages/Tickets.tsx @@ -20,6 +20,8 @@ import { } from 'lucide-react'; import api from '../services/api'; import { toast } from 'react-hot-toast'; +import Button from '../components/ui/Button'; +import Modal from '../components/ui/Modal'; interface TicketMessage { id: string; @@ -340,47 +342,63 @@ export default function Tickets() { {/* Ticket Details & Admin Reply Modal */} {selectedTicket && ( -
-
- {/* Modal Header */} -
-
-
-

{selectedTicket.subject}

- - {selectedTicket.ticketNumber} - -
-
- کاربر: {selectedTicket.user?.firstName} {selectedTicket.user?.lastName} ({selectedTicket.user?.mobile}) - {selectedTicket.pet && ( - • 🐾 {selectedTicket.pet.name} ({selectedTicket.pet.breed || selectedTicket.pet.type}) - )} -
+ setSelectedTicket(null)} + title={`تیکت ${selectedTicket.ticketNumber}: ${selectedTicket.subject}`} + icon={MessageSquare} + maxWidth="3xl" + footer={ +
+ + +
+ } + > +
+ {/* Header meta */} +
+
+ کاربر: {selectedTicket.user?.firstName} {selectedTicket.user?.lastName} ({selectedTicket.user?.mobile}) + {selectedTicket.pet && ( + 🐾 {selectedTicket.pet.name} ({selectedTicket.pet.breed || selectedTicket.pet.type}) + )}
+
+ تغییر وضعیت: -
{/* Messages Chat Scroll */} -
+
{(selectedTicket.messages || []).map((msg) => { const isAdminOrVet = msg.senderRole === 'ADMIN' || msg.senderRole === 'VET_DOCTOR'; return ( @@ -388,7 +406,7 @@ export default function Tickets() { key={msg.id} className={`p-4 rounded-2xl max-w-[85%] text-xs font-medium leading-relaxed ${ isAdminOrVet - ? 'bg-emerald-50 border border-emerald-200 text-emerald-950 mr-auto' + ? 'bg-purple-50 border border-purple-200 text-purple-950 mr-auto' : 'bg-blue-50 border border-blue-200 text-blue-950 ml-auto' }`} > @@ -396,8 +414,8 @@ export default function Tickets() { {isAdminOrVet ? ( <> - - {msg.senderName} (تیم پزشکی / ادمین) + + {msg.senderName} (تیم پزشکی / ادمین) ) : ( <> @@ -417,7 +435,7 @@ export default function Tickets() {
{/* Admin Response Composer */} -
+
@@ -439,29 +457,13 @@ export default function Tickets() { placeholder="متن پاسخ تخصصی به کاربر را وارد کنید..." value={replyMessage} onChange={(e) => setReplyMessage(e.target.value)} - className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-xs font-medium focus:border-canina-blue outline-none" + className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl text-xs font-medium focus:border-purple-500 focus:ring-2 focus:ring-purple-200 outline-none resize-none leading-relaxed" /> -
- - -
-
+ )}
); } + diff --git a/frontend/admin-panel/src/pages/Users.tsx b/frontend/admin-panel/src/pages/Users.tsx index f766c9e..081de78 100644 --- a/frontend/admin-panel/src/pages/Users.tsx +++ b/frontend/admin-panel/src/pages/Users.tsx @@ -265,13 +265,15 @@ export default function Users() {
- + + - + انتخاب کاور +
@@ -437,14 +456,15 @@ export default function Videos() { placeholder="https://... یا کد آیفرم آپارات/یوتیوب" className="flex-1 bg-gray-50 border border-gray-200 rounded-xl py-3 px-4 text-xs font-mono focus:outline-none focus:ring-2 focus:ring-purple-500/20" /> - + انتخاب ویدئو +
@@ -471,27 +491,12 @@ export default function Videos() { نمایش به عنوان ویدئوی ویژه در بخش «مشاوره ویدئویی» صفحه اصلی سایت
- -
- - -
-
+ )} + {/* Cover Thumbnail Media Selector Modal */}

مدیریت اصطلاحات علمی، مواد تشکیل‌دهنده و خواص درمانی

- +
@@ -184,8 +188,8 @@ export default function Wiki() { {t.definition}
- - + +
@@ -202,99 +206,109 @@ export default function Wiki() {
{isModalOpen && ( -
-
-
-

- {editingTerm ? 'ویرایش دانشنامه' : 'افزودن مفهوم جدید'} -

- + setIsModalOpen(false)} + title={editingTerm ? 'ویرایش دانشنامه' : 'افزودن مفهوم جدید'} + icon={BookOpen} + maxWidth="2xl" + footer={ +
+ +
- -
-
-
-
- - setFormData({ ...formData, key: e.target.value })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-mono text-sm disabled:bg-gray-100" /> -
-
- - setFormData({ ...formData, term: e.target.value })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" /> -
-
- + } + > +
+ +
- - + + setFormData({ ...formData, key: e.target.value })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-mono text-sm disabled:bg-gray-100" />
- - {/* Related Products Selector (TASK-2.23) */} -
- - - -
- {formData.relatedProducts.map(pId => { - const prod = productsList.find(p => p.id === pId); - return ( - - {prod ? prod.nameFa : pId} - - - ); - })} -
+
+ + setFormData({ ...formData, term: e.target.value })} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-purple-500 outline-none" />
+
-
-

تنظیمات سئو (SEO)

-
- - setFormData({ ...formData, metaTitle: e.target.value })} className="w-full px-3 py-2 rounded-lg border border-gray-200 focus:border-purple-500 outline-none text-sm" /> -
-
- - setFormData({ ...formData, keywords: e.target.value })} className="w-full px-3 py-2 rounded-lg border border-gray-200 focus:border-purple-500 outline-none text-sm" /> -
-
- - -
+
+ + +
+ + {/* Related Products Selector */} +
+ + + +
+ {formData.relatedProducts.map(pId => { + const prod = productsList.find(p => p.id === pId); + return ( + + {prod ? prod.nameFa : pId} + + + ); + })}
+
- -
- -
- - -
+
+

تنظیمات سئو (SEO)

+
+ + setFormData({ ...formData, metaTitle: e.target.value })} className="w-full px-3 py-2 rounded-lg border border-gray-200 focus:border-purple-500 outline-none text-sm" /> +
+
+ + setFormData({ ...formData, keywords: e.target.value })} className="w-full px-3 py-2 rounded-lg border border-gray-200 focus:border-purple-500 outline-none text-sm" /> +
+
+ + +
+
+
-
+ )} + ('refunds'); @@ -378,25 +381,27 @@ export default function ZibalPortalPage() {
{/* Top Bar with Filter & Search Controls */}
-
- + دانلود گزارش + - + />
+ {/* Filter Inputs Bar */}
{/* Date Filter */} @@ -634,25 +639,27 @@ export default function ZibalPortalPage() { {activeMainTab === 'checkouts' && (
-
- + دانلود گزارش + - + />
+ {/* Filter Inputs Bar */}
@@ -851,25 +858,27 @@ export default function ZibalPortalPage() { {activeMainTab === 'gateway_transactions' && (
-
- + دانلود گزارش درگاه + - + />
+ {/* Filter Inputs */}
@@ -1013,25 +1022,40 @@ export default function ZibalPortalPage() { )} {/* ======================================================== */} - {/* MODAL: SUBMIT NEW REFUND / REVERSE */} + {/* MODAL: NEW REFUND SUBMIT */} {/* ======================================================== */} {newRefundModal && ( -
-
-
-

- - ثبت درخواست استرداد وجه (Refund / Reverse) -

- + انصراف + +
- -
+ } + > +
+
- -
- - -
-
+ )} {/* ======================================================== */} {/* MODAL: DETAIL / INQUIRY VIEWER */} {/* ======================================================== */} {detailModalItem && ( -
-
-
-

{detailModalItem.title}

- + setDetailModalItem(null)} + title={detailModalItem.title} + maxWidth="lg" + footer={ +
+
-
-              {JSON.stringify(detailModalItem.data, null, 2)}
-            
-
-
+ } + > +
+            {JSON.stringify(detailModalItem.data, null, 2)}
+          
+ )}
); } +