diff --git a/frontend/src/components/HighlightedQuestionCard.tsx b/frontend/src/components/HighlightedQuestionCard.tsx index 3d9214e8..778807cc 100644 --- a/frontend/src/components/HighlightedQuestionCard.tsx +++ b/frontend/src/components/HighlightedQuestionCard.tsx @@ -12,9 +12,9 @@ export const HighlightedQuestionCard: React.FC> = ({ id, title, content, - likeCount, + upvoteCount, difficulty, - commentCount, + answerCount, author, }) => { return ( @@ -29,11 +29,11 @@ export const HighlightedQuestionCard: React.FC> = ({
- {likeCount} votes + {upvoteCount} votes
- {commentCount} answers + {answerCount} answers
{difficulty && (
diff --git a/frontend/src/components/HighlightedQuestionsBox.tsx b/frontend/src/components/HighlightedQuestionsBox.tsx index e0dff917..b5642878 100644 --- a/frontend/src/components/HighlightedQuestionsBox.tsx +++ b/frontend/src/components/HighlightedQuestionsBox.tsx @@ -17,8 +17,8 @@ export const HighlightedQuestionsBox: React.FC<{ id={question.id} title={question.title} content={question.content} - likeCount={question.likeCount} - commentCount={question.commentCount} + upvoteCount={question.upvoteCount} + answerCount={question.answerCount} author={question.author} /> ))} diff --git a/frontend/src/routes/profile.tsx b/frontend/src/routes/profile.tsx index 13ef94f6..14504739 100644 --- a/frontend/src/routes/profile.tsx +++ b/frontend/src/routes/profile.tsx @@ -219,8 +219,8 @@ export default function Profile() { id={question.id} title={question.title} content={question.content ?? ""} - votes={question.likeCount} - answerCount={question.commentCount} + votes={question.upvoteCount} + answerCount={question.answerCount} author={question.author} /> ))} diff --git a/frontend/src/routes/question.test.tsx b/frontend/src/routes/question.test.tsx index eee0e02a..f35209c7 100644 --- a/frontend/src/routes/question.test.tsx +++ b/frontend/src/routes/question.test.tsx @@ -1,4 +1,4 @@ -import { useGetQuestionDetails } from "@/services/api/programmingForumComponents"; +import { useGetQuestionDetails, useSearchTags } from "@/services/api/programmingForumComponents"; import { QuestionDetails } from "@/services/api/programmingForumSchemas"; import useAuthStore from "@/services/auth"; import { testAccessibility } from "@/utils/test-accessibility"; @@ -40,6 +40,7 @@ const mockQuestionData = vi.hoisted( createdAt: "2023-01-01T00:00:00Z", updatedAt: "2023-01-01T00:00:00Z", dislikeCount: 0, + difficulty: "EASY", bookmarked: false, selfVoted: 1, selfDifficultyVote: "EASY", @@ -89,6 +90,16 @@ vi.mock("@/services/api/programmingForumComponents", () => ({ }, }), })), + useSearchTags: vi.fn(() => ({ + data: { data: { items: [{ tagId: "1", name: "Tag1" }, { tagId: "2", name: "Tag2" }] } }, + isLoading: false, + })), + useUpdateQuestion: vi.fn(() => ({ + mutateAsync: vi.fn().mockResolvedValue({ + data: { success: true }, + }), + isPending: false, + })), })); vi.mock("@/services/exercism", () => ({ @@ -116,9 +127,13 @@ describe("QuestionPage", () => { isLoading: false, error: null, }); + (useSearchTags as Mock).mockReturnValue({ + data: { data: { items: [{ tagId: "1", name: "Tag1" }, { tagId: "2", name: "Tag2" }] } }, + isLoading: false, + }); vi.mocked(useAuthStore).mockReturnValue({ - selfProfile: null, - token: null, + selfProfile: { id: mockQuestionData.author.id }, // Ensure the user matches the question's author + token: "mock-token", }); }); diff --git a/frontend/src/routes/question.tsx b/frontend/src/routes/question.tsx index 4b3547c2..554585fb 100644 --- a/frontend/src/routes/question.tsx +++ b/frontend/src/routes/question.tsx @@ -11,16 +11,23 @@ import { FullscreenLoading } from "@/components/FullscreenLoading"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { toast } from "@/components/ui/use-toast"; +import { TagDetails } from "@/services/api/programmingForumSchemas"; + import { useDeleteQuestion as useDeleteQuestionById, useDownvoteQuestion, useGetQuestionDetails, useUpvoteQuestion, + useUpdateQuestion, + useSearchTags, } from "@/services/api/programmingForumComponents"; +import { MultiSelect } from "@/components/multi-select"; +import { Input } from "@/components/ui/input"; +import { Textarea } from "@/components/ui/textarea"; import useAuthStore from "@/services/auth"; import { convertTagToTrack, useExercismSearch } from "@/services/exercism"; import { Flag, MessageSquare, ThumbsDown, ThumbsUp, Trash } from "lucide-react"; -import { useState } from "react"; +import { useEffect,useRef, useState } from "react"; import { Link, useParams } from "react-router-dom"; export default function QuestionPage() { @@ -85,15 +92,72 @@ export default function QuestionPage() { }, ); + const { data: tagSearchData } = useSearchTags( + { queryParams: { q: "", pageSize: 1000 } }, + { enabled: true }, + ); + + useEffect(() => { + if (tagSearchData?.data) { + const tagsData = (tagSearchData.data as { items: TagDetails[] }).items; + setAvailableTags(tagsData); + } + }, [tagSearchData]); + + + + const question = data! || {}; + const [isEditing, setIsEditing] = useState(false); // To toggle edit mode + const [isPreviewMode, setIsPreviewMode] = useState(false); // Preview toggle for description + + const titleRef = useRef(null); + const contentRef = useRef(null); + + const [tags, setTags] = useState(question.tags?.map((tag) => Number(tag.id)) || []); // Tag IDs state + const [availableTags, setAvailableTags] = useState<{ tagId: string; name: string }[]>([]); // Available tags + + const { mutateAsync: updateQuestion, isPending } = useUpdateQuestion({ + onSuccess: () => { + refetch(); + setIsEditing(false); + }, + }); + + + const saveChanges = async () => { + try { + await updateQuestion({ + pathParams: { questionId: question.id }, + body: { + title: titleRef.current?.value || question.title, + content: contentRef.current?.value || question.content, + tags: tags, + }, + }); + toast({ + variant: "default", + title: "Changes saved", + description: "The question has been updated successfully.", + }); + setIsEditing(false); + } catch (err) {console.error( + "Failed to save changes", + err + ); + toast({ + variant: "destructive", + title: "Failed to save changes", + description: "An error occurred while updating the question.", + }); + } + }; + if (isLoading) { return ; } if (error) { return ; } - - const question = data! || {}; - if (!question) { return (
+ {isEditing ? ( + + ) : (

{question.title}

+ )} +
{/* Question Content */} - + {isEditing ? ( +
+
+ + +
+ {isPreviewMode ? ( +
+ +
+ ) : ( +