[resumes][feat] add comment edit (#386)

* [resumes][feat] add comment edit

* [resumes][fix] use react-hook-form validation

Co-authored-by: Terence Ho <>
pull/387/head
Terence 2 years ago committed by GitHub
parent c9f7b59d52
commit 25039b52de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,8 +1,14 @@
import { useState } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';
import {
ArrowDownCircleIcon,
ArrowUpCircleIcon,
} from '@heroicons/react/20/solid';
import { FaceSmileIcon } from '@heroicons/react/24/outline';
import { Button, TextArea } from '@tih/ui';
import { trpc } from '~/utils/trpc';
import ResumeExpandableText from '../shared/ResumeExpandableText';
@ -13,11 +19,63 @@ type ResumeCommentListItemProps = {
userId?: string;
};
type ICommentInput = {
description: string;
};
export default function ResumeCommentListItem({
comment,
userId,
}: ResumeCommentListItemProps) {
const isCommentOwner = userId === comment.user.userId;
const [isEditingComment, setIsEditingComment] = useState(false);
const {
register,
handleSubmit,
setValue,
formState: { errors, isDirty },
reset,
} = useForm<ICommentInput>({
defaultValues: {
description: comment.description,
},
});
const trpcContext = trpc.useContext();
const commentUpdateMutation = trpc.useMutation(
'resumes.comments.user.update',
{
onSuccess: () => {
// Comment updated, invalidate query to trigger refetch
trpcContext.invalidateQueries(['resumes.comments.list']);
},
},
);
const onCancel = () => {
reset({ description: comment.description });
setIsEditingComment(false);
};
const onSubmit: SubmitHandler<ICommentInput> = async (data) => {
const { id } = comment;
return await commentUpdateMutation.mutate(
{
id,
...data,
},
{
onSuccess: () => {
setIsEditingComment(false);
},
},
);
};
const setFormValue = (value: string) => {
setValue('description', value.trim(), { shouldDirty: true });
};
return (
<div className="border-primary-300 w-3/4 rounded-md border-2 bg-white p-2 drop-shadow-md">
@ -54,7 +112,45 @@ export default function ResumeCommentListItem({
</div>
{/* Description */}
<ResumeExpandableText text={comment.description} />
{isEditingComment ? (
<form onSubmit={handleSubmit(onSubmit)}>
<div className="flex-column mt-1 space-y-2">
<TextArea
{...(register('description', {
required: 'Comments cannot be empty!',
}),
{})}
defaultValue={comment.description}
disabled={commentUpdateMutation.isLoading}
errorMessage={errors.description?.message}
label="Edit comment"
placeholder="Leave your comment here"
onChange={setFormValue}
/>
<div className="flex-row space-x-2">
<Button
disabled={commentUpdateMutation.isLoading}
label="Cancel"
size="sm"
variant="tertiary"
onClick={onCancel}
/>
<Button
disabled={!isDirty || commentUpdateMutation.isLoading}
isLoading={commentUpdateMutation.isLoading}
label="Confirm"
size="sm"
type="submit"
variant="primary"
/>
</div>
</div>
</form>
) : (
<ResumeExpandableText text={comment.description} />
)}
{/* Upvote and edit */}
<div className="flex flex-row space-x-1 pt-1 align-middle">
@ -63,12 +159,14 @@ export default function ResumeCommentListItem({
<div className="text-xs">{comment.numVotes}</div>
<ArrowDownCircleIcon className="h-4 w-4 fill-gray-400" />
{/* TODO: Implement edit */}
{isCommentOwner ? (
<div className="text-primary-800 hover:text-primary-400 px-1 text-xs">
{isCommentOwner && !isEditingComment && (
<a
className="text-primary-800 hover:text-primary-400 px-1 text-xs"
href="#"
onClick={() => setIsEditingComment(true)}>
Edit
</div>
) : null}
</a>
)}
</div>
</div>
</div>

@ -10,9 +10,8 @@ type ResumeCommentInput = Readonly<{
userId: string;
}>;
export const resumesCommentsUserRouter = createProtectedRouter().mutation(
'create',
{
export const resumesCommentsUserRouter = createProtectedRouter()
.mutation('create', {
input: z.object({
education: z.string(),
experience: z.string(),
@ -50,5 +49,22 @@ export const resumesCommentsUserRouter = createProtectedRouter().mutation(
data: comments,
});
},
},
);
})
.mutation('update', {
input: z.object({
description: z.string(),
id: z.string(),
}),
async resolve({ ctx, input }) {
const { id, description } = input;
return await ctx.prisma.resumesComment.update({
data: {
description,
},
where: {
id,
},
});
},
});

Loading…
Cancel
Save