diff --git a/src/app/components/CourseViewer.tsx b/src/app/components/CourseViewer.tsx index 7e7c7c7..dbd509c 100644 --- a/src/app/components/CourseViewer.tsx +++ b/src/app/components/CourseViewer.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; -import { BookOpen, Calendar, ArrowRight, Video, LayoutGrid } from "lucide-react"; +import { BookOpen, Calendar, ArrowRight, Monitor, LayoutGrid } from "lucide-react"; interface Course { id: string; @@ -122,7 +122,7 @@ export default function CourseViewer() {
- {isActive ?
{isActive && ( diff --git a/src/app/room/classChat/ChatHeader.tsx b/src/app/room/classChat/ChatHeader.tsx index d3afd6c..9fd8ece 100644 --- a/src/app/room/classChat/ChatHeader.tsx +++ b/src/app/room/classChat/ChatHeader.tsx @@ -6,6 +6,9 @@ import { PanelRightClose, Users, GraduationCap, Search, X } from "lucide-react"; import { useMediaQuery } from "@/hooks/use-media-query"; import { SlideUpdateContext } from "../SlideUpdateContext"; import type { Role } from "@/utils/types"; +import Link from "next/link"; +import Image from "next/image"; +import askEasyLogo from "@/app/icon.png"; interface ChatHeaderProps { role: Role; @@ -154,6 +157,13 @@ export default function ChatHeader({ )} )} + + AskEasy logo + )} diff --git a/src/app/room/classChat/index.tsx b/src/app/room/classChat/index.tsx index 5ba99a5..d74c000 100644 --- a/src/app/room/classChat/index.tsx +++ b/src/app/room/classChat/index.tsx @@ -225,6 +225,12 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { ); }; + const onQuestionUnresolved = (payload: { id: string }) => { + setQuestions((prev) => + prev.map((q) => (q.id === payload.id ? { ...q, isResolved: false } : q)) + ); + }; + const onAnswerCreated = (payload: { id: string; questionId: string; @@ -386,6 +392,7 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { socket.on("question:created", onQuestionCreated); socket.on("question:updated", onQuestionUpdated); socket.on("question:resolved", onQuestionResolved); + socket.on("question:unresolved", onQuestionUnresolved); socket.on("question:deleted", onQuestionDeleted); socket.on("answer:created", onAnswerCreated); socket.on("answer:updated", onAnswerUpdated); @@ -400,6 +407,7 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { socket.off("question:created", onQuestionCreated); socket.off("question:updated", onQuestionUpdated); socket.off("question:resolved", onQuestionResolved); + socket.off("question:unresolved", onQuestionUnresolved); socket.off("question:deleted", onQuestionDeleted); socket.off("answer:created", onAnswerCreated); socket.off("answer:updated", onAnswerUpdated); @@ -454,6 +462,15 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { setQuestions((prev) => prev.map((q) => (q.id === questionId ? { ...q, isResolved: true } : q))); }; + const handleUnresolve = (questionId: string) => { + if (!socket) return; + socket.emit("question:unresolve", { questionId }); + // Optimistic update + setQuestions((prev) => + prev.map((q) => (q.id === questionId ? { ...q, isResolved: false } : q)) + ); + }; + const handleSubmitAnswer = (questionId: string, content: string) => { if (!socket) return; socket.emit("answer:create", { questionId, content, isAnonymous: globalIsAnonymous }); @@ -498,19 +515,36 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { // ------------------------------------------------------------------------- const filteredQuestions = (() => { + let list = questions; + + // Search filter const q = searchQuery.trim().toLowerCase(); - if (!q) return questions; - const tokens = q.split(/\s+/).filter(Boolean); - return questions.filter((question) => { - const haystack = [ - question.content, - question.user?.username ?? "", - ...question.replies.map((r) => r.content), - ...question.replies.map((r) => r.user?.username ?? ""), - ] - .join(" ") - .toLowerCase(); - return tokens.every((token) => haystack.includes(token)); + if (q) { + const tokens = q.split(/\s+/).filter(Boolean); + list = list.filter((question) => { + const haystack = [ + question.content, + question.user?.username ?? "", + ...question.replies.map((r) => r.content), + ...question.replies.map((r) => r.user?.username ?? ""), + ] + .join(" ") + .toLowerCase(); + return tokens.every((token) => haystack.includes(token)); + }); + } + + // Priority sort: resolved sink to bottom (on "All" tab), then by upvotes + // desc, then oldest first as tiebreaker (index in original array = time order) + return [...list].sort((a, b) => { + // Resolved questions sink to bottom on the "All" tab + if (commentView === "all") { + if (a.isResolved !== b.isResolved) return a.isResolved ? 1 : -1; + } + // Higher upvotes first + if (b.upvotes !== a.upvotes) return b.upvotes - a.upvotes; + // Oldest first (earlier index in the original array = posted earlier) + return list.indexOf(a) - list.indexOf(b); }); })(); @@ -565,6 +599,7 @@ export default function ClassChat({ chatHistoryRef }: ClassChatProps) { ? () => handleResolve(q.id) : undefined } + onUnresolve={isInstructor ? () => handleUnresolve(q.id) : undefined} canAnswer={canAnswerGlobal || q.user?.id === userId} onSubmitAnswer={(content) => handleSubmitAnswer(q.id, content)} onAnswerUpvote={handleAnswerUpvote} diff --git a/src/app/room/classChat/post/QuestionPost.tsx b/src/app/room/classChat/post/QuestionPost.tsx index 2b383dd..c9633e6 100644 --- a/src/app/room/classChat/post/QuestionPost.tsx +++ b/src/app/room/classChat/post/QuestionPost.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; -import { MessageCircle, CheckCircle2, Trash2, ChevronDown, ChevronUp } from "lucide-react"; +import { MessageCircle, CheckCircle2, Undo2, Trash2, ChevronDown, ChevronUp } from "lucide-react"; import { Question, Post } from "@/utils/types"; import { UpvoteButton, renderUsername } from "./PostUtils"; @@ -111,6 +111,7 @@ export default function QuestionPost({ canAnswer = true, onUpvote, onResolve, + onUnresolve, onDelete, onSubmitAnswer, children, @@ -122,6 +123,7 @@ export default function QuestionPost({ canAnswer?: boolean; onUpvote?: () => void; onResolve?: () => void; + onUnresolve?: () => void; onDelete?: () => void; onSubmitAnswer?: (content: string) => void; children?: React.ReactNode; @@ -159,7 +161,9 @@ export default function QuestionPost({ const showThread = threadState !== "collapsed" && (visibleReplies.length > 0 || isReplying); return ( -
+
{/* Question body */}
{post.content}
@@ -248,6 +252,19 @@ export default function QuestionPost({ )} + {onUnresolve && resolved && ( + + )} + {onDelete && (