From e3141030ab1f4b114e4034b19e0697b3864b48bc Mon Sep 17 00:00:00 2001 From: HP <*bang*> Date: Wed, 14 Jan 2026 15:49:04 -0500 Subject: [PATCH] A working state for the product expiry page --- src/app/api/products.ts | 12 +++ src/app/products/page.tsx | 201 +++++++++++++++++++++++++++++--------- src/sql/products.sql | 21 ++-- 3 files changed, 178 insertions(+), 56 deletions(-) diff --git a/src/app/api/products.ts b/src/app/api/products.ts index 2d97940..997ca9e 100644 --- a/src/app/api/products.ts +++ b/src/app/api/products.ts @@ -16,3 +16,15 @@ export async function GET(req: NextRequest){ return NextResponse.json({ message: "Serverside eception occurred" }, { status: 500 }) } } + +export async function POST(req: NextRequest){ + try{ + const body = (await req.json()) as { name: string, expiration_date: string } + console.log("GOT BODY:", body) + const result = await db.query("INSERT INTO products (name, exp_date) VALUES ($1, $2) RETURNING record_num;", [name, expiration_date]); + return NextResponse.json(result.rows[0].record_num ?? []); + } catch(error){ + + return NextResponse.json({ message: "Serverside eception occurred" }, { status: 500 }) + } +} diff --git a/src/app/products/page.tsx b/src/app/products/page.tsx index 82b9e18..295e733 100644 --- a/src/app/products/page.tsx +++ b/src/app/products/page.tsx @@ -28,6 +28,21 @@ const OrbitControls = dynamic( { ssr: false } ); +const Text = dynamic(() => import("@react-three/drei").then((m) => m.Text), { + ssr: false, +}); + +const RoundedBox = dynamic( + () => import("@react-three/drei").then((m) => m.RoundedBox), + { ssr: false } +); + +const Environment = dynamic( + () => import("@react-three/drei").then((m) => m.Environment), + { ssr: false } +); + + type Product = { record_num: number; product_id: number; @@ -45,7 +60,6 @@ function isoTodayPlus(days: number) { } const seed: Product[] = [ - { record_num: 1, product_id: 1001, name: "Milk", exp_date: isoTodayPlus(30) }, { record_num: 2, product_id: 1002, name: "Eggs", exp_date: isoTodayPlus(12) }, { record_num: 3, product_id: 1003, name: "Bread", exp_date: isoTodayPlus(6) }, { record_num: 4, product_id: 1004, name: "Cheese", exp_date: isoTodayPlus(90) }, @@ -67,7 +81,7 @@ const glassPaperSx = { color: "rgba(245,245,245,0.92)", }; -const PLANE_SIDE = 5; +const PLANE_SIDE = 4; const CUBE_SIZE = 1.0; const GAP = 0.25; const GAP_Z = 0.9; @@ -78,7 +92,7 @@ export default function Products() { const [mode, setMode] = React.useState<"view3d" | "form">("view3d"); const sorted = React.useMemo(() => { - return [...products].sort((a, b) => (a.exp_date < b.exp_date ? 1 : -1)); + return [...products].sort((a, b) => (a.exp_date < b.exp_date ? -1 : 1)); }, [products]); const startCreate = () => { @@ -281,37 +295,35 @@ function computePosition( gapZ: number ) { const perPlane = side * side; + const plane = Math.floor(i / perPlane); - const within = i % perPlane; + const planeStart = plane * perPlane; + const planeCount = Math.min(perPlane, Math.max(0, total - planeStart)); - // We fill "down" first, then left: - // row changes slowest? Actually: for down-first, row = within % rowsUsed, col = floor(within / rowsUsed) - // But we still want a bounded side x side layout. Easiest is: - // row = within % side (down) - // col = floor(within / side) (left) - const row = within % side; // 0..side-1 (top->down) - const col = Math.floor(within / side); // 0..side-1 (right->left) + const within = i - planeStart; // 0..planeCount-1 - // How many rows are actually used in this plane? - const rowsUsed = rowsUsedForPlane(total, plane, side); + // Fill DOWN first, then LEFT (next column) + const col = Math.floor(within / side); // 0..side-1 (right -> left) + const row = within % side; // 0..side-1 (top -> down) - // Center the grid in X around 0, but start at right-most column + // How many cubes are actually in *this column* on this plane? + // (last column may be partial) + const countInThisCol = Math.min(side, planeCount - col * side); + + // spacing const step = cubeSize + gap; + + // Right-most column is col=0 => x positive const xHalfSpan = ((side - 1) * step) / 2; + const x = xHalfSpan - col * step; - // Top-right start: - // col=0 should be right-most => x positive - const x = (xHalfSpan - col * step); + // FLOOR-LOCK PER COLUMN: + // bottom cube of this column sits at y = cubeSize/2 + // row=0 is top => higher y + const y = cubeSize / 2 + (countInThisCol - 1 - row) * step; - // FLOOR-LOCK: - // Bottom row sits on floor (grid) at y = cubeSize/2 - // If rowsUsed < side, we compress downward so the lowest row hits the floor. - // row=0 is the top row; we want top row higher. - const rowInUsedRange = row; // row already 0..side-1 - const clampedRow = Math.min(rowInUsedRange, rowsUsed - 1); - const y = cubeSize / 2 + (rowsUsed - 1 - clampedRow) * step; - - // Next plane goes "forward" (you said z is forward/back); using negative z to go "forward" + // Next plane goes "forward/back". You said you want front first, + // so plane 0 should be the front. Put further planes behind it: const z = -plane * (cubeSize + gapZ); return [x, y, z] as const; @@ -320,23 +332,32 @@ function computePosition( function World3D({ products }: { products: Product[] }) { return ( - { - camera.lookAt(2.8, 2.6, -2); + camera.lookAt(2.8, 2.6, -2); }} + gl={{ antialias: true }} > + {/* Nice reflections / lighting */} + {/* eslint-disable-next-line react/no-unknown-property */} - + {/* eslint-disable-next-line react/no-unknown-property */} - + + {/* eslint-disable-next-line react/no-unknown-property */} + + - {/* products */} {products.map((p, i) => ( - + ))} - {/* eslint-disable-next-line react/no-unknown-property */} - - {/* eslint-disable-next-line react/no-unknown-property */} - - + + {/* Rounded cube */} + + {/* eslint-disable-next-line react/no-unknown-property */} + + + + {/* +Z (front) */} + + {label} + + + {/* -Z (back) */} + + {label} + + + {/* +X (right) */} + + {label} + + + {/* -X (left) */} + + {label} + + + {/* +Y (top) */} + + {label} + + + {/* -Y (bottom) */} + + {label} + + ); } + function ProductForm({ value, onCancel, diff --git a/src/sql/products.sql b/src/sql/products.sql index 7a14459..6eda445 100644 --- a/src/sql/products.sql +++ b/src/sql/products.sql @@ -1,13 +1,14 @@ -DROP TABLE products IF EXISTS; -DROP product_record_num_sequence IF EXISTS; -DROP product_product_id_sequence IF EXISTS; +DROP TABLE IF EXISTS products; +DROP SEQUENCE IF EXISTS product_record_num_sequence; +DROP SEQUENCE IF EXISTS product_product_id_sequence; -CREATE product_record_num_sequence CASCADE; -CREATE product_product_id_sequence CASCADE; +CREATE SEQUENCE product_record_num_sequence; +CREATE SEQUENCE product_product_id_sequence; -CREATE TABLE products( - record_num INTEGER PRIMARY KEY DEFAULT nextval('product_record_num_sequence'), - product_id INTEGER PRIMARY KEY DEFAULT, - name VARCHAR(20), - exp_date DATE NOT NULL, +CREATE TABLE products ( + record_num INTEGER PRIMARY KEY DEFAULT nextval('product_record_num_sequence'), + product_id INTEGER NOT NULL DEFAULT nextval('product_product_id_sequence'), + name VARCHAR(20), + exp_date DATE NOT NULL ); +