admin soft delete functionality, also fixed google sign in when user doesn't have first and last name
This commit is contained in:
@@ -44,7 +44,7 @@ const AuthModal: React.FC<AuthModalProps> = ({
|
||||
const handleGoogleLogin = () => {
|
||||
const clientId = process.env.REACT_APP_GOOGLE_CLIENT_ID;
|
||||
const redirectUri = `${window.location.origin}/auth/google/callback`;
|
||||
const scope = 'email profile';
|
||||
const scope = 'openid email profile';
|
||||
const responseType = 'code';
|
||||
|
||||
const googleAuthUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
|
||||
|
||||
@@ -13,6 +13,9 @@ interface CommentThreadProps {
|
||||
isPostAuthor?: boolean;
|
||||
acceptedAnswerId?: string;
|
||||
depth?: number;
|
||||
isAdmin?: boolean;
|
||||
onAdminDelete?: (commentId: string) => Promise<void>;
|
||||
onAdminRestore?: (commentId: string) => Promise<void>;
|
||||
}
|
||||
|
||||
const CommentThread: React.FC<CommentThreadProps> = ({
|
||||
@@ -25,6 +28,9 @@ const CommentThread: React.FC<CommentThreadProps> = ({
|
||||
isPostAuthor = false,
|
||||
acceptedAnswerId,
|
||||
depth = 0,
|
||||
isAdmin = false,
|
||||
onAdminDelete,
|
||||
onAdminRestore,
|
||||
}) => {
|
||||
const [showReplyForm, setShowReplyForm] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
@@ -89,7 +95,7 @@ const CommentThread: React.FC<CommentThreadProps> = ({
|
||||
}
|
||||
};
|
||||
|
||||
if (comment.isDeleted) {
|
||||
if (comment.isDeleted && !isAdmin) {
|
||||
return (
|
||||
<div className={`comment-deleted ${depth > 0 ? "ms-4" : ""} mb-3`}>
|
||||
<div className="card bg-light">
|
||||
@@ -111,6 +117,9 @@ const CommentThread: React.FC<CommentThreadProps> = ({
|
||||
isPostAuthor={isPostAuthor}
|
||||
acceptedAnswerId={acceptedAnswerId}
|
||||
depth={depth + 1}
|
||||
isAdmin={isAdmin}
|
||||
onAdminDelete={onAdminDelete}
|
||||
onAdminRestore={onAdminRestore}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -121,8 +130,17 @@ const CommentThread: React.FC<CommentThreadProps> = ({
|
||||
|
||||
return (
|
||||
<div className={`comment ${depth > 0 ? "ms-4" : ""} mb-3`}>
|
||||
<div className={`card ${isAcceptedAnswer ? "border-success" : ""}`}>
|
||||
<div className={`card ${isAcceptedAnswer ? "border-success" : ""} ${comment.isDeleted && isAdmin ? "border-danger" : ""}`}>
|
||||
<div className="card-body">
|
||||
{comment.isDeleted && isAdmin && (
|
||||
<div className="alert alert-danger alert-sm py-1 px-2 mb-2">
|
||||
<small>
|
||||
<i className="bi bi-trash me-1"></i>
|
||||
<strong>Deleted by Admin</strong>
|
||||
{comment.deletedAt && ` on ${formatDate(comment.deletedAt)}`}
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
{isAcceptedAnswer && (
|
||||
<div className="mb-2">
|
||||
<span className="badge bg-success">
|
||||
@@ -264,6 +282,24 @@ const CommentThread: React.FC<CommentThreadProps> = ({
|
||||
Delete
|
||||
</button>
|
||||
)}
|
||||
{isAdmin && onAdminDelete && !comment.isDeleted && !isEditing && (
|
||||
<button
|
||||
className="btn btn-sm btn-link text-danger text-decoration-none p-0"
|
||||
onClick={() => onAdminDelete(comment.id)}
|
||||
>
|
||||
<i className="bi bi-trash me-1"></i>
|
||||
Delete (Admin)
|
||||
</button>
|
||||
)}
|
||||
{isAdmin && onAdminRestore && comment.isDeleted && !isEditing && (
|
||||
<button
|
||||
className="btn btn-sm btn-link text-success text-decoration-none p-0"
|
||||
onClick={() => onAdminRestore(comment.id)}
|
||||
>
|
||||
<i className="bi bi-arrow-counterclockwise me-1"></i>
|
||||
Restore (Admin)
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -294,6 +330,9 @@ const CommentThread: React.FC<CommentThreadProps> = ({
|
||||
isPostAuthor={isPostAuthor}
|
||||
acceptedAnswerId={acceptedAnswerId}
|
||||
depth={depth + 1}
|
||||
isAdmin={isAdmin}
|
||||
onAdminDelete={onAdminDelete}
|
||||
onAdminRestore={onAdminRestore}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -3,12 +3,16 @@ import { Link } from "react-router-dom";
|
||||
import { ForumPost } from "../types";
|
||||
import CategoryBadge from "./CategoryBadge";
|
||||
import PostStatusBadge from "./PostStatusBadge";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
|
||||
interface ForumPostListItemProps {
|
||||
post: ForumPost;
|
||||
filter?: string;
|
||||
}
|
||||
|
||||
const ForumPostListItem: React.FC<ForumPostListItemProps> = ({ post }) => {
|
||||
const ForumPostListItem: React.FC<ForumPostListItemProps> = ({ post, filter }) => {
|
||||
const { user } = useAuth();
|
||||
const isAdmin = user?.role === 'admin';
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
const now = new Date();
|
||||
@@ -38,9 +42,12 @@ const ForumPostListItem: React.FC<ForumPostListItemProps> = ({ post }) => {
|
||||
: text;
|
||||
};
|
||||
|
||||
// Build link with filter param if admin and filter is set
|
||||
const linkTo = filter && isAdmin ? `/forum/${post.id}?filter=${filter}` : `/forum/${post.id}`;
|
||||
|
||||
return (
|
||||
<div className="list-group-item list-group-item-action p-3">
|
||||
<Link to={`/forum/${post.id}`} className="text-decoration-none d-block">
|
||||
<Link to={linkTo} className="text-decoration-none d-block">
|
||||
<div className="row align-items-center">
|
||||
{/* Main content - 60% */}
|
||||
<div className="col-md-7">
|
||||
@@ -51,6 +58,12 @@ const ForumPostListItem: React.FC<ForumPostListItemProps> = ({ post }) => {
|
||||
<i className="bi bi-pin-angle-fill"></i>
|
||||
</span>
|
||||
)}
|
||||
{post.isDeleted && isAdmin && (
|
||||
<span className="badge bg-danger badge-sm">
|
||||
<i className="bi bi-trash me-1"></i>
|
||||
Deleted
|
||||
</span>
|
||||
)}
|
||||
<CategoryBadge category={post.category} />
|
||||
<PostStatusBadge status={post.status} />
|
||||
{post.tags &&
|
||||
|
||||
Reference in New Issue
Block a user