From 5a7c679753210aff1e55c0db5c4e0eff24fd7c4a Mon Sep 17 00:00:00 2001 From: Pau Matas Date: Fri, 15 Mar 2024 14:31:08 +0100 Subject: [PATCH 1/2] feat: refresh web when a comment or answer is published During the fix I have also restyled a little bit the Editor interface so the title can use the placeholder instead of having placed text. --- src/app/[slug]/page.tsx | 6 +- src/app/api/subject/answer/create/route.ts | 2 +- src/components/Editor.tsx | 104 +++++++++++++-------- src/components/MiniCreateAnswer.tsx | 25 ++--- src/components/MiniCreateComment.tsx | 14 ++- src/components/MiniCreateQuestion.tsx | 21 +---- src/lib/validators/question.ts | 4 +- 7 files changed, 94 insertions(+), 82 deletions(-) diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx index e1d174a..8b664e4 100644 --- a/src/app/[slug]/page.tsx +++ b/src/app/[slug]/page.tsx @@ -23,7 +23,11 @@ const page = async ({ params }: PageProps) => { include: { author: true, votes: true, - comments: true, + comments: { + include: { + _count: true, + }, + }, subject: true, }, orderBy: { diff --git a/src/app/api/subject/answer/create/route.ts b/src/app/api/subject/answer/create/route.ts index c58ba95..b3c0b56 100644 --- a/src/app/api/subject/answer/create/route.ts +++ b/src/app/api/subject/answer/create/route.ts @@ -23,7 +23,7 @@ export async function POST(req: Request) { authorId: session.user.id, }, }) - return new Response("Answer created", { status: 201 }) + return new Response(JSON.stringify(questionId), { status: 201 }) } catch (error) { if (error instanceof z.ZodError) { return new Response(error.message, { status: 422 }) diff --git a/src/components/Editor.tsx b/src/components/Editor.tsx index c5a0a89..b0b648a 100644 --- a/src/components/Editor.tsx +++ b/src/components/Editor.tsx @@ -1,6 +1,13 @@ "use client" -import { FC, useCallback, useEffect, useRef, useState } from "react" +import { + FC, + startTransition, + useCallback, + useEffect, + useRef, + useState, +} from "react" import TextareaAutosize from "react-textarea-autosize" import { useForm } from "react-hook-form" import { @@ -15,27 +22,34 @@ import { toast } from "@/hooks/use-toast" import { useMutation } from "@tanstack/react-query" import axios from "axios" import { usePathname, useRouter } from "next/navigation" +import { Button } from "./ui/Button" + +type FormData = QuestionCreationRequest | AnswerCreationRequest interface EditorProps { subjectId: string contentType: "question" | "answer" questionId?: string + formId: string } -const Editor: FC = ({ subjectId, contentType, questionId }) => { +const Editor: FC = ({ + subjectId, + contentType, + questionId, + formId, +}) => { const { register, handleSubmit, formState: { errors }, - } = useForm({ + } = useForm({ resolver: zodResolver( contentType === "question" ? QuestionValidator : AnswerValidator, ), defaultValues: { subjectId, - title: `${ - contentType === "question" ? "Pregunta" : "Resposta" - } ${new Date().toLocaleDateString()}`, + title: "", content: null, questionId: questionId || "", // Add questionId as a value in the form }, @@ -61,6 +75,7 @@ const Editor: FC = ({ subjectId, contentType, questionId }) => { if (!ref.current) { const editor = new EditorJS({ holder: "editor", + minHeight: 200, onReady() { ref.current = editor }, @@ -124,13 +139,9 @@ const Editor: FC = ({ subjectId, contentType, questionId }) => { } }, [isMounted, initializeEditor]) - const { mutate: createContent } = useMutation({ - mutationFn: async ({ - title, - content, - subjectId, - }: QuestionCreationRequest | AnswerCreationRequest) => { - const payload: QuestionCreationRequest | AnswerCreationRequest = { + const { mutate: createContent, isLoading } = useMutation({ + mutationFn: async ({ title, content, subjectId }: FormData) => { + const payload: FormData = { title, content, subjectId, @@ -158,22 +169,26 @@ const Editor: FC = ({ subjectId, contentType, questionId }) => { const questionId = data as string const newPathname = pathname.replace("/q", `/q/${questionId}`) router.push(newPathname) + router.refresh() + } else { + ref.current?.clear() + _titleRef.current!.value = "" + window.location.reload() } - router.refresh() return toast({ description: `La teva ${ - contentType === "question" ? "pregunta" : "reposta" + contentType === "question" ? "pregunta" : "resposta" } s'ha creat correctament`, }) }, }) - async function onSubmit() { + async function onSubmit(data: FormData) { const blocks = await ref.current?.save() - const title = (await _titleRef.current?.value) as string - const payload: QuestionCreationRequest | AnswerCreationRequest = { - title: title, + + const payload: FormData = { + title: data.title, content: blocks, subjectId: subjectId, ...(contentType === "answer" && { questionId: questionId }), @@ -187,25 +202,38 @@ const Editor: FC = ({ subjectId, contentType, questionId }) => { const { ref: titleRef, ...rest } = register("title") return ( -
-
-
- { - titleRef(e) - // @ts-ignore - _titleRef.current = e - }} - placeholder="Títol" - className="w-full resize-none appearance-none overflow-hidden bg-transparent text-xl font-bold focus:outline-none h-12" - /> -
-
-
+
+
+
+
+ { + titleRef(e) + // @ts-ignore + _titleRef.current = e + }} + {...rest} + placeholder="Títol" + className="w-full resize-none appearance-none overflow-hidden bg-transparent text-xl font-bold focus:outline-none h-12" + /> +
+
+
+
+
+ +
) } diff --git a/src/components/MiniCreateAnswer.tsx b/src/components/MiniCreateAnswer.tsx index 190e25b..30c6d7f 100644 --- a/src/components/MiniCreateAnswer.tsx +++ b/src/components/MiniCreateAnswer.tsx @@ -30,25 +30,12 @@ const MiniCreateAnswer: FC = ({
{/* form */} -
-
- -
-
- - -
- +
diff --git a/src/components/MiniCreateComment.tsx b/src/components/MiniCreateComment.tsx index 524a1a3..c9a331d 100644 --- a/src/components/MiniCreateComment.tsx +++ b/src/components/MiniCreateComment.tsx @@ -17,7 +17,7 @@ const MiniCreateComment: FC = ({ session, postId }) => { const [content, setContent] = useState("") // Define the mutation function using useMutation hook - const { mutate: createComment } = useMutation({ + const { mutate: createComment, isLoading } = useMutation({ mutationFn: async () => { const { data } = await axios.post("/api/subject/comment/create", { content: content, @@ -26,14 +26,18 @@ const MiniCreateComment: FC = ({ session, postId }) => { return data }, onSuccess: ({}) => { - // Handle success and show toast toast({ description: `Comment created successfully`, }) - // You can add any additional handling specific to your needs here + setContent("") + window.location.reload() }, onError: ({}) => { - // Handle error if needed + toast({ + title: "Something went wrong", + description: `The comment could not be created. Please try again later.`, + variant: "destructive", + }) }, }) @@ -72,8 +76,8 @@ const MiniCreateComment: FC = ({ session, postId }) => { diff --git a/src/components/MiniCreateQuestion.tsx b/src/components/MiniCreateQuestion.tsx index 16a82a9..606b6f8 100644 --- a/src/components/MiniCreateQuestion.tsx +++ b/src/components/MiniCreateQuestion.tsx @@ -1,7 +1,6 @@ "use client" import { Session } from "next-auth" -import { Button } from "@/components/ui/Button" import { FC } from "react" import UserAvatar from "./UserAvatar" import Editor from "@/components/Editor" @@ -28,21 +27,11 @@ const MiniCreateQuestion: FC = ({ {/* form */} -
-
- -
-
- - -
- +
diff --git a/src/lib/validators/question.ts b/src/lib/validators/question.ts index 835df5b..5cba920 100644 --- a/src/lib/validators/question.ts +++ b/src/lib/validators/question.ts @@ -11,8 +11,8 @@ export const QuestionValidator = z.object({ export const AnswerValidator = z.object({ title: z .string() - .min(3, { message: "Content must be at least 3 characters long" }) - .max(128, { message: "Content must be at most 2048 characters long" }), + .min(3, { message: "Title must be at least 3 characters long" }) + .max(128, { message: "Title must be at most 2048 characters long" }), subjectId: z.string(), content: z.any(), questionId: z.string(), From 5402c4d7ff2f04fa083d84906f35e22c734d0cf7 Mon Sep 17 00:00:00 2001 From: Pau Matas Date: Fri, 15 Mar 2024 14:35:42 +0100 Subject: [PATCH 2/2] refactor: https://open.spotify.com/track/3e3K2dVdbOZRdJttaCsLNh?si=992d85092fde4803 --- src/app/[slug]/page.tsx | 4 ++-- src/app/[slug]/q/page.tsx | 4 ++-- .../{MiniCreateAnswer.tsx => CreateAnswer.tsx} | 10 +++------- .../{MiniCreateComment.tsx => CreateComment.tsx} | 6 +++--- src/components/{MiniCreatePost.tsx => CreatePost.tsx} | 6 +++--- .../{MiniCreateQuestion.tsx => CreateQuestion.tsx} | 9 +++------ src/components/PostView.tsx | 4 ++-- src/components/QuestionView.tsx | 4 ++-- 8 files changed, 20 insertions(+), 27 deletions(-) rename src/components/{MiniCreateAnswer.tsx => CreateAnswer.tsx} (87%) rename src/components/{MiniCreateComment.tsx => CreateComment.tsx} (94%) rename src/components/{MiniCreatePost.tsx => CreatePost.tsx} (90%) rename src/components/{MiniCreateQuestion.tsx => CreateQuestion.tsx} (86%) diff --git a/src/app/[slug]/page.tsx b/src/app/[slug]/page.tsx index 8b664e4..4a49ab6 100644 --- a/src/app/[slug]/page.tsx +++ b/src/app/[slug]/page.tsx @@ -1,4 +1,4 @@ -import MiniCreatePost from "@/components/MiniCreatePost" +import CreatePost from "@/components/CreatePost" import PostFeed from "@/components/PostFeed" import { INFINITE_SCROLL_PAGINATION_RESULTS } from "@/config" import { getAuthSession } from "@/lib/auth" @@ -48,7 +48,7 @@ const page = async ({ params }: PageProps) => { {subject.name} - + diff --git a/src/app/[slug]/q/page.tsx b/src/app/[slug]/q/page.tsx index e3cfb9a..16620e3 100644 --- a/src/app/[slug]/q/page.tsx +++ b/src/app/[slug]/q/page.tsx @@ -1,7 +1,7 @@ import { INFINITE_SCROLL_PAGINATION_RESULTS } from "@/config" import { getAuthSession } from "@/lib/auth" import { db } from "@/lib/db" -import MiniCreateQuestion from "@/components/MiniCreateQuestion" +import CreateQuestion from "@/components/CreateQuestion" import QuestionFeed from "@/components/QuestionFeed" import { notFound } from "next/navigation" @@ -41,7 +41,7 @@ const page = async ({ params }: PageProps) => { {subject.name} - Preguntes - + = ({ - session, - subjectId, - questionId, -}) => { +const CreateAnswer: FC = ({ session, subjectId, questionId }) => { return (
@@ -42,4 +38,4 @@ const MiniCreateAnswer: FC = ({ ) } -export default MiniCreateAnswer +export default CreateAnswer diff --git a/src/components/MiniCreateComment.tsx b/src/components/CreateComment.tsx similarity index 94% rename from src/components/MiniCreateComment.tsx rename to src/components/CreateComment.tsx index c9a331d..73f55af 100644 --- a/src/components/MiniCreateComment.tsx +++ b/src/components/CreateComment.tsx @@ -8,12 +8,12 @@ import axios from "axios" import { useMutation } from "@tanstack/react-query" import { toast } from "@/hooks/use-toast" -interface MiniCreateComment { +interface CreateComment { session: Session | null postId: string } -const MiniCreateComment: FC = ({ session, postId }) => { +const CreateComment: FC = ({ session, postId }) => { const [content, setContent] = useState("") // Define the mutation function using useMutation hook @@ -87,4 +87,4 @@ const MiniCreateComment: FC = ({ session, postId }) => { ) } -export default MiniCreateComment +export default CreateComment diff --git a/src/components/MiniCreatePost.tsx b/src/components/CreatePost.tsx similarity index 90% rename from src/components/MiniCreatePost.tsx rename to src/components/CreatePost.tsx index cd9462d..63b8091 100644 --- a/src/components/MiniCreatePost.tsx +++ b/src/components/CreatePost.tsx @@ -7,11 +7,11 @@ import UserAvatar from "./UserAvatar" import Link from "next/link" import { buttonVariants } from "@/components/ui/Button" -interface MiniCreatePostProps { +interface CreatePostProps { session: Session | null } -const MiniCreatePost: FC = ({ session }) => { +const CreatePost: FC = ({ session }) => { const router = useRouter() const pathname = usePathname() @@ -51,4 +51,4 @@ const MiniCreatePost: FC = ({ session }) => { ) } -export default MiniCreatePost +export default CreatePost diff --git a/src/components/MiniCreateQuestion.tsx b/src/components/CreateQuestion.tsx similarity index 86% rename from src/components/MiniCreateQuestion.tsx rename to src/components/CreateQuestion.tsx index 606b6f8..efd583b 100644 --- a/src/components/MiniCreateQuestion.tsx +++ b/src/components/CreateQuestion.tsx @@ -5,14 +5,11 @@ import { FC } from "react" import UserAvatar from "./UserAvatar" import Editor from "@/components/Editor" -interface MiniCreateQuestionProps { +interface CreateQuestionProps { session: Session | null subjectId: string } -const MiniCreateQuestion: FC = ({ - session, - subjectId, -}) => { +const CreateQuestion: FC = ({ session, subjectId }) => { return (
@@ -38,4 +35,4 @@ const MiniCreateQuestion: FC = ({ ) } -export default MiniCreateQuestion +export default CreateQuestion diff --git a/src/components/PostView.tsx b/src/components/PostView.tsx index 0755cad..9da30e5 100644 --- a/src/components/PostView.tsx +++ b/src/components/PostView.tsx @@ -2,7 +2,7 @@ import { FC } from "react" import { ExtendedPost, ExtendedComment } from "@/types/db" import { useSession } from "next-auth/react" -import MiniCreateComment from "@/components/MiniCreateComment" +import CreateComment from "@/components/CreateComment" import CommentFeed from "@/components/CommentFeed" import Post from "@/components/Post" @@ -29,7 +29,7 @@ export const PostView: FC = ({ post, comments }) => { />
- +
= ({ question, answers }) => { />
-