diff --git a/src/app/api/product/route.ts b/src/app/api/product/route.ts index 422f3e3..d292337 100644 --- a/src/app/api/product/route.ts +++ b/src/app/api/product/route.ts @@ -1,41 +1,40 @@ import prisma from "@/lib/prisma"; -import { Prisma } from "@prisma/client"; import { NextResponse } from "next/server"; export const dynamic = "force-dynamic"; -export async function POST(_request: Request) { - try { - const products = [ - { name: "ZenBook 14", brand: "ASUS" }, - { name: "Swift 3", brand: "Acer" }, - { name: "Surface Laptop 5", brand: "Microsoft" }, - ]; +// export async function POST(_request: Request) { +// try { +// const products = [ +// { name: "ZenBook 14", brand: "ASUS" }, +// { name: "Swift 3", brand: "Acer" }, +// { name: "Surface Laptop 5", brand: "Microsoft" }, +// ]; - const result = await prisma.product.createMany({ - data: products, - }); +// const result = await prisma.product.createMany({ +// data: products, +// }); - return NextResponse.json( - { - message: "Booking successful", - data: result, - }, - { status: 201 }, - ); - } catch (error: unknown) { - console.error("Create product error:", error); +// return NextResponse.json( +// { +// message: "Booking successful", +// data: result, +// }, +// { status: 201 }, +// ); +// } catch (error: unknown) { +// console.error("Create product error:", error); - if (error instanceof Prisma.PrismaClientKnownRequestError) { - return NextResponse.json({ error: error.message }, { status: 400 }); - } +// if (error instanceof Prisma.PrismaClientKnownRequestError) { +// return NextResponse.json({ error: error.message }, { status: 400 }); +// } - return NextResponse.json( - { error: "Internal Server Error" }, - { status: 500 }, - ); - } -} +// return NextResponse.json( +// { error: "Internal Server Error" }, +// { status: 500 }, +// ); +// } +// } export async function GET() { try { diff --git a/src/app/api/review/route.ts b/src/app/api/review/route.ts index 7b4ceef..4ea3719 100644 --- a/src/app/api/review/route.ts +++ b/src/app/api/review/route.ts @@ -13,7 +13,7 @@ export async function POST(_request: Request) { content: "Laptop ini sangat ringan dan performanya cepat untuk kerja harian.", keywords: ["ringan", "cepat", "kerja"], - sentiment: Sentiment.positive, + sentiment: Sentiment.POSITIVE, confidenceScore: 0.92, }, { @@ -21,7 +21,7 @@ export async function POST(_request: Request) { modelId: 1, content: "Baterainya awet, tapi harganya cukup mahal.", keywords: ["baterai", "awet", "mahal"], - sentiment: Sentiment.neutral, + sentiment: Sentiment.NEUTRAL, confidenceScore: 0.75, }, { @@ -29,7 +29,7 @@ export async function POST(_request: Request) { modelId: 1, content: "Performa kurang stabil dan sering panas.", keywords: ["performa", "panas", "stabil"], - sentiment: Sentiment.negative, + sentiment: Sentiment.NEGATIVE, confidenceScore: 0.88, }, ]; diff --git a/src/app/dashboard/lib/actions.ts b/src/app/dashboard/lib/actions.ts index 69dc054..129a59b 100644 --- a/src/app/dashboard/lib/actions.ts +++ b/src/app/dashboard/lib/actions.ts @@ -1,6 +1,8 @@ "use server"; import prisma from "@/lib/prisma"; +import { getServerSession } from "next-auth"; import { notFound } from "next/navigation"; +import { authOptions } from "../../api/auth/[...nextauth]/route"; export const getClassificationReport = async () => { try { @@ -28,3 +30,80 @@ export const getClassificationReport = async () => { throw error; } }; + +export const getTotalBrandAnalysis = async () => { + try { + const session = await getServerSession(authOptions); + + if (!session?.user?.email) { + console.log("User belum login"); + return null; + } + + const userAnalyses = await prisma.analysis.findMany({ + where: { + user: { + email: session.user.email, + }, + }, + include: { + product: { + select: { + id: true, + brand: true, + _count: { + select: { + reviews: true, + }, + }, + }, + }, + }, + }); + + const countedProductIds = new Set(); + + const brandCounts = userAnalyses.reduce( + (acc: Record, analysis) => { + const productId = analysis.product?.id; + const rawBrand = analysis.product?.brand || "Unknown"; + const reviewCount = analysis.product?._count?.reviews || 0; + + if (productId && countedProductIds.has(productId)) { + return acc; + } + if (productId) { + countedProductIds.add(productId); + } + + const formattedBrand = rawBrand + .trim() + .toLowerCase() + .replace(/\b\w/g, (char) => char.toUpperCase()); + + if (!acc[formattedBrand]) { + acc[formattedBrand] = 0; + } + + acc[formattedBrand] += reviewCount; + + return acc; + }, + {}, + ); + + const formattedBrands = Object.entries(brandCounts).map( + ([name, count]) => ({ + name, + count, + }), + ); + + formattedBrands.sort((a, b) => b.count - a.count); + + return formattedBrands; + } catch (error) { + console.error("Gagal mengambil data review:", error); + return []; + } +}; diff --git a/src/components/dashboards/BrandFilter.tsx b/src/components/dashboards/BrandFilter.tsx index 0287f05..a6df500 100644 --- a/src/components/dashboards/BrandFilter.tsx +++ b/src/components/dashboards/BrandFilter.tsx @@ -1,11 +1,23 @@ +"use client"; + import { cn } from "@/lib/utils"; import { BrandFilterProps } from "@/src/types"; +import { useBrandFilter } from "@/src/hooks/useBrandFilter"; export function BrandFilter({ - brands, selectedBrand, onSelect, -}: BrandFilterProps) { +}: Omit) { + const { brands, isLoading, totalCount } = useBrandFilter(); + + if (isLoading) { + return ( +
+ Memuat brand... +
+ ); + } + return (
+ {brands.map((brand) => (
diff --git a/src/components/dashboards/SentimentBadge.tsx b/src/components/dashboards/SentimentBadge.tsx index 23aaf97..a1da21d 100644 --- a/src/components/dashboards/SentimentBadge.tsx +++ b/src/components/dashboards/SentimentBadge.tsx @@ -4,15 +4,15 @@ import { cn } from "@/lib/utils"; const getSentimentBadge = (sentiment: Review["sentiment"]) => { const styles: Record = { - positive: "sentiment-positive", - negative: "sentiment-negative", - neutral: "sentiment-neutral", + POSITIVE: "sentiment-positive", + NEGATIVE: "sentiment-negative", + NEUTRAL: "sentiment-neutral", }; const labels: Record = { - positive: "Positif", - negative: "Negatif", - neutral: "Netral", + POSITIVE: "Positif", + NEGATIVE: "Negatif", + NEUTRAL: "Netral", }; return ( diff --git a/src/hooks/useAnalyzeText.ts b/src/hooks/useAnalyzeText.ts index fb63726..9396363 100644 --- a/src/hooks/useAnalyzeText.ts +++ b/src/hooks/useAnalyzeText.ts @@ -1,7 +1,10 @@ import { useState } from "react"; +import { useSession } from "next-auth/react"; import { AnalysisResults } from "../types"; export const useAnalyseText = () => { + const { data: session } = useSession(); + const [url1, setUrl1] = useState(""); const [url2, setUrl2] = useState(""); const [url3, setUrl3] = useState(""); @@ -12,6 +15,13 @@ export const useAnalyseText = () => { const [showField, setShowField] = useState(false); const handleAnalyze = async () => { + if (!session?.user?.email) { + alert( + "Anda harus login terlebih dahulu untuk menyimpan riwayat analisis.", + ); + return; + } + setLoading(true); setResult(null); @@ -53,6 +63,7 @@ export const useAnalyseText = () => { })); const payload = { + user_email: session.user.email, profession: profession, candidates: candidates, }; diff --git a/src/hooks/useBrandFilter.ts b/src/hooks/useBrandFilter.ts new file mode 100644 index 0000000..143a9b5 --- /dev/null +++ b/src/hooks/useBrandFilter.ts @@ -0,0 +1,28 @@ +import { useEffect, useState } from "react"; +import { getTotalBrandAnalysis } from "../app/dashboard/lib/actions"; + +export const useBrandFilter = () => { + const [brands, setBrands] = useState<{ name: string; count: number }[]>([]); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchBrands = async () => { + try { + const data = await getTotalBrandAnalysis(); + if (data) { + setBrands(data); + } + } catch (error) { + console.error("Gagal memuat filter brand", error); + } finally { + setIsLoading(false); + } + }; + + fetchBrands(); + }, []); + + const totalCount = brands.reduce((sum, b) => sum + (b?.count || 0), 0); + + return { brands, isLoading, totalCount }; +}; diff --git a/src/types/index.ts b/src/types/index.ts index 13e5de1..8412b71 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,5 +1,5 @@ import { LucideIcon } from "lucide-react"; -import Brand from "@prisma/client"; +import Brand, { Sentiment } from "@prisma/client"; export interface ModelDB { modelName: string; @@ -27,7 +27,7 @@ interface Brand { } export interface BrandFilterProps { - brands: Brand[]; + // brands: Brand[]; selectedBrand: string | null; onSelect: (brand: string | null) => void; } @@ -50,7 +50,7 @@ export interface ReviewTableProps { } export interface AnalysisResult { - sentiment: "positif" | "negatif" | "netral"; + sentiment: Sentiment; confidence: number; keywords: string[]; } @@ -142,8 +142,6 @@ export interface ApiResponse { data: ReviewItem[]; } -export type Sentiment = "positive" | "negative" | "neutral"; - export type WordItem = { text: string; value: number;