feat: add word cloud get function
This commit is contained in:
parent
44dbeaf547
commit
f5c01c27ba
|
|
@ -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 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,12 +2,12 @@ import { cn } from "@/lib/utils";
|
||||||
import { WordCloudItemProps } from "@/src/types";
|
import { WordCloudItemProps } from "@/src/types";
|
||||||
import { setWordCloud } from "@/src/utils/datas";
|
import { setWordCloud } from "@/src/utils/datas";
|
||||||
|
|
||||||
const WordCloudItem: React.FC<WordCloudItemProps> = ({
|
const WordCloudItem = ({
|
||||||
word,
|
word,
|
||||||
index,
|
index,
|
||||||
minValue,
|
minValue,
|
||||||
maxValue,
|
maxValue,
|
||||||
}) => {
|
}: WordCloudItemProps) => {
|
||||||
const { getSize, getColor } = setWordCloud({ minValue, maxValue });
|
const { getSize, getColor } = setWordCloud({ minValue, maxValue });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import {
|
import { WordItem } from "@/src/types";
|
||||||
KeywordStats,
|
|
||||||
Review,
|
|
||||||
ReviewResponse,
|
|
||||||
Sentiment,
|
|
||||||
WordItem,
|
|
||||||
} from "@/src/types";
|
|
||||||
import { WORD_LIMIT } from "../utils/const";
|
import { WORD_LIMIT } from "../utils/const";
|
||||||
|
import { Sentiment } from "@prisma/client";
|
||||||
|
|
||||||
export const useWordCloud = () => {
|
export const useWordCloud = () => {
|
||||||
const [words, setWords] = useState<WordItem[]>([]);
|
const [words, setWords] = useState<WordItem[]>([]);
|
||||||
|
|
@ -16,64 +11,29 @@ export const useWordCloud = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchWords = async () => {
|
const fetchWords = async () => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch("/api/review");
|
const res = await fetch("/api/word-cloud");
|
||||||
const result: unknown = await res.json();
|
const json = await res.json();
|
||||||
|
|
||||||
if (
|
if (!json?.success || !Array.isArray(json.data)) {
|
||||||
typeof result !== "object" ||
|
console.error("Invalid response from /api/word-cloud");
|
||||||
result === null ||
|
|
||||||
!("data" in result)
|
|
||||||
) {
|
|
||||||
console.error("Invalid response from /api/review");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reviewsData = (result as ReviewResponse).data || [];
|
const keywords: string[] = json.data;
|
||||||
const reviews: Review[] = reviewsData;
|
|
||||||
|
|
||||||
const keywordMap: Record<string, KeywordStats> = reviews.reduce(
|
const keywordMap: Record<string, number> = {};
|
||||||
(acc, review) => {
|
|
||||||
const sentiment: Sentiment = [
|
|
||||||
"positive",
|
|
||||||
"negative",
|
|
||||||
"neutral",
|
|
||||||
].includes(review.sentiment)
|
|
||||||
? review.sentiment
|
|
||||||
: "neutral";
|
|
||||||
|
|
||||||
(review.keywords || []).forEach((keyword) => {
|
keywords.forEach((keyword) => {
|
||||||
const key = keyword.toLowerCase();
|
const key = keyword.toLowerCase();
|
||||||
|
keywordMap[key] = (keywordMap[key] || 0) + 1;
|
||||||
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>,
|
|
||||||
);
|
|
||||||
|
|
||||||
const wordItems: WordItem[] = Object.entries(keywordMap)
|
const wordItems: WordItem[] = Object.entries(keywordMap)
|
||||||
.map(([text, data]) => {
|
.map(([text, count]) => ({
|
||||||
let sentiment: Sentiment = "neutral";
|
text,
|
||||||
if (
|
value: count,
|
||||||
data.positive >= data.negative &&
|
sentiment: "NEUTRAL" as Sentiment,
|
||||||
data.positive >= data.neutral
|
}))
|
||||||
) {
|
|
||||||
sentiment = "positive";
|
|
||||||
} else if (
|
|
||||||
data.negative >= data.positive &&
|
|
||||||
data.negative >= data.neutral
|
|
||||||
) {
|
|
||||||
sentiment = "negative";
|
|
||||||
}
|
|
||||||
|
|
||||||
return { text, value: data.count, sentiment };
|
|
||||||
})
|
|
||||||
.sort((a, b) => b.value - a.value)
|
.sort((a, b) => b.value - a.value)
|
||||||
.slice(0, WORD_LIMIT);
|
.slice(0, WORD_LIMIT);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -201,3 +201,7 @@ export interface AnalysisResults {
|
||||||
export interface ResultProps {
|
export interface ResultProps {
|
||||||
result: AnalysisResults | null;
|
result: AnalysisResults | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WordCLoud {
|
||||||
|
topKeywords: string;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,9 @@ export const setWordCloud = ({ maxValue, minValue }: WordCloudConfig) => {
|
||||||
|
|
||||||
const getColor = (sentiment: WordItem["sentiment"]) => {
|
const getColor = (sentiment: WordItem["sentiment"]) => {
|
||||||
switch (sentiment) {
|
switch (sentiment) {
|
||||||
case "positive":
|
case "POSITIVE":
|
||||||
return "text-sentiment-positive hover:bg-sentiment-positive-light";
|
return "text-sentiment-positive hover:bg-sentiment-positive-light";
|
||||||
case "negative":
|
case "NEGATIVE":
|
||||||
return "text-sentiment-negative hover:bg-sentiment-negative-light";
|
return "text-sentiment-negative hover:bg-sentiment-negative-light";
|
||||||
default:
|
default:
|
||||||
return "text-sentiment-neutral hover:bg-sentiment-neutral-light";
|
return "text-sentiment-neutral hover:bg-sentiment-neutral-light";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue