From 981230041ae82718b5b7acbaf14cfb8635b1c667 Mon Sep 17 00:00:00 2001
From: Mahen
Date: Sun, 15 Feb 2026 12:01:57 +0700
Subject: [PATCH] fix: update personalize sentiment stats data
---
.../migration.sql | 145 ------------------
.../migration.sql | 71 ---------
.../migration.sql | 17 --
.../migration.sql | 38 -----
.../migration.sql | 12 --
.../migration.sql | 16 --
.../migration.sql | 8 -
prisma/migrations/migration_lock.toml | 3 -
prisma/schema.prisma | 6 +
src/app/api/review/sentiment-stats/route.ts | 45 +++++-
src/components/dashboards/DashboardClient.tsx | 1 -
src/hooks/useDashboard.ts | 20 ++-
12 files changed, 57 insertions(+), 325 deletions(-)
delete mode 100644 prisma/migrations/20260207024809_add_nextauth_model_config/migration.sql
delete mode 100644 prisma/migrations/20260212102528_update_model_cardinality/migration.sql
delete mode 100644 prisma/migrations/20260212110614_update_top_keyword_rows/migration.sql
delete mode 100644 prisma/migrations/20260213040203_update_user_preference_table/migration.sql
delete mode 100644 prisma/migrations/20260213123206_update_user_table_rows/migration.sql
delete mode 100644 prisma/migrations/20260213123708_delete_user_role_enum_def/migration.sql
delete mode 100644 prisma/migrations/20260214025107_delete_model_version_rows/migration.sql
delete mode 100644 prisma/migrations/migration_lock.toml
diff --git a/prisma/migrations/20260207024809_add_nextauth_model_config/migration.sql b/prisma/migrations/20260207024809_add_nextauth_model_config/migration.sql
deleted file mode 100644
index 763cea4..0000000
--- a/prisma/migrations/20260207024809_add_nextauth_model_config/migration.sql
+++ /dev/null
@@ -1,145 +0,0 @@
--- CreateEnum
-CREATE TYPE "UserGender" AS ENUM ('male', 'female', 'other');
-
--- CreateEnum
-CREATE TYPE "Sentiment" AS ENUM ('positive', 'negative', 'neutral');
-
--- CreateTable
-CREATE TABLE "Account" (
- "id" SERIAL NOT NULL,
- "userId" INTEGER NOT NULL,
- "type" TEXT NOT NULL,
- "provider" TEXT NOT NULL,
- "providerAccountId" TEXT NOT NULL,
- "refresh_token" TEXT,
- "access_token" TEXT,
- "expires_at" INTEGER,
- "token_type" TEXT,
- "scope" TEXT,
- "id_token" TEXT,
- "session_state" TEXT,
-
- CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
-);
-
--- CreateTable
-CREATE TABLE "Session" (
- "id" SERIAL NOT NULL,
- "sessionToken" TEXT NOT NULL,
- "userId" INTEGER NOT NULL,
- "expires" TIMESTAMP(3) NOT NULL,
-
- CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
-);
-
--- CreateTable
-CREATE TABLE "VerificationToken" (
- "identifier" TEXT NOT NULL,
- "token" TEXT NOT NULL,
- "expires" TIMESTAMP(3) NOT NULL
-);
-
--- CreateTable
-CREATE TABLE "User" (
- "id" SERIAL NOT NULL,
- "name" TEXT,
- "email" TEXT,
- "emailVerified" TIMESTAMP(3),
- "image" TEXT,
- "gender" "UserGender",
- "productReference" TEXT,
- "password" TEXT,
- "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "updatedAt" TIMESTAMP(3) NOT NULL,
-
- CONSTRAINT "User_pkey" PRIMARY KEY ("id")
-);
-
--- CreateTable
-CREATE TABLE "Analysis" (
- "id" SERIAL NOT NULL,
- "userId" INTEGER NOT NULL,
- "reviewId" INTEGER NOT NULL,
- "productId" INTEGER NOT NULL,
- "modelId" INTEGER NOT NULL,
- "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "updatedAt" TIMESTAMP(3) NOT NULL,
-
- CONSTRAINT "Analysis_pkey" PRIMARY KEY ("id")
-);
-
--- CreateTable
-CREATE TABLE "Product" (
- "id" SERIAL NOT NULL,
- "name" TEXT NOT NULL,
- "brand" TEXT NOT NULL,
- "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "updatedAt" TIMESTAMP(3) NOT NULL,
-
- CONSTRAINT "Product_pkey" PRIMARY KEY ("id")
-);
-
--- CreateTable
-CREATE TABLE "Review" (
- "id" SERIAL NOT NULL,
- "productId" INTEGER NOT NULL,
- "content" TEXT NOT NULL,
- "keywords" TEXT[],
- "sentiment" "Sentiment" NOT NULL,
- "confidenceScore" DOUBLE PRECISION NOT NULL,
- "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "updatedAt" TIMESTAMP(3) NOT NULL,
-
- CONSTRAINT "Review_pkey" PRIMARY KEY ("id")
-);
-
--- CreateTable
-CREATE TABLE "Model" (
- "id" SERIAL NOT NULL,
- "modelName" TEXT NOT NULL,
- "description" TEXT NOT NULL,
- "accuracy" DOUBLE PRECISION NOT NULL,
- "macroF1" DOUBLE PRECISION NOT NULL,
- "f1Negative" DOUBLE PRECISION NOT NULL,
- "f1Neutral" DOUBLE PRECISION NOT NULL,
- "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "updatedAt" TIMESTAMP(3) NOT NULL,
-
- CONSTRAINT "Model_pkey" PRIMARY KEY ("id")
-);
-
--- CreateIndex
-CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
-
--- CreateIndex
-CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
-
--- CreateIndex
-CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
-
--- CreateIndex
-CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
-
--- CreateIndex
-CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-
--- AddForeignKey
-ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-
--- AddForeignKey
-ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-
--- AddForeignKey
-ALTER TABLE "Analysis" ADD CONSTRAINT "Analysis_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-
--- AddForeignKey
-ALTER TABLE "Analysis" ADD CONSTRAINT "Analysis_reviewId_fkey" FOREIGN KEY ("reviewId") REFERENCES "Review"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-
--- AddForeignKey
-ALTER TABLE "Analysis" ADD CONSTRAINT "Analysis_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-
--- AddForeignKey
-ALTER TABLE "Analysis" ADD CONSTRAINT "Analysis_modelId_fkey" FOREIGN KEY ("modelId") REFERENCES "Model"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-
--- AddForeignKey
-ALTER TABLE "Review" ADD CONSTRAINT "Review_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
diff --git a/prisma/migrations/20260212102528_update_model_cardinality/migration.sql b/prisma/migrations/20260212102528_update_model_cardinality/migration.sql
deleted file mode 100644
index 315fdf9..0000000
--- a/prisma/migrations/20260212102528_update_model_cardinality/migration.sql
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- Warnings:
-
- - The values [positive,negative,neutral] on the enum `Sentiment` will be removed. If these variants are still used in the database, this will fail.
- - You are about to drop the column `reviewId` on the `Analysis` table. All the data in the column will be lost.
- - You are about to drop the column `updatedAt` on the `Analysis` table. All the data in the column will be lost.
- - You are about to drop the column `productReference` on the `User` table. All the data in the column will be lost.
- - A unique constraint covering the columns `[url]` on the table `Product` will be added. If there are existing duplicate values, this will fail.
- - Added the required column `compatibilityScore` to the `Analysis` table without a default value. This is not possible if the table is not empty.
- - Added the required column `generalScore` to the `Analysis` table without a default value. This is not possible if the table is not empty.
- - Added the required column `targetProfession` to the `Analysis` table without a default value. This is not possible if the table is not empty.
- - Added the required column `verdict` to the `Analysis` table without a default value. This is not possible if the table is not empty.
- - Added the required column `url` to the `Product` table without a default value. This is not possible if the table is not empty.
-
-*/
--- CreateEnum
-CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN');
-
--- AlterEnum
-BEGIN;
-CREATE TYPE "Sentiment_new" AS ENUM ('POSITIVE', 'NEGATIVE', 'NEUTRAL');
-ALTER TABLE "Review" ALTER COLUMN "sentiment" TYPE "Sentiment_new" USING ("sentiment"::text::"Sentiment_new");
-ALTER TYPE "Sentiment" RENAME TO "Sentiment_old";
-ALTER TYPE "Sentiment_new" RENAME TO "Sentiment";
-DROP TYPE "public"."Sentiment_old";
-COMMIT;
-
--- DropForeignKey
-ALTER TABLE "Analysis" DROP CONSTRAINT "Analysis_reviewId_fkey";
-
--- DropForeignKey
-ALTER TABLE "Analysis" DROP CONSTRAINT "Analysis_userId_fkey";
-
--- DropForeignKey
-ALTER TABLE "Review" DROP CONSTRAINT "Review_productId_fkey";
-
--- AlterTable
-ALTER TABLE "Analysis" DROP COLUMN "reviewId",
-DROP COLUMN "updatedAt",
-ADD COLUMN "compatibilityScore" DOUBLE PRECISION NOT NULL,
-ADD COLUMN "generalScore" DOUBLE PRECISION NOT NULL,
-ADD COLUMN "targetProfession" TEXT NOT NULL,
-ADD COLUMN "verdict" TEXT NOT NULL;
-
--- AlterTable
-ALTER TABLE "Model" ADD COLUMN "version" TEXT,
-ALTER COLUMN "description" DROP NOT NULL;
-
--- AlterTable
-ALTER TABLE "Product" ADD COLUMN "image" TEXT,
-ADD COLUMN "url" TEXT NOT NULL,
-ALTER COLUMN "brand" DROP NOT NULL;
-
--- AlterTable
-ALTER TABLE "Review" ADD COLUMN "modelId" INTEGER;
-
--- AlterTable
-ALTER TABLE "User" DROP COLUMN "productReference",
-ADD COLUMN "role" "Role" NOT NULL DEFAULT 'USER';
-
--- CreateIndex
-CREATE UNIQUE INDEX "Product_url_key" ON "Product"("url");
-
--- AddForeignKey
-ALTER TABLE "Review" ADD CONSTRAINT "Review_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-
--- AddForeignKey
-ALTER TABLE "Review" ADD CONSTRAINT "Review_modelId_fkey" FOREIGN KEY ("modelId") REFERENCES "Model"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-
--- AddForeignKey
-ALTER TABLE "Analysis" ADD CONSTRAINT "Analysis_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/prisma/migrations/20260212110614_update_top_keyword_rows/migration.sql b/prisma/migrations/20260212110614_update_top_keyword_rows/migration.sql
deleted file mode 100644
index 326a4f8..0000000
--- a/prisma/migrations/20260212110614_update_top_keyword_rows/migration.sql
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- Warnings:
-
- - You are about to drop the column `generalScore` on the `Analysis` table. All the data in the column will be lost.
- - Added the required column `generalSentiment` to the `Analysis` table without a default value. This is not possible if the table is not empty.
-
-*/
--- DropForeignKey
-ALTER TABLE "Analysis" DROP CONSTRAINT "Analysis_productId_fkey";
-
--- AlterTable
-ALTER TABLE "Analysis" DROP COLUMN "generalScore",
-ADD COLUMN "generalSentiment" DOUBLE PRECISION NOT NULL,
-ADD COLUMN "topKeywords" TEXT[];
-
--- AddForeignKey
-ALTER TABLE "Analysis" ADD CONSTRAINT "Analysis_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/prisma/migrations/20260213040203_update_user_preference_table/migration.sql b/prisma/migrations/20260213040203_update_user_preference_table/migration.sql
deleted file mode 100644
index e5b37e1..0000000
--- a/prisma/migrations/20260213040203_update_user_preference_table/migration.sql
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- Warnings:
-
- - You are about to drop the column `role` on the `User` table. All the data in the column will be lost.
-
-*/
--- CreateEnum
-CREATE TYPE "OS" AS ENUM ('WINDOWS', 'MACOS', 'LINUX', 'CHROME_OS', 'OTHER');
-
--- CreateEnum
-CREATE TYPE "Brand" AS ENUM ('APPLE', 'ASUS', 'ACER', 'LENOVO', 'HP', 'DELL', 'MSI', 'AXIOO', 'ADVAN', 'ZYREX', 'OTHER');
-
--- AlterTable
-ALTER TABLE "User" DROP COLUMN "role",
-ADD COLUMN "bio" TEXT,
-ADD COLUMN "birthDate" TIMESTAMP(3),
-ADD COLUMN "location" TEXT;
-
--- CreateTable
-CREATE TABLE "UserPreference" (
- "id" SERIAL NOT NULL,
- "profession" TEXT,
- "preferredOS" "OS",
- "preferedBrand" "Brand",
- "budgetMin" INTEGER,
- "budgetMax" INTEGER,
- "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
- "updatedAt" TIMESTAMP(3) NOT NULL,
- "userId" INTEGER NOT NULL,
-
- CONSTRAINT "UserPreference_pkey" PRIMARY KEY ("id")
-);
-
--- CreateIndex
-CREATE UNIQUE INDEX "UserPreference_userId_key" ON "UserPreference"("userId");
-
--- AddForeignKey
-ALTER TABLE "UserPreference" ADD CONSTRAINT "UserPreference_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/prisma/migrations/20260213123206_update_user_table_rows/migration.sql b/prisma/migrations/20260213123206_update_user_table_rows/migration.sql
deleted file mode 100644
index 7175f3d..0000000
--- a/prisma/migrations/20260213123206_update_user_table_rows/migration.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-/*
- Warnings:
-
- - You are about to drop the column `birthDate` on the `User` table. All the data in the column will be lost.
- - You are about to drop the column `gender` on the `User` table. All the data in the column will be lost.
- - You are about to drop the column `location` on the `User` table. All the data in the column will be lost.
-
-*/
--- AlterTable
-ALTER TABLE "User" DROP COLUMN "birthDate",
-DROP COLUMN "gender",
-DROP COLUMN "location";
diff --git a/prisma/migrations/20260213123708_delete_user_role_enum_def/migration.sql b/prisma/migrations/20260213123708_delete_user_role_enum_def/migration.sql
deleted file mode 100644
index de02bcc..0000000
--- a/prisma/migrations/20260213123708_delete_user_role_enum_def/migration.sql
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- Warnings:
-
- - The values [male,female,other] on the enum `UserGender` will be removed. If these variants are still used in the database, this will fail.
-
-*/
--- AlterEnum
-BEGIN;
-CREATE TYPE "UserGender_new" AS ENUM ('MALE', 'FEMALE', 'OTHER');
-ALTER TYPE "UserGender" RENAME TO "UserGender_old";
-ALTER TYPE "UserGender_new" RENAME TO "UserGender";
-DROP TYPE "public"."UserGender_old";
-COMMIT;
-
--- DropEnum
-DROP TYPE "Role";
diff --git a/prisma/migrations/20260214025107_delete_model_version_rows/migration.sql b/prisma/migrations/20260214025107_delete_model_version_rows/migration.sql
deleted file mode 100644
index ab9d0fc..0000000
--- a/prisma/migrations/20260214025107_delete_model_version_rows/migration.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- Warnings:
-
- - You are about to drop the column `version` on the `Model` table. All the data in the column will be lost.
-
-*/
--- AlterTable
-ALTER TABLE "Model" DROP COLUMN "version";
diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml
deleted file mode 100644
index 044d57c..0000000
--- a/prisma/migrations/migration_lock.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-# Please do not edit this file manually
-# It should be added in your version-control system (e.g., Git)
-provider = "postgresql"
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 5df410e..2a5b3aa 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -91,6 +91,7 @@ model User {
analyses Analysis[]
preference UserPreference?
+ review Review[]
}
model UserPreference {
@@ -137,6 +138,10 @@ model Review {
modelId Int?
model Model? @relation(fields: [modelId], references: [id])
+
+ userId Int?
+ user User? @relation(fields: [userId], references: [id])
+
}
model Analysis {
@@ -160,6 +165,7 @@ model Analysis {
modelId Int
model Model @relation(fields: [modelId], references: [id])
+
}
model Model {
diff --git a/src/app/api/review/sentiment-stats/route.ts b/src/app/api/review/sentiment-stats/route.ts
index 0bae249..14f86bc 100644
--- a/src/app/api/review/sentiment-stats/route.ts
+++ b/src/app/api/review/sentiment-stats/route.ts
@@ -1,11 +1,39 @@
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. User belum login." },
+ { 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 tidak ditemukan." },
+ { status: 404 },
+ );
+ }
+
const grouped = await prisma.review.groupBy({
by: ["sentiment"],
- _count: { _all: true },
+ where: {
+ userId: user.id,
+ },
+ _count: {
+ _all: true,
+ },
});
const result = {
@@ -15,14 +43,17 @@ export async function GET() {
};
grouped.forEach((item) => {
- if (item.sentiment === "positive") result.positive = item._count._all;
- if (item.sentiment === "negative") result.negative = item._count._all;
- if (item.sentiment === "neutral") result.neutral = item._count._all;
+ if (item.sentiment === "POSITIVE") result.positive = item._count._all;
+ if (item.sentiment === "NEGATIVE") result.negative = item._count._all;
+ if (item.sentiment === "NEUTRAL") result.neutral = item._count._all;
});
- return NextResponse.json(result);
+ return NextResponse.json({ success: true, data: result }, { status: 200 });
} catch (error) {
- console.log(error);
- return [];
+ console.error("Gagal mengambil data sentimen:", error);
+ return NextResponse.json(
+ { success: false, message: "Internal Server Error" },
+ { status: 500 },
+ );
}
}
diff --git a/src/components/dashboards/DashboardClient.tsx b/src/components/dashboards/DashboardClient.tsx
index 5fc2c1f..b249b71 100644
--- a/src/components/dashboards/DashboardClient.tsx
+++ b/src/components/dashboards/DashboardClient.tsx
@@ -143,7 +143,6 @@ export default function DashboardClient() {
diff --git a/src/hooks/useDashboard.ts b/src/hooks/useDashboard.ts
index df8ab6c..84c395a 100644
--- a/src/hooks/useDashboard.ts
+++ b/src/hooks/useDashboard.ts
@@ -19,16 +19,22 @@ export const useDashboards = () => {
useEffect(() => {
async function fetchStats() {
setLoading(true);
- const res = await fetch("/api/review/sentiment-stats");
- const data = await res.json();
- const total = data.positive + data.negative + data.neutral;
+ const res = await fetch("/api/review/sentiment-stats");
+ const json = await res.json();
+
+ const statsData = json.data;
+
+ const total =
+ (statsData?.positive ?? 0) +
+ (statsData?.negative ?? 0) +
+ (statsData?.neutral ?? 0);
setStats({
- totalReviews: total ?? 0,
- positive: data.positive ?? 0,
- negative: data.negative ?? 0,
- neutral: data.neutral ?? 0,
+ totalReviews: total,
+ positive: statsData?.positive ?? 0,
+ negative: statsData?.negative ?? 0,
+ neutral: statsData?.neutral ?? 0,
});
setLoading(false);