[resumes][feat] Add reply form

pull/401/head
Terence Ho 3 years ago
parent 671484147e
commit a1445e9de8

@ -2,6 +2,7 @@ import { useState } from 'react';
import { FaceSmileIcon } from '@heroicons/react/24/outline';
import ResumeCommentEditForm from './comment/ResumeCommentEditForm';
import ResumeCommentReplyForm from './comment/ResumeCommentReplyForm';
import ResumeCommentVoteButtons from './comment/ResumeCommentVoteButtons';
import ResumeUserBadges from '../badges/ResumeUserBadges';
import ResumeExpandableText from '../shared/ResumeExpandableText';
@ -19,6 +20,7 @@ export default function ResumeCommentListItem({
}: ResumeCommentListItemProps) {
const isCommentOwner = userId === comment.user.userId;
const [isEditingComment, setIsEditingComment] = useState(false);
const [isReplyingComment, setIsReplyingComment] = useState(false);
return (
<div className="border-primary-300 w-11/12 min-w-fit rounded-md border-2 bg-white p-2 drop-shadow-md">
@ -70,15 +72,34 @@ export default function ResumeCommentListItem({
<div className="flex flex-row space-x-1 pt-1 align-middle">
<ResumeCommentVoteButtons commentId={comment.id} userId={userId} />
{isCommentOwner && !isEditingComment && (
<button
className="text-primary-800 hover:text-primary-400 px-1 text-xs"
type="button"
onClick={() => setIsEditingComment(true)}>
Edit
</button>
{isCommentOwner && !isEditingComment && !isReplyingComment && (
<>
<button
className="text-primary-800 hover:text-primary-400 px-1 text-xs"
type="button"
onClick={() => setIsEditingComment(true)}>
Edit
</button>
<button
className="text-primary-800 hover:text-primary-400 px-1 text-xs"
type="button"
onClick={() => setIsReplyingComment(true)}>
Reply
</button>
</>
)}
</div>
{/* Replies */}
{isReplyingComment && (
<ResumeCommentReplyForm
parentId={comment.id}
resumeId={comment.resumeId}
section={comment.section}
setIsReplyingComment={setIsReplyingComment}
/>
)}
</div>
</div>
</div>

@ -0,0 +1,107 @@
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import type { ResumesSection } from '@prisma/client';
import { Button, TextArea } from '@tih/ui';
import { trpc } from '~/utils/trpc';
type ResumeCommentEditFormProps = {
parentId: string;
resumeId: string;
section: ResumesSection;
setIsReplyingComment: (value: boolean) => void;
};
type IReplyInput = {
description: string;
};
export default function ResumeCommentReplyForm({
parentId,
setIsReplyingComment,
resumeId,
section,
}: ResumeCommentEditFormProps) {
const {
register,
handleSubmit,
setValue,
formState: { errors, isDirty },
reset,
} = useForm<IReplyInput>({
defaultValues: {
description: '',
},
});
const trpcContext = trpc.useContext();
const commentReplyMutation = trpc.useMutation('resumes.comments.user.reply', {
onSuccess: () => {
// Comment updated, invalidate query to trigger refetch
trpcContext.invalidateQueries(['resumes.comments.list']);
},
});
const onCancel = () => {
reset({ description: '' });
setIsReplyingComment(false);
};
const onSubmit: SubmitHandler<IReplyInput> = async (data) => {
return commentReplyMutation.mutate(
{
parentId,
resumeId,
section,
...data,
},
{
onSuccess: () => {
setIsReplyingComment(false);
},
},
);
};
const setFormValue = (value: string) => {
setValue('description', value.trim(), { shouldDirty: true });
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex-column mt-1 space-y-2">
<TextArea
{...(register('description', {
required: 'Reply cannot be empty!',
}),
{})}
defaultValue=""
disabled={commentReplyMutation.isLoading}
errorMessage={errors.description?.message}
label=""
placeholder="Leave your reply here"
onChange={setFormValue}
/>
<div className="flex-row space-x-2">
<Button
disabled={commentReplyMutation.isLoading}
label="Cancel"
size="sm"
variant="tertiary"
onClick={onCancel}
/>
<Button
disabled={!isDirty || commentReplyMutation.isLoading}
isLoading={commentReplyMutation.isLoading}
label="Confirm"
size="sm"
type="submit"
variant="primary"
/>
</div>
</div>
</form>
);
}

@ -67,4 +67,26 @@ export const resumesCommentsUserRouter = createProtectedRouter()
},
});
},
})
.mutation('reply', {
input: z.object({
description: z.string(),
parentId: z.string(),
resumeId: z.string(),
section: z.nativeEnum(ResumesSection),
}),
async resolve({ ctx, input }) {
const userId = ctx.session.user.id;
const { description, parentId, resumeId, section } = input;
return await ctx.prisma.resumesComment.create({
data: {
description,
parentId,
resumeId,
section,
userId,
},
});
},
});

Loading…
Cancel
Save