diff --git a/src/app/api/word-cloud/route.ts b/src/app/api/word-cloud/route.ts new file mode 100644 index 0000000..35fa16c --- /dev/null +++ b/src/app/api/word-cloud/route.ts @@ -0,0 +1,52 @@ +import prisma from "@/lib/prisma"; +import { NextResponse } from "next/server"; +import { getServerSession } from "next-auth"; +import { authOptions } from "../auth/[...nextauth]/route"; + +export async function GET() { + try { + const session = await getServerSession(authOptions); + + if (!session?.user?.email) { + return NextResponse.json( + { success: false, message: "Unauthorized" }, + { status: 401 }, + ); + } + + const user = await prisma.user.findUnique({ + where: { email: session.user.email }, + select: { id: true }, + }); + + if (!user) { + return NextResponse.json( + { success: false, message: "User not found" }, + { status: 404 }, + ); + } + + const analyses = await prisma.analysis.findMany({ + where: { userId: user.id }, + select: { topKeywords: true }, + }); + + const allKeywords: string[] = analyses.flatMap((a) => { + if (Array.isArray(a.topKeywords)) { + return a.topKeywords.filter((k): k is string => typeof k === "string"); + } + return []; + }); + + return NextResponse.json( + { success: true, data: allKeywords }, + { status: 200 }, + ); + } catch (error) { + console.error("Failed to fetch word cloud data:", error); + return NextResponse.json( + { success: false, message: "Internal Server Error" }, + { status: 500 }, + ); + } +} diff --git a/src/components/dashboards/WordCloudItem.tsx b/src/components/dashboards/WordCloudItem.tsx index 7fa139a..0a4ecd9 100644 --- a/src/components/dashboards/WordCloudItem.tsx +++ b/src/components/dashboards/WordCloudItem.tsx @@ -2,12 +2,12 @@ import { cn } from "@/lib/utils"; import { WordCloudItemProps } from "@/src/types"; import { setWordCloud } from "@/src/utils/datas"; -const WordCloudItem: React.FC = ({ +const WordCloudItem = ({ word, index, minValue, maxValue, -}) => { +}: WordCloudItemProps) => { const { getSize, getColor } = setWordCloud({ minValue, maxValue }); return ( diff --git a/src/hooks/useWordCloud.ts b/src/hooks/useWordCloud.ts index 0df8787..1326fa8 100644 --- a/src/hooks/useWordCloud.ts +++ b/src/hooks/useWordCloud.ts @@ -1,12 +1,7 @@ import { useEffect, useState } from "react"; -import { - KeywordStats, - Review, - ReviewResponse, - Sentiment, - WordItem, -} from "@/src/types"; +import { WordItem } from "@/src/types"; import { WORD_LIMIT } from "../utils/const"; +import { Sentiment } from "@prisma/client"; export const useWordCloud = () => { const [words, setWords] = useState([]); @@ -16,64 +11,29 @@ export const useWordCloud = () => { useEffect(() => { const fetchWords = async () => { try { - const res = await fetch("/api/review"); - const result: unknown = await res.json(); + const res = await fetch("/api/word-cloud"); + const json = await res.json(); - if ( - typeof result !== "object" || - result === null || - !("data" in result) - ) { - console.error("Invalid response from /api/review"); + if (!json?.success || !Array.isArray(json.data)) { + console.error("Invalid response from /api/word-cloud"); return; } - const reviewsData = (result as ReviewResponse).data || []; - const reviews: Review[] = reviewsData; + const keywords: string[] = json.data; - const keywordMap: Record = reviews.reduce( - (acc, review) => { - const sentiment: Sentiment = [ - "positive", - "negative", - "neutral", - ].includes(review.sentiment) - ? review.sentiment - : "neutral"; + const keywordMap: Record = {}; - (review.keywords || []).forEach((keyword) => { - const key = keyword.toLowerCase(); - - if (!acc[key]) { - acc[key] = { count: 0, positive: 0, negative: 0, neutral: 0 }; - } - - acc[key].count += 1; - acc[key][sentiment] += 1; - }); - - return acc; - }, - {} as Record, - ); + keywords.forEach((keyword) => { + const key = keyword.toLowerCase(); + keywordMap[key] = (keywordMap[key] || 0) + 1; + }); const wordItems: WordItem[] = Object.entries(keywordMap) - .map(([text, data]) => { - let sentiment: Sentiment = "neutral"; - if ( - data.positive >= data.negative && - data.positive >= data.neutral - ) { - sentiment = "positive"; - } else if ( - data.negative >= data.positive && - data.negative >= data.neutral - ) { - sentiment = "negative"; - } - - return { text, value: data.count, sentiment }; - }) + .map(([text, count]) => ({ + text, + value: count, + sentiment: "NEUTRAL" as Sentiment, + })) .sort((a, b) => b.value - a.value) .slice(0, WORD_LIMIT); diff --git a/src/types/index.ts b/src/types/index.ts index 8412b71..e0dfb6f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -201,3 +201,7 @@ export interface AnalysisResults { export interface ResultProps { result: AnalysisResults | null; } + +export interface WordCLoud { + topKeywords: string; +} diff --git a/src/utils/datas.ts b/src/utils/datas.ts index 1693841..50f93ee 100644 --- a/src/utils/datas.ts +++ b/src/utils/datas.ts @@ -47,9 +47,9 @@ export const setWordCloud = ({ maxValue, minValue }: WordCloudConfig) => { const getColor = (sentiment: WordItem["sentiment"]) => { switch (sentiment) { - case "positive": + case "POSITIVE": return "text-sentiment-positive hover:bg-sentiment-positive-light"; - case "negative": + case "NEGATIVE": return "text-sentiment-negative hover:bg-sentiment-negative-light"; default: return "text-sentiment-neutral hover:bg-sentiment-neutral-light";