diff --git a/package-lock.json b/package-lock.json index 9012573..18cf248 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,12 +8,16 @@ "name": "admin.gaertan.art", "version": "0.1.0", "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/utilities": "^3.2.2", "@hookform/resolvers": "^5.1.1", + "@prisma/client": "^6.11.1", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-navigation-menu": "^1.2.13", + "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.5", @@ -42,7 +46,7 @@ "prisma": "^6.11.1", "tailwindcss": "^4", "tw-animate-css": "^1.3.5", - "typescript": "^5" + "typescript": "^5.8.3" } }, "node_modules/@alloc/quick-lru": { @@ -101,6 +105,45 @@ } } }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -1079,11 +1122,33 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@prisma/client": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.11.1.tgz", + "integrity": "sha512-5CLFh8QP6KxRm83pJ84jaVCeSVPQr8k0L2SEtOJHwdkS57/VQDcI/wQpGmdyOZi+D9gdNabdo8tj1Uk+w+upsQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/@prisma/config": { "version": "6.11.1", "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.11.1.tgz", "integrity": "sha512-z6rCTQN741wxDq82cpdzx2uVykpnQIXalLhrWQSR0jlBVOxCIkz3HZnd8ern3uYTcWKfB3IpVAF7K2FU8t/8AQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "jiti": "2.4.2" @@ -1093,14 +1158,14 @@ "version": "6.11.1", "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.11.1.tgz", "integrity": "sha512-lWRb/YSWu8l4Yum1UXfGLtqFzZkVS2ygkWYpgkbgMHn9XJlMITIgeMvJyX5GepChzhmxuSuiq/MY/kGFweOpGw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines": { "version": "6.11.1", "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.11.1.tgz", "integrity": "sha512-6eKEcV6V8W2eZAUwX2xTktxqPM4vnx3sxz3SDtpZwjHKpC6lhOtc4vtAtFUuf5+eEqBk+dbJ9Dcaj6uQU+FNNg==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -1114,14 +1179,14 @@ "version": "6.11.1-1.f40f79ec31188888a2e33acda0ecc8fd10a853a9", "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.11.1-1.f40f79ec31188888a2e33acda0ecc8fd10a853a9.tgz", "integrity": "sha512-swFJTOOg4tHyOM1zB/pHb3MeH0i6t7jFKn5l+ZsB23d9AQACuIRo9MouvuKGvnDogzkcjbWnXi/NvOZ0+n5Jfw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/fetch-engine": { "version": "6.11.1", "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.11.1.tgz", "integrity": "sha512-NBYzmkXTkj9+LxNPRSndaAeALOL1Gr3tjvgRYNqruIPlZ6/ixLeuE/5boYOewant58tnaYFZ5Ne0jFBPfGXHpQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.11.1", @@ -1133,7 +1198,7 @@ "version": "6.11.1", "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.11.1.tgz", "integrity": "sha512-b2Z8oV2gwvdCkFemBTFd0x4lsL4O2jLSx8lB7D+XqoFALOQZPa7eAPE1NU0Mj1V8gPHRxIsHnyUNtw2i92psUw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "dependencies": { "@prisma/debug": "6.11.1" @@ -1627,6 +1692,30 @@ } } }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", + "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", @@ -5168,7 +5257,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -6215,7 +6304,7 @@ "version": "6.11.1", "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.11.1.tgz", "integrity": "sha512-VzJToRlV0s9Vu2bfqHiRJw73hZNCG/AyJeX+kopbu4GATTjTUdEWUteO3p4BLYoHpMS4o8pD3v6tF44BHNZI1w==", - "dev": true, + "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -7266,7 +7355,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index 2fb6f18..a57486b 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,16 @@ "lint": "next lint" }, "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/utilities": "^3.2.2", "@hookform/resolvers": "^5.1.1", + "@prisma/client": "^6.11.1", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-navigation-menu": "^1.2.13", + "@radix-ui/react-progress": "^1.1.7", "@radix-ui/react-select": "^2.2.5", "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-switch": "^1.2.5", @@ -43,6 +47,6 @@ "prisma": "^6.11.1", "tailwindcss": "^4", "tw-animate-css": "^1.3.5", - "typescript": "^5" + "typescript": "^5.8.3" } } diff --git a/prisma/migrations/20250704173417_init/migration.sql b/prisma/migrations/20250704173417_init/migration.sql new file mode 100644 index 0000000..8c174a3 --- /dev/null +++ b/prisma/migrations/20250704173417_init/migration.sql @@ -0,0 +1,97 @@ +-- CreateEnum +CREATE TYPE "Role" AS ENUM ('ADMIN', 'ARTIST'); + +-- CreateEnum +CREATE TYPE "RequestStatus" AS ENUM ('PENDING', 'ACCEPTED', 'IN_PROGRESS', 'DONE', 'REJECTED'); + +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "name" TEXT, + "role" "Role" NOT NULL DEFAULT 'ADMIN', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "CommissionType" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "basePrice" DOUBLE PRECISION NOT NULL, + "deliveryEst" TEXT, + "tags" TEXT[], + "active" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "CommissionType_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "CommissionRequest" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "message" TEXT NOT NULL, + "typeId" TEXT NOT NULL, + "status" "RequestStatus" NOT NULL DEFAULT 'PENDING', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "CommissionRequest_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Artwork" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "imageUrl" TEXT NOT NULL, + "description" TEXT, + "tags" TEXT[], + "formats" TEXT[], + "isPublic" BOOLEAN NOT NULL DEFAULT true, + "groupId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Artwork_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "PresentationGroup" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "description" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "PresentationGroup_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Preferences" ( + "id" TEXT NOT NULL, + "commissionOpen" BOOLEAN NOT NULL DEFAULT true, + "defaultDelivery" TEXT, + "autoReplyMessage" TEXT, + "notifyByEmail" BOOLEAN NOT NULL DEFAULT true, + + CONSTRAINT "Preferences_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "TOS" ( + "id" TEXT NOT NULL, + "content" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "TOS_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- AddForeignKey +ALTER TABLE "CommissionRequest" ADD CONSTRAINT "CommissionRequest_typeId_fkey" FOREIGN KEY ("typeId") REFERENCES "CommissionType"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Artwork" ADD CONSTRAINT "Artwork_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "PresentationGroup"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# 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 aaeee1b..6c1e4e1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -13,3 +13,84 @@ datasource db { provider = "postgresql" url = env("DATABASE_URL") } + +model User { + id String @id @default(cuid()) + email String @unique + name String? + role Role @default(ADMIN) + createdAt DateTime @default(now()) +} + +enum Role { + ADMIN + ARTIST +} + +model CommissionType { + id String @id @default(cuid()) + title String + description String? + basePrice Float + deliveryEst String? // e.g. "2 weeks" + tags String[] // e.g. shaded, sketch, full-body + active Boolean @default(true) + createdAt DateTime @default(now()) + CommissionRequest CommissionRequest[] +} + +model CommissionRequest { + id String @id @default(cuid()) + name String + email String + message String + typeId String + status RequestStatus @default(PENDING) + createdAt DateTime @default(now()) + + type CommissionType @relation(fields: [typeId], references: [id]) +} + +enum RequestStatus { + PENDING + ACCEPTED + IN_PROGRESS + DONE + REJECTED +} + +model Artwork { + id String @id @default(cuid()) + title String + imageUrl String + description String? + tags String[] + formats String[] + isPublic Boolean @default(true) + groupId String? + createdAt DateTime @default(now()) + + group PresentationGroup? @relation(fields: [groupId], references: [id]) +} + +model PresentationGroup { + id String @id @default(cuid()) + name String + description String? + createdAt DateTime @default(now()) + Artwork Artwork[] +} + +model Preferences { + id String @id @default(cuid()) + commissionOpen Boolean @default(true) + defaultDelivery String? // e.g. "7 days" + autoReplyMessage String? + notifyByEmail Boolean @default(true) +} + +model TOS { + id String @id @default(cuid()) + content String // Markdown or rich text + createdAt DateTime @default(now()) +} diff --git a/src/app/commissions/kanban/page.tsx b/src/app/commissions/kanban/page.tsx new file mode 100644 index 0000000..4c81423 --- /dev/null +++ b/src/app/commissions/kanban/page.tsx @@ -0,0 +1,10 @@ +import { KanbanBoard } from "@/components/kanban/KanbanBoard" + +export default function KanbanPage() { + return ( +