diff --git a/bun.lockb b/bun.lockb index 8b6a761..9215306 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docker-compose.yml b/docker-compose.yml index 3992aa3..96dcb28 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,10 +14,10 @@ services: app: container_name: app build: - context: ./app + context: . dockerfile: Dockerfile env_file: - - app/.env + - ./.env ports: - "6000:3000" networks: diff --git a/package.json b/package.json index 6d548fe..9c58b1c 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "dependencies": { "@yudiel/react-qr-scanner": "^2.0.4", + "caniuse-lite": "^1.0.30001689", "moment": "^2.30.1", "moment-timezone": "^0.5.45", "mongoose": "^8.4.3", diff --git a/public/icons/add-svgrepo-com.svg b/public/icons/add-svgrepo-com.svg new file mode 100644 index 0000000..b8885df --- /dev/null +++ b/public/icons/add-svgrepo-com.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/public/icons/remove-svgrepo-com.svg b/public/icons/remove-svgrepo-com.svg new file mode 100644 index 0000000..25137fa --- /dev/null +++ b/public/icons/remove-svgrepo-com.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/app/admin/manage/order/[[...orderId]]/page.tsx b/src/app/admin/manage/order/[[...orderId]]/page.tsx index f0acfcd..9de8ece 100644 --- a/src/app/admin/manage/order/[[...orderId]]/page.tsx +++ b/src/app/admin/manage/order/[[...orderId]]/page.tsx @@ -4,7 +4,7 @@ import { useEffect, useState } from "react"; import { IDetectedBarcode, Scanner } from "@yudiel/react-qr-scanner"; import { getFromLocalStorage } from "@/lib/localStorage"; import WithAuth from "@/app/admin/WithAuth"; -import { ORDER_STATES, OrderDocument, OrderStatus, OrderWithId } from "@/model/order"; +import { Order, ORDER_STATES, OrderDocument, OrderStatus } from "@/model/order"; import { formatDateTime, getDateFromTimeSlot } from "@/lib/time"; import SearchInput from "@/app/components/SearchInput"; import ErrorMessage from "@/app/components/ErrorMessage"; @@ -12,8 +12,8 @@ import ErrorMessage from "@/app/components/ErrorMessage"; const Page = ({ params }: { params: { orderId: string } }) => { const [error, setError] = useState(''); - const [orders, setOrders] = useState([]); - const [filteredOrders, setFilteredOrders] = useState([] as OrderWithId[]); // state to hold order status] + const [orders, setOrders] = useState([]); + const [filteredOrders, setFilteredOrders] = useState([] as OrderDocument[]); // state to hold order status] const [filter, setFilter] = useState(''); // state to hold order status const [noFinished, setNoFinished] = useState(true); @@ -41,7 +41,7 @@ const Page = ({ params }: { params: { orderId: string } }) => { } return data; }) - .then(data => setOrders(data)) + .then(data => setOrders(data as OrderDocument[])) .catch(error => { console.error('Error fetching orders', error); setError(error.message) @@ -63,11 +63,11 @@ const Page = ({ params }: { params: { orderId: string } }) => { // Filter the orders useEffect(() => { if (filter) { - setFilteredOrders(orders.filter((order: OrderWithId) => { + setFilteredOrders(orders.filter((order) => { if (order.name.toLowerCase().includes(filter.toLowerCase())) { return true; } - if (order._id.toLowerCase().includes(filter.toLowerCase())) { + if (order._id.toString().toLowerCase().includes(filter.toLowerCase())) { return true; } if (order.status.toLowerCase().includes(filter.toLowerCase())) { @@ -86,7 +86,7 @@ const Page = ({ params }: { params: { orderId: string } }) => { * @param status */ const updateOrderStatus = (_id: string, status: OrderStatus) => { - const order = orders.find(order => order._id === _id); + const order = orders.find(order => order._id.toString() === _id); if (!order) { setError('Order not found'); return; @@ -108,7 +108,7 @@ const Page = ({ params }: { params: { orderId: string } }) => { return data; }) .then(() => { - setOrders(orders.map(order => order._id === _id ? newOrder : order)); + setOrders(orders.map(order => order._id.toString() === _id ? newOrder as OrderDocument : order)); }) .catch(error => setError(error.message)); } @@ -130,7 +130,7 @@ const Page = ({ params }: { params: { orderId: string } }) => { .then(() => { // Update the order by id const newOrders = orders.map((order) => { - if (order._id === _id) { + if (order._id.toString() === _id) { order.isPaid = isPaid; } return order; @@ -205,12 +205,12 @@ const Page = ({ params }: { params: { orderId: string } }) => { .filter(order => noFinished ? !['delivered', 'cancelled'].includes(order.status) : true) // Filter by finished .toSorted((a, b) => getDateFromTimeSlot(a.timeslot).toDate().getTime() - getDateFromTimeSlot(b.timeslot).toDate().getTime()) // Sort by date .map((order, index) => ( // Map the orders -
+
@@ -275,7 +275,7 @@ const Page = ({ params }: { params: { orderId: string } }) => { key={state} disabled={state === order.status} className={`rounded-full px-4 py-2 text-sm font-medium transition duration-200 ${state === order.status ? 'bg-green-500 text-white' : 'bg-gray-300 text-gray-700 hover:bg-gray-400'} focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed`} - onClick={() => updateOrderStatus(order._id, state)} + onClick={() => updateOrderStatus(order._id.toString(), state)} > {state} @@ -284,7 +284,7 @@ const Page = ({ params }: { params: { orderId: string } }) => { className={`rounded-full px-4 py-2 text-sm font-medium transition duration-200 ${order.isPaid ? 'bg-green-300 text-green-700 hover:bg-green-400' : 'bg-red-300 text-red-700 hover:bg-red-400'} focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed`} - onClick={() => setOrderAsPaid(order._id, !order.isPaid)} + onClick={() => setOrderAsPaid(order._id.toString(), !order.isPaid)} > {order.isPaid ? 'Paid' : 'Not Paid'} diff --git a/src/app/admin/manage/page.tsx b/src/app/admin/manage/page.tsx index fed6563..cf747ce 100644 --- a/src/app/admin/manage/page.tsx +++ b/src/app/admin/manage/page.tsx @@ -61,7 +61,7 @@ const Page = () => { .then(data => setStatus(data.status)) .catch((error) => { console.error('There was an error!', error); - setMessage(error) + setMessage('Error getting system status') }) } @@ -135,4 +135,4 @@ const Page = () => { ); } -export default WithAuth(Page); +export default Page; diff --git a/src/app/admin/prepare/page.tsx b/src/app/admin/prepare/page.tsx index b81c12d..9c09dcd 100644 --- a/src/app/admin/prepare/page.tsx +++ b/src/app/admin/prepare/page.tsx @@ -4,7 +4,8 @@ import { useEffect, useState } from "react"; import { getFromLocalStorage } from "@/lib/localStorage"; import ErrorMessage from "@/app/components/ErrorMessage.jsx"; import { getDateFromTimeSlot } from "@/lib/time"; -import { ItemStatus, Order, OrderWithId } from "@/model/order"; +import { ItemStatus } from "@/model/order"; +import type { OrderDocument, OrderModel } from "@/model/order"; import WithAuth from "@/app/admin/WithAuth"; import SearchInput from "@/app/components/SearchInput"; import './Prepare.css'; @@ -14,9 +15,9 @@ const Page = () => { const [error, setError] = useState(''); const [filter, setFilter] = useState('') - const [orders, setOrders] = useState([]); - const [filteredOrders, setFilteredOrders] = useState([]); - const [ordersByTimeslot, setOrdersByTimeslot] = useState(new Map) + const [orders, setOrders] = useState([]); + const [filteredOrders, setFilteredOrders] = useState([]); + const [ordersByTimeslot, setOrdersByTimeslot] = useState(new Map) const [currentTime, setCurrentTime] = useState(new Date()); const [isClient, setIsClient] = useState(false); @@ -59,9 +60,9 @@ const Page = () => { // Filter the orders useEffect(() => { if (filter) { - setFilteredOrders(orders.filter((order: OrderWithId) => + setFilteredOrders(orders.filter((order) => order.name.toLowerCase().includes(filter.toLowerCase()) || - order._id.toLowerCase().includes(filter.toLowerCase()) || + order._id.toString().toLowerCase().includes(filter.toLowerCase()) || order.status.toLowerCase().includes(filter.toLowerCase()) || order.totalPrice.toString().includes(filter) || order.timeslot.includes(filter) || @@ -72,7 +73,7 @@ const Page = () => { }, [filter, orders]); useEffect(() => { - const ordersByTimeslot = new Map + const ordersByTimeslot = new Map filteredOrders.forEach(order => { const timeslot = order.timeslot // Add order to list at the respective timeslot @@ -94,10 +95,10 @@ const Page = () => { setOrdersByTimeslot(sortedOrdersByTimeslots) }, [filteredOrders]) - const setFoodStatusFromLocalStorage = (orders: OrderWithId[]) => { + const setFoodStatusFromLocalStorage = (orders: OrderDocument[]) => { // Get from local storage orders.map(order => { - let foodItems = getFromLocalStorage(`foodItems.${order._id}`, []); + let foodItems = getFromLocalStorage(`foodItems.${order._id.toString()}`, []); if (!foodItems || foodItems.length === 0) { return; } @@ -123,7 +124,7 @@ const Page = () => { * @param status */ const updateItemStatus = (_id: string, itemIndex: number, status: ItemStatus) => { - const order = orders.find(order => order._id === _id); + const order = orders.find(order => order._id.toString() === _id); if (!order) { console.error('Order not found'); return; @@ -148,7 +149,7 @@ const Page = () => { }) .then(() => { // Update the order by id - setOrders(orders.map(order => order._id === _id ? newOrder : order)); + setOrders(orders.map(order => order._id.toString() === _id ? newOrder as OrderDocument : order)); }) .catch(error => setError(error.message)); }; @@ -164,7 +165,7 @@ const Page = () => { * Check if the order has a comment * @param order */ - const hasComment = (order: Order) => { + const hasComment = (order: OrderDocument) => { return ( typeof order.comment === "string" && order.comment != "" && @@ -259,7 +260,7 @@ const Page = () => { diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 0e23893..3531eb8 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -19,7 +19,7 @@ export async function POST(req: Request): Promise { try { // Validate token - const correct_token: string = process.env.PAYMENT_ADMIN_TOKEN || tokens.PAYMENT_ADMIN_TOKEN || ''; + const correct_token = process.env.PAYMENT_ADMIN_TOKEN; if (!correct_token || correct_token.length === 0) { return NextResponse.json({ message: 'Token not set' diff --git a/src/app/api/manage/db/delete/route.ts b/src/app/api/manage/db/delete/route.ts index 9fdd1ba..3beb37a 100644 --- a/src/app/api/manage/db/delete/route.ts +++ b/src/app/api/manage/db/delete/route.ts @@ -1,10 +1,12 @@ // Fill the database -import { Food } from "@/model/food"; +import { FoodModel } from "@/model/food"; import { headers } from "next/headers"; import { extractBearerFromHeaders, validateToken } from "@/lib/auth"; import dbConnect from "@/lib/dbConnect"; -import { Order } from "@/model/order"; +import { OrderModel } from "@/model/order"; import { NextResponse } from "next/server"; +import { System } from "@/model/system"; +import { constants } from "@/config"; // Thanks to https://medium.com/phantom3/next-js-14-build-prerender-error-fix-f3c51de2fe1d export const dynamic = "force-dynamic"; @@ -25,8 +27,8 @@ export async function POST() { }, { status: 401 }); } - await Food.deleteMany({}) - await Order.deleteMany({}) + await FoodModel.deleteMany({}) + await OrderModel.deleteMany({}) return Response.json({ message: 'Successfully deleted database' }) } diff --git a/src/app/api/manage/db/prepare/route.ts b/src/app/api/manage/db/prepare/route.ts index cf62e05..a7456a1 100644 --- a/src/app/api/manage/db/prepare/route.ts +++ b/src/app/api/manage/db/prepare/route.ts @@ -1,5 +1,5 @@ // Fill the database -import { Food } from "@/model/food"; +import { FoodModel } from "@/model/food"; import { headers } from "next/headers"; import { extractBearerFromHeaders, validateToken } from "@/lib/auth"; import dbConnect from "@/lib/dbConnect"; @@ -11,27 +11,14 @@ import { NextResponse } from "next/server"; export const dynamic = "force-dynamic"; export const fetchCache = "force-no-store"; -const pizzas = [ - { name: "Salami", ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Salami 🍕"] }, - { name: "Ham and mushrooms", ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Ham 🥓", "Mushrooms 🍄"] }, - { - name: "Capriccosa", - ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Artichokes 🌱", "Olives 🫒", "Ham 🥓", "Basil 🌿"] - }, - { name: "Margherita", ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Basil 🌿"] }, - { - name: "Veggies", - ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Onions 🧅", "Green Peppers 🫑", "Olives 🫒"] - }, - { name: "Margherita vegan", ingredients: ["Vegan Cheese 🧀", "Tomato Sauce 🍅", "Basil 🌿"] }, - { - name: "Capriccosa vegan", - ingredients: ["Vegan Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Artichokes 🌱", "Olives 🫒", "Basil 🌿"] - } -]; - -const pizza_by_name = (pizza_name: string) => { - return pizzas.filter(pizza => pizza.name == pizza_name).map(pizza => pizza.ingredients).flat(); +const pizzasByName = { + Salami: ["Cheese 🧀", "Tomato Sauce 🍅", "Salami 🍕"], + "Ham and mushrooms": ["Cheese 🧀", "Tomato Sauce 🍅", "Ham 🥓", "Mushrooms 🍄"], + Capriccosa: ["Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Artichokes 🌱", "Olives 🫒", "Ham 🥓", "Basil 🌿"], + Margherita: ["Cheese 🧀", "Tomato Sauce 🍅", "Basil 🌿"], + Veggies: ["Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Onions 🧅", "Green Peppers 🫑", "Olives 🫒"], + "Margherita vegan": ["Vegan Cheese 🧀", "Tomato Sauce 🍅", "Basil 🌿"], + "Capriccosa vegan": ["Vegan Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Artichokes 🌱", "Olives 🫒", "Basil 🌿"] }; /** @@ -56,7 +43,7 @@ export async function POST() { price: 4, dietary: 'meat', type: 'pizza', - ingredients: pizza_by_name('Salami'), + ingredients: pizzasByName['Salami'], size: 0.5 }, { @@ -64,7 +51,7 @@ export async function POST() { price: 8, dietary: 'meat', type: 'pizza', - ingredients: pizza_by_name('Salami'), + ingredients: pizzasByName['Salami'], size: 1 }, { @@ -72,7 +59,7 @@ export async function POST() { price: 4, dietary: 'meat', type: 'pizza', - ingredients: pizza_by_name('Ham and mushrooms'), + ingredients: pizzasByName['Ham and mushrooms'], size: 0.5 }, { @@ -80,7 +67,7 @@ export async function POST() { price: 8, dietary: 'meat', type: 'pizza', - ingredients: pizza_by_name('Ham and mushrooms'), + ingredients: pizzasByName['Ham and mushrooms'], size: 1 }, { @@ -88,7 +75,7 @@ export async function POST() { price: 4, type: 'pizza', dietary: 'meat', - ingredients: pizza_by_name('Capriccosa'), + ingredients: pizzasByName['Capriccosa'], size: 0.5 }, { @@ -96,19 +83,19 @@ export async function POST() { price: 8, type: 'pizza', dietary: 'meat', - ingredients: pizza_by_name('Capriccosa'), + ingredients: pizzasByName['Capriccosa'], size: 1 }, - { name: 'Margherita half', price: 3, type: 'pizza', ingredients: pizza_by_name('Margherita'), size: 0.5 }, - { name: 'Margherita full', price: 6, type: 'pizza', ingredients: pizza_by_name('Margherita'), size: 1 }, - { name: 'Veggies half', price: 3, type: 'pizza', ingredients: pizza_by_name('Veggies'), size: 0.5 }, - { name: 'Veggies full', price: 6, type: 'pizza', ingredients: pizza_by_name('Veggies'), size: 1 }, + { name: 'Margherita half', price: 3, type: 'pizza', ingredients: pizzasByName['Margherita'], size: 0.5 }, + { name: 'Margherita full', price: 6, type: 'pizza', ingredients: pizzasByName['Margherita'], size: 1 }, + { name: 'Veggies half', price: 3, type: 'pizza', ingredients: pizzasByName['Veggies'], size: 0.5 }, + { name: 'Veggies full', price: 6, type: 'pizza', ingredients: pizzasByName['Veggies'], size: 1 }, { name: 'Margherita vegan half', price: 3, dietary: 'vegan', type: 'pizza', - ingredients: pizza_by_name('Margherita vegan'), + ingredients: pizzasByName['Margherita vegan'], size: 0.5 }, { @@ -116,7 +103,7 @@ export async function POST() { price: 6, dietary: 'vegan', type: 'pizza', - ingredients: pizza_by_name('Margherita vegan'), + ingredients: pizzasByName['Margherita vegan'], size: 1 }, { @@ -124,7 +111,7 @@ export async function POST() { price: 3, dietary: 'vegan', type: 'pizza', - ingredients: pizza_by_name('Capriccosa vegan'), + ingredients: pizzasByName['Capriccosa vegan'], size: 0.5 }, { @@ -132,12 +119,13 @@ export async function POST() { price: 6, dietary: 'vegan', type: 'pizza', - ingredients: pizza_by_name('Capriccosa vegan'), + ingredients: pizzasByName['Capriccosa vegan'], size: 1 }, ]; + console.log('Pizzas:', pizzas); for (const pizza of pizzas) { - await new Food(pizza).save(); + await new FoodModel(pizza).save(); } // Add the system diff --git a/src/app/api/manage/system/status/route.ts b/src/app/api/manage/system/status/route.ts index 3077bdc..bd18b38 100644 --- a/src/app/api/manage/system/status/route.ts +++ b/src/app/api/manage/system/status/route.ts @@ -13,7 +13,7 @@ export const fetchCache = "force-no-store"; * Set the system status * @constructor */ -export async function GET(req: Request, { params }: { params: { status: string } }) { +export async function GET(req: Request) { await dbConnect(); // Set the system status diff --git a/src/app/api/order/[id]/cancel/route.ts b/src/app/api/order/[id]/cancel/route.ts index d5c1a8f..a495220 100644 --- a/src/app/api/order/[id]/cancel/route.ts +++ b/src/app/api/order/[id]/cancel/route.ts @@ -1,6 +1,6 @@ import dbConnect from "@/lib/dbConnect"; import mongoose from "mongoose"; -import { Order } from "@/model/order"; +import { OrderModel } from "@/model/order"; /** * Allow user to cancel an order. @@ -27,7 +27,7 @@ export async function PUT(req: Request, { params }: { params: { id: string } }) try { // Find the order by ID - const foundOrder = await Order.findById(id); + const foundOrder = await OrderModel.findById(id); if (!foundOrder) { return new Response('Order not found', { status: 404 }); diff --git a/src/app/api/order/[id]/pay/route.ts b/src/app/api/order/[id]/pay/route.ts index 7057532..37ad324 100644 --- a/src/app/api/order/[id]/pay/route.ts +++ b/src/app/api/order/[id]/pay/route.ts @@ -2,7 +2,7 @@ import mongoose from "mongoose"; import dbConnect from "@/lib/dbConnect"; import { headers } from "next/headers"; import { extractBearerFromHeaders, validateToken } from "@/lib/auth"; -import { Order } from "@/model/order"; +import { OrderModel } from "@/model/order"; import { NextResponse } from "next/server"; /** @@ -25,7 +25,7 @@ export async function GET(req: Request, { params }: { params: { id: string } }) } // Find the order by ID - const order = await Order.findById(id); + const order = await OrderModel.findById(id); if (!order) { return NextResponse.json({ message: 'The Order was not found.' @@ -66,7 +66,7 @@ export async function PUT(req: Request, { params }: { params: { id: string } }) try { // Find the order by ID - const foundOrder = await Order.findById(id); + const foundOrder = await OrderModel.findById(id); if (!foundOrder) { return NextResponse.json({ diff --git a/src/app/api/order/[id]/route.ts b/src/app/api/order/[id]/route.ts index e2030cd..da15aba 100644 --- a/src/app/api/order/[id]/route.ts +++ b/src/app/api/order/[id]/route.ts @@ -1,7 +1,7 @@ import dbConnect from "@/lib/dbConnect"; import mongoose from "mongoose"; -import { Order } from "@/model/order"; -import { Food, FoodDocument } from "@/model/food"; +import { OrderModel } from "@/model/order"; +import { FoodModel, FoodDocument } from "@/model/food"; export async function GET(req: Request, { params }: { params: { id: string } }) { @@ -22,7 +22,7 @@ export async function GET(req: Request, { params }: { params: { id: string } }) } // Find the order by ID - const order = await Order.findById(id); + const order = await OrderModel.findById(id); if (!order) { return new Response(` The order with the ID ${id} was not found. @@ -32,7 +32,7 @@ export async function GET(req: Request, { params }: { params: { id: string } }) } // Get the foods for the order - const foodsDetails = await Food + const foodsDetails = await FoodModel .find({ _id: { $in: order.items.map((item) => item.food) } }) // Create a map of food details @@ -48,7 +48,7 @@ export async function GET(req: Request, { params }: { params: { id: string } }) name: order.name, comment: order.comment || "", items: order.items.map((item) => ({ - food: foodById[item.food._id], + food: foodById[item.food._id.toString()], status: item.status })), orderDate: order.orderDate, diff --git a/src/app/api/order/route.ts b/src/app/api/order/route.ts index 8e1173a..e43b3b6 100644 --- a/src/app/api/order/route.ts +++ b/src/app/api/order/route.ts @@ -1,9 +1,9 @@ import mongoose from "mongoose"; -import { Food, FoodDocument } from "@/model/food"; +import { FoodModel, FoodDocument } from "@/model/food"; import dbConnect from "@/lib/dbConnect"; import { headers } from "next/headers"; import { extractBearerFromHeaders, validateToken } from "@/lib/auth"; -import { Order } from "@/model/order"; +import { OrderModel } from "@/model/order"; import { constants, ORDER } from "@/config"; import { System } from "@/model/system"; import { NextResponse } from "next/server"; @@ -13,14 +13,14 @@ export async function GET(req: Request) { // Authenticate the user const headersList = headers() - /* if (!await validateToken(extractBearerFromHeaders(headersList))) { + if (!await validateToken(extractBearerFromHeaders(headersList))) { return NextResponse.json({ message: 'Unauthorized' }, { status: 401 }); - } */ + } - const orders = await Order.find(); - const foods = await Food.find(); + const orders = await OrderModel.find(); + const foods = await FoodModel.find(); const transformedOrders = await Promise.all(orders.map(async order => { // Get the foods for the order @@ -38,7 +38,7 @@ export async function GET(req: Request) { name: order.name, comment: order.comment || "", items: order.items.map((item) => ({ - food: foodById[item.food._id], + food: foodById[item.food._id.toString()], status: item.status })), orderDate: order.orderDate, @@ -84,7 +84,7 @@ export async function POST(req: Request) { // Check if the pizzas are valid const foodIds: string[] = items.map((pizza: { _id: string }) => pizza._id); - if (!foodIds.every(async (foodId: string) => await Food.exists({ _id: foodId }))) { + if (!foodIds.every(async (foodId: string) => await FoodModel.exists({ _id: foodId }))) { console.error('Some items are missing', items); return NextResponse.json({ message: 'Some items are missing' @@ -92,14 +92,14 @@ export async function POST(req: Request) { } // Find orders with the same time slot - const orders = await Order.find( + const orders = await OrderModel.find( { timeslot: timeslot, status: { $nin: ['cancelled'] } } ); // Find all food items - const food = await Food.find(); + const food = await FoodModel.find(); const foodById = food .reduce((map: { [id: string]: FoodDocument }, food) => { map[food._id.toString()] = food; @@ -108,7 +108,7 @@ export async function POST(req: Request) { // Sum up all items in the orders const orderItemsTotal = orders .flatMap(order => order.items) - .reduce((total, item) => total + foodById[item.food._id].size, 0); + .reduce((total, item) => total + foodById[item.food._id.toString()].size, 0); // Check if the total number of items is not too high console.log('Order items total:', orderItemsTotal, currentOrderItemsTotal); if (orderItemsTotal + currentOrderItemsTotal > ORDER.MAX_ITEMS_PER_TIMESLOT) { @@ -122,7 +122,7 @@ export async function POST(req: Request) { // Don't trust the price from the request body const totalPrice: number = await foodIds .reduce(async (total: Promise, foodId: string) => { - const food = await Food.findOne({ _id: foodId }); + const food = await FoodModel.findOne({ _id: foodId }); if (!food) { console.error('Pizza not found', foodId) return total; @@ -131,7 +131,7 @@ export async function POST(req: Request) { }, Promise.resolve(0)); // Create the order - const order = new Order(); + const order = new OrderModel(); order.name = (name || "anonymous").slice(0, 30); order.comment = (comment || "No comment").slice(0, 500); order.items = items.map((item: { _id: string }) => ({ food: item._id })); @@ -167,7 +167,7 @@ export async function PUT(req: Request) { try { // Find the order by ID - const foundOrder = await Order.findById(id); + const foundOrder = await OrderModel.findById(id); if (!foundOrder) { return new Response('Order not found', { status: 404 }); diff --git a/src/app/api/pizza/route.ts b/src/app/api/pizza/route.ts index 55dc971..42f9cc0 100644 --- a/src/app/api/pizza/route.ts +++ b/src/app/api/pizza/route.ts @@ -1,4 +1,4 @@ -import { Food } from "@/model/food"; +import { FoodModel } from "@/model/food"; import dbConnect from "@/lib/dbConnect"; import { headers } from "next/headers"; import { extractBearerFromHeaders, validateToken } from "@/lib/auth"; @@ -9,7 +9,7 @@ export async function GET(req: Request) { await dbConnect(); try { - const pizzas = await Food.find({ type: 'pizza' }); + const pizzas = await FoodModel.find({ type: 'pizza' }); return Response.json(pizzas) } catch (error) { console.error('Error fetching pizzas:', error); @@ -32,7 +32,7 @@ export async function POST(req: Request) { const newPizza = await req.json() try { - const pizza = new Food(newPizza); + const pizza = new FoodModel(newPizza); await pizza.save(); return Response.json(pizza); } catch (error) { @@ -55,7 +55,7 @@ export async function PUT(req: Request) { const pizza = await req.json() try { - const updatedPizza = await Food.findById(pizza._id); + const updatedPizza = await FoodModel.findById(pizza._id); if (!updatedPizza) { return NextResponse.json({ message: 'Pizza not found' diff --git a/src/app/api/timeline/route.ts b/src/app/api/timeline/route.ts index c0033a5..947a50e 100644 --- a/src/app/api/timeline/route.ts +++ b/src/app/api/timeline/route.ts @@ -1,9 +1,9 @@ import dbConnect from "@/lib/dbConnect"; import { constants } from '@/config'; -import { Order } from '@/model/order'; +import { OrderModel } from '@/model/order'; import moment from 'moment-timezone'; -import { Food, FoodDocument } from "@/model/food"; +import { FoodModel, FoodDocument } from "@/model/food"; import { getDateFromTimeSlot } from "@/lib/time"; // Thanks to https://medium.com/phantom3/next-js-14-build-prerender-error-fix-f3c51de2fe1d @@ -39,14 +39,14 @@ export async function GET(request: Request) { currentTime = moment(currentTime).add(TIME_SLOT_SIZE_MINUTES, 'minutes'); } - const orders = await Order.find({ + const orders = await OrderModel.find({ orderDate: { $gte: moment().utc().subtract(10 * TIME_SLOT_SIZE_MINUTES, 'minutes'), $lt: moment().utc().add(20 * TIME_SLOT_SIZE_MINUTES, 'minutes'), }, status: { $ne: 'cancelled' }, // TODO: Check if this is needed }); - const food = await Food.find(); + const food = await FoodModel.find(); const foodById = food .reduce((map: { [id: string]: FoodDocument }, food) => { map[food._id.toString()] = food; @@ -59,7 +59,7 @@ export async function GET(request: Request) { const timeSlot = getDateFromTimeSlot(timeslot); if (timeSlot >= startTime && timeSlot < stopTime) { items.forEach(({ food }) => { - totalAmount += foodById[food._id].size; + totalAmount += foodById[food._id.toString()].size; }); } }); diff --git a/src/app/order/Order.css b/src/app/order/Order.css index f28fca8..91e4d06 100644 --- a/src/app/order/Order.css +++ b/src/app/order/Order.css @@ -41,12 +41,6 @@ transition: background-color 0.3s, transform 0.3s; } -.pizza.order:hover { - background-color: rgba(var(--danger-color-rgb), 0.2); - transform: scale(1.01); -} - .pizza:hover { - background-color: rgba(var(--primary-color-rgb), 0.1); transform: scale(1.01); } diff --git a/src/app/order/[orderNumber]/page.tsx b/src/app/order/[orderNumber]/page.tsx index 23df9c8..b7aedcb 100755 --- a/src/app/order/[orderNumber]/page.tsx +++ b/src/app/order/[orderNumber]/page.tsx @@ -4,13 +4,13 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import { getFromLocalStorage } from "@/lib/localStorage"; import { formatDateTime, getDateFromTimeSlot } from "@/lib/time"; -import { OrderStatus, OrderWithId } from "@/model/order"; +import { OrderStatus, Order } from "@/model/order"; import OrderQR from "@/app/components/order/OrderQR"; const Page = ({ params }: { params: { orderNumber: string } }) => { const [error, setError] = useState(null); - const [order, setOrder] = useState(null) + const [order, setOrder] = useState(null) // Check if logged in const router = useRouter(); @@ -138,7 +138,7 @@ const Page = ({ params }: { params: { orderNumber: string } }) => { {order?.items?.map((item) => ( <>
-
+
{/* Product Image */}
diff --git a/src/app/page.jsx b/src/app/page.jsx deleted file mode 100644 index 35c685a..0000000 --- a/src/app/page.jsx +++ /dev/null @@ -1,328 +0,0 @@ -'use client' - -import './order/Order.css'; -import {useEffect, useState} from "react"; -import OrderButton from "@/app/components/order/OrderButton.jsx"; -import Timeline from "@/app/components/Timeline.jsx"; -import ErrorMessage from "@/app/components/ErrorMessage.jsx"; -import WithSystemCheck from "./WithSystemCheck.jsx"; -import {getDateFromTimeSlot} from "@/lib/time"; - -const EVERY_X_SECONDS = 60; - -const Food = ({food, className, onClick}) => { - return ( -
  • -
    - {food.price}€ {food.name} -
    -
    - {food.dietary && {food.dietary}} - {food.type} -
    -
  • - ); -}; - -const FloatingIslandElement = ({content, title}) => { - return ( -
    -
    -
    - {content} -
    -
    -
    - {title} -
    -
    - ) -} - - -const PizzaIngredientsTable = () => { - const tableCellClass = "border border-gray-300 px-4 py-2"; - const headerCellClass = `bg-gray-200 ${tableCellClass}`; - - const pizzas = [ - {name: "Salami", ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Salami 🍕"]}, - {name: "Ham and mushrooms", ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Ham 🥓", "Mushrooms 🍄"]}, - { - name: "Capriccosa", - ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Artichokes 🌱", "Olives 🫒", "Ham 🥓", "Basil 🌿"] - }, - {name: "Margherita", ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Basil 🌿"]}, - { - name: "Veggies", - ingredients: ["Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Onions 🧅", "Green Peppers 🫑", "Olives 🫒"] - }, - {name: "Margherita vegan", ingredients: ["Vegan Cheese 🧀", "Tomato Sauce 🍅", "Basil 🌿"]}, - { - name: "Capriccosa vegan", - ingredients: ["Vegan Cheese 🧀", "Tomato Sauce 🍅", "Mushrooms 🍄", "Artichokes 🌱", "Olives 🫒", "Basil 🌿"] - } - ]; - - return ( -
    -

    Pizza Ingredients:

    - - - - - - - - - {pizzas.map((pizza, index) => ( - - - - - ))} - -
    PizzaIngredients
    - {pizza.name} - - {pizza.ingredients.join(", ")} -
    -
    - ); -}; - -// Order component -const Page = () => { - // State to hold the order - const [error, setError] = useState(''); - const [foods, setFoods] = useState([]); - const [order, setOrder] = useState({name: '', items: [], comment: '', timeslot: null}); - - // Set the start and end date for the timeline - const start = new Date(); - start.setHours(start.getHours() - 1); // Previous hour - start.setMinutes(0, 0, 0); - - const end = new Date(); - end.setHours(end.getHours() + 1); // Next hour - end.setMinutes(59, 59, 999); - - // Fetch the pizza menu from the server - useEffect(() => { - // Fetch the pizza menu from the server - fetch("/api/pizza") - .then(async response => { - const data = await response.json(); - if (!response.ok) { - const error = (data && data.message) || response.statusText; - throw new Error(error); - } - return data; - }) - .then(data => { - setFoods(data); - }) - .catch(error => { - console.error('There was an error!', error); - setError(error.message); - }); - }, []); - - /** - * Update the order with the new values - * @param updatedOrder - */ - const updateOrder = (updatedOrder) => { - setOrder({...order, ...updatedOrder}); - }; - - /** - * Add food to the order - * @param food - */ - const addToOrder = (food) => { - setError(''); - const newOrder = [...order.items]; - newOrder.push(food); - updateOrder({items: newOrder}); - } - - /** - * Remove food from the order - * @param index - */ - const removeFromOrder = (index) => { - setError(''); - const newOrder = [...order.items]; - newOrder.splice(index, 1); - updateOrder({items: newOrder}); - } - - /** - * Set the timeslot of the order - */ - const setTimeslot = (timeslot) => { - // Check if the timeslot is not in the past - - const BUFFER = 10; - - // Get time with buffer - const currentTime = new Date(); - currentTime.setMinutes(currentTime.getMinutes() + BUFFER); - - const timeslotTime = getDateFromTimeSlot(timeslot).toDate() - - if (timeslotTime < currentTime) { - setError('You cannot choose a timeslot in the past.'); - setTimeout(() => setError(''), 5000); - return; - } - - updateOrder({timeslot: timeslot}); - } - - /** - * Set the name of the order - * @param name - */ - const setName = (name) => { - updateOrder({name: name}); - }; - - /** - * Set the comment of the order - */ - const setComment = (comment) => { - updateOrder({comment: comment}); - } - - - return ( -
    -
    -

    Order your pizza at Sommerfest 2024!

    -
    -
      -
    1. -

      Choose Pizza:

      -

      Select whole or halved from the list below (a whole pizza has a diameter of 12 inches / 30 cm).

      -
    2. -
    3. -

      Pick-Up Time:

      -

      Choose a time (some slots may be full).

      -
    4. -
    5. -

      Pay in Cash:

      -

      Pay when collecting at the counter.

      -
    6. -
    -
    -

    Order Times:

    -

    Earliest pick-up: 17:25

    -

    Latest order: 23:40

    -
    -

    Enjoy your evening!

    -
    -
    - -
    -
    -
    -

    Menu:

    -

    Select your pizza from the list below. - Ingredients are at the bottom.

    - -
      - {foods - .filter(food => food.enabled) - .map((food, index) => ( - addToOrder(food)} - /> - ))} - {!foods.length &&

      Loading...

      } -
    -
    - -
    - -

    Your current order:

    - {error && } -
      - {order.items - .map((food, index) => ( - removeFromOrder(food)} - /> - ))} -
    - -
    - - setName(e.target.value)} - /> - -