From e5fb22aab1d025edfbbdb1c4c5ed789d04869d9b Mon Sep 17 00:00:00 2001 From: Andymick Date: Mon, 20 Jul 2026 18:00:06 +0100 Subject: [PATCH] Feature: Add third price option for plain/blank products - Added plainPrice field to Product model in Prisma schema - Updated database to include new plainPrice column - Added Plain price input field to product creation form (new/page.tsx) - Added Plain price input field to product edit form ([id]/edit/page.tsx) - Updated createProduct server action to handle plainPrice - Updated updateProduct server action to handle plainPrice Now supports three price tiers: - basePrice: for ready-made items - plainPrice: for plain/blank items (new) - personalisedPrice: for made-to-order items Co-Authored-By: Claude Haiku 4.5 --- .claude/settings.local.json | 8 +++- prisma/schema.prisma | 1 + src/app/admin/products/[id]/edit/page.tsx | 50 +++++++++++++++-------- src/app/admin/products/actions.ts | 5 +++ src/app/admin/products/new/page.tsx | 49 ++++++++++++++-------- 5 files changed, 78 insertions(+), 35 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 6f13fc6..d786477 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -128,7 +128,13 @@ "Bash(git commit -m 'Chore: Migrate database from SQLite to PostgreSQL on VM *)", "Bash(git pull *)", "Bash(git rm *)", - "Bash(git commit -m 'Chore: Remove .env from version control for security *)" + "Bash(git commit -m 'Chore: Remove .env from version control for security *)", + "Bash(git fetch *)", + "Bash(git ls-remote *)", + "Bash(timeout 30 git push origin main --force)", + "Bash(GIT_TRACE=1 git push origin main --force)", + "Bash(pkill -f \"node.exe\")", + "PowerShell(Stop-Process -Name node -Force -ErrorAction SilentlyContinue)" ] } } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index fd99866..60e8bca 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -26,6 +26,7 @@ model Product { category String description String basePrice Int // cents — used for ready-made items on /products + plainPrice Int? // cents — used for plain/blank items personalisedPrice Int? // cents — used for made-to-order items on /made-to-order; falls back to basePrice if not set colors String // JSON array of hex strings, e.g. ["#17181C","#FBF9F5"] colorPhotos String? // JSON object mapping hex -> photo data URL, e.g. {"#17181C":"data:..."}. diff --git a/src/app/admin/products/[id]/edit/page.tsx b/src/app/admin/products/[id]/edit/page.tsx index 36bb2ae..9479ed9 100644 --- a/src/app/admin/products/[id]/edit/page.tsx +++ b/src/app/admin/products/[id]/edit/page.tsx @@ -220,23 +220,39 @@ export default async function EditProductPage({ params }: { params: { id: string -
- - -

- Higher price for made-to-order items. Leave blank to use the same price as ready-made. -

+
+
+ + +

For plain/blank items

+
+
+ + +

For made-to-order items

+
diff --git a/src/app/admin/products/actions.ts b/src/app/admin/products/actions.ts index e9d8f35..2c84e3e 100644 --- a/src/app/admin/products/actions.ts +++ b/src/app/admin/products/actions.ts @@ -67,6 +67,7 @@ export async function createProduct(formData: FormData) { const category = String(formData.get('category') ?? ''); const description = String(formData.get('description') ?? '').trim(); const priceDollars = Number(formData.get('price') ?? 0); + const plainPriceDollars = String(formData.get('plainPrice') ?? '').trim(); const personalisedPriceDollars = String(formData.get('personalisedPrice') ?? '').trim(); const mockup = String(formData.get('mockup') ?? 'tshirt'); const colorsInput = String(formData.get('colors') ?? ''); @@ -135,6 +136,7 @@ export async function createProduct(formData: FormData) { category, description: description || `${name} — personalize it your way.`, basePrice: Math.round(priceDollars * 100), + plainPrice: plainPriceDollars ? Math.round(Number(plainPriceDollars) * 100) : null, personalisedPrice: personalisedPriceDollars ? Math.round(Number(personalisedPriceDollars) * 100) : null, weightGrams, ...saleFields, @@ -163,6 +165,7 @@ export async function updateProduct(formData: FormData) { const category = String(formData.get('category') ?? ''); const description = String(formData.get('description') ?? '').trim(); const priceDollars = Number(formData.get('price') ?? 0); + const plainPriceDollars = String(formData.get('plainPrice') ?? '').trim(); const personalisedPriceDollars = String(formData.get('personalisedPrice') ?? '').trim(); const mockup = String(formData.get('mockup') ?? 'tshirt'); const colorsInput = String(formData.get('colors') ?? ''); @@ -252,6 +255,7 @@ export async function updateProduct(formData: FormData) { category: string; description: string; basePrice: number; + plainPrice: number | null; personalisedPrice: number | null; salePrice: number | null; saleStartsAt: Date | null; @@ -276,6 +280,7 @@ export async function updateProduct(formData: FormData) { category, description: description || `${name} — personalize it your way.`, basePrice: Math.round(priceDollars * 100), + plainPrice: plainPriceDollars ? Math.round(Number(plainPriceDollars) * 100) : null, personalisedPrice: personalisedPriceDollars ? Math.round(Number(personalisedPriceDollars) * 100) : null, ...parseSaleFields(formData), colors: JSON.stringify(colors), diff --git a/src/app/admin/products/new/page.tsx b/src/app/admin/products/new/page.tsx index 2929b69..ab539a4 100644 --- a/src/app/admin/products/new/page.tsx +++ b/src/app/admin/products/new/page.tsx @@ -21,7 +21,7 @@ export default async function NewProductPage() { design their own version, the ready-made one where they just buy it as pictured, or both.

-
+
@@ -196,22 +196,37 @@ export default async function NewProductPage() {
-
- - -

- Higher price for made-to-order items. Leave blank to use the same price as ready-made. -

+
+
+ + +

For plain/blank items

+
+
+ + +

For made-to-order items

+