- SellItemPage: real file upload + API listing creation + activate - CreateProfilePage: save profile via PUT /users/profile - ProductDetailPage: wire edit/delete/message buttons, show edit for owner - ListingCard: persist favorites via API, show real images - Footer: connect newsletter subscribe to API - Router: add /dashboard/listings and /dashboard/saved routes - Backend: add GET /listings/favorites endpoint - New pages: MyListingsPage, SavedItemsPage - Fix unused imports causing build failures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
314 lines
11 KiB
SQL
314 lines
11 KiB
SQL
-- CreateEnum
|
|
CREATE TYPE "Category" AS ENUM ('ELECTRONICS', 'FURNITURE', 'CLOTHING', 'HOME_GARDEN', 'SPORTS', 'BOOKS', 'GAMES', 'VEHICLES', 'OTHER');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "ListingCondition" AS ENUM ('NEW', 'LIKE_NEW', 'GENTLY_USED', 'USED', 'FAIR');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "ListingStatus" AS ENUM ('DRAFT', 'ACTIVE', 'SOLD', 'DELETED');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "OfferStatus" AS ENUM ('PENDING', 'ACCEPTED', 'DECLINED', 'COUNTERED', 'CANCELLED', 'EXPIRED');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "NotificationType" AS ENUM ('NEW_OFFER', 'OFFER_ACCEPTED', 'OFFER_DECLINED', 'ITEM_SOLD', 'NEW_MESSAGE', 'ITEM_FAVORITED');
|
|
|
|
-- CreateEnum
|
|
CREATE TYPE "PaymentStatus" AS ENUM ('PENDING', 'COMPLETED', 'FAILED', 'REFUNDED');
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "User" (
|
|
"id" TEXT NOT NULL,
|
|
"email" TEXT NOT NULL,
|
|
"passwordHash" TEXT NOT NULL,
|
|
"fullName" TEXT NOT NULL,
|
|
"nickname" TEXT,
|
|
"avatar" TEXT,
|
|
"phone" TEXT,
|
|
"location" TEXT,
|
|
"bio" TEXT,
|
|
"rating" DOUBLE PRECISION NOT NULL DEFAULT 0,
|
|
"ratingCount" INTEGER NOT NULL DEFAULT 0,
|
|
"showEmail" BOOLEAN NOT NULL DEFAULT false,
|
|
"showPhone" BOOLEAN NOT NULL DEFAULT true,
|
|
"showLocation" BOOLEAN NOT NULL DEFAULT true,
|
|
"showOnline" BOOLEAN NOT NULL DEFAULT true,
|
|
"showRating" BOOLEAN NOT NULL DEFAULT true,
|
|
"twoFactorEnabled" BOOLEAN NOT NULL DEFAULT false,
|
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
|
"notifNewOffer" BOOLEAN NOT NULL DEFAULT true,
|
|
"notifMessages" BOOLEAN NOT NULL DEFAULT true,
|
|
"notifItemSold" BOOLEAN NOT NULL DEFAULT true,
|
|
"notifFavorites" BOOLEAN NOT NULL DEFAULT true,
|
|
"notifEmail" BOOLEAN NOT NULL DEFAULT true,
|
|
"marketingEmail" BOOLEAN NOT NULL DEFAULT false,
|
|
"resetToken" TEXT,
|
|
"resetTokenExpiry" TIMESTAMP(3),
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Session" (
|
|
"id" TEXT NOT NULL,
|
|
"userId" TEXT NOT NULL,
|
|
"refreshToken" TEXT NOT NULL,
|
|
"userAgent" TEXT,
|
|
"ipAddress" TEXT,
|
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Listing" (
|
|
"id" TEXT NOT NULL,
|
|
"title" TEXT NOT NULL,
|
|
"description" TEXT NOT NULL,
|
|
"price" DOUBLE PRECISION NOT NULL,
|
|
"obo" BOOLEAN NOT NULL DEFAULT false,
|
|
"category" "Category" NOT NULL,
|
|
"condition" "ListingCondition" NOT NULL,
|
|
"status" "ListingStatus" NOT NULL DEFAULT 'DRAFT',
|
|
"location" TEXT NOT NULL,
|
|
"viewCount" INTEGER NOT NULL DEFAULT 0,
|
|
"sellerId" TEXT NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "Listing_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "ListingImage" (
|
|
"id" TEXT NOT NULL,
|
|
"url" TEXT NOT NULL,
|
|
"order" INTEGER NOT NULL DEFAULT 0,
|
|
"listingId" TEXT NOT NULL,
|
|
"uploadedBy" TEXT NOT NULL,
|
|
|
|
CONSTRAINT "ListingImage_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Offer" (
|
|
"id" TEXT NOT NULL,
|
|
"amount" DOUBLE PRECISION NOT NULL,
|
|
"message" TEXT,
|
|
"status" "OfferStatus" NOT NULL DEFAULT 'PENDING',
|
|
"counterAmount" DOUBLE PRECISION,
|
|
"expiresAt" TIMESTAMP(3),
|
|
"buyerId" TEXT NOT NULL,
|
|
"sellerId" TEXT NOT NULL,
|
|
"listingId" TEXT NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "Offer_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Conversation" (
|
|
"id" TEXT NOT NULL,
|
|
"user1Id" TEXT NOT NULL,
|
|
"user2Id" TEXT NOT NULL,
|
|
"listingId" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "Conversation_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Message" (
|
|
"id" TEXT NOT NULL,
|
|
"content" TEXT NOT NULL,
|
|
"senderId" TEXT NOT NULL,
|
|
"conversationId" TEXT NOT NULL,
|
|
"isRead" BOOLEAN NOT NULL DEFAULT false,
|
|
"offerAmount" DOUBLE PRECISION,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "Message_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Favorite" (
|
|
"id" TEXT NOT NULL,
|
|
"userId" TEXT NOT NULL,
|
|
"listingId" TEXT NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "Favorite_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Notification" (
|
|
"id" TEXT NOT NULL,
|
|
"userId" TEXT NOT NULL,
|
|
"type" "NotificationType" NOT NULL,
|
|
"title" TEXT NOT NULL,
|
|
"body" TEXT NOT NULL,
|
|
"data" JSONB,
|
|
"isRead" BOOLEAN NOT NULL DEFAULT false,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "Notification_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "Payment" (
|
|
"id" TEXT NOT NULL,
|
|
"userId" TEXT NOT NULL,
|
|
"listingId" TEXT NOT NULL,
|
|
"stripePaymentId" TEXT,
|
|
"amount" DOUBLE PRECISION NOT NULL,
|
|
"status" "PaymentStatus" NOT NULL DEFAULT 'PENDING',
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
|
|
CONSTRAINT "Payment_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateTable
|
|
CREATE TABLE "BlockedUser" (
|
|
"id" TEXT NOT NULL,
|
|
"blockerId" TEXT NOT NULL,
|
|
"blockedId" TEXT NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "BlockedUser_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "User_resetToken_key" ON "User"("resetToken");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Session_refreshToken_key" ON "Session"("refreshToken");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Session_userId_idx" ON "Session"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Listing_sellerId_idx" ON "Listing"("sellerId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Listing_category_idx" ON "Listing"("category");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Listing_status_idx" ON "Listing"("status");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Listing_createdAt_idx" ON "Listing"("createdAt");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "ListingImage_listingId_idx" ON "ListingImage"("listingId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Offer_buyerId_idx" ON "Offer"("buyerId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Offer_sellerId_idx" ON "Offer"("sellerId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Offer_listingId_idx" ON "Offer"("listingId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Conversation_user1Id_idx" ON "Conversation"("user1Id");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Conversation_user2Id_idx" ON "Conversation"("user2Id");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Conversation_user1Id_user2Id_listingId_key" ON "Conversation"("user1Id", "user2Id", "listingId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Message_conversationId_idx" ON "Message"("conversationId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Message_senderId_idx" ON "Message"("senderId");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Favorite_userId_listingId_key" ON "Favorite"("userId", "listingId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Notification_userId_idx" ON "Notification"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Notification_createdAt_idx" ON "Notification"("createdAt");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "Payment_stripePaymentId_key" ON "Payment"("stripePaymentId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Payment_userId_idx" ON "Payment"("userId");
|
|
|
|
-- CreateIndex
|
|
CREATE INDEX "Payment_listingId_idx" ON "Payment"("listingId");
|
|
|
|
-- CreateIndex
|
|
CREATE UNIQUE INDEX "BlockedUser_blockerId_blockedId_key" ON "BlockedUser"("blockerId", "blockedId");
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Listing" ADD CONSTRAINT "Listing_sellerId_fkey" FOREIGN KEY ("sellerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ListingImage" ADD CONSTRAINT "ListingImage_listingId_fkey" FOREIGN KEY ("listingId") REFERENCES "Listing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "ListingImage" ADD CONSTRAINT "ListingImage_uploadedBy_fkey" FOREIGN KEY ("uploadedBy") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Offer" ADD CONSTRAINT "Offer_buyerId_fkey" FOREIGN KEY ("buyerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Offer" ADD CONSTRAINT "Offer_sellerId_fkey" FOREIGN KEY ("sellerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Offer" ADD CONSTRAINT "Offer_listingId_fkey" FOREIGN KEY ("listingId") REFERENCES "Listing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Conversation" ADD CONSTRAINT "Conversation_user1Id_fkey" FOREIGN KEY ("user1Id") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Conversation" ADD CONSTRAINT "Conversation_user2Id_fkey" FOREIGN KEY ("user2Id") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Conversation" ADD CONSTRAINT "Conversation_listingId_fkey" FOREIGN KEY ("listingId") REFERENCES "Listing"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Message" ADD CONSTRAINT "Message_senderId_fkey" FOREIGN KEY ("senderId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Message" ADD CONSTRAINT "Message_conversationId_fkey" FOREIGN KEY ("conversationId") REFERENCES "Conversation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Favorite" ADD CONSTRAINT "Favorite_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Favorite" ADD CONSTRAINT "Favorite_listingId_fkey" FOREIGN KEY ("listingId") REFERENCES "Listing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Notification" ADD CONSTRAINT "Notification_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Payment" ADD CONSTRAINT "Payment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "Payment" ADD CONSTRAINT "Payment_listingId_fkey" FOREIGN KEY ("listingId") REFERENCES "Listing"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "BlockedUser" ADD CONSTRAINT "BlockedUser_blockerId_fkey" FOREIGN KEY ("blockerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
|
|
-- AddForeignKey
|
|
ALTER TABLE "BlockedUser" ADD CONSTRAINT "BlockedUser_blockedId_fkey" FOREIGN KEY ("blockedId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|