From ebb5bf9a52924aefbda4e087e5c71a3d1b0c6986 Mon Sep 17 00:00:00 2001 From: Citali Date: Sun, 6 Jul 2025 10:26:33 +0200 Subject: [PATCH] Doing stuff --- Dockerfile | 19 + package-lock.json | 41 +- package.json | 1 + prisma/schema.prisma | 159 +++++ src/app/commissions/page.tsx | 19 +- src/components/commissions/CommissionCard.tsx | 62 +- src/components/commissions/CommissionForm.tsx | 55 -- .../commissions/CommissionOrderForm.tsx | 238 +++++++ src/components/commissions/FileDropzone.tsx | 24 + src/data/mockCommissions.ts | 614 +++++++++--------- src/lib/prisma.ts | 14 + src/schemas/commission.ts | 24 +- src/schemas/commissionOrder.ts | 10 + src/types/commissions.ts | 104 +-- src/utils/calculatePrice.ts | 49 ++ 15 files changed, 968 insertions(+), 465 deletions(-) create mode 100644 Dockerfile delete mode 100644 src/components/commissions/CommissionForm.tsx create mode 100644 src/components/commissions/CommissionOrderForm.tsx create mode 100644 src/components/commissions/FileDropzone.tsx create mode 100644 src/lib/prisma.ts create mode 100644 src/schemas/commissionOrder.ts create mode 100644 src/utils/calculatePrice.ts diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e04b9de --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +# Base image +FROM node:20 + +# Set working directory +WORKDIR /app + +# Install dependencies +COPY package.json package-lock.json ./ +RUN npm install + +# Copy the rest of the code +COPY . . +RUN npx prisma generate + +# Expose the dev port +EXPOSE 3000 + +# Run dev server +CMD ["npm", "run", "dev"] diff --git a/package-lock.json b/package-lock.json index c0afc92..fc1f135 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@hookform/resolvers": "^5.1.1", + "@prisma/client": "^6.11.1", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dropdown-menu": "^2.1.15", @@ -1041,11 +1042,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" @@ -1055,14 +1078,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": { @@ -1076,14 +1099,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", @@ -1095,7 +1118,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" @@ -5124,7 +5147,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" @@ -6107,7 +6130,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": { @@ -7168,7 +7191,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 6974fce..f442455 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@hookform/resolvers": "^5.1.1", + "@prisma/client": "^6.11.1", "@radix-ui/react-checkbox": "^1.3.2", "@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-dropdown-menu": "^2.1.15", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index aaeee1b..dc6855f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -13,3 +13,162 @@ datasource db { provider = "postgresql" url = env("DATABASE_URL") } + +model CommissionType { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sortIndex Int @default(0) + + name String + + description String? + + options CommissionTypeOption[] + extras CommissionTypeExtra[] +} + +model CommissionOption { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sortIndex Int @default(0) + + name String @unique + + description String? + + types CommissionTypeOption[] +} + +model CommissionExtra { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sortIndex Int @default(0) + + name String @unique + + description String? + + types CommissionTypeExtra[] +} + +model CommissionTypeOption { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sortIndex Int @default(0) + + typeId String + optionId String + + priceRange String? + pricePercent Float? + price Float? + + type CommissionType @relation(fields: [typeId], references: [id]) + option CommissionOption @relation(fields: [optionId], references: [id]) + + @@unique([typeId, optionId]) +} + +model CommissionTypeExtra { + id String @id @default(cuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sortIndex Int @default(0) + + typeId String + extraId String + + priceRange String? + pricePercent Float? + price Float? + + type CommissionType @relation(fields: [typeId], references: [id]) + extra CommissionExtra @relation(fields: [extraId], references: [id]) + + @@unique([typeId, extraId]) +} + +// 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/page.tsx b/src/app/commissions/page.tsx index 3bc15cb..643e335 100644 --- a/src/app/commissions/page.tsx +++ b/src/app/commissions/page.tsx @@ -1,20 +1,27 @@ import { CommissionCard } from "@/components/commissions/CommissionCard"; -import { generateMockCommissions } from "@/utils/generateMockCommissions"; +import { CommissionOrderForm } from "@/components/commissions/CommissionOrderForm"; +import prisma from "@/lib/prisma"; -export default function CommissionsPage() { - const commissions = generateMockCommissions() +export default async function CommissionsPage() { + const commissions = await prisma.commissionType.findMany({ + include: { + options: { include: { option: true }, orderBy: { sortIndex: "asc" } }, + extras: { include: { extra: true }, orderBy: { sortIndex: "asc" } }, + }, + orderBy: [{ sortIndex: "asc" }, { name: "asc" }], + }) return (

Commission Pricing

- {commissions.map((data) => ( - + {commissions.map((commission) => ( + ))}

Request a Commission

- {/* */} +
); } \ No newline at end of file diff --git a/src/components/commissions/CommissionCard.tsx b/src/components/commissions/CommissionCard.tsx index dd7bc91..76ab9ff 100644 --- a/src/components/commissions/CommissionCard.tsx +++ b/src/components/commissions/CommissionCard.tsx @@ -1,25 +1,32 @@ -// components/commissions/CommissionCard.tsx "use client" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { CommissionCardProps } from "@/types/commissions" -import Image from "next/image" -import { useState } from "react" -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible" +import { CommissionExtra, CommissionOption, CommissionType, CommissionTypeExtra, CommissionTypeOption } from "@/generated/prisma" +// import { useState } from "react" +import { Badge } from "../ui/badge" -export function CommissionCard({ type, options, extras, examples }: CommissionCardProps) { - const [open, setOpen] = useState(false) +type CommissionTypeWithItems = CommissionType & { + options: (CommissionTypeOption & { + option: CommissionOption | null + })[] + extras: (CommissionTypeExtra & { + extra: CommissionExtra | null + })[] +} + +export function CommissionCard({ commission }: { commission: CommissionTypeWithItems }) { + // const [open, setOpen] = useState(false) return (
- {type.name} -

{type.description}

+ {commission.name} +

{commission.description}

- {examples && examples.length > 0 && ( + {/* {examples && examples.length > 0 && ( {open ? "Hide Examples" : "See Examples"} @@ -41,13 +48,20 @@ export function CommissionCard({ type, options, extras, examples }: CommissionCa
- )} + )} */}

Options

@@ -56,28 +70,28 @@ export function CommissionCard({ type, options, extras, examples }: CommissionCa

Extras

    - {extras.map((extra) => ( -
  • - {extra.name}:{" "} - {extra.price !== undefined + {commission.extras.map((extra) => ( +
  • + {extra.extra?.name}:{" "} + {extra.price ? `${extra.price}€` : extra.pricePercent ? `+${extra.pricePercent}%` : extra.priceRange - ? `${extra.priceRange.replace("#", "–")}€` + ? `${extra.priceRange}€` : "Included"}
  • ))}
- {/*
- {extras.map((extra) => ( - - {extra.name} +
+ {commission.extras.map((extra) => ( + + {extra.extra?.name} ))} -
*/} +
diff --git a/src/components/commissions/CommissionForm.tsx b/src/components/commissions/CommissionForm.tsx deleted file mode 100644 index d9cba39..0000000 --- a/src/components/commissions/CommissionForm.tsx +++ /dev/null @@ -1,55 +0,0 @@ -// src/app/(frontend)/commissions/CommissionForm.tsx -"use client" - -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { Textarea } from "@/components/ui/textarea" -import { CommissionFormData, commissionFormSchema } from "@/schemas/commission" -import { zodResolver } from "@hookform/resolvers/zod" -import { useForm } from "react-hook-form" -// import { CommissionFormData, commissionFormSchema } from "./schema" - -export function CommissionForm() { - const { - register, - handleSubmit, - formState: { errors }, - } = useForm({ - resolver: zodResolver(commissionFormSchema), - }) - - function onSubmit(data: CommissionFormData) { - console.log("Order submitted", data) - } - - return ( -
-
- - - {errors.name &&

{errors.name.message}

} -
- -
- - - {errors.email &&

{errors.email.message}

} -
- -
- - - {errors.type &&

{errors.type.message}

} -
- -
- -