instaclone/frontend/src/components/CommentSheet.tsx
2026-07-15 12:36:06 +05:30

81 lines
3.2 KiB
TypeScript

import { motion } from 'framer-motion';
import { X } from 'lucide-react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { toast } from 'sonner';
import { api } from '../api';
import { useAuth } from '../context/AuthContext';
const commentSchema = z.object({
content: z.string().min(1, 'Comment cannot be empty').max(500),
});
type CommentFormData = z.infer<typeof commentSchema>;
interface CommentSheetProps {
postId: string;
onClose: () => void;
}
export default function CommentSheet({ postId, onClose }: CommentSheetProps) {
const { user } = useAuth();
const queryClient = useQueryClient();
const { register, handleSubmit, reset, formState: { errors } } = useForm<CommentFormData>({
resolver: zodResolver(commentSchema),
});
const { mutate: addComment, isPending } = useMutation({
mutationFn: (data: CommentFormData) => api.post(`/api/posts/${postId}/comments`, data),
onSuccess: () => {
toast.success('Comment added!');
queryClient.invalidateQueries({ queryKey: ['posts'] });
reset();
},
onError: (error) => {
toast.error('Failed to add comment.');
console.error(error);
}
});
const onSubmit = (data: CommentFormData) => {
addComment(data);
};
return (
<div className="fixed inset-0 bg-black/50 z-50 flex justify-center items-end" onClick={onClose}>
<motion.div
initial={{ y: '100%' }}
animate={{ y: '0%' }}
exit={{ y: '100%' }}
transition={{ type: 'spring', stiffness: 300, damping: 30 }}
className="bg-canvas w-full max-w-md h-[80vh] rounded-t-lg flex flex-col"
onClick={(e) => e.stopPropagation()}
>
<header className="flex items-center justify-between p-4 border-b border-hairline-soft">
<h2 className="font-bold text-lg text-ink">Comments</h2>
<button onClick={onClose}><X size={24} /></button>
</header>
<div className="flex-grow p-4 overflow-y-auto">
{/* NOTE: API to GET comments is not available in the spec. */}
<div className="text-center text-muted py-16">
<p>Be the first to comment!</p>
<p className="text-sm">(Viewing comments is not yet supported)</p>
</div>
</div>
<form onSubmit={handleSubmit(onSubmit)} className="p-4 border-t border-hairline-soft flex items-center gap-2">
<img src={`https://api.dicebear.com/7.x/pixel-art/svg?seed=${user?.email}`} alt="Your avatar" className="w-10 h-10 rounded-full"/>
<input
{...register('content')}
placeholder="Add a comment..."
className="flex-grow bg-surface-soft border border-hairline rounded-full px-4 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-primary"
disabled={isPending}
/>
<button type="submit" disabled={isPending} className="text-primary font-semibold disabled:text-muted">Post</button>
</form>
{errors.content && <p className="text-xs text-red-500 px-4 pb-2">{errors.content.message}</p>}
</motion.div>
</div>
);
}