Initial commit: Meat Monster TypeScript Fullstack
This commit is contained in:
commit
eed1ef4266
41 files changed
+8319
No files matched your search
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
# Keep environment variables out of version control
|
||||
.env
|
||||
|
||||
/src/generated/prisma
|
||||
Generated
+2869
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node dist/server.js",
|
||||
"dev": "ts-node-dev --respawn --transpile-only src/server.ts",
|
||||
"build": "tsc"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@prisma/client": "^7.8.0",
|
||||
"cors": "^2.8.6",
|
||||
"dotenv": "^17.4.2",
|
||||
"express": "^5.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^25.9.1",
|
||||
"prisma": "^7.8.0",
|
||||
"ts-node-dev": "^2.0.0",
|
||||
"typescript": "^6.0.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// This file was generated by Prisma, and assumes you have installed the following:
|
||||
// npm install --save-dev prisma dotenv
|
||||
import "dotenv/config";
|
||||
import { defineConfig } from "prisma/config";
|
||||
|
||||
export default defineConfig({
|
||||
schema: "prisma/schema.prisma",
|
||||
migrations: {
|
||||
path: "prisma/migrations",
|
||||
},
|
||||
datasource: {
|
||||
url: process.env["DATABASE_URL"],
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "StockItem" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"category" TEXT NOT NULL,
|
||||
"price" REAL NOT NULL,
|
||||
"unit" TEXT NOT NULL DEFAULT 'kg',
|
||||
"available" BOOLEAN NOT NULL DEFAULT true,
|
||||
"description" TEXT NOT NULL,
|
||||
"bulk1Threshold" REAL NOT NULL DEFAULT 0,
|
||||
"bulk1Price" REAL NOT NULL DEFAULT 0,
|
||||
"bulk2Threshold" REAL NOT NULL DEFAULT 0,
|
||||
"bulk2Price" REAL NOT NULL DEFAULT 0
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Order" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"phone" TEXT NOT NULL,
|
||||
"totalWeight" REAL NOT NULL,
|
||||
"estimatedTotal" REAL NOT NULL,
|
||||
"timestamp" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"items" TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Config" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"adminCode" TEXT NOT NULL DEFAULT '1234'
|
||||
);
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "sqlite"
|
||||
@@ -0,0 +1,39 @@
|
||||
// This is your Prisma schema file,
|
||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
}
|
||||
|
||||
model StockItem {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
category String
|
||||
price Float
|
||||
unit String @default("kg")
|
||||
available Boolean @default(true)
|
||||
description String
|
||||
bulk1Threshold Float @default(0)
|
||||
bulk1Price Float @default(0)
|
||||
bulk2Threshold Float @default(0)
|
||||
bulk2Price Float @default(0)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
model Config {
|
||||
id Int @id @default(autoincrement())
|
||||
adminCode String @default("1234")
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import path from 'path';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
// API Routes
|
||||
|
||||
// Fetch all data (Stock, Orders, Config)
|
||||
app.get('/api/data', async (req, res) => {
|
||||
try {
|
||||
const stock = await prisma.stockItem.findMany();
|
||||
const orders = await prisma.order.findMany();
|
||||
const config = await prisma.config.findFirst();
|
||||
|
||||
// Parse order items from string to JSON
|
||||
const parsedOrders = orders.map(order => ({
|
||||
...order,
|
||||
items: JSON.parse(order.items)
|
||||
}));
|
||||
|
||||
res.json({ stock, orders: parsedOrders, config });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Failed to fetch data' });
|
||||
}
|
||||
});
|
||||
|
||||
// Submit a new order
|
||||
app.post('/api/orders', async (req, res) => {
|
||||
try {
|
||||
const { name, phone, items, totalWeight, estimatedTotal } = req.body;
|
||||
const order = await prisma.order.create({
|
||||
data: {
|
||||
name,
|
||||
phone,
|
||||
totalWeight,
|
||||
estimatedTotal,
|
||||
items: JSON.stringify(items)
|
||||
}
|
||||
});
|
||||
res.json(order);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Failed to submit order' });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete an order
|
||||
app.delete('/api/orders/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
await prisma.order.delete({ where: { id: parseInt(id) } });
|
||||
res.sendStatus(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Failed to delete order' });
|
||||
}
|
||||
});
|
||||
|
||||
// Clear all orders
|
||||
app.delete('/api/orders', async (req, res) => {
|
||||
try {
|
||||
await prisma.order.deleteMany();
|
||||
res.sendStatus(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Failed to clear orders' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update or Create Stock Item
|
||||
app.post('/api/stock', async (req, res) => {
|
||||
try {
|
||||
const item = req.body;
|
||||
if (item.id) {
|
||||
const updated = await prisma.stockItem.update({
|
||||
where: { id: item.id },
|
||||
data: {
|
||||
...item,
|
||||
id: undefined // Don't update ID
|
||||
}
|
||||
});
|
||||
res.json(updated);
|
||||
} else {
|
||||
const created = await prisma.stockItem.create({ data: item });
|
||||
res.json(created);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Failed to update stock' });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete Stock Item
|
||||
app.delete('/api/stock/:id', async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
await prisma.stockItem.delete({ where: { id: parseInt(id) } });
|
||||
res.sendStatus(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Failed to delete stock item' });
|
||||
}
|
||||
});
|
||||
|
||||
// Update Config
|
||||
app.post('/api/config', async (req, res) => {
|
||||
try {
|
||||
const { adminCode } = req.body;
|
||||
const config = await prisma.config.findFirst();
|
||||
if (config) {
|
||||
await prisma.config.update({
|
||||
where: { id: config.id },
|
||||
data: { adminCode }
|
||||
});
|
||||
} else {
|
||||
await prisma.config.create({ data: { adminCode } });
|
||||
}
|
||||
res.sendStatus(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Failed to update config' });
|
||||
}
|
||||
});
|
||||
|
||||
// Reset Database to defaults
|
||||
app.post('/api/reset', async (req, res) => {
|
||||
try {
|
||||
await prisma.order.deleteMany();
|
||||
await prisma.stockItem.deleteMany();
|
||||
await prisma.config.deleteMany();
|
||||
|
||||
// Seed default admin code
|
||||
await prisma.config.create({ data: { adminCode: '1234' } });
|
||||
|
||||
// Seed some default stock items
|
||||
await prisma.stockItem.createMany({
|
||||
data: [
|
||||
{ name: 'Beef Brisket', category: 'Beef', price: 45, unit: 'kg', description: 'Low and slow smoked brisket', available: true },
|
||||
{ name: 'Pork Shoulder', category: 'Pork', price: 35, unit: 'kg', description: 'Pulled pork perfection', available: true }
|
||||
]
|
||||
});
|
||||
|
||||
res.sendStatus(200);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
res.status(500).json({ error: 'Failed to reset database' });
|
||||
}
|
||||
});
|
||||
|
||||
// Serve Static Files in Production
|
||||
const frontendPath = path.join(__dirname, '../../frontend/dist');
|
||||
app.use(express.static(frontendPath));
|
||||
|
||||
app.get('*', (req, res) => {
|
||||
if (req.path.startsWith('/api')) return;
|
||||
res.sendFile(path.join(frontendPath, 'index.html'));
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server is running on http://localhost:${PORT}`);
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
// Visit https://aka.ms/tsconfig to read more about this file
|
||||
"compilerOptions": {
|
||||
// File Layout
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
|
||||
// Environment Settings
|
||||
// See also https://aka.ms/tsconfig/module
|
||||
"module": "commonjs",
|
||||
"target": "es2020",
|
||||
"types": [],
|
||||
// For nodejs:
|
||||
// "lib": ["esnext"],
|
||||
// "types": ["node"],
|
||||
// and npm install -D @types/node
|
||||
|
||||
// Other Outputs
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
|
||||
// Stricter Typechecking Options
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
|
||||
// Style Options
|
||||
// "noImplicitReturns": true,
|
||||
// "noImplicitOverride": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
// "noFallthroughCasesInSwitch": true,
|
||||
// "noPropertyAccessFromIndexSignature": true,
|
||||
|
||||
// Recommended Options
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"moduleDetection": "force",
|
||||
"skipLibCheck": true,
|
||||
|
||||
"esModuleInterop": true,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user