From fd1d7c0025c9f5bdba88b76a599f40cac7744542 Mon Sep 17 00:00:00 2001 From: Quintine Date: Wed, 20 May 2026 13:48:27 +1000 Subject: [PATCH] Feature: Order tracking with IDs, 30% deposit calculation, PayID support, and post-submission instructions --- backend/prisma/schema.prisma | 23 +- backend/src/server.ts | 38 +- frontend/package-lock.json | 402 ++++++++++++++++++--- frontend/package.json | 2 + frontend/postcss.config.js | 2 +- frontend/src/App.tsx | 17 +- frontend/src/components/AdminDashboard.tsx | 82 ++++- frontend/src/components/CartModal.tsx | 82 ++++- frontend/src/components/CustomerView.tsx | 52 ++- frontend/src/index.css | 4 +- frontend/src/services/api.ts | 30 +- frontend/vite.config.ts | 3 +- 12 files changed, 621 insertions(+), 116 deletions(-) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 69fccdb..39c8e6f 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -25,16 +25,21 @@ model StockItem { } model Order { - id Int @id @default(autoincrement()) - name String - phone String - totalWeight Float - estimatedTotal Float - timestamp DateTime @default(now()) - items String // Storing items as a JSON string for simplicity with SQLite + id Int @id @default(autoincrement()) + orderNumber String? + name String + phone String + items String + totalWeight Float + estimatedTotal Float + status String @default("Pending") + timestamp DateTime @default(now()) } model Config { - id Int @id @default(autoincrement()) - adminCode String @default("1234") + id Int @id @default(1) + adminCode String @default("1234") + finalDepositDate String? + cookDay String? + payIdInfo String? @default("Your PayID Here") } diff --git a/backend/src/server.ts b/backend/src/server.ts index 954d7c4..4c35e1a 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -36,13 +36,21 @@ app.get('/api/data', async (req, res) => { app.post('/api/orders', async (req, res) => { try { const { name, phone, items, totalWeight, estimatedTotal } = req.body; + + // Generate simple order number + const lastOrder = await prisma.order.findFirst({ orderBy: { id: 'desc' } }); + const nextId = (lastOrder?.id || 0) + 1; + const orderNumber = `#${nextId + 1000}`; + const order = await prisma.order.create({ data: { + orderNumber, name, phone, totalWeight, estimatedTotal, - items: JSON.stringify(items) + items: JSON.stringify(items), + status: 'Pending' } }); res.json(order); @@ -52,6 +60,22 @@ app.post('/api/orders', async (req, res) => { } }); +// Update Order Status +app.patch('/api/orders/:id/status', async (req, res) => { + try { + const { id } = req.params; + const { status } = req.body; + const order = await prisma.order.update({ + where: { id: parseInt(id) }, + data: { status } + }); + res.json(order); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Failed to update order status' }); + } +}); + // Delete an order app.delete('/api/orders/:id', async (req, res) => { try { @@ -113,15 +137,21 @@ app.delete('/api/stock/:id', async (req, res) => { // Update Config app.post('/api/config', async (req, res) => { try { - const { adminCode } = req.body; + const { adminCode, finalDepositDate, cookDay, payIdInfo } = req.body; const config = await prisma.config.findFirst(); + const data: any = {}; + if (adminCode !== undefined) data.adminCode = adminCode; + if (finalDepositDate !== undefined) data.finalDepositDate = finalDepositDate; + if (cookDay !== undefined) data.cookDay = cookDay; + if (payIdInfo !== undefined) data.payIdInfo = payIdInfo; + if (config) { await prisma.config.update({ where: { id: config.id }, - data: { adminCode } + data }); } else { - await prisma.config.create({ data: { adminCode } }); + await prisma.config.create({ data }); } res.sendStatus(200); } catch (error) { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f8ceb54..96b5352 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,8 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@tailwindcss/postcss": "^4.3.0", + "@tailwindcss/vite": "^4.3.0", "react": "^19.2.6", "react-dom": "^19.2.6" }, @@ -31,6 +33,18 @@ "vite": "^8.0.12" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -275,7 +289,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -287,7 +300,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -298,7 +310,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -503,7 +514,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -514,7 +524,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -525,7 +534,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -535,14 +543,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -553,7 +559,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -572,7 +577,6 @@ "version": "0.130.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -601,7 +605,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -618,7 +621,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -635,7 +637,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -652,7 +653,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -669,7 +669,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -686,7 +685,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "glibc" ], @@ -706,7 +704,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "musl" ], @@ -726,7 +723,6 @@ "cpu": [ "ppc64" ], - "dev": true, "libc": [ "glibc" ], @@ -746,7 +742,6 @@ "cpu": [ "s390x" ], - "dev": true, "libc": [ "glibc" ], @@ -766,7 +761,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "glibc" ], @@ -786,7 +780,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "musl" ], @@ -806,7 +799,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -823,7 +815,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -842,7 +833,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -859,7 +849,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -873,14 +862,294 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, "license": "MIT" }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.0.tgz", + "integrity": "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "postcss": "^8.5.10", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "tailwindcss": "4.3.0" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -912,7 +1181,7 @@ "version": "24.12.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -1433,7 +1702,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -1446,6 +1714,19 @@ "dev": true, "license": "ISC" }, + "node_modules/enhanced-resolve": { + "version": "5.21.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.5.tgz", + "integrity": "sha512-mLCNbrQli11K1ySUmuNt4ZUB3OpGIDq4q2vTBTf5cL2lpsRjI9QKqSD0ndjW8FyvcW/Jj46gMe9syyHAsvMa/A==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1676,7 +1957,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1759,7 +2039,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1806,6 +2085,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -1873,6 +2158,15 @@ "dev": true, "license": "ISC" }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1955,7 +2249,6 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -1988,7 +2281,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2009,7 +2301,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2030,7 +2321,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2051,7 +2341,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2072,7 +2361,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2093,7 +2381,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "glibc" ], @@ -2117,7 +2404,6 @@ "cpu": [ "arm64" ], - "dev": true, "libc": [ "musl" ], @@ -2141,7 +2427,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "glibc" ], @@ -2165,7 +2450,6 @@ "cpu": [ "x64" ], - "dev": true, "libc": [ "musl" ], @@ -2189,7 +2473,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2210,7 +2493,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -2250,6 +2532,15 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -2277,7 +2568,6 @@ "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, "funding": [ { "type": "github", @@ -2380,14 +2670,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2447,7 +2735,6 @@ "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2524,7 +2811,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", - "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.130.0", @@ -2597,7 +2883,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -2607,14 +2892,25 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", - "dev": true, "license": "MIT" }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2644,7 +2940,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true }, @@ -2703,7 +2998,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/update-browserslist-db": { @@ -2751,7 +3046,6 @@ "version": "8.0.13", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz", "integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==", - "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", diff --git a/frontend/package.json b/frontend/package.json index 5b0ffad..2cadce9 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,8 @@ "preview": "vite preview" }, "dependencies": { + "@tailwindcss/postcss": "^4.3.0", + "@tailwindcss/vite": "^4.3.0", "react": "^19.2.6", "react-dom": "^19.2.6" }, diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index 2e7af2b..1c87846 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -1,6 +1,6 @@ export default { plugins: { - tailwindcss: {}, + '@tailwindcss/postcss': {}, autoprefixer: {}, }, } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7457a8c..15fea0b 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -16,6 +16,7 @@ function App() { const [adminCodeInput, setAdminCodeInput] = useState(''); const [notification, setNotification] = useState(null); const [adminCode, setAdminCode] = useState('1234'); + const [config, setConfig] = useState(null); const [confirmModal, setConfirmModal] = useState<{ show: boolean, message: string, onConfirm: (() => void) | null }>({ show: false, message: '', onConfirm: null }); useEffect(() => { @@ -29,8 +30,9 @@ function App() { const data = await API.fetchData(); setStock(data.stock); setOrders(data.orders.sort((a: any, b: any) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())); - if (data.config && data.config.adminCode) { - setAdminCode(data.config.adminCode); + if (data.config) { + setAdminCode(data.config.adminCode || '1234'); + setConfig(data.config); } setIsLoading(false); } catch (err) { @@ -101,13 +103,14 @@ function App() { }; try { - await API.submitOrder(orderData); + const order = await API.submitOrder(orderData); setCart([]); - setShowCartModal(false); refreshData(); - showNotification("Order Request Sent! We'll contact you."); + showNotification("Order Request Sent!"); + return order; } catch (error) { showNotification("Error submitting order."); + throw error; } }; @@ -155,10 +158,10 @@ function App() {
{notification &&
{notification}
} - {view === 'customer' ? : } + {view === 'customer' ? : }
- {showCartModal && setShowCartModal(false)} onRemove={removeFromCart} onUpdateQty={updateCartQty} onSubmit={submitOrder} />} + {showCartModal && setShowCartModal(false)} onRemove={removeFromCart} onUpdateQty={updateCartQty} onSubmit={submitOrder} />} {showAdminLogin && (
diff --git a/frontend/src/components/AdminDashboard.tsx b/frontend/src/components/AdminDashboard.tsx index e2c9b00..5bc127f 100644 --- a/frontend/src/components/AdminDashboard.tsx +++ b/frontend/src/components/AdminDashboard.tsx @@ -130,25 +130,45 @@ export default function AdminDashboard({ stock, orders, refreshData, notify, pro
-

{order.name}

-

{order.phone}

-

{new Date(order.timestamp).toLocaleString()}

+
+ {order.orderNumber || `#${order.id}`} +

{order.name}

+
+

{order.phone}

+
+
+ +
-
-
+
{order.items.map((item: any, idx: number) => ( -
-
- {item.name} - {item.qty}{item.unit} @ ${item.finalPricePerUnit || item.price}/{item.unit} -
- ${(item.lineTotal || 0).toFixed(2)} +
+ {item.qty}{item.unit} {item.name} + ${(item.lineTotal || 0).toFixed(2)}
))} -
- Total Est. - ${order.estimatedTotal?.toFixed(2)} +
+
+
+

{new Date(order.timestamp).toLocaleString('en-AU')}

+

{order.totalWeight}kg Weight

+
+
+

Estimated Total

+

${order.estimatedTotal?.toFixed(2)}

+

30% Deposit: ${(order.estimatedTotal * 0.3).toFixed(2)}

@@ -162,6 +182,40 @@ export default function AdminDashboard({ stock, orders, refreshData, notify, pro

Admin Settings

+
+

Scheduling & Deadlines

+
+
+ + API.updateConfig({ finalDepositDate: e.target.value }).then(refreshData)} + className="w-full bg-stone-900 border border-stone-700 rounded p-3 text-white focus:border-red-600 outline-none" + /> +
+
+ + API.updateConfig({ cookDay: e.target.value }).then(refreshData)} + className="w-full bg-stone-900 border border-stone-700 rounded p-3 text-white focus:border-red-600 outline-none" + /> +
+
+
+ + API.updateConfig({ payIdInfo: e.target.value }).then(refreshData)} + className="w-full bg-stone-900 border border-stone-700 rounded p-3 text-white focus:border-red-600 outline-none" + /> +
+
+
setNewAdminCode(e.target.value)} className="w-full bg-stone-900 border border-stone-700 rounded p-3 text-white mb-4 focus:border-red-600 outline-none" /> diff --git a/frontend/src/components/CartModal.tsx b/frontend/src/components/CartModal.tsx index f9729d2..bc68a69 100644 --- a/frontend/src/components/CartModal.tsx +++ b/frontend/src/components/CartModal.tsx @@ -13,9 +13,11 @@ interface CartItem { bulk2Price: number; } -export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubmit }: { cart: CartItem[], onClose: () => void, onRemove: (id: number) => void, onUpdateQty: (id: number, delta: number) => void, onSubmit: (name: string, phone: string) => void }) { +export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubmit, config }: { cart: CartItem[], onClose: () => void, onRemove: (id: number) => void, onUpdateQty: (id: number, delta: number) => void, onSubmit: (name: string, phone: string) => Promise, config?: any }) { const [name, setName] = useState(''); const [phone, setPhone] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + const [submittedOrder, setSubmittedOrder] = useState(null); const getPrice = (item: CartItem, qty: number) => { if (item.bulk2Threshold > 0 && qty >= item.bulk2Threshold && item.bulk2Price > 0) return item.bulk2Price; @@ -29,6 +31,60 @@ export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubm }, 0); const totalWeight = cart.reduce((acc, i) => acc + i.qty, 0); + const depositAmount = total * 0.3; + + const handleSubmit = async () => { + if (!name || !phone) return; + setIsSubmitting(true); + try { + const order = await onSubmit(name, phone); + setSubmittedOrder(order); + } finally { + setIsSubmitting(false); + } + }; + + if (submittedOrder) { + return ( +
+
+
+
+ +
+
+

Order Received!

+

ID: {submittedOrder.orderNumber}

+
+ +
+

Payment Instructions

+
+
+
+

A 30% deposit of ${depositAmount.toFixed(2)} is required before we start the smoker.

+
+
+

Option 1: PayID (Instant)

+
+ {config?.payIdInfo || '0400 000 000'} + Copy +
+

Please use "{submittedOrder.orderNumber}" as the payment description.

+
+
+

Option 2: Cash Deposit

+

Contact us via the phone number provided to arrange a cash drop-off.

+
+
+
+ + +
+
+
+ ); + } return (
@@ -97,14 +153,20 @@ export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubm {cart.length > 0 && (
-
- Total Est. ({totalWeight}kg) - ${total.toFixed(2)} +
+
+ Total Est. ({totalWeight}kg) + ${total.toFixed(2)} +
+
+ 30% Deposit Due + ${depositAmount.toFixed(2)} +
-

Payment Required

-

By submitting, you agree to pay upfront via cash. Delivery/Pickup will be negotiated upon confirmation.

+

Deposit Required

+

Order will not be cooked until the 30% deposit is paid. We accept PayID and Cash.

@@ -116,7 +178,13 @@ export default function CartModal({ cart, onClose, onRemove, onUpdateQty, onSubm setPhone(e.target.value)} className="w-full bg-stone-900 border border-stone-700 rounded-lg p-4 text-white focus:outline-none focus:border-red-600 focus:ring-1 focus:ring-red-600 transition-all" />
- +
)} diff --git a/frontend/src/components/CustomerView.tsx b/frontend/src/components/CustomerView.tsx index 4951aa9..ba731c3 100644 --- a/frontend/src/components/CustomerView.tsx +++ b/frontend/src/components/CustomerView.tsx @@ -65,10 +65,23 @@ function StockCard({ item, onAdd, disabled }: { item: StockItem, onAdd?: () => v ); } -export default function CustomerView({ stock, addToCart }: { stock: StockItem[], addToCart: (item: StockItem) => void }) { +export default function CustomerView({ stock, addToCart, config }: { stock: StockItem[], addToCart: (item: StockItem) => void, config?: any }) { const availableStock = stock.filter(item => item.available); const soldOutStock = stock.filter(item => !item.available); + const formatDate = (d: string) => { + if (!d) return 'TBA'; + try { + return new Date(d).toLocaleString('en-AU', { + weekday: 'long', + day: 'numeric', + month: 'long', + hour: '2-digit', + minute: '2-digit' + }); + } catch (e) { return d; } + }; + return (
@@ -79,14 +92,35 @@ export default function CustomerView({ stock, addToCart }: { stock: StockItem[],

-
-
-
-

Terms of Service

-

- Cash payment is required up front before any smoking begins to cover material costs. - Delivery or pickup options can be negotiated directly with us after your request is confirmed. -

+
+
+
+
+
+

+ Terms of Service & Ordering Policy +

+

+ To secure your order, a 30% non-refundable deposit is required upfront before smoking begins. + Payments can be made via PayID or Cash. + The remaining balance is payable upon pickup or delivery. +

+
+ +
+
+

Final Deposit Deadline

+

{config?.finalDepositDate ? formatDate(config.finalDepositDate) : 'TBA'}

+
+
+

Scheduled Cook Day

+

{config?.cookDay ? new Date(config.cookDay).toLocaleDateString('en-AU', { weekday: 'long', day: 'numeric', month: 'long' }) : 'TBA'}

+
+
+ +
+ Deposits cover material costs and are final. PayID details provided after ordering. +
diff --git a/frontend/src/index.css b/frontend/src/index.css index 469db62..5139d8d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,6 +1,4 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; @layer base { body { diff --git a/frontend/src/services/api.ts b/frontend/src/services/api.ts index 50a34e0..1efda57 100644 --- a/frontend/src/services/api.ts +++ b/frontend/src/services/api.ts @@ -1,13 +1,29 @@ export const API_BASE = import.meta.env.VITE_API_URL || ''; export const API = { - fetchData: () => fetch(`${API_BASE}/api/data`).then(res => res.json()), - submitOrder: (order: any) => fetch(`${API_BASE}/api/orders`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(order) - }).then(res => res.json()), - deleteOrder: (id: number) => fetch(`${API_BASE}/api/orders/${id}`, { method: 'DELETE' }), + fetchData: async () => { + const res = await fetch(`${API_BASE}/api/data`); + return res.json(); + }, + submitOrder: async (order: any) => { + const res = await fetch(`${API_BASE}/api/orders`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(order) + }); + return res.json(); + }, + updateOrderStatus: async (id: number, status: string) => { + const res = await fetch(`${API_BASE}/api/orders/${id}/status`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ status }) + }); + return res.json(); + }, + deleteOrder: async (id: number) => { + await fetch(`${API_BASE}/api/orders/${id}`, { method: 'DELETE' }); + }, clearOrders: () => fetch(`${API_BASE}/api/orders`, { method: 'DELETE' }), updateStock: (item: any) => fetch(`${API_BASE}/api/stock`, { method: 'POST', diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index abc84d6..1df780d 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,9 +1,10 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [react(), tailwindcss()], server: { proxy: { '/api': 'http://localhost:3000'