+
@@ -440,7 +441,7 @@ const BannerManagementPage: React.FC = () => {
-
+
)}
diff --git a/Frontend/src/pages/admin/BlogManagementPage.tsx b/Frontend/src/pages/admin/BlogManagementPage.tsx
index ddc927c5..dc88ad14 100644
--- a/Frontend/src/pages/admin/BlogManagementPage.tsx
+++ b/Frontend/src/pages/admin/BlogManagementPage.tsx
@@ -1,5 +1,5 @@
import React, { useEffect, useState, useRef } from 'react';
-import { Plus, Search, Edit, Trash2, X, Eye, EyeOff, Loader2, Calendar, User, Tag, GripVertical, Image as ImageIcon, Type, Quote, List, Video, ArrowRight, MoveUp, MoveDown, Sparkles, Upload } from 'lucide-react';
+import { Plus, Search, Edit, Trash2, X, Eye, EyeOff, Loader2, Tag, GripVertical, Image as ImageIcon, Type, Quote, List, Video, ArrowRight, MoveUp, MoveDown, Sparkles, Upload } from 'lucide-react';
import { toast } from 'react-toastify';
import Loading from '../../shared/components/Loading';
import Pagination from '../../shared/components/Pagination';
@@ -42,6 +42,16 @@ const BlogManagementPage: React.FC = () => {
const [saving, setSaving] = useState(false);
const [showSectionBuilder, setShowSectionBuilder] = useState(false);
const [uploadingImages, setUploadingImages] = useState<{ [key: string]: boolean }>({});
+
+ // Tag management state
+ const [showTagController, setShowTagController] = useState(false);
+ const [tags, setTags] = useState
>([]);
+ const [tagsLoading, setTagsLoading] = useState(false);
+ const [editingTag, setEditingTag] = useState<{ old: string; new: string } | null>(null);
+ const [deleteTagConfirm, setDeleteTagConfirm] = useState<{ show: boolean; tag: string | null }>({
+ show: false,
+ tag: null,
+ });
useEffect(() => {
setCurrentPage(1);
@@ -51,6 +61,12 @@ const BlogManagementPage: React.FC = () => {
fetchPosts();
}, [filters, currentPage]);
+ useEffect(() => {
+ if (showTagController) {
+ fetchTags();
+ }
+ }, [showTagController]);
+
const fetchPosts = async () => {
try {
setLoading(true);
@@ -275,6 +291,50 @@ const BlogManagementPage: React.FC = () => {
}
};
+ // Tag management functions
+ const fetchTags = async () => {
+ try {
+ setTagsLoading(true);
+ const response = await blogService.getAllTags();
+ if (response.status === 'success' && response.data) {
+ setTags(response.data.tags);
+ }
+ } catch (error: any) {
+ toast.error(error.response?.data?.detail || 'Failed to load tags');
+ } finally {
+ setTagsLoading(false);
+ }
+ };
+
+ const handleRenameTag = async () => {
+ if (!editingTag || !editingTag.new.trim() || editingTag.old === editingTag.new.trim()) {
+ setEditingTag(null);
+ return;
+ }
+ try {
+ const response = await blogService.renameTag(editingTag.old, editingTag.new.trim());
+ toast.success(response.message || 'Tag renamed successfully');
+ setEditingTag(null);
+ fetchTags();
+ fetchPosts(); // Refresh posts to show updated tags
+ } catch (error: any) {
+ toast.error(error.response?.data?.detail || 'Failed to rename tag');
+ }
+ };
+
+ const handleDeleteTag = async () => {
+ if (!deleteTagConfirm.tag) return;
+ try {
+ const response = await blogService.deleteTag(deleteTagConfirm.tag);
+ toast.success(response.message || 'Tag deleted successfully');
+ setDeleteTagConfirm({ show: false, tag: null });
+ fetchTags();
+ fetchPosts(); // Refresh posts to show updated tags
+ } catch (error: any) {
+ toast.error(error.response?.data?.detail || 'Failed to delete tag');
+ }
+ };
+
const formatDate = (dateString?: string) => {
if (!dateString) return 'Not published';
return new Date(dateString).toLocaleDateString('en-US', {
@@ -398,13 +458,22 @@ const BlogManagementPage: React.FC = () => {
Blog Management
Manage your blog posts and content