feat: add word cloud get function

This commit is contained in:
Mahen 2026-02-15 12:59:41 +07:00
parent 44dbeaf547
commit f5c01c27ba
5 changed files with 77 additions and 61 deletions

View File

@ -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 },
);
}
}

View File

@ -2,12 +2,12 @@ import { cn } from "@/lib/utils";
import { WordCloudItemProps } from "@/src/types";
import { setWordCloud } from "@/src/utils/datas";
const WordCloudItem: React.FC<WordCloudItemProps> = ({
const WordCloudItem = ({
word,
index,
minValue,
maxValue,
}) => {
}: WordCloudItemProps) => {
const { getSize, getColor } = setWordCloud({ minValue, maxValue });
return (

View File

@ -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<WordItem[]>([]);
@ -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<string, KeywordStats> = reviews.reduce(
(acc, review) => {
const sentiment: Sentiment = [
"positive",
"negative",
"neutral",
].includes(review.sentiment)
? review.sentiment
: "neutral";
const keywordMap: Record<string, number> = {};
(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<string, KeywordStats>,
);
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);

View File

@ -201,3 +201,7 @@ export interface AnalysisResults {
export interface ResultProps {
result: AnalysisResults | null;
}
export interface WordCLoud {
topKeywords: string;
}

View File

@ -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";