diff --git a/admin-panel/package.json b/admin-panel/package.json index d7a82c4..71ea438 100644 --- a/admin-panel/package.json +++ b/admin-panel/package.json @@ -18,6 +18,7 @@ "cmdk": "^1.0.4", "date-fns": "^3.6.0", "framer-motion": "^11.16.4", + "jodit-react": "^4.1.2", "lucide-react": "^0.468.0", "moment": "^2.30.1", "nepali-date-converter": "^3.3.2", diff --git a/admin-panel/src/App.tsx b/admin-panel/src/App.tsx index aaefe45..cb75b58 100644 --- a/admin-panel/src/App.tsx +++ b/admin-panel/src/App.tsx @@ -15,6 +15,7 @@ import UsersPage from "@/pages/UsersPage"; import PostsPage from "@/pages/PostsPage"; import LogoutPage from "@/pages/LogoutPage"; import CategoriesPage from "@/pages/CategoriesPage"; +import CreatePost from "@/pages/createPostPage"; function App() { const { isCheckingAuth, checkAuth, isAuthenticated } = useAuthStore(); @@ -66,18 +67,21 @@ function App() { path="/categories" element={ - + } /> - } /> - - + + + + } + /> + } /> - + ); } diff --git a/admin-panel/src/components/Sidebar/Routes.tsx b/admin-panel/src/components/Sidebar/Routes.tsx index d593871..746a4bb 100644 --- a/admin-panel/src/components/Sidebar/Routes.tsx +++ b/admin-panel/src/components/Sidebar/Routes.tsx @@ -1,4 +1,4 @@ -import { Paperclip, Users, Home, LogOut, List } from "lucide-react"; +import { Paperclip, Users, Home, LogOut, List, CirclePlus } from "lucide-react"; import { Link, useLocation } from "react-router-dom"; // Import useLocation hook const Routes = () => { @@ -18,9 +18,9 @@ const Routes = () => { title="Users" to="/users" /> - @@ -30,7 +30,13 @@ const Routes = () => { title="Posts" to="/posts" /> - + + + + + + + ); +}; + +export default CreatePost; diff --git a/admin-panel/src/pages/PostsPage.tsx b/admin-panel/src/pages/PostsPage.tsx index ffde58c..430023d 100644 --- a/admin-panel/src/pages/PostsPage.tsx +++ b/admin-panel/src/pages/PostsPage.tsx @@ -7,127 +7,164 @@ import { useCategories } from "@/hooks/useCategories"; import { useState } from "react"; const PostsPage: React.FC = () => { - const { post } = useAuthStore(); // Access the token - const page = 1; - const limit = 10; + const { post } = useAuthStore(); // Access the token + const [page, setPage] = useState(1); + const limit = 10; - const { posts, error: postsError } = usePosts(page, limit); - const { categories, error: categoriesError, isLoading } = useCategories(); // Fetch categories from API + const { posts, totalPosts, error: postsError } = usePosts(page, limit); + const { categories, error: categoriesError, isLoading } = useCategories(); // Fetch categories from API + const totalPages = Math.ceil(totalPosts / limit); - // Search and Category Filter states - const [searchQuery, setSearchQuery] = useState(""); - const [selectedCategory, setSelectedCategory] = useState(""); + // Search and Category Filter states + const [searchQuery, setSearchQuery] = useState(""); + const [selectedCategory, setSelectedCategory] = useState(""); + const handleNextPage = () => { + if (page < totalPages) { + setPage(page + 1); + } + }; + const handlePreviousPage = () => { + if (page > 1) { + setPage(page - 1); + } + }; - // Filtered posts based on search query and selected category - const filteredPosts = posts.filter((post) => { - const matchesSearch = post.title.toLowerCase().includes(searchQuery.toLowerCase()); - const matchesCategory = selectedCategory === "" || post.category.name === selectedCategory; - return matchesSearch && matchesCategory; - }); + // Filtered posts based on search query and selected category + const filteredPosts = posts.filter((post) => { + const matchesSearch = post.title + .toLowerCase() + .includes(searchQuery.toLowerCase()); + const matchesCategory = + selectedCategory === "" || post.category.name === selectedCategory; + return matchesSearch && matchesCategory; + }); - return ( - -
-
-

Posts

- - Create Post - -
+ return ( + +
+
+

Posts

+ + Create Post + +
- {/* Search and Category Filter */} -
- setSearchQuery(e.target.value)} - /> - -
+ {/* Search and Category Filter */} +
+ setSearchQuery(e.target.value)} + /> + +
- {categoriesError &&
Failed to load categories.
} + {categoriesError && ( +
Failed to load categories.
+ )} -
- {postsError &&
{postsError}
} +
+ {postsError &&
{postsError}
} - - - - - - - - - - - - - - {filteredPosts.map((post, index) => ( - - - - - - - - - - ))} - -
NoTitleStatusCategoryNepali NameDateActions
{index + 1}{post.title}{post.status}{post.category.name}{post.category.nepaliName} - {new Date(post.createdAt).toLocaleString("en-US", { - year: "numeric", - month: "short", - day: "numeric", - hour: "numeric", - minute: "numeric", - })} - -
- - - - - - -
-
+ + + + + + + + + + + + + + {filteredPosts.map((post, index) => ( + + + + + + + + + + ))} + +
NoTitleStatusCategoryNepali NameDateActions
{index + 1}{post.title}{post.status}{post.category.name}{post.category.nepaliName} + {new Date(post.createdAt).toLocaleString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + hour: "numeric", + minute: "numeric", + })} + +
+ + + + + + +
+
- {/* Empty state for no results */} - {filteredPosts.length === 0 && ( -
- No posts found for the given search or category. -
- )} -
+ {/* Pagination Controls */} +
+ + + Page {page} of {totalPages} + + +
+ + {/* Empty state for no results */} + {filteredPosts.length === 0 && ( +
+ No posts found for the given search or category.
- - ); + )} +
+
+
+ ); }; export default PostsPage; diff --git a/admin-panel/src/services/postsService.ts b/admin-panel/src/services/postsService.ts index d66eab9..62cdfef 100644 --- a/admin-panel/src/services/postsService.ts +++ b/admin-panel/src/services/postsService.ts @@ -12,11 +12,19 @@ import { api } from "@/config/index"; params: { page, limit } }); console.log("Full response:", response.data.data); - return response.data.data; + return { + posts: response.data.data.posts || [], // Array of posts + total: response.data.data.pagination?.total || 0, // Total number of posts + }; } catch (error: any) { console.error("Error fetching posts:", error); // Log error details throw new Error("Failed to fetch posts: " + error.response?.data?.message || error.message); } }; +export const createPost = async (postData: any) => { + const response = await api.post("/posts/admin", postData); + return response.data.data; +}; + export { fetchPosts }; \ No newline at end of file