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);