Skip to content

Instantly share code, notes, and snippets.

@alaminfirdows
Created May 6, 2024 06:32
Show Gist options
  • Save alaminfirdows/f0e40dda5b33150640824675f7a383ea to your computer and use it in GitHub Desktop.
Save alaminfirdows/f0e40dda5b33150640824675f7a383ea to your computer and use it in GitHub Desktop.
import React, { useState } from 'react';
import axios from 'axios';
import classNames from 'classnames';
import { usePage } from '@inertiajs/react';
import { CommentType, PageProps, PostType, User } from '@/types';
import { formatDate } from '@/utils';
import CommentBox from './CommentBox';
type CommentProps = {
comment: CommentType;
post: PostType;
parentId?: number;
onCommentDelete: (commentId: number) => void;
};
const Comment = ({
post,
comment,
parentId,
onCommentDelete,
}: CommentProps) => {
const [showReplyBox, setShowReplyBox] = useState(false);
const { auth } = usePage<PageProps>().props;
const [commentState, setCommentState] = useState<CommentType>(comment);
const deleteComment = (commentId: number) => {
if (!confirm('Are you sure you want to delete this comment?')) {
return;
}
axios
.delete(route('admin.comments.destroy', [commentId]))
.then(() => {
onCommentDelete(commentId);
})
.catch((error) => {
alert(error.response.data.message);
});
};
const appendComment = (comment: CommentType) => {
setCommentState({
...commentState,
children: [comment, ...commentState.children],
});
};
return (
<div className="flex py-3">
<div className="w-9 mr-3">
<img
src={commentState.user?.avatar}
className={classNames(
'rounded-full h-7 w-7',
commentState.user?.role === 'admin' ? 'ring-2 ring-indigo-500' : ''
)}
/>
</div>
<div className="flex-1">
<div className="flex items-center text-sm mb-2">
<div className="font-semibold dark:text-gray-300">
{commentState.user?.name}
</div>
{commentState.status && (
<div className="text-sm text-gray-700 ml-2">
<span>marked this post as</span>
<span
className="uppercase text-xs font-bold ml-2 text-white px-2 py-1 rounded"
style={{ backgroundColor: commentState.status.color }}
>
{commentState.status.name}
</span>
</div>
)}
</div>
<div
className="text-sm text-gray-800 dark:text-gray-300 mb-2"
dangerouslySetInnerHTML={{ __html: commentState.body }}
></div>
<div className="flex text-xs text-gray-500">
<div className="">{formatDate(commentState.created_at)}</div>
{(commentState.parent_id === null ||
commentState.parent_id === 0) && (
<>
<div className="mx-1">•</div>
<div
className="cursor-pointer hover:text-gray-800 dark:hover:text-gray-300"
onClick={(_) => setShowReplyBox((prev) => !prev)}
>
Reply
</div>
</>
)}
{auth.user?.role === 'admin' && (
<>
<div className="mx-1">•</div>
<div className="">
<button
className="text-xs text-red-500 dark:text-red-300"
onClick={() => deleteComment(commentState.id)}
>
Delete
</button>
</div>
</>
)}
</div>
{commentState.children.length > 0 && (
<div className="mt-3">
{commentState.children.map((child) => (
<Comment
key={child.id}
post={post}
comment={child}
parentId={comment.id}
onCommentDelete={() => {}}
/>
))}
</div>
)}
{showReplyBox && (
<div className="mt-4">
<CommentBox
post={post}
parent={parentId}
onComment={appendComment}
/>
</div>
)}
</div>
</div>
);
};
export default Comment;
import React, { useEffect, useState } from 'react';
import { BoardType, CommentType, PostType } from '@/types';
import CommentBox from './CommentBox';
import Comment from '@/Components/Comment';
type CommentsProps = {
post: PostType;
board: BoardType;
};
const Comments: React.FC<CommentsProps> = ({ post }) => {
const [comments, setComments] = useState<CommentType[]>([]);
const [sort, setSort] = useState('latest');
const [isFetching, setIsFetching] = useState(false);
const fetchComments = async () => {
setIsFetching(true);
try {
const response = await fetch(
route('post.comments.index', {
post: post.slug,
sort: sort,
})
);
const data = await response.json();
setIsFetching(false);
setComments(data);
} catch (error) {
console.error('Error fetching comments:', error);
setIsFetching(false);
}
};
useEffect(() => {
fetchComments();
}, [sort]);
const appendToComments = (comment: CommentType) => {
setComments([comment, ...comments]);
};
return (
<div className="mt-8">
<div className="mb-8 ml-12">
<CommentBox post={post} onComment={appendToComments} />
</div>
{comments.length > 0 && !isFetching && (
<div className="flex justify-between items-center text-gray-700 ml-12 mb-8 pb-4 border-b dark:border-gray-700">
<h3 className="text-lg font-semibold dark:text-gray-300">Comments</h3>
<div className="flex items-center">
<div className="text-sm mr-2 dark:text-gray-400">Sort By</div>
<select
className="px-2 min-w-28 text-sm py-1.5 rounded border border-gray-200"
value={sort}
onChange={(e) => setSort(e.target.value)}
>
<option value="latest">Latest</option>
<option value="oldest">Oldest</option>
</select>
</div>
</div>
)}
<div className="mt-4">
{comments.map((comment) => (
<Comment
key={comment.id}
post={post}
comment={comment}
parentId={comment.id}
onCommentDelete={() => {
setComments(comments.filter((c) => c.id !== comment.id));
}}
/>
))}
</div>
</div>
);
};
export default Comments;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment