diff --git a/frontend/admin-panel/src/pages/Blogs.tsx b/frontend/admin-panel/src/pages/Blogs.tsx index 9a7eae6..74faaf6 100644 --- a/frontend/admin-panel/src/pages/Blogs.tsx +++ b/frontend/admin-panel/src/pages/Blogs.tsx @@ -11,6 +11,8 @@ import Modal from '../components/ui/Modal'; +import { useSearchParams } from 'react-router-dom'; + export interface BlogPost { author?: { firstName?: string; @@ -29,10 +31,11 @@ export interface BlogPost { } export default function Blogs() { + const [searchParams, setSearchParams] = useSearchParams(); const [blogs, setBlogs] = useState([]); const [isLoading, setIsLoading] = useState(true); - const [search, setSearch] = useState(''); - const [page, setPage] = useState(1); + const [search, setSearch] = useState(() => searchParams.get('search') || ''); + const [page, setPage] = useState(() => Number(searchParams.get('page')) || 1); const [totalPages, setTotalPages] = useState(1); const limit = 10; @@ -41,6 +44,16 @@ export default function Blogs() { const [editingBlog, setEditingBlog] = useState(null); const [deleteTargetId, setDeleteTargetId] = useState(null); + const updateUrlParams = (paramsObj: Record) => { + const current = Object.fromEntries(searchParams.entries()); + const merged = { ...current, ...paramsObj }; + const cleaned: Record = {}; + Object.entries(merged).forEach(([k, v]) => { + if (v !== undefined && v !== null && String(v).trim() !== '') cleaned[k] = String(v); + }); + setSearchParams(cleaned, { replace: true }); + }; + const [formData, setFormData] = useState({ title: '', slug: '', @@ -57,15 +70,25 @@ export default function Blogs() { setIsLoading(true); const res = await api.get('/admin/blogs', { params: { page, limit, search } }); if (res.data?.data) { - setBlogs(res.data.data); + const blogList: BlogPost[] = res.data.data; + setBlogs(blogList); setTotalPages(res.data.meta?.lastPage || 1); + + const modalParam = searchParams.get('modal'); + const blogId = searchParams.get('blogId'); + if (modalParam === 'create') { + openModal(null); + } else if (modalParam === 'edit' && blogId) { + const found = blogList.find((b) => b.id === blogId); + if (found) openModal(found); + } } } catch (err) { console.error(err); } finally { setIsLoading(false); } - }, [page, search]); + }, [page, search, searchParams]); useEffect(() => { const timer = setTimeout(() => { @@ -84,7 +107,7 @@ export default function Blogs() { await api.post('/admin/blogs', formData); toast.success('مقاله جدید با موفقیت ذخیره شد'); } - setIsModalOpen(false); + closeModal(); fetchBlogs(); } catch (error) { console.error('Save failed', error); @@ -109,6 +132,7 @@ export default function Blogs() { const openModal = (blog: BlogPost | null = null) => { if (blog) { setEditingBlog(blog); + updateUrlParams({ modal: 'edit', blogId: blog.id }); setFormData({ title: blog.title, slug: blog.slug, @@ -121,6 +145,7 @@ export default function Blogs() { }); } else { setEditingBlog(null); + updateUrlParams({ modal: 'create', blogId: undefined }); setFormData({ title: '', slug: '', content: '', isPublished: true, metaTitle: '', metaDescription: '', keywords: '', imageUrl: '' }); @@ -128,6 +153,12 @@ export default function Blogs() { setIsModalOpen(true); }; + const closeModal = () => { + setIsModalOpen(false); + updateUrlParams({ modal: undefined, blogId: undefined }); + }; + + return (
@@ -234,7 +265,7 @@ export default function Blogs() { {isModalOpen && ( setIsModalOpen(false)} + onClose={closeModal} title={editingBlog ? 'ویرایش مقاله' : 'افزودن مقاله جدید'} icon={FileText} maxWidth="4xl" @@ -244,10 +275,11 @@ export default function Blogs() { variant="secondary" size="sm" type="button" - onClick={() => setIsModalOpen(false)} + onClick={closeModal} > انصراف + + @@ -562,6 +629,7 @@ export default function Users() { type="number" min="0" step="1000" + placeholder="0" value={createFormData.walletBalance} onChange={(e) => setCreateFormData({ ...createFormData, walletBalance: Number(e.target.value) })} className="w-full px-3 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-mono" @@ -575,7 +643,7 @@ export default function Users() { {editUser && ( setEditUser(null)} + onClose={handleCloseEdit} title="ویرایش اطلاعات کاربر" icon={Edit3} maxWidth="lg" @@ -584,7 +652,7 @@ export default function Users() { @@ -691,7 +759,7 @@ export default function Users() { {walletUser && ( setWalletUser(null)} + onClose={handleCloseWallet} title="مدیریت کیف پول کاربر" icon={Wallet} maxWidth="md" @@ -700,7 +768,7 @@ export default function Users() { @@ -774,7 +842,6 @@ export default function Users() { type="number" min="1000" step="1000" - required value={walletFormData.amount} onChange={(e) => setWalletFormData({ ...walletFormData, amount: Number(e.target.value) })} className="w-full px-3 py-2.5 rounded-xl border border-gray-200 focus:border-purple-500 outline-none font-mono font-bold text-sm" @@ -800,7 +867,7 @@ export default function Users() { {userToDelete && ( setUserToDelete(null)} + onClose={handleCloseDelete} title="تایید حذف کاربر" icon={AlertTriangle} maxWidth="md" @@ -809,7 +876,7 @@ export default function Users() { @@ -840,7 +907,7 @@ export default function Users() { {viewUser && ( setViewUser(null)} + onClose={handleCloseView} title="مشاهده جزئیات پروفایل کاربر" icon={UsersIcon} maxWidth="lg" diff --git a/frontend/admin-panel/src/pages/Wiki.tsx b/frontend/admin-panel/src/pages/Wiki.tsx index 6ded219..4d71456 100644 --- a/frontend/admin-panel/src/pages/Wiki.tsx +++ b/frontend/admin-panel/src/pages/Wiki.tsx @@ -7,6 +7,7 @@ import Pagination from '../components/ui/Pagination'; import ConfirmModal from '../components/ui/ConfirmModal'; import Button from '../components/ui/Button'; import Modal from '../components/ui/Modal'; +import { useSearchParams } from 'react-router-dom'; export interface WikiTerm { @@ -27,10 +28,11 @@ export interface ProductItem { } export default function Wiki() { + const [searchParams, setSearchParams] = useSearchParams(); const [terms, setTerms] = useState([]); const [isLoading, setIsLoading] = useState(true); - const [search, setSearch] = useState(''); - const [page, setPage] = useState(1); + const [search, setSearch] = useState(() => searchParams.get('search') || ''); + const [page, setPage] = useState(() => Number(searchParams.get('page')) || 1); const [totalPages, setTotalPages] = useState(1); const limit = 10; @@ -38,6 +40,17 @@ export default function Wiki() { const [editingTerm, setEditingTerm] = useState(null); const [deleteTargetKey, setDeleteTargetKey] = useState(null); const [productsList, setProductsList] = useState([]); + + const updateUrlParams = (paramsObj: Record) => { + const current = Object.fromEntries(searchParams.entries()); + const merged = { ...current, ...paramsObj }; + const cleaned: Record = {}; + Object.entries(merged).forEach(([k, v]) => { + if (v !== undefined && v !== null && String(v).trim() !== '') cleaned[k] = String(v); + }); + setSearchParams(cleaned, { replace: true }); + }; + const [formData, setFormData] = useState({ key: '', term: '', @@ -54,15 +67,25 @@ export default function Wiki() { setIsLoading(true); const res = await api.get('/admin/wiki', { params: { page, limit, search } }); if (res.data?.data) { - setTerms(res.data.data); + const termList: WikiTerm[] = res.data.data; + setTerms(termList); setTotalPages(res.data.meta?.lastPage || 1); + + const modalParam = searchParams.get('modal'); + const termKey = searchParams.get('termKey'); + if (modalParam === 'create') { + openModal(null); + } else if (modalParam === 'edit' && termKey) { + const found = termList.find((t) => t.key === termKey); + if (found) openModal(found); + } } } catch (err) { console.error(err); } finally { setIsLoading(false); } - }, [page, search]); + }, [page, search, searchParams]); useEffect(() => { const timer = setTimeout(() => { @@ -81,7 +104,7 @@ export default function Wiki() { await api.post('/admin/wiki', formData); toast.success('اصطلاح علمی جدید با موفقیت ایجاد شد'); } - setIsModalOpen(false); + closeModal(); fetchTerms(); } catch (error) { console.error('Save failed', error); @@ -112,6 +135,7 @@ export default function Wiki() { const openModal = (term: WikiTerm | null = null) => { if (term) { setEditingTerm(term); + updateUrlParams({ modal: 'edit', termKey: term.key }); setFormData({ key: term.key, term: term.term, @@ -124,6 +148,7 @@ export default function Wiki() { }); } else { setEditingTerm(null); + updateUrlParams({ modal: 'create', termKey: undefined }); setFormData({ key: '', term: '', definition: '', relatedProducts: [], wikiId: 'general', metaTitle: '', metaDescription: '', keywords: '' }); @@ -131,6 +156,11 @@ export default function Wiki() { setIsModalOpen(true); }; + const closeModal = () => { + setIsModalOpen(false); + updateUrlParams({ modal: undefined, termKey: undefined }); + }; + return (
@@ -221,7 +251,7 @@ export default function Wiki() { {isModalOpen && ( setIsModalOpen(false)} + onClose={closeModal} title={editingTerm ? 'ویرایش دانشنامه' : 'افزودن مفهوم جدید'} icon={BookOpen} maxWidth="2xl" @@ -231,10 +261,11 @@ export default function Wiki() { variant="secondary" size="sm" type="button" - onClick={() => setIsModalOpen(false)} + onClick={closeModal} > انصراف +