handling closing posts
This commit is contained in:
@@ -20,7 +20,7 @@ const ForumPostDetail: React.FC = () => {
|
||||
const [actionLoading, setActionLoading] = useState(false);
|
||||
const [showAdminModal, setShowAdminModal] = useState(false);
|
||||
const [adminAction, setAdminAction] = useState<{
|
||||
type: 'deletePost' | 'deleteComment' | 'restorePost' | 'restoreComment';
|
||||
type: 'deletePost' | 'deleteComment' | 'restorePost' | 'restoreComment' | 'closePost' | 'reopenPost';
|
||||
id?: string;
|
||||
} | null>(null);
|
||||
|
||||
@@ -151,6 +151,16 @@ const ForumPostDetail: React.FC = () => {
|
||||
setShowAdminModal(true);
|
||||
};
|
||||
|
||||
const handleAdminClosePost = async () => {
|
||||
setAdminAction({ type: 'closePost' });
|
||||
setShowAdminModal(true);
|
||||
};
|
||||
|
||||
const handleAdminReopenPost = async () => {
|
||||
setAdminAction({ type: 'reopenPost' });
|
||||
setShowAdminModal(true);
|
||||
};
|
||||
|
||||
const handleAdminDeleteComment = async (commentId: string) => {
|
||||
setAdminAction({ type: 'deleteComment', id: commentId });
|
||||
setShowAdminModal(true);
|
||||
@@ -175,6 +185,12 @@ const ForumPostDetail: React.FC = () => {
|
||||
case 'restorePost':
|
||||
await forumAPI.adminRestorePost(id!);
|
||||
break;
|
||||
case 'closePost':
|
||||
await forumAPI.adminClosePost(id!);
|
||||
break;
|
||||
case 'reopenPost':
|
||||
await forumAPI.adminReopenPost(id!);
|
||||
break;
|
||||
case 'deleteComment':
|
||||
await forumAPI.adminDeleteComment(adminAction.id!);
|
||||
break;
|
||||
@@ -199,7 +215,13 @@ const ForumPostDetail: React.FC = () => {
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString();
|
||||
return date.toLocaleString(undefined, {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit'
|
||||
});
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
@@ -310,9 +332,16 @@ const ForumPostDetail: React.FC = () => {
|
||||
|
||||
{isAuthor && (
|
||||
<div className="d-flex gap-2 flex-wrap mb-3">
|
||||
<Link
|
||||
to={`/forum/${post.id}/edit`}
|
||||
className="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i className="bi bi-pencil me-1"></i>
|
||||
Edit
|
||||
</Link>
|
||||
{post.status !== 'closed' && (
|
||||
<button
|
||||
className="btn btn-sm btn-secondary"
|
||||
className="btn btn-sm btn-outline-secondary"
|
||||
onClick={() => handleStatusChange('closed')}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
@@ -322,7 +351,7 @@ const ForumPostDetail: React.FC = () => {
|
||||
)}
|
||||
{post.status === 'closed' && (
|
||||
<button
|
||||
className="btn btn-sm btn-success"
|
||||
className="btn btn-sm btn-outline-secondary"
|
||||
onClick={() => handleStatusChange('open')}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
@@ -330,13 +359,6 @@ const ForumPostDetail: React.FC = () => {
|
||||
Reopen Post
|
||||
</button>
|
||||
)}
|
||||
<Link
|
||||
to={`/forum/${post.id}/edit`}
|
||||
className="btn btn-sm btn-outline-primary"
|
||||
>
|
||||
<i className="bi bi-pencil me-1"></i>
|
||||
Edit
|
||||
</Link>
|
||||
<button
|
||||
className="btn btn-sm btn-outline-danger"
|
||||
onClick={handleDeletePost}
|
||||
@@ -422,7 +444,32 @@ const ForumPostDetail: React.FC = () => {
|
||||
|
||||
<hr />
|
||||
|
||||
{user ? (
|
||||
{/* Admin Close/Reopen Controls */}
|
||||
{isAdmin && (
|
||||
<div className="d-flex gap-2 mb-3">
|
||||
{post.status === 'closed' ? (
|
||||
<button
|
||||
className="btn btn-success"
|
||||
onClick={handleAdminReopenPost}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
<i className="bi bi-unlock me-1"></i>
|
||||
Reopen Discussion (Admin)
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="btn btn-warning"
|
||||
onClick={handleAdminClosePost}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
<i className="bi bi-lock me-1"></i>
|
||||
Close Discussion (Admin)
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{post.status !== 'closed' && user ? (
|
||||
<div>
|
||||
<h6>Add a comment</h6>
|
||||
<CommentForm
|
||||
@@ -431,11 +478,23 @@ const ForumPostDetail: React.FC = () => {
|
||||
buttonText="Post Comment"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
) : post.status !== 'closed' && !user ? (
|
||||
<div className="alert alert-info">
|
||||
<i className="bi bi-info-circle me-2"></i>
|
||||
<AuthButton mode="login" className="alert-link" asLink>Log in</AuthButton> to join the discussion.
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Show closed banner at the bottom for all users */}
|
||||
{post.status === 'closed' && post.closedBy && (
|
||||
<div className="text-muted mt-3">
|
||||
<i className="bi bi-lock me-2"></i>
|
||||
<strong>
|
||||
Closed by {post.closer ? `${post.closer.firstName} ${post.closer.lastName}` : 'Unknown'}
|
||||
</strong>
|
||||
{post.closedAt && ` on ${formatDate(post.closedAt)}`}
|
||||
{' - '}No new comments can be added
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -466,6 +525,12 @@ const ForumPostDetail: React.FC = () => {
|
||||
{adminAction?.type === 'restorePost' && (
|
||||
<p>Are you sure you want to restore this post? It will become visible to all users again.</p>
|
||||
)}
|
||||
{adminAction?.type === 'closePost' && (
|
||||
<p>Are you sure you want to close this discussion? No users will be able to add new comments. All participants will be notified by email.</p>
|
||||
)}
|
||||
{adminAction?.type === 'reopenPost' && (
|
||||
<p>Are you sure you want to reopen this discussion? Users will be able to add comments again.</p>
|
||||
)}
|
||||
{adminAction?.type === 'deleteComment' && (
|
||||
<p>Are you sure you want to delete this comment? It will be deleted and hidden from regular users but can be restored later.</p>
|
||||
)}
|
||||
@@ -484,7 +549,11 @@ const ForumPostDetail: React.FC = () => {
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`btn ${adminAction?.type.includes('delete') ? 'btn-danger' : 'btn-success'}`}
|
||||
className={`btn ${
|
||||
adminAction?.type.includes('delete') ? 'btn-danger' :
|
||||
adminAction?.type === 'closePost' ? 'btn-warning' :
|
||||
'btn-success'
|
||||
}`}
|
||||
onClick={confirmAdminAction}
|
||||
disabled={actionLoading}
|
||||
>
|
||||
@@ -495,7 +564,10 @@ const ForumPostDetail: React.FC = () => {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{adminAction?.type.includes('delete') ? 'Delete' : 'Restore'}
|
||||
{adminAction?.type.includes('delete') ? 'Delete' :
|
||||
adminAction?.type === 'closePost' ? 'Close' :
|
||||
adminAction?.type === 'reopenPost' ? 'Reopen' :
|
||||
'Restore'}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@@ -286,6 +286,8 @@ export const forumAPI = {
|
||||
adminRestorePost: (id: string) => api.patch(`/forum/admin/posts/${id}/restore`),
|
||||
adminDeleteComment: (id: string) => api.delete(`/forum/admin/comments/${id}`),
|
||||
adminRestoreComment: (id: string) => api.patch(`/forum/admin/comments/${id}/restore`),
|
||||
adminClosePost: (id: string) => api.patch(`/forum/admin/posts/${id}/close`),
|
||||
adminReopenPost: (id: string) => api.patch(`/forum/admin/posts/${id}/reopen`),
|
||||
};
|
||||
|
||||
export const stripeAPI = {
|
||||
|
||||
@@ -271,6 +271,9 @@ export interface ForumPost {
|
||||
isDeleted?: boolean;
|
||||
deletedBy?: string;
|
||||
deletedAt?: string;
|
||||
closedBy?: string;
|
||||
closedAt?: string;
|
||||
closer?: User;
|
||||
hasDeletedComments?: boolean;
|
||||
author?: User;
|
||||
tags?: PostTag[];
|
||||
|
||||
Reference in New Issue
Block a user