Skip to content

Commit

Permalink
fix(qna): fixed answers parsing, everything should work now
Browse files Browse the repository at this point in the history
  • Loading branch information
WomB0ComB0 committed Sep 2, 2024
1 parent 29e1235 commit 7997410
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 119 deletions.
53 changes: 52 additions & 1 deletion src/app/(dashboard)/dashboard/_components/cards/FurtherInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,57 @@ const FurtherInfoCard = memo(function FurtherInfoCard({ furtherInfo }: Props) {
);
}

const stopWords = [
'the',
'and',
'of',
'to',
'a',
'in',
'that',
'is',
'with',
'as',
'for',
'on',
'by',
'an',
'be',
'this',
'which',
'or',
'at',
'from',
'it',
'are',
'was',
'were',
'who',
'what',
'when',
'where',
'why',
'how',
];

function extractMainPoint(title: string): string {
return title
.split(' ')
.filter((word) => !stopWords.includes(word.toLowerCase()))
.join(' ');
}

const uniqueTitles = [...new Set(furtherInfo.map((info) => info.title))];
const mostCommonTitles = uniqueTitles.reduce((acc: Record<string, number>, title) => {
acc[title] = (acc[title] || 0) + 1;
return acc;
}, {});
const mostCommonTitle = Object.keys(mostCommonTitles).reduce((a, b) =>
mostCommonTitles[a] > mostCommonTitles[b] ? a : b,
);

const mainPoint = extractMainPoint(mostCommonTitle);

return (
<Card className="w-full h-[calc(100vh-200px)] bg-background">
<CardHeader className="bg-muted sr-only">
Expand Down Expand Up @@ -80,7 +131,7 @@ const FurtherInfoCard = memo(function FurtherInfoCard({ furtherInfo }: Props) {
Resource {index + 1}
</Badge>
<Badge variant="outline" className="text-xs">
Civic Engagement
{mainPoint}
</Badge>
</div>
<Button
Expand Down
184 changes: 119 additions & 65 deletions src/app/(dashboard)/dashboard/_components/cards/QuestionAndAnswer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Separator } from '@/components/ui/separator';
import { CheckCircle, HelpCircle, XCircle } from 'lucide-react';
import React, { memo, useState } from 'react';
import { CheckCircle, Eye, HelpCircle, XCircle } from 'lucide-react';
import React, { memo, useState, useEffect } from 'react';
import { toast } from 'sonner';

type Question = {
id: string;
id: number;
question: string;
options: string[];
answer: string[];
Expand All @@ -21,12 +21,48 @@ type Props = {
};

const QuestionAndAnswer = memo(function QuestionAndAnswer({ questions }: Props) {
const [answeredQuestions, setAnsweredQuestions] = useState<Record<string, number[]>>({});

const handleOptionClick = (questionId: string, optionIndex: number) => {
const [processedQuestions, setProcessedQuestions] = useState<Question[]>([]);
const [answeredQuestions, setAnsweredQuestions] = useState<Record<number, number[]>>({});
const [revealedAnswers, setRevealedAnswers] = useState<Record<number, boolean>>({});

useEffect(() => {
const mergeAnswersWithQuestions = (questions: Question[]): Question[] => {
const questionMap: Record<number, Question> = {};
const answerMap: Record<number, string[]> = {};

questions.forEach((question) => {
if (question.options.length > 0) {
questionMap[question.id] = { ...question, answer: [] };
} else {
const answerMatch = question.question.match(/^([a-d])\) (.+)/);
if (answerMatch) {
const [, letter, answerText] = answerMatch;
const questionId = question.id - 10;
if (!answerMap[questionId]) {
answerMap[questionId] = [];
}
answerMap[questionId].push(answerText);
}
}
});

Object.entries(answerMap).forEach(([questionId, answers]) => {
const id = parseInt(questionId, 10);
if (questionMap[id]) {
questionMap[id].answer = answers;
}
});

return Object.values(questionMap);
};

setProcessedQuestions(mergeAnswersWithQuestions(questions));
}, [questions]);

const handleOptionClick = (questionId: number, optionIndex: number) => {
setAnsweredQuestions((prev) => {
const currentAnswers = prev[questionId] || [];
const question = questions.find((q) => q.id === questionId);
const question = processedQuestions.find((q) => q.id === questionId);

if (question?.multipleAnswers) {
return {
Expand All @@ -41,8 +77,8 @@ const QuestionAndAnswer = memo(function QuestionAndAnswer({ questions }: Props)
});
};

const handleSubmit = (questionId: string) => {
const question = questions.find((q) => q.id === questionId);
const handleSubmit = (questionId: number) => {
const question = processedQuestions.find((q) => q.id === questionId);
const selectedAnswers = answeredQuestions[questionId] || [];

if (selectedAnswers.length === 0) {
Expand All @@ -62,13 +98,16 @@ const QuestionAndAnswer = memo(function QuestionAndAnswer({ questions }: Props)
}
};

const resetQuestion = (questionId: string) => {
const resetQuestion = (questionId: number) => {
setAnsweredQuestions((prev) => ({ ...prev, [questionId]: [] }));
setRevealedAnswers((prev) => ({ ...prev, [questionId]: false }));
};

const revealAnswer = (questionId: number) => {
setRevealedAnswers((prev) => ({ ...prev, [questionId]: true }));
};

// Add this check at the beginning of the component
if (!Array.isArray(questions)) {
console.error('Questions prop is not an array:', questions);
if (!Array.isArray(questions) || processedQuestions.length === 0) {
return (
<Card className="w-full h-[calc(100vh-200px)] bg-background">
<CardContent className="p-6">
Expand All @@ -88,60 +127,75 @@ const QuestionAndAnswer = memo(function QuestionAndAnswer({ questions }: Props)
</CardHeader>
<CardContent className="p-0">
<ScrollArea className="h-[calc(100vh-300px)]">
{questions.length > 0 ? (
questions.map((question, index) => (
<div key={question.id} className="p-6 border-b border-border last:border-b-0">
<h3 className="text-lg font-semibold mb-4 text-primary">
Question {index + 1}: {question.question}
</h3>
<div className="space-y-2 mb-4">
{question.options.map((option, optionIndex) => {
const isSelected = (answeredQuestions[question.id] || []).includes(optionIndex);
const isCorrect = question.answer.includes(option);
let buttonVariant: 'default' | 'outline' | 'secondary' = 'outline';
let icon = null;

if (isSelected) {
buttonVariant = 'secondary';
icon = isCorrect ? (
<CheckCircle className="w-4 h-4 ml-2 text-green-500" />
) : (
<XCircle className="w-4 h-4 ml-2 text-red-500" />
);
}

return (
<Button
key={optionIndex}
onClick={() => handleOptionClick(question.id, optionIndex)}
variant={buttonVariant}
className="w-full justify-between"
>
<span>{option}</span>
{icon}
</Button>
{processedQuestions.map((question, index) => (
<div key={question.id} className="p-6 border-b border-border last:border-b-0">
<h3 className="text-lg font-semibold mb-4 text-primary">
Question {index + 1}: {question.question}
</h3>
<div className="space-y-2 mb-4">
{question.options.map((option, optionIndex) => {
const isSelected = (answeredQuestions[question.id] || []).includes(optionIndex);
const isCorrect = question.answer.includes(option);
const isRevealed = revealedAnswers[question.id];
let buttonVariant: 'default' | 'outline' | 'secondary' = 'outline';
let icon = null;

if (isSelected || (isRevealed && isCorrect)) {
buttonVariant = 'secondary';
icon = isCorrect ? (
<CheckCircle className="w-4 h-4 ml-2 text-green-500" />
) : (
<XCircle className="w-4 h-4 ml-2 text-red-500" />
);
})}
</div>
<div className="flex space-x-2">
<Button
onClick={() => handleSubmit(question.id)}
className="text-white bg-navy hover:bg-navy/90"
>
Submit
</Button>
<Button variant="outline" onClick={() => resetQuestion(question.id)}>
Reset
</Button>
</div>
{index < questions.length - 1 && <Separator className="my-6" />}
}

return (
<Button
key={optionIndex}
onClick={() => handleOptionClick(question.id, optionIndex)}
variant={buttonVariant}
className={`w-full justify-between ${isRevealed && isCorrect ? 'bg-green-100 hover:bg-green-200' : ''
}`}
>
<span>{option}</span>
{icon}
</Button>
);
})}
</div>
<div className="flex space-x-2">
<Button
onClick={() => handleSubmit(question.id)}
className="text-white bg-navy hover:bg-navy/90"
>
Submit
</Button>
<Button variant="outline" onClick={() => resetQuestion(question.id)}>
Reset
</Button>
<Button
variant="outline"
onClick={() => revealAnswer(question.id)}
className="bg-gray-100 hover:bg-gray-200"
disabled={revealedAnswers[question.id]}
>
<Eye className="w-4 h-4 mr-2" />
Reveal Answer
</Button>
</div>
))
) : (
<div className="flex items-center justify-center h-full">
<p className="text-center text-muted-foreground">No questions found</p>
{revealedAnswers[question.id] && (
<div className="mt-4 p-3 bg-blue-100 rounded-md">
<p className="font-semibold text-blue-800">Correct Answer(s):</p>
<ul className="list-disc list-inside text-blue-700">
{question.answer.map((ans, i) => (
<li key={i}>{ans}</li>
))}
</ul>
</div>
)}
{index < processedQuestions.length - 1 && <Separator className="my-6" />}
</div>
)}
))}
</ScrollArea>
</CardContent>
</Card>
Expand All @@ -150,4 +204,4 @@ const QuestionAndAnswer = memo(function QuestionAndAnswer({ questions }: Props)

QuestionAndAnswer.displayName = 'QuestionAndAnswer';

export default QuestionAndAnswer;
export default QuestionAndAnswer;
31 changes: 25 additions & 6 deletions src/app/(dashboard)/dashboard/_components/cards/VideoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import he from 'he';
import { ExternalLink, Eye, ThumbsUp } from 'lucide-react';
import Link from 'next/link';
import type React from 'react';
import { memo } from 'react';
import { memo, useState } from 'react';
import type { VideoCardProps, VideoItem } from '../interfaces';

const VideoCard: React.FC<VideoCardProps> = memo(({ videos }) => {
Expand All @@ -23,6 +23,17 @@ const VideoCard: React.FC<VideoCardProps> = memo(({ videos }) => {

const validVideos = videos?.filter(isValidVideo) || [];

const [likedVideos, setLikedVideos] = useState<{ [key: string]: boolean }>({});
const [viewedVideos, setViewedVideos] = useState<{ [key: string]: boolean }>({});

const handleLike = (videoId: string) => {
setLikedVideos((prev) => ({ ...prev, [videoId]: !prev[videoId] }));
};

const handleView = (videoId: string) => {
setViewedVideos((prev) => ({ ...prev, [videoId]: true }));
};

return (
<Card className="w-full h-[calc(100vh-200px)] bg-background">
<CardHeader className="bg-muted sr-only">
Expand All @@ -35,6 +46,7 @@ const VideoCard: React.FC<VideoCardProps> = memo(({ videos }) => {
<div
key={video.id.videoId}
className="flex flex-col md:flex-row p-4 border-b border-border hover:bg-accent/5 transition-colors"
onMouseEnter={() => handleView(video.id.videoId)}
>
<div className="md:w-1/3 mb-4 md:mb-0 md:pr-4">
<div className="relative aspect-video rounded-lg overflow-hidden">
Expand All @@ -60,19 +72,26 @@ const VideoCard: React.FC<VideoCardProps> = memo(({ videos }) => {
{he.decode(video.snippet.description)}
</p>
<div className="flex flex-wrap gap-2 mb-4">
<Badge variant="secondary" className="text-xs">
<Badge
variant="secondary"
className={`text-xs text-white ${viewedVideos[video.id.videoId] ? 'bg-navy' : ''}`}
>
<Eye className="w-3 h-3 mr-1" />
{Math.floor(Math.random() * 1000000)} views
{viewedVideos[video.id.videoId] ? 'Not viewed' : 'Viewed'}
</Badge>
<Badge variant="secondary" className="text-xs">
<Badge
variant="secondary"
className={`text-xs cursor-pointer text-navy ${likedVideos[video.id.videoId] ? 'bg-navy' : ''}`}
onClick={() => handleLike(video.id.videoId)}
>
<ThumbsUp className="w-3 h-3 mr-1" />
{Math.floor(Math.random() * 50000)} likes
{likedVideos[video.id.videoId] ? 'Liked' : 'Not liked'}
</Badge>
</div>
<Button
variant="outline"
size="sm"
className="text-white bg-navy hover:bg-navy/90"
className="text-white bg-navy hover:bg-navy/90 hover:text-white"
asChild
>
<Link
Expand Down
Loading

0 comments on commit 7997410

Please sign in to comment.