├── .gitignore
├── README.md
├── app
├── (main-area)
│ ├── generateQR
│ │ └── page.js
│ ├── menuBuilder
│ │ └── page.js
│ └── services
│ │ └── page.js
├── About
│ └── page.js
├── Auth
│ └── page.js
├── Contact
│ └── page.js
├── MenuInterface
│ └── [user]
│ │ └── page.js
├── api
│ ├── Activeuser
│ │ └── route.js
│ ├── auth
│ │ └── [...nextauth]
│ │ │ └── route.js
│ ├── gemini
│ │ └── route.js
│ ├── get-ip
│ │ └── route.js
│ ├── history
│ │ └── route.js
│ ├── menuItems
│ │ └── route.js
│ ├── order
│ │ ├── cancel
│ │ │ └── route.js
│ │ ├── done
│ │ │ └── route.js
│ │ └── route.js
│ ├── sections
│ │ └── route.js
│ └── upload
│ │ └── route.js
├── dashboard
│ ├── layout.js
│ ├── liveorder
│ │ └── page.js
│ ├── page.js
│ ├── salesrevenue
│ │ └── page.js
│ └── transactionhistory
│ │ └── page.js
├── globals.css
├── layout.js
├── lib
│ ├── DataBase.js
│ ├── model
│ │ ├── Menu.js
│ │ ├── SectionDB.js
│ │ ├── Visitors.js
│ │ ├── orderHistory.js
│ │ └── orderItem.js
│ └── mongodb.js
└── page.js
├── components
├── AccountDropdown.js
├── CompanyShow.js
├── GenerateQR.js
├── ServiceCard.js
├── Sessionwrap.js
├── Signout.js
├── TypeWriter.js
├── dashboard
│ ├── aside.js
│ ├── home.js
│ └── orderTracking.js
├── doc
│ └── recept.js
├── footer.js
├── homeNavButton.js
├── menuCard.js
├── menuDone.js
├── menuInterface
│ ├── AI_interface.js
│ ├── Cart.js
│ ├── Confirmpage.js
│ ├── Footer.js
│ ├── Menupopulate.js
│ ├── SectionTitle.js
│ ├── ai_chat.js
│ ├── confirm_pop.js
│ ├── itemCard.js
│ ├── itemContext.js
│ ├── navbar.js
│ └── sections.js
├── menuSubmitbtn.js
├── menu_ItemCard_default.js
├── menu_builder_section.js
├── navbar.js
└── service-poster.js
├── eslint.config.mjs
├── jsconfig.json
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.mjs
├── public
├── MenuInterface
│ ├── Ai.mp4
│ ├── ailogo.mp4
│ ├── b1.jpg
│ └── b2.jpg
├── dashboard
│ └── b1.jpg
├── file.svg
├── globe.svg
├── homepage
│ └── bg.png
├── next.svg
├── notification.mp3
├── profile.jpg
├── qrbuilder
│ ├── default-img.png
│ └── download.jpeg
├── serviceTab
│ ├── poster-1.jpeg
│ ├── poster-2.png
│ ├── poster-3.png
│ ├── qr.png
│ ├── s-1.jpg
│ ├── s-2.jpg
│ ├── s-3.jpg
│ └── s-4.jpg
├── shared
│ ├── Softcore.png
│ ├── TNE-logo.png
│ ├── close-up-hands-holding-smartphones.jpg
│ ├── logo.mp4
│ ├── logo.png
│ └── profile-icon.jpeg
├── uploads
│ ├── 360_F_733954401_TdqG5vccceLm5JuSBIGoImDaBxyoMvOc.jpg
│ ├── 564931.jpg
│ ├── 708311.jpg
│ ├── 7509855.jpg
│ ├── Baingan Bharta.jpeg
│ ├── Boondi Raita.jpeg
│ ├── Butter Naan.jpeg
│ ├── Chicken Biryani.jpeg
│ ├── Chicken Butter Masala.jpeg
│ ├── Chicken Shorba.jpg
│ ├── Chicken Tikka.jpeg
│ ├── Chicken Tikka.jpg
│ ├── Coconut Chutney.jpeg
│ ├── Dal Makhani.jpeg
│ ├── Fish Amritsari.jpg
│ ├── Fish Curry.jpeg
│ ├── Gajar Ka Halwa.webp
│ ├── Garlic Naan.jpeg
│ ├── Gulab Jamun.jpg
│ ├── Hyderabadi Biryani.jpeg
│ ├── Jalebi (per plate).jpeg
│ ├── Jeera Rice.jpeg
│ ├── Kachumber Salad.jpeg
│ ├── Lachha Paratha.jpg
│ ├── Manchow Soup.jpeg
│ ├── Mutton Biryani.jpeg
│ ├── Mutton Rogan Josh.jpeg
│ ├── Mutton Seekh Kebab.jpg
│ ├── OIP.jpg
│ ├── OnionCucumber Raita.jpeg
│ ├── Pakoras (Mix Veg).jpeg
│ ├── Pakoras (Mix Veg).jpg
│ ├── Palak Paneer.jpeg
│ ├── Paneer Butter Masala.jpeg
│ ├── Paneer Butter Masala.jpg
│ ├── Peas Pulao.jpeg
│ ├── Rasgulla.jpeg
│ ├── Rasmalai (2 pcs).jpeg
│ ├── Roomali Roti.jpg
│ ├── Steamed Basmati Rice.jpeg
│ ├── Sweet Corn Soup.webp
│ ├── Tandoori Roti.jpeg
│ ├── Tomato Soup.jpeg
│ ├── Veg Biryani.jpeg
│ ├── aloo tikki.jpeg
│ ├── aura++.gif
│ ├── baingan-bharta.jpg
│ ├── cholekulche2.jpg
│ ├── momos.jpg
│ ├── pain-naruto-indigo-5120x2880-10830.png
│ ├── paneer-tikka.jpg
│ ├── samosa.jpeg
│ └── sung-jinwoo-movie-5120x2880-20245.jpg
├── vercel.svg
└── window.svg
└── tailwind.config.mjs
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.*
7 | .yarn/*
8 | !.yarn/patches
9 | !.yarn/plugins
10 | !.yarn/releases
11 | !.yarn/versions
12 |
13 | # testing
14 | /coverage
15 |
16 | # next.js
17 | /.next/
18 | /out/
19 |
20 | # production
21 | /build
22 |
23 | # misc
24 | .DS_Store
25 | *.pem
26 |
27 | # debug
28 | npm-debug.log*
29 | yarn-debug.log*
30 | yarn-error.log*
31 | .pnpm-debug.log*
32 |
33 | # env files (can opt-in for committing if needed)
34 | .env*
35 |
36 | # vercel
37 | .vercel
38 |
39 | # typescript
40 | *.tsbuildinfo
41 | next-env.d.ts
42 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🍽️ QR MENU - Digital Menu & Smart Ordering System
2 |
3 |
4 | ## 🚀 Overview
5 | QR MENU is a **Next.js-based** digital restaurant menu and ordering system designed to replace traditional **paper menus**. It allows **restaurant owners** to dynamically update their menus, generate **QR codes for tables**, and track **orders, revenue, and visitor trends** in real-time.
6 |
7 | 🔹 **No More Reprinting Menus** – Update anytime, instantly!
8 | 🔹 **QR Code Table Management** – Unique QR per table for **seamless ordering**.
9 | 🔹️ **🤖 AI-Powered Food Assistant** – Helps customers with **menu suggestions, dietary preferences, and dish recommendations**.
10 | 🔹 **Real-time Orders & Analytics** – Track transactions, revenue, and customer trends.
11 | 🔹 **Secure Owner Login** – Prevents unauthorized menu access using **NextAuth.js**.
12 | 🔹 **Smooth UI & Mobile Responsive** – Powered by **Next.js + Tailwind CSS**.
13 |
14 | ---
15 |
16 | ## 🏗️ Tech Stack
17 | - **Frontend:** Next.js (React) + Tailwind CSS
18 | - **Backend:** Node.js + Express.js
19 | - **Database:** MongoDB
20 | - **Authentication:** NextAuth.js
21 |
22 | ---
23 |
24 | ## 📌 Installation & Setup
25 | ### **1️⃣ Clone the Repository**
26 | ```sh
27 | git clone https://github.com/Softenrj/qr-menu.git
28 | cd qr-menu
29 | ```
30 |
31 | ### **2️⃣ Install Dependencies**
32 | ```sh
33 | # Install dependencies
34 | npm install
35 | ```
36 |
37 | ### **3️⃣ Environment Variables**
38 | Create a `.env` file and add the following:
39 | ```sh
40 | NEXTAUTH_URL=your-site-url
41 | MONGODB_URL=your-mongodb-url
42 | NEXTAUTH_SECRET=your-secret-key
43 | //example
44 | GITHUB_ID=key
45 | GITHUB_SECRET=key
46 | GEMINI_URL=key
47 | ```
48 |
49 | ### **4️⃣ Start the Development Server**
50 | ```sh
51 | npm run dev
52 | ```
53 |
54 | ---
55 |
56 | ## 🎨 UI & User Experience
57 | ✅ **Modern, responsive design** with **Tailwind CSS**.
58 | ✅ **Smooth animations & intuitive navigation** for a better experience.
59 | ✅ **Mobile-first approach** ensures seamless use across devices.
60 |
61 | ## 🔒 Access the Dashboard without authentication for **testing purposes**.
62 | `Remove This File from 👉 'qr-menu/app/dashboard/page.js'`
63 | ```sh
64 | if (status === "unauthenticated") {
65 | router.push('/');
66 | }
67 | ```
68 |
69 |
70 | ## 📜 License
71 | This project is intended for **educational purposes only**. If you wish to use it for commercial or other purposes, please request **permission**
72 |
73 | ---
74 |
75 | ## 🤝 Contributing
76 | Contributions are welcome! Submit issues or pull requests to improve the project.
77 |
78 | ---
79 |
80 | ## 📬 Contact
81 | 📧 Email: rjsharmase@gmail.com
82 |
83 |
--------------------------------------------------------------------------------
/app/(main-area)/generateQR/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useState, useEffect } from "react";
3 | import GenerateQR from "@/components/GenerateQR";
4 | import { Save, Delete, Download } from "@mui/icons-material";
5 | import { v4 as uuidv4 } from "uuid";
6 | import Navbar from "@/components/navbar";
7 | import Footer from "@/components/footer";
8 | import TypeWriter from "@/components/TypeWriter";
9 |
10 | const QRGeneratorPage = () => {
11 | const [inputId, setInputId] = useState("");
12 | const [qrValue, setQrValue] = useState("");
13 | const [savedQrs, setSavedQrs] = useState([]);
14 | const [date, setDate] = useState("");
15 |
16 | useEffect(() => {
17 | setDate(Date.now());
18 | }, []);
19 |
20 | const generateNewQR = () => {
21 | const uniqueId = uuidv4().slice(0, 8);
22 | const newValue = inputId || uniqueId;
23 | setQrValue(newValue);
24 | };
25 |
26 | const handleSaveQR = () => {
27 | if (qrValue) {
28 | setSavedQrs((prev) => [
29 | ...prev,
30 | {
31 | id: inputId || `QR-${uuidv4().slice(0, 4)}`,
32 | value: qrValue,
33 | timestamp: Date.now(),
34 | },
35 | ]);
36 | setInputId("");
37 | setQrValue("");
38 | }
39 | };
40 |
41 | const handleDownload = (id, date) => {
42 | const canvas = document.querySelector(`#${id} canvas`);
43 | if (!canvas) {
44 | console.error("Canvas not found for ID:", id);
45 | return;
46 | }
47 | const link = document.createElement("a");
48 | link.download = `qr-code-${date}.png`;
49 | link.href = canvas.toDataURL();
50 | link.click();
51 | };
52 |
53 | const handleDelete = (timestamp) => {
54 | setSavedQrs(savedQrs.filter((qr) => qr.timestamp !== timestamp));
55 | };
56 |
57 | return (
58 |
59 |
60 |
61 |
62 | Generate Your
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | Custom Identifier (optional)
79 |
80 | setInputId(e.target.value)}
84 | placeholder="e.g., Table 2"
85 | className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-pink-500 focus:border-pink-500"
86 | />
87 |
88 |
89 |
93 | Generate QR Code
94 |
95 |
96 | {qrValue && (
97 |
101 |
102 | Save to List
103 |
104 | )}
105 |
106 |
107 | {/* QR Preview */}
108 |
109 |
110 |
111 |
112 |
113 |
114 | {/* Saved QR Codes */}
115 | {savedQrs.length > 0 && (
116 |
117 |
Saved QR Codes
118 |
119 | {savedQrs.map((qr) => (
120 |
121 |
122 |
123 | {qr.id}
124 |
125 |
126 |
127 | handleDownload(`qr-code-${qr.timestamp}`, date)}
129 | className="p-2 bg-white rounded-full shadow-lg hover:bg-pink-50"
130 | >
131 |
132 |
133 | handleDelete(qr.timestamp)}
135 | className="p-2 bg-white rounded-full shadow-lg hover:bg-red-50"
136 | >
137 |
138 |
139 |
140 |
141 | ))}
142 |
143 |
144 | )}
145 |
146 |
147 |
148 | );
149 | };
150 |
151 | export default QRGeneratorPage;
152 |
--------------------------------------------------------------------------------
/app/(main-area)/menuBuilder/page.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Navbar from '@/components/navbar';
3 | import Footer from '@/components/footer';
4 | import Section from '@/components/menu_builder_section';
5 | import TypeWriter from '@/components/TypeWriter';
6 | import Menudone from '@/components/menuDone'
7 |
8 | const Page = () => {
9 | return (
10 |
11 |
12 |
13 |
14 |
Let Make Your Menu
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | );
28 | }
29 |
30 | export default Page;
31 |
--------------------------------------------------------------------------------
/app/(main-area)/services/page.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Navbar from '@/components/navbar';
3 | import Footer from '@/components/footer';
4 | import Poster from '@/components/service-poster';
5 | import QrCodeIcon from '@mui/icons-material/QrCode';
6 | import AccessTimeIcon from '@mui/icons-material/AccessTime';
7 | import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
8 | import CurrencyRupeeIcon from '@mui/icons-material/CurrencyRupee';
9 | import ServiceCard from '@/components/ServiceCard';
10 | import TypeWriter from '@/components/TypeWriter';
11 | import CompanyShow from '@/components/CompanyShow';
12 |
13 | export default function Service() {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 | Elevating Your Experience with
26 |
27 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | QR Menu Generator allows restaurants and dhabas to create digital menus with a scannable QR code.
52 | Customers can easily access the menu by scanning the code, eliminating the need for printed menus.
53 | Our platform offers easy menu customization, real-time updates, and a seamless user experience.
54 |
55 |
56 |
Instant QR generate
57 |
RealTime Information
58 |
Integrated Payment
59 |
Menu Pamphlet
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | Available Service
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | );
80 | }
81 |
--------------------------------------------------------------------------------
/app/About/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useState } from "react";
4 | import { TextField, Button, Container, Typography, Paper, Grid, Snackbar } from "@mui/material";
5 | import { FaEnvelope, FaPhone, FaMapMarkerAlt } from "react-icons/fa";
6 | import Navbar from "@/components/navbar";
7 | import Footer from "@/components/footer";
8 |
9 | const Contact = () => {
10 | const [formData, setFormData] = useState({ name: "", email: "", message: "" });
11 | const [submitted, setSubmitted] = useState(false);
12 |
13 | const handleChange = (e) => {
14 | setFormData({ ...formData, [e.target.name]: e.target.value });
15 | };
16 |
17 | const handleSubmit = (e) => {
18 | e.preventDefault();
19 | setSubmitted(true);
20 | setTimeout(() => setSubmitted(false), 3000);
21 | };
22 |
23 | return (
24 |
25 |
26 |
27 |
28 |
29 |
30 | Contact Us
31 |
32 |
33 | Have questions or feedback? Reach out to us!
34 |
35 |
36 |
37 |
38 |
39 | support@freelan.com
40 |
41 |
42 |
43 | +1 (555) 123-4567
44 |
45 |
46 |
47 | 123 Freelan Street, Tech City
48 |
49 |
50 |
51 |
89 |
90 |
91 |
97 |
98 |
99 |
100 |
101 | );
102 | };
103 |
104 | export default Contact;
--------------------------------------------------------------------------------
/app/Auth/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useSession, signIn, signOut } from "next-auth/react";
3 | import React, { useEffect } from "react";
4 | import Navbar from "@/components/navbar";
5 | import Footer from "@/components/footer";
6 | import { GitHub, Google, LinkedIn } from "@mui/icons-material";
7 | import { useRouter } from "next/navigation";
8 |
9 | const Page = () => {
10 |
11 | const { data: session } = useSession();
12 | const router = useRouter();
13 |
14 | useEffect(() => {
15 | if (session) {
16 | router.push("/");
17 | }
18 | }, [session,router]);
19 |
20 | if (!session) {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 |
32 |
33 |
34 |
Login
35 |
36 |
37 | signIn("google")}
40 | >
41 |
42 | Continue with Google
43 |
44 |
45 | signIn("github")}
48 | >
49 |
50 | Continue with GitHub
51 |
52 |
53 | signIn("linkedin")}
56 | >
57 |
58 | Continue with LinkedIn
59 |
60 |
61 |
62 |
63 |
64 |
65 | );
66 | }
67 |
68 | return null;
69 | };
70 |
71 | export default Page;
72 |
--------------------------------------------------------------------------------
/app/Contact/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { useForm } from "react-hook-form";
4 | import { Container, TextField, Button, Card, CardContent, Typography, Grid } from "@mui/material";
5 | import Navbar from "@/components/navbar";
6 | import Footer from "@/components/footer";
7 |
8 | const ContactPage = () => {
9 | const { register, handleSubmit, formState: { errors } } = useForm();
10 |
11 | const onSubmit = (data) => {
12 | console.log(data);
13 | };
14 |
15 | return (
16 | <>
17 |
18 |
19 |
20 | Get in Touch
21 |
22 |
23 | Have questions about Qmenu? Want to discuss enterprise solutions?
24 | Our team is ready to help you transform your restaurant management.
25 |
26 |
27 |
28 | {/* Contact Form */}
29 |
30 |
31 |
32 |
69 |
70 |
71 |
72 |
73 | {/* Contact Info */}
74 |
75 |
76 |
77 | Contact Information
78 | 📍 123 Restaurant Row, New York, NY 10001
79 | 📞 +1 (555) 123-4567 (Mon-Fri, 9am-5pm EST)
80 | ✉️ support@qmenu.com | sales@qmenu.com
81 |
82 |
83 |
84 | {/* Map Placeholder */}
85 |
86 |
87 | Our Location
88 |
89 | Map Placeholder
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | >
98 | );
99 | };
100 |
101 | export default ContactPage;
--------------------------------------------------------------------------------
/app/MenuInterface/[user]/page.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React,{useContext} from 'react';
3 | import Navbar from '@/components/menuInterface/navbar';
4 | import Sections from '@/components/menuInterface/sections';
5 | import SectionTitle from '@/components/menuInterface/SectionTitle';
6 | import Menupopulate from '@/components/menuInterface/Menupopulate';
7 | import Footer from '@/components/menuInterface/Footer';
8 | import Cart from '@/components/menuInterface/Cart';
9 | import ItemContext from '@/components/menuInterface/itemContext';
10 | import Confirmpage from '@/components/menuInterface/Confirmpage';
11 | import { useParams } from 'next/navigation';
12 | import AIInterface from '@/components/menuInterface/AI_interface';
13 |
14 | const Page = () => {
15 | const [activeSection,setAS] = React.useState("All");
16 | const [itemprop,setitemprop] = React.useState([]);
17 | const [doneAdd,setDone] = React.useState(false);
18 | const [scaned,setScaned] = React.useState(false);
19 | const {user} = useParams();
20 | const handleSection = (s) => {
21 | setAS(s);
22 | }
23 | const handleprocess = () => {
24 | setDone(!doneAdd);
25 | }
26 |
27 | React.useEffect(() => {
28 | if (!scaned && user) {
29 | setScaned(true);
30 | fetch('/api/Activeuser',{
31 | method: "POST",
32 | headers: { "Content-Type": "application/json" },
33 | body: JSON.stringify({ user }),
34 | });
35 | }
36 | },[user,scaned])
37 |
38 | return (
39 |
40 |
41 |
42 |
43 |
47 |
48 | What's Your Mood Today?
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | {
58 | itemprop.length !== 0 ?
59 | :null
60 | }
61 | {
62 | doneAdd ?
63 | :null
64 | }
65 |
66 |
67 |
68 |
69 |
70 | );
71 | }
72 |
73 | export default Page;
--------------------------------------------------------------------------------
/app/api/Activeuser/route.js:
--------------------------------------------------------------------------------
1 | import { connectToDB } from "@/app/lib/DataBase";
2 | import Visitors from "@/app/lib/model/Visitors";
3 | import { NextResponse } from "next/server";
4 | let activeUsers = []; // Store active users temporarily (Resets on server restart)
5 |
6 | export async function POST(req) {
7 | try {
8 | const { user } = await req.json();
9 | console.log("Received user:", user);
10 |
11 | // Store the user in memory
12 | if (!activeUsers.includes(user)) {
13 | activeUsers.push(user);
14 | const newVisitor = new Visitors({
15 | username: user,
16 | visitedAt: new Date() // Store visit time
17 | });
18 |
19 | await newVisitor.save();
20 |
21 | return NextResponse.json({ message: "Visitor saved successfully!" }, { status: 201 });
22 | }
23 |
24 | return Response.json({ message: "User tracked successfully!" }, { status: 200 });
25 | } catch (error) {
26 | console.error("Error handling request:", error);
27 | return Response.json({ error: "Internal Server Error" }, { status: 500 });
28 | }
29 | }
30 |
31 | export async function GET(req) {
32 | try {
33 | const param = new URL(req.url);
34 | const notifi = param.searchParams.get('notifi');
35 |
36 | await connectToDB();
37 |
38 | if (!notifi) {
39 | // ✅ Fetch all visitors
40 | const visitors = await Visitors.find().sort({ visitedAt: -1 });
41 | return NextResponse.json({ visitors }, { status: 200 });
42 | }
43 |
44 | console.log("Fetching active users...");
45 | return NextResponse.json({ activeUsers }, { status: 200 });
46 |
47 | } catch (error) {
48 | console.error("Error handling request:", error);
49 | return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
50 | }
51 | }
--------------------------------------------------------------------------------
/app/api/auth/[...nextauth]/route.js:
--------------------------------------------------------------------------------
1 | import NextAuth from 'next-auth'
2 | import LinkedInProvider from "next-auth/providers/linkedin";
3 | import GoogleProvider from 'next-auth/providers/google'
4 | import GitHubProvider from "next-auth/providers/github";
5 |
6 |
7 | const handler=NextAuth({
8 | providers: [
9 |
10 | GoogleProvider({
11 | clientId: process.env.GOOGLE_ID,
12 | clientSecret: process.env.GOOGLE_SECRET
13 | }),
14 | LinkedInProvider({
15 | clientId: process.env.LINKEDIN_CLIENT_ID,
16 | clientSecret: process.env.LINKEDIN_CLIENT_SECRET
17 | }),
18 | GitHubProvider({
19 | clientId: process.env.GITHUB_ID,
20 | clientSecret: process.env.GITHUB_SECRET
21 | })
22 | ],
23 | secret: process.env.NEXTAUTH_SECRET
24 | })
25 | export {handler as GET,handler as POST}
--------------------------------------------------------------------------------
/app/api/gemini/route.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import axios from "axios";
3 |
4 | export async function POST(req) {
5 | try {
6 | const { message ,Avitem } = await req.json(); // Parse request body
7 |
8 | if (!message) {
9 | return NextResponse.json({ error: "Message is required" }, { status: 400 });
10 | }
11 |
12 | const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${process.env.GEMINI_URL}`;
13 |
14 | const response = await axios.post(API_URL, {
15 | contents: [
16 | {
17 | parts: [
18 | {
19 | text: `You are a friendly restaurant AI assistant. Answer only food-related questions and keep responses under three sentences. Do not generate code, provide technical support, or discuss non-food topics. Focus on menu items and dining experiences.
20 |
21 | Available food items in our restaurant: ${Avitem}
22 |
23 | Question: ${message}`
24 | }
25 | ]
26 |
27 | }
28 | ]
29 | }, {
30 | headers: {
31 | "Content-Type": "application/json",
32 | },
33 | });
34 |
35 | return NextResponse.json({ response: response.data.candidates[0].content.parts[0].text });
36 |
37 | } catch (error) {
38 | console.error("Gemini API Error:", error);
39 | return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/api/get-ip/route.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import os from "os";
3 |
4 | export async function GET() {
5 | const networkInterfaces = os.networkInterfaces();
6 | let localIP = "localhost";
7 |
8 | for (const key in networkInterfaces) {
9 | for (const net of networkInterfaces[key]) {
10 | if (net.family === "IPv4" && !net.internal) {
11 | localIP = net.address;
12 | break;
13 | }
14 | }
15 | }
16 |
17 | return NextResponse.json({ ip: `http://${localIP}:3000` });
18 | }
19 |
--------------------------------------------------------------------------------
/app/api/history/route.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import OrderHistory from "@/app/lib/model/orderHistory";
3 | import { connectToDB } from "@/app/lib/DataBase";
4 |
5 | export async function GET(req) {
6 | try {
7 | const { searchParams } = new URL(req.url);
8 | const params = searchParams.get('mode');
9 | connectToDB();
10 | if(!params){
11 | const data = await OrderHistory.find({});
12 | return NextResponse.json({data},{status: 200});
13 | }
14 | return NextResponse.json({ data: "hello ji" }, { status: 200 });
15 | } catch {
16 |
17 | }
18 | }
--------------------------------------------------------------------------------
/app/api/menuItems/route.js:
--------------------------------------------------------------------------------
1 | import { connectToDB } from "@/app/lib/DataBase";
2 | import Menu from "@/app/lib/model/Menu";
3 | import { NextResponse } from "next/server";
4 |
5 | export async function POST(req) {
6 | await connectToDB();
7 |
8 | try {
9 | const { id,section,image,originalPrice,price,title } = await req.json();
10 |
11 | const newItem = new Menu({
12 | id,
13 | section,
14 | image,
15 | originalPrice,
16 | price,
17 | title,
18 | });
19 |
20 | await newItem.save();
21 | return NextResponse.json(newItem, { status: 201 });
22 | } catch (error) {
23 | return NextResponse.json({ message: "Error adding menu item" }, { status: 500 });
24 | }
25 | }
26 |
27 | export async function GET() {
28 | await connectToDB();
29 |
30 | try {
31 | const items = await Menu.find();
32 | return NextResponse.json(items, { status: 200 });
33 | } catch (error) {
34 | return NextResponse.json({ message: "Error fetching menu items" }, { status: 500 });
35 | }
36 | }
37 |
38 |
39 | export async function DELETE(req) {
40 | try {
41 | await connectToDB(); // ✅ Corrected to use await
42 |
43 | const { searchParams } = new URL(req.url);
44 | const id = searchParams.get("id");
45 |
46 | let label;
47 | if (!id) {
48 | // Only parse JSON if ID is not provided
49 | const body = await req.json();
50 | label = body.label;
51 | }
52 |
53 | if (id) {
54 | // ✅ Fix: Use _id for MongoDB documents
55 | const deleteCard = await Menu.findOneAndDelete({id});
56 |
57 | if (!deleteCard) {
58 | return NextResponse.json({ error: "Item not found" }, { status: 404 });
59 | }
60 | return NextResponse.json({ message: "Item deleted successfully" }, { status: 200 });
61 | }
62 |
63 |
64 | if (label) {
65 | // ✅ Fix: Correct way to delete all items under a section
66 | const deleteAll = await Menu.deleteMany({ section: label });
67 |
68 | if (deleteAll.deletedCount === 0) {
69 | return NextResponse.json({ error: "No items found under this section" }, { status: 202 });
70 | }
71 |
72 | return NextResponse.json({ message: "Section and all related items deleted" }, { status: 200 });
73 | }
74 |
75 | return NextResponse.json({ error: "Invalid request: Provide either 'id' or 'label'" }, { status: 400 });
76 |
77 | } catch (error) {
78 | return NextResponse.json({ error: "Failed to delete Card", details: error.message }, { status: 500 });
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/api/order/cancel/route.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import Order from "@/app/lib/model/orderItem";
3 | import OrderHistory from "@/app/lib/model/orderHistory";
4 | import { connectToDB } from "@/app/lib/DataBase";
5 |
6 | export async function POST(req) {
7 | try {
8 | await connectToDB();
9 | const { orderId } = await req.json();
10 | if (!orderId) {
11 | return NextResponse.json({ error: "Missing order ID" }, { status: 400 });
12 | }
13 | const order = await Order.findById(orderId);
14 | if (!order) {
15 | return NextResponse.json({ error: "Order not found" }, { status: 404 });
16 | }
17 |
18 | // Mark the order as canceled
19 | order.status = "canceled";
20 |
21 | // Create a record in OrderHistory
22 | const orderHistory = new OrderHistory({
23 | table: order.table,
24 | items: order.items,
25 | total_Amount: order.total_Amount,
26 | status: order.status,
27 | time: order.time,
28 | createdAt: order.createdAt,
29 | });
30 | await orderHistory.save();
31 |
32 | // Remove the order from live orders
33 | await Order.findByIdAndDelete(orderId);
34 |
35 | return NextResponse.json(
36 | { message: "Order canceled and moved to history", order: orderHistory },
37 | { status: 200 }
38 | );
39 | } catch (error) {
40 | return NextResponse.json(
41 | { error: "Failed to cancel order", details: error.message },
42 | { status: 500 }
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/api/order/done/route.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import Order from "@/app/lib/model/orderItem";
3 | import OrderHistory from "@/app/lib/model/orderHistory";
4 | import { connectToDB } from "@/app/lib/DataBase";
5 |
6 | export async function POST(req) {
7 | try {
8 | await connectToDB();
9 | const { orderId } = await req.json();
10 | if (!orderId) {
11 | return NextResponse.json({ error: "Missing order ID" }, { status: 400 });
12 | }
13 | const order = await Order.findById(orderId);
14 | if (!order) {
15 | return NextResponse.json({ error: "Order not found" }, { status: 404 });
16 | }
17 |
18 | // Mark the order as completed
19 | order.status = "completed";
20 |
21 | // Create a record in OrderHistory
22 | const orderHistory = new OrderHistory({
23 | table: order.table,
24 | items: order.items,
25 | total_Amount: order.total_Amount,
26 | status: order.status,
27 | time: order.time,
28 | createdAt: order.createdAt, // preserve original creation time if desired
29 | });
30 | await orderHistory.save();
31 |
32 | // Remove the order from live orders
33 | await Order.findByIdAndDelete(orderId);
34 |
35 | return NextResponse.json(
36 | { message: "Order marked as done and moved to history", order: orderHistory },
37 | { status: 200 }
38 | );
39 | } catch (error) {
40 | return NextResponse.json(
41 | { error: "Failed to mark order as done", details: error.message },
42 | { status: 500 }
43 | );
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/api/order/route.js:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server";
2 | import Order from "@/app/lib/model/orderItem";
3 | import OrderHistory from "@/app/lib/model/orderHistory";
4 | import { connectToDB } from "@/app/lib/DataBase";
5 |
6 | // Fetch all live orders
7 | export async function GET() {
8 | try {
9 | await connectToDB();
10 | const orders = await Order.find().sort({ createdAt: -1 });
11 | return NextResponse.json({ orders }, { status: 200 });
12 | } catch (error) {
13 | return NextResponse.json(
14 | { error: "Failed to fetch orders", details: error.message },
15 | { status: 500 }
16 | );
17 | }
18 | }
19 |
20 | // Create a new order
21 | export async function POST(req) {
22 | try {
23 | await connectToDB();
24 | const { table, items, status, total_Amount } = await req.json();
25 |
26 | if (!table || !items.length) {
27 | return NextResponse.json(
28 | { error: "Missing required fields" },
29 | { status: 400 }
30 | );
31 | }
32 |
33 | const newOrder = new Order({
34 | table,
35 | items,
36 | total_Amount,
37 | status,
38 | time: new Date().toLocaleTimeString(),
39 | });
40 |
41 | await newOrder.save();
42 | return NextResponse.json(
43 | { message: "Order placed successfully", order: newOrder },
44 | { status: 201 }
45 | );
46 | } catch (error) {
47 | return NextResponse.json(
48 | { error: "Internal Server Error", details: error.message },
49 | { status: 500 }
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/api/sections/route.js:
--------------------------------------------------------------------------------
1 | import Section from "@/app/lib/model/SectionDB";
2 | import { connectToDB } from "@/app/lib/DataBase";
3 | import { NextResponse } from "next/server";
4 |
5 | // CREATE a new section
6 | export async function POST(req) {
7 | try {
8 | const { label } = await req.json();
9 | await connectToDB();
10 |
11 | const newSection = new Section({ label });
12 | await newSection.save();
13 |
14 | return NextResponse.json({ message: "Section saved", section: newSection }, { status: 201 });
15 | } catch (error) {
16 | return NextResponse.json({ error: "Failed to save section", details: error.message }, { status: 500 });
17 | }
18 | }
19 |
20 | // GET all sections
21 | export async function GET() {
22 | try {
23 | await connectToDB();
24 | const sections = await Section.find({});
25 | return NextResponse.json(sections, { status: 200 });
26 | } catch (error) {
27 | return NextResponse.json({ error: "Failed to fetch sections", details: error.message }, { status: 500 });
28 | }
29 | }
30 |
31 | // UPDATE a section
32 | export async function PUT(req) {
33 | try {
34 | const { oldLabel, newLabel } = await req.json();
35 | await connectToDB();
36 |
37 | const updatedSection = await Section.findOneAndUpdate(
38 | { label: oldLabel }, // Find section by old label
39 | { label: newLabel }, // Update with new label
40 | { new: true } // Return updated document
41 | );
42 |
43 | if (!updatedSection) {
44 | return NextResponse.json({ error: "Section not found" }, { status: 404 });
45 | }
46 |
47 | return NextResponse.json({ message: "Section updated", section: updatedSection }, { status: 200 });
48 | } catch (error) {
49 | return NextResponse.json({ error: "Failed to update section", details: error.message }, { status: 500 });
50 | }
51 | }
52 |
53 |
54 | // DELETE a section
55 | export async function DELETE(req) {
56 | try {
57 | await connectToDB();
58 | // Extract label from query parameters
59 | const { label } = await req.json();
60 |
61 |
62 | if (!label) {
63 | return NextResponse.json({ error: "Label is required" }, { status: 400 });
64 | }
65 |
66 | console.log(label);
67 |
68 | const deletedSection = await Section.findOneAndDelete({
69 | label
70 | });
71 |
72 | if (!deletedSection) {
73 | return NextResponse.json({ error: "Section not found" }, { status: 404 });
74 | }
75 |
76 | return NextResponse.json({ message: "Section deleted" }, { status: 200 });
77 | } catch (error) {
78 | return NextResponse.json({ error: "Failed to delete section", details: error.message }, { status: 500 });
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/api/upload/route.js:
--------------------------------------------------------------------------------
1 | import { writeFile } from "fs/promises";
2 | import path from "path";
3 | import { NextResponse } from "next/server";
4 |
5 | export async function POST(req) {
6 | try {
7 | const formData = await req.formData();
8 | const file = formData.get("image");
9 |
10 | if (!file) {
11 | return NextResponse.json({ error: "No file uploaded" }, { status: 400 });
12 | }
13 |
14 | const filename = file.name; // Use only the original file name
15 | const uploadDir = path.join(process.cwd(), "public/uploads", filename);
16 |
17 | // Convert to buffer and save the file (overwrites if it exists)
18 | const buffer = Buffer.from(await file.arrayBuffer());
19 | await writeFile(uploadDir, buffer);
20 |
21 | return NextResponse.json({ imageUrl: `/uploads/${filename}` }, { status: 200 });
22 | } catch (error) {
23 | return NextResponse.json({ error: "Upload failed", details: error.message }, { status: 500 });
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/dashboard/layout.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useEffect, useState } from "react";
3 | import { ToastContainer, toast } from "react-toastify";
4 | import "react-toastify/dist/ReactToastify.css";
5 |
6 | const DashboardLayout = ({ children }) => {
7 | const [activeUsers, setActiveUsers] = useState([]);
8 | const notificationSound = "/notification.mp3"; // Path to notification sound
9 |
10 | useEffect(() => {
11 | let isMounted = true; // Prevents state update on unmounted components
12 |
13 | const fetchUsers = async () => {
14 | try {
15 | const res = await fetch("/api/Activeuser?notifi=true");
16 | const data = await res.json();
17 |
18 | if (isMounted && data.activeUsers) {
19 | // Check for new users
20 | const newUsers = data.activeUsers.filter(user => !activeUsers.includes(user));
21 |
22 | if (newUsers.length > 0) {
23 | newUsers.forEach(user => {
24 | toast.success(`New User Scanned: ${user}`, {
25 | position: "top-right",
26 | autoClose: 3000,
27 | });
28 |
29 | // Play notification sound
30 | const audio = new Audio(notificationSound);
31 | audio.play();
32 | });
33 |
34 | setActiveUsers(data.activeUsers); // Update state with new users
35 | }
36 | }
37 | } catch (error) {
38 | console.error("Error fetching active users:", error);
39 | }
40 | };
41 |
42 | // Fetch immediately, then every 5 seconds
43 | fetchUsers();
44 | const interval = setInterval(fetchUsers, 5000);
45 |
46 | // Cleanup function to clear interval when unmounting
47 | return () => {
48 | isMounted = false;
49 | clearInterval(interval);
50 | };
51 | }, [activeUsers]); // Dependency added to track new users
52 |
53 | return (
54 |
55 |
56 | {children}
57 |
58 | );
59 | };
60 |
61 | export default DashboardLayout;
62 |
--------------------------------------------------------------------------------
/app/dashboard/liveorder/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useState, useEffect } from "react";
4 | import { useSession } from "next-auth/react";
5 | import axios from "axios";
6 | import Aside from "@/components/dashboard/aside";
7 |
8 | const LiveOrders = () => {
9 | const { data: session } = useSession();
10 | const [orders, setOrders] = useState([]);
11 | const [loading, setLoading] = useState(true);
12 | const [error, setError] = useState(null);
13 |
14 | // Fetch orders from the API
15 | useEffect(() => {
16 | const fetchOrders = async () => {
17 | try {
18 | const response = await axios.get("/api/order");
19 | setOrders(response.data.orders);
20 | } catch (err) {
21 | console.error("Error fetching orders:", err);
22 | setError("Failed to fetch orders");
23 | } finally {
24 | setLoading(false);
25 | }
26 | };
27 |
28 | fetchOrders();
29 |
30 | const interval = setInterval(fetchOrders, 5000);
31 |
32 | return () => clearInterval(interval);
33 | }, []);
34 |
35 |
36 | // Mark order as done: call /api/order/done endpoint
37 | const markOrderAsDone = async (orderId) => {
38 | try {
39 | await axios.post("/api/order/done", { orderId });
40 | setOrders((prevOrders) => prevOrders.filter((order) => order._id !== orderId));
41 | } catch (err) {
42 | console.error("Error marking order as done:", err);
43 | setError("Failed to mark order as done");
44 | }
45 | };
46 |
47 | // Cancel order: call /api/order/cancel endpoint
48 | const cancelOrder = async (orderId) => {
49 | try {
50 | await axios.post("/api/order/cancel", { orderId });
51 | setOrders((prevOrders) => prevOrders.filter((order) => order._id !== orderId));
52 | } catch (err) {
53 | console.error("Error canceling order:", err);
54 | setError("Failed to cancel order");
55 | }
56 | };
57 |
58 | return (
59 |
60 |
61 |
62 | {/* Header */}
63 |
64 |
65 |
Live Orders
66 |
Real-time order tracking system
67 |
68 |
73 |
74 |
75 | {/* Loading & Error States */}
76 | {loading ? (
77 |
Loading orders...
78 | ) : error ? (
79 |
{error}
80 | ) : orders.length === 0 ? (
81 |
No live orders found.
82 | ) : (
83 |
84 | {orders.map((order) => (
85 |
86 |
87 |
88 |
#{order._id.slice(-4)}
89 |
{order.time}
90 |
Total Amount: {order.total_Amount}
91 |
92 |
97 | {order.status}
98 |
99 |
100 |
101 |
102 |
103 | {order.items.map((item) => `${item.quantity}x ${item.name}`).join(", ")}
104 |
105 |
106 | 🪑 Table {order.table}
107 |
108 |
109 |
110 |
111 | markOrderAsDone(order._id)} className="px-4 py-2 bg-blue-100 text-blue-600 rounded-lg hover:bg-blue-200"
113 | >
114 | Done Order
115 |
116 | cancelOrder(order._id)}
118 | className="px-4 py-2 bg-red-100 text-red-600 rounded-lg hover:bg-red-200"
119 | >
120 | Cancel Order
121 |
122 |
123 |
124 | ))}
125 |
126 | )}
127 |
128 | );
129 | };
130 |
131 | export default LiveOrders;
132 |
--------------------------------------------------------------------------------
/app/dashboard/page.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React from 'react';
3 | import { useSession } from 'next-auth/react';
4 | import { useRouter } from 'next/navigation';
5 | import Aside from '@/components/dashboard/aside';
6 | import Home from '@/components/dashboard/home';
7 |
8 | const Page = () => {
9 | const { data: session, status } = useSession();
10 | const router = useRouter();
11 | if (status === "loading") {
12 | return Loading...
;
13 | }
14 |
15 | if (status === "unauthenticated") {
16 | router.push('/');
17 | }
18 | return (
19 |
23 | );
24 | }
25 |
26 | export default Page;
27 |
--------------------------------------------------------------------------------
/app/dashboard/salesrevenue/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
4 | import Aside from "@/components/dashboard/aside";
5 |
6 | const SalesRevenue = () => {
7 | // Mock data
8 | const financialData = {
9 | totalRevenue: "$15,230",
10 | averageOrder: "$42.50",
11 | todaySales: "$2,450",
12 | conversionRate: "18.7%"
13 | };
14 |
15 | const chartData = [
16 | { day: 'Mon', sales: 3450 },
17 | { day: 'Tue', sales: 4210 },
18 | { day: 'Wed', sales: 3780 },
19 | { day: 'Thu', sales: 5290 },
20 | { day: 'Fri', sales: 6310 },
21 | { day: 'Sat', sales: 7840 },
22 | { day: 'Sun', sales: 6520 },
23 | ];
24 |
25 | const paymentMethods = [
26 | { method: 'Credit Card', percentage: 62, color: '#3B82F6' },
27 | { method: 'Mobile Pay', percentage: 28, color: '#10B981' },
28 | { method: 'Cash', percentage: 10, color: '#6366F1' },
29 | ];
30 |
31 | return (
32 |
33 |
34 | {/* Header */}
35 |
36 |
37 |
Sales Analytics
38 |
Real-time financial performance dashboard
39 |
40 |
41 | Day
42 | Week
43 | Month
44 |
45 |
46 |
47 | {/* Financial Overview Cards */}
48 |
49 | {Object.entries(financialData).map(([key, value]) => (
50 |
51 |
{key.replace(/([A-Z])/g, ' $1')}
52 |
{value}
53 |
54 | ))}
55 |
56 |
57 | {/* Main Content Grid */}
58 |
59 | {/* Sales Chart */}
60 |
61 |
Weekly Sales Performance
62 |
63 |
64 |
65 |
66 |
67 |
68 |
73 |
74 |
75 |
76 |
77 |
78 | {/* Payment Methods */}
79 |
80 |
Payment Methods
81 |
82 | {paymentMethods.map((method, index) => (
83 |
84 |
85 | {method.method}
86 | {method.percentage}%
87 |
88 |
97 |
98 | ))}
99 |
100 |
101 |
102 | {/* Live Sales Feed */}
103 |
104 |
Live Sales Feed
105 |
106 | {[...Array(10)].map((_, index) => (
107 |
108 |
109 |
Order #100{index}
110 |
Table 0{index+1}
111 |
112 |
113 |
${(Math.random()*50 + 20).toFixed(2)}
114 |
Completed
115 |
116 |
117 | ))}
118 |
119 |
120 |
121 |
122 | );
123 | };
124 |
125 | export default SalesRevenue;
--------------------------------------------------------------------------------
/app/dashboard/transactionhistory/page.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useState, useRef, useCallback, useEffect } from "react";
3 | import { useVirtualizer } from "@tanstack/react-virtual";
4 | import dynamic from "next/dynamic";
5 | import Aside from "@/components/dashboard/aside";
6 | import axios from "axios";
7 |
8 | const CSVLink = dynamic(
9 | () => import("react-csv").then((mod) => mod.CSVLink),
10 | { ssr: false }
11 | );
12 |
13 | const TransactionHistory = () => {
14 | const [csvData, setCsvData] = useState([]);
15 | const [filter, setFilter] = useState("all");
16 | const [transactions, setTransactions] = useState([]);
17 | const [searchQuery, setSearchQuery] = useState("");
18 | const parentRef = useRef();
19 |
20 | useEffect(() => {
21 | const fetchTransactions = async () => {
22 | try {
23 | const res = await axios.get("/api/history");
24 | setTransactions(res.data.data);
25 |
26 | // Prepare CSV data after fetch
27 | setCsvData(res.data.data.map(tx => ({
28 | ID: tx._id,
29 | Date: new Date(tx.createdAt).toLocaleDateString(),
30 | Amount: `₹${tx.total_Amount}`,
31 | Status: tx.status,
32 | Items: tx.items.map(item => item.name).join(", ")
33 | })));
34 |
35 | } catch (err) {
36 | console.log(err);
37 | }
38 | };
39 | fetchTransactions();
40 | }, []);
41 |
42 |
43 | const filteredTransactions = transactions.filter((transaction) => {
44 | const matchesFilter = filter === "all" || transaction.status === filter;
45 | const searchLower = searchQuery.toLowerCase();
46 | const matchesSearch =
47 | !searchQuery ||
48 | transaction._id.toLowerCase().includes(searchLower) ||
49 | transaction.items?.some(item =>
50 | item.name.toLowerCase().includes(searchLower)
51 | );
52 |
53 | return matchesFilter && matchesSearch;
54 | });
55 |
56 | const rowVirtualizer = useVirtualizer({
57 | count: filteredTransactions.length,
58 | getScrollElement: () => parentRef.current,
59 | estimateSize: useCallback(() => 96, []),
60 | overscan: 5,
61 | });
62 |
63 | const statusStyles = {
64 | completed: "bg-green-100 text-green-800",
65 | Canceled: "bg-red-100 text-red-800",
66 | pending: "bg-yellow-100 text-yellow-800"
67 | };
68 |
69 | return (
70 |
71 |
72 |
73 | {/* Header Section */}
74 |
75 |
76 |
Transaction History
77 |
Track and manage payment transactions
78 |
79 |
80 |
81 |
82 | setSearchQuery(e.target.value)}
88 | />
89 | {searchQuery && (
90 | setSearchQuery("")}
92 | className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-500"
93 | >
94 | ✕
95 |
96 | )}
97 |
98 |
99 |
105 | Export CSV
106 |
107 |
108 |
109 |
110 | {/* Filter Tabs */}
111 |
112 | {["all", "completed", "canceled"].map((f) => (
113 | setFilter(f)}
116 | className={`px-4 py-2 rounded-md text-sm font-medium transition-colors ${filter === f
117 | ? "bg-blue-600 text-white"
118 | : "bg-gray-100 text-gray-600 hover:bg-gray-200"
119 | }`}
120 | >
121 | {f.charAt(0).toUpperCase() + f.slice(1)}
122 |
123 | ))}
124 |
125 |
126 | {/* Loading/Error State */}
127 |
128 |
132 |
138 | {filteredTransactions.length === 0 ? (
139 |
140 | No transactions found matching your criteria
141 |
142 | ) : (
143 | rowVirtualizer.getVirtualItems().map((virtualRow) => {
144 | const transaction = filteredTransactions[virtualRow.index];
145 | return (
146 |
158 |
159 |
160 |
161 |
162 | #{transaction._id.slice(-6)}
163 |
164 |
168 | {transaction.status}
169 |
170 |
171 |
172 | {new Date(transaction.createdAt).toLocaleDateString('en-US', {
173 | year: 'numeric',
174 | month: 'short',
175 | day: 'numeric',
176 | hour: '2-digit',
177 | minute: '2-digit'
178 | })}
179 |
180 |
181 | Items: {transaction.items?.map(item => item.name).join(", ")}
182 |
183 |
184 |
185 |
186 |
187 | ₹{transaction.total_Amount}
188 |
189 |
190 |
191 |
192 | );
193 | })
194 | )}
195 |
196 |
197 |
198 |
199 | {/* Live Status */}
200 |
201 |
202 |
203 |
204 |
205 | Receiving live updates
206 |
207 |
208 | );
209 | };
210 |
211 | export default TransactionHistory;
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 | @import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&family=Playfair:ital,opsz,wght@0,5..1200,300..900;1,5..1200,300..900&display=swap');
5 |
6 | :root {
7 | --background: #ffffff;
8 | --foreground: #171717;
9 | }
10 |
11 | @media (prefers-color-scheme: dark) {
12 | :root {
13 | --background: #0a0a0a;
14 | --foreground: #ededed;
15 | }
16 | }
17 |
18 | body {
19 | color: var(--foreground);
20 | background: var(--background);
21 | font-family: Arial, Helvetica, sans-serif;
22 | }
23 |
24 |
25 |
26 | .checkmark__circle {
27 | stroke-dasharray: 166;
28 | stroke-dashoffset: 166;
29 | stroke-width: 2;
30 | stroke-miterlimit: 10;
31 | stroke: #4CAF50;
32 | fill: none;
33 | animation: stroke 0.6s cubic-bezier(0.65, 0, 0.45, 1) forwards;
34 | }
35 |
36 | .checkmark__check {
37 | transform-origin: 50% 50%;
38 | stroke-dasharray: 48;
39 | stroke-dashoffset: 48;
40 | animation: stroke 0.3s cubic-bezier(0.65, 0, 0.45, 1) 0.8s forwards;
41 | }
42 |
43 | @keyframes stroke {
44 | 100% {
45 | stroke-dashoffset: 0;
46 | }
47 | }
48 |
49 | @keyframes scale {
50 | 0%, 100% {
51 | transform: none;
52 | }
53 | 50% {
54 | transform: scale3d(1.1, 1.1, 1);
55 | }
56 | }
57 |
58 | .animate-success {
59 | animation: fadeInUp 0.5s ease-out;
60 | }
61 |
62 | @keyframes fadeInUp {
63 | from {
64 | opacity: 0;
65 | transform: translateY(20px);
66 | }
67 | to {
68 | opacity: 1;
69 | transform: translateY(0);
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/app/layout.js:
--------------------------------------------------------------------------------
1 | import { Geist, Geist_Mono, DM_Sans, Playfair_Display } from "next/font/google";
2 | import "./globals.css";
3 | import Sessionwrap from '@/components/Sessionwrap'
4 |
5 | const geistSans = Geist({
6 | variable: "--font-geist-sans",
7 | subsets: ["latin"],
8 | });
9 |
10 | const geistMono = Geist_Mono({
11 | variable: "--font-geist-mono",
12 | subsets: ["latin"],
13 | });
14 | const dmSans = DM_Sans({
15 | variable: "--font-dm-sans",
16 | subsets: ["latin"],
17 | weight: ["500"],
18 | });
19 |
20 | const playfair = Playfair_Display({
21 | variable: "--font-playfair-display",
22 | subsets: ["latin"],
23 | weight: ["500"],
24 | })
25 |
26 | export const metadata = {
27 | title: "My QR Menu",
28 | description: "Easily generate and manage your digital QR-based menu -Raj",
29 | };
30 |
31 |
32 | export default function RootLayout({ children,session }) {
33 | return (
34 |
35 |
38 |
39 | {children}
40 |
41 |
42 |
43 | );
44 | }
45 |
--------------------------------------------------------------------------------
/app/lib/DataBase.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const MONGODB = process.env.MONGODB_URL;
4 |
5 | if (!MONGODB) {
6 | throw new Error("Please define the MONGODB_URI environment variable in .env.local");
7 | }
8 |
9 | let cached = globalThis.mongoose;
10 |
11 | if (!cached) {
12 | cached = globalThis.mongoose={ conn: null, promise: null }
13 | }
14 |
15 | export async function connectToDB() {
16 | if (cached.conn) {
17 | return cached.conn;
18 | }
19 | if (!cached.promise) {
20 | cached.promise = mongoose.connect(MONGODB, {
21 | }).then((mongoose) => mongoose);
22 | }
23 | cached.conn = await cached.promise;
24 | return cached.conn;
25 | }
--------------------------------------------------------------------------------
/app/lib/model/Menu.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const MenuItemSchema = new mongoose.Schema({
4 | id: String,
5 | section: String,
6 | image: String,
7 | originalPrice: String,
8 | price: String,
9 | title: String,
10 | });
11 |
12 | export default mongoose.models.Menu || mongoose.model("Menu", MenuItemSchema);
13 |
--------------------------------------------------------------------------------
/app/lib/model/SectionDB.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const SectionSchema = new mongoose.Schema({
4 | label: { type: String, required: true },
5 | })
6 |
7 | export default mongoose.models.SectionDB || mongoose.model("SectionDB",SectionSchema);
--------------------------------------------------------------------------------
/app/lib/model/Visitors.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const VisitorUsersSchema = new mongoose.Schema({
4 | username: { type: String, required: true, unique: true },
5 | lastActive: { type: Date, default: Date.now }
6 | });
7 |
8 | export default mongoose.models.VisitorUsers || mongoose.model("VisitorUsers", VisitorUsersSchema);
9 |
--------------------------------------------------------------------------------
/app/lib/model/orderHistory.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const OrderHistorySchema = new mongoose.Schema({
4 | table: String,
5 | items: [
6 | {
7 | name: String,
8 | quantity: Number
9 | }
10 | ],
11 | status: String, // completed | canceled
12 | time: String,
13 | total_Amount: Number,
14 | createdAt: { type: Date, default: Date.now }
15 | });
16 |
17 | export default mongoose.models.OrderHistory || mongoose.model("OrderHistory", OrderHistorySchema);
18 |
--------------------------------------------------------------------------------
/app/lib/model/orderItem.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const OrderSchema = new mongoose.Schema({
4 | table: {
5 | type: String,
6 | required: true,
7 | },
8 | status: {
9 | type: String,
10 | required: true,
11 | },
12 | items: [
13 | {
14 | name: { type: String, required: true },
15 | quantity: { type: Number, required: true },
16 | }
17 | ],
18 | total_Amount: {
19 | type: Number,
20 | required: true,
21 | },
22 | time: {
23 | type: String,
24 | required: true,
25 | },
26 | }, { timestamps: true });
27 |
28 | export default mongoose.models.Order || mongoose.model("Order", OrderSchema);
29 |
--------------------------------------------------------------------------------
/app/lib/mongodb.js:
--------------------------------------------------------------------------------
1 | // lib/mongodb/route.js
2 | import mongoose from "mongoose";
3 |
4 | const connectDb = async () => {
5 | if (mongoose.connection.readyState >= 1) {
6 | return;
7 | }
8 | try {
9 | await mongoose.connect(process.env.MONGODB_URI, {
10 | useNewUrlParser: true,
11 | useUnifiedTopology: true,
12 | });
13 | console.log("MongoDB connected");
14 | } catch (error) {
15 | console.error("MongoDB connection error:", error);
16 | throw new Error("MongoDB connection failed");
17 | }
18 | };
19 |
20 | export default connectDb;
21 |
--------------------------------------------------------------------------------
/app/page.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React from "react";
3 | import { useSession } from "next-auth/react";
4 | import Navbar from "@/components/navbar";
5 | import Footer from "@/components/footer";
6 | import Link from "next/link";
7 |
8 | function Home() {
9 | const { data: session } = useSession();
10 |
11 | return (
12 |
13 |
14 |
15 |
16 | A better way to serve
17 | your {" "}
18 | guests
19 |
20 |
21 | Generate a QR code for your restaurant or dhaba menu instantly.
22 | Customers can scan to view the menu online—no app required!
23 |
24 |
25 |
29 | Get Started
30 |
31 |
32 |
36 | Know more
37 |
38 |
39 |
40 |
41 |
42 | );
43 | }
44 |
45 | export default Home;
46 |
--------------------------------------------------------------------------------
/components/AccountDropdown.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/react'
3 | import Signout from "@/components/Signout"
4 | import Link from 'next/link';
5 |
6 | const AccountDropdown = () => {
7 | return (
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
21 | Home
22 |
23 |
24 |
25 |
29 | Dashboard
30 |
31 |
32 |
33 |
37 | Services
38 |
39 |
40 |
41 |
42 |
46 | Menu QR Builder
47 |
48 |
49 |
50 |
51 |
55 | Generate Qr
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | );
68 | }
69 |
70 | export default AccountDropdown;
71 |
--------------------------------------------------------------------------------
/components/CompanyShow.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const CompanySow = () => {
4 | return (
5 |
6 |
7 |
Trusted by
8 | 2000+ customers worldwide
9 |
10 |
11 |
13 |
14 |
17 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
32 |
33 |
34 |
36 |
37 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
48 |
50 |
51 |
52 |
53 |
61 |
63 |
64 |
65 |
67 |
68 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | );
78 | }
79 |
80 | export default CompanySow;
81 |
--------------------------------------------------------------------------------
/components/GenerateQR.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useState } from "react";
3 | import { QRCode } from "react-qrcode-logo";
4 |
5 | const GenerateQR = ({ id, value, size }) => {
6 | const [networkIP, setNetworkIP] = useState("http://localhost:3000");
7 |
8 | useEffect(() => {
9 | const fetchIP = async () => {
10 | try {
11 | const res = await fetch("/api/get-ip");
12 | const data = await res.json();
13 | if (data?.ip) setNetworkIP(data.ip);
14 | } catch (error) {
15 | console.error("Error fetching IP:", error);
16 | }
17 | };
18 |
19 | fetchIP();
20 | }, []);
21 |
22 | return (
23 |
39 | );
40 | };
41 |
42 | export default GenerateQR;
43 |
--------------------------------------------------------------------------------
/components/ServiceCard.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from 'next/link';
3 | import Image from 'next/image';
4 |
5 | const services = [
6 | {
7 | title: "QR Menu - Scan & Explore",
8 | description: "No more paper menus! Customers can simply scan a QR code to access your full menu digitally, complete with images and details.",
9 | imgSrc: "/serviceTab/s-2.jpg",
10 | link: "/menuBuilder"
11 | },
12 | {
13 | title: "Download Pamphlet",
14 | description: "Allow customers to download a digital version of your menu, so they can explore your offerings anytime and share with others.",
15 | imgSrc: "/serviceTab/s-1.jpg",
16 | link: "#"
17 | },
18 | {
19 | title: "Real-Time Order History",
20 | description: "Give customers instant access to their past orders, making it easy to track previous meals and reorder their favorites effortlessly.",
21 | imgSrc: "/serviceTab/s-3.jpg",
22 | link: "/dashboard/transactionhistory"
23 | },
24 | ];
25 |
26 |
27 |
28 | const ServiceCard = () => {
29 | return (
30 |
31 | {services.map((service, index) => (
32 |
36 |
37 |
45 |
46 |
47 |
48 | {service.title}
49 |
50 |
51 | {service.description}
52 |
53 |
57 | Go to page
58 |
59 |
60 |
61 |
62 |
63 |
64 | ))}
65 |
66 |
67 | );
68 | }
69 |
70 | export default ServiceCard;
71 |
--------------------------------------------------------------------------------
/components/Sessionwrap.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { SessionProvider } from "next-auth/react";
3 | export default function page({children,session}){
4 | return
5 | {children}
6 |
7 | }
--------------------------------------------------------------------------------
/components/Signout.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { useSession, signIn, signOut } from "next-auth/react"
3 |
4 | export default function Component() {
5 | const { data: session } = useSession()
6 | if (session) {
7 | return <>
8 | signOut()}>Sign out
9 | >
10 | }
11 | return <>
12 |
16 | Sign in
17 |
18 | >
19 | }
--------------------------------------------------------------------------------
/components/TypeWriter.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { Typewriter } from 'react-simple-typewriter'
3 | import React from 'react';
4 |
5 | const TypeWriter = ({lp,content}) => {
6 | return (
7 |
16 | );
17 | }
18 |
19 | export default TypeWriter;
20 |
--------------------------------------------------------------------------------
/components/dashboard/aside.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useState } from "react";
3 | import Link from "next/link";
4 | import { useRouter } from "next/navigation";
5 |
6 | const Aside = ({ activeTab }) => {
7 | const router = useRouter();
8 | const [isMobileMenuOpen, setMobileMenuOpen] = useState(false);
9 |
10 | const handleLinkClick = () => {
11 | setMobileMenuOpen(false);
12 | };
13 |
14 | return (
15 | <>
16 | {/* Desktop Sidebar */}
17 |
19 | {/* Logo Section */}
20 |
21 |
26 |
27 | Qmenu
28 |
29 |
30 |
31 | {/* Navigation Links */}
32 |
33 | {[
34 | { name: "Dashboard", href: "dashboard", key: "dashboard" , icon: "🏠" },
35 | { name: "Live Orders", href: "dashboard/liveorder", key: "liveorder", icon: "📦" },
36 | { name: "Sales Analytics", href: "dashboard/salesrevenue", key: "salesrevenue", icon: "💰" },
37 | { name: "Transactions", href: "dashboard/transactionhistory", key: "transactionhistory", icon: "📊" },
38 | { name: "Menu Builder", href: "menuBuilder", key: "menuBuilder", icon: "🍽️" },
39 | ].map(({ name, href, key, icon }) => (
40 |
51 | {icon}
52 | {name}
53 |
56 |
57 | ))}
58 |
59 |
60 |
61 | {/* Mobile Navigation */}
62 |
63 |
64 |
69 |
70 | Qmenu
71 |
72 |
73 | setMobileMenuOpen(!isMobileMenuOpen)}
76 | >
77 | {isMobileMenuOpen ? "✕" : "☰"}
78 |
79 |
80 |
81 | {/* Mobile Menu */}
82 | {isMobileMenuOpen && (
83 |
84 | {[
85 | { name: "Dashboard", href: "dashboard", key: "dashboard" , icon: "🏠" },
86 | { name: "Live Orders", href: "dashboard/liveorder", key: "liveorder", icon: "📦" },
87 | { name: "Sales Analytics", href: "dashboard/salesrevenue", key: "salesrevenue", icon: "💰" },
88 | { name: "Transactions", href: "dashboard/transactionhistory", key: "transactionhistory", icon: "📊" },
89 | { name: "Menu Builder", href: "menuBuilder", key: "menuBuilder", icon: "🍽️" },
90 | ].map(({ name, href, key, icon }) => (
91 |
102 | {icon}
103 | {name}
104 |
105 | ))}
106 |
107 | )}
108 | >
109 | );
110 | };
111 |
112 | export default Aside;
113 |
--------------------------------------------------------------------------------
/components/dashboard/home.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import { useSession } from "next-auth/react";
4 | import TypeWriter from "../TypeWriter";
5 | import AccountDropdown from "../AccountDropdown";
6 | import axios from "axios";
7 | import OrderTrackingChart from "./orderTracking";
8 |
9 | const Home = () => {
10 | const { data: session, status } = useSession();
11 | const [orders, setOrders] = React.useState([]);
12 | const [activeUsers, setActiveUsers] = React.useState(0);
13 | const [topOrderedItems, setTopOrderedItems] = React.useState([]);
14 | const [isLoading, setIsLoading] = React.useState(true);
15 | const [eachItems,setEachitems] = React.useState([]);
16 |
17 | // Memoized calculations
18 | const totalRevenue = React.useMemo(() => (
19 | orders.filter(order => order.status === "completed")
20 | .reduce((sum, order) => sum + order.total_Amount, 0)
21 | ), [orders]);
22 |
23 |
24 | const { totalRevenueToday, totalRevenueYesterday } = React.useMemo(() => {
25 | const today = new Date().toISOString().split("T")[0];
26 | const yesterday = new Date();
27 | yesterday.setDate(yesterday.getDate() - 1);
28 | const yesterdayStr = yesterday.toISOString().split("T")[0];
29 |
30 | return {
31 | totalRevenueToday: orders
32 | .filter(order => order.status === "completed" && order.createdAt.startsWith(today))
33 | .reduce((sum, order) => sum + order.total_Amount, 0),
34 | totalRevenueYesterday: orders
35 | .filter(order => order.status === "completed" && order.createdAt.startsWith(yesterdayStr))
36 | .reduce((sum, order) => sum + order.total_Amount, 0)
37 | };
38 | }, [orders]);
39 |
40 | const revenueTrend = React.useMemo(() => {
41 | if (totalRevenueYesterday === 0) return "No data for yesterday";
42 | const percentageChange = ((totalRevenueToday - totalRevenueYesterday) / totalRevenueYesterday) * 100;
43 | return `${percentageChange >= 0 ? "+" : ""}${percentageChange.toFixed(2)}% from last day`;
44 | }, [totalRevenueToday, totalRevenueYesterday]);
45 |
46 | const orderTrend = React.useMemo(() => {
47 | const today = new Date().toISOString().split("T")[0];
48 | const yesterday = new Date();
49 | yesterday.setDate(yesterday.getDate() - 1);
50 | const yesterdayDate = yesterday.toISOString().split("T")[0];
51 |
52 | const todayOrders = orders.filter(order => order.createdAt.startsWith(today)).length;
53 | const yesterdayOrders = orders.filter(order => order.createdAt.startsWith(yesterdayDate)).length;
54 |
55 | if (yesterdayOrders === 0) return "No orders yesterday";
56 | const trend = ((todayOrders - yesterdayOrders) / yesterdayOrders) * 100;
57 | return `${trend > 0 ? "+" : ""}${trend.toFixed(2)}% from yesterday`;
58 | }, [orders]);
59 |
60 | React.useEffect(() => {
61 | const fetchData = async () => {
62 | try {
63 | const [historyRes, activeUserRes] = await Promise.all([
64 | axios.get('/api/history'),
65 | axios.get('/api/Activeuser')
66 | ]);
67 | setOrders(historyRes.data.data);
68 | setActiveUsers(activeUserRes.data.visitors.length);
69 | } catch (error) {
70 | console.error("Error fetching data:", error);
71 | } finally {
72 | setIsLoading(false);
73 | }
74 | };
75 | fetchData();
76 | }, []);
77 |
78 | React.useEffect(() => {
79 | if (!orders.length) return;
80 |
81 | const itemCounts = orders.reduce((acc, order) => {
82 | order.items?.forEach(item => {
83 | acc[item.name] = (acc[item.name] || 0) + 1;
84 | });
85 | return acc;
86 | }, {});
87 |
88 | const sortedItems = Object.entries(itemCounts)
89 | .map(([name, count]) => ({ name, count }))
90 | .sort((a, b) => b.count - a.count)
91 | .slice(0, 4);
92 |
93 | setTopOrderedItems(sortedItems);
94 |
95 | orders.reduce((acc, order) => {
96 | setEachitems((prev) => (
97 | [
98 | ...prev,
99 | order.items.length
100 | ]
101 | ))
102 | })
103 | }, [orders]);
104 |
105 | const stats = [
106 | { title: "Total Orders", value: orders.length, icon: "📦", trend: orderTrend, color: "bg-blue-100" },
107 | { title: "Revenue", value: `₹${totalRevenue}`, icon: "💰", trend: revenueTrend, color: "bg-green-100" },
108 | { title: "Visitors", value: activeUsers, icon: "👥", trend: "Total visitors", color: "bg-purple-100" },
109 | {
110 | title: "Most Ordered",
111 | value: topOrderedItems[0]?.name || "N/A",
112 | icon: "🍕",
113 | trend: `Ordered ${topOrderedItems[0]?.count || 0} times`,
114 | color: "bg-orange-100"
115 | },
116 | ];
117 |
118 | return (
119 |
120 | {/* Header Section */}
121 |
122 |
123 |
124 | Hello,{" "}
125 |
126 | {session?.user?.name || "Guest"} 👋
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 | {/* Stats Grid */}
137 |
138 | {isLoading ? (
139 | Array(4).fill(0).map((_, index) => (
140 |
145 | ))
146 | ) : stats.map((stat, index) => (
147 |
148 |
149 |
150 |
{stat.title}
151 |
{stat.value}
152 |
153 |
154 | {stat.icon}
155 |
156 |
157 |
158 | {stat.trend}
159 |
160 |
161 | ))}
162 |
163 |
164 | {/* Main Content Grid */}
165 |
166 | {/* Live Orders Card */}
167 |
168 |
Live Order Tracking
169 |
170 |
171 |
172 | {/* Popular Items */}
173 |
174 |
Popular Items
175 |
176 | {isLoading ? (
177 | Array(4).fill(0).map((_, index) => (
178 |
182 | ))
183 | ) : topOrderedItems.length > 0 ? (
184 | topOrderedItems.map((item, index) => (
185 |
186 | {item.name}
187 | {item.count} orders
188 |
189 | ))
190 | ) : (
191 |
No popular items yet
192 | )}
193 |
194 |
195 |
196 |
197 |
198 | );
199 | };
200 |
201 | export default Home;
--------------------------------------------------------------------------------
/components/dashboard/orderTracking.js:
--------------------------------------------------------------------------------
1 | import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts";
2 |
3 | const OrderTrackingChart = ({ orders }) => {
4 | // Convert orders array into a format suitable for the graph
5 | const chartData = orders.slice(0, Math.ceil(orders.length / 2)).map((order, index) => ({
6 | order: `Order ${index + 1}`,
7 | items: Array.isArray(order) ? order.length : order, // Ensure numerical value
8 | }));
9 |
10 | return (
11 |
12 |
Live Order Tracking
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | export default OrderTrackingChart;
28 |
--------------------------------------------------------------------------------
/components/doc/recept.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Recept = ({ userName, items, totalAmount }) => {
4 | return (
5 |
6 |
7 |
8 |
9 |
Smart Qmenu Elits
10 |
😚🤗🤩
11 |
12 |
13 |
14 | Receipt No.:
15 | #{Math.floor(Math.random() * 10000)}
16 |
17 |
18 | Order Type:
19 | Dine-in
20 |
21 |
22 | Customer:
23 | {userName}
24 |
25 |
26 |
27 |
28 |
29 |
30 | Description
31 | Quantity
32 | Total
33 |
34 |
35 |
36 | {items.map((item, index) => (
37 |
38 | {item.title}
39 | {item.quantity}
40 | ₹{(item.quantity * parseFloat(item.pr.replace(/[^0-9.]/g, ""))).toFixed(2)}
41 |
42 | ))}
43 |
44 |
45 |
46 |
Total:
47 |
₹{totalAmount}
48 |
49 |
50 |
51 |
📧 rjsharmase@gmail.com
52 |
📍 Smart Qmenu, City
53 |
54 |
55 |
56 |
57 | );
58 | };
59 |
60 | export default Recept;
61 |
--------------------------------------------------------------------------------
/components/footer.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React from "react";
3 | import { LinkedIn, Facebook, Instagram, GitHub } from "@mui/icons-material";
4 |
5 | function Footer() {
6 | const [date,setdate] = React.useState("");
7 | React.useEffect(() => {
8 | setdate(new Date().getFullYear())
9 | },[])
10 | return (
11 |
49 | );
50 | }
51 |
52 | export default Footer;
53 |
--------------------------------------------------------------------------------
/components/homeNavButton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from 'next/link';
3 |
4 | const HomeNavButton = ({link,content}) => {
5 | return (
6 |
12 |
13 | {content}
14 |
15 | );
16 | }
17 |
18 | export default HomeNavButton;
19 |
--------------------------------------------------------------------------------
/components/menuCard.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React from "react";
3 | import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
4 |
5 | const MenuItemCard = ({ deleteCard, image, title, price, originalPrice }) => {
6 | const discount = originalPrice ? Math.round(((originalPrice - price) / originalPrice) * 100) : 0;
7 |
8 | return (
9 |
10 |
11 |
12 | {discount > 0 && (
13 |
14 | {discount}% OFF
15 |
16 | )}
17 |
22 |
23 |
24 |
{title}
25 |
26 |
27 | {price}
28 | {originalPrice && {originalPrice} }
29 |
30 |
31 |
32 |
40 |
41 |
42 | Add to Cart
43 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default MenuItemCard;
50 |
--------------------------------------------------------------------------------
/components/menuDone.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import MenuSubmitbtn from './menuSubmitbtn';
3 |
4 | const MenuDone = () => {
5 | return (
6 |
7 |
8 | 🔴When you done adding items For generating Qr click on down button
9 |
10 |
11 |
12 | );
13 | }
14 |
15 | export default MenuDone;
16 |
--------------------------------------------------------------------------------
/components/menuInterface/AI_interface.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import AiChat from "./ai_chat";
3 |
4 | const AIInterface = () => {
5 | const [clicked, setClick] = useState(false);
6 |
7 | const handleClose = () => {
8 | setClick(false);
9 | };
10 |
11 | return (
12 | <>
13 | {/* Floating AI Logo */}
14 | setClick(!clicked)}
18 | >
19 |
27 |
28 |
29 | {/* AI Chat Component (positioned separately) */}
30 | {clicked && (
31 |
34 | )}
35 | >
36 | );
37 | };
38 |
39 | export default AIInterface;
40 |
--------------------------------------------------------------------------------
/components/menuInterface/Cart.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React,{useContext} from 'react';
3 | import NavigateNextIcon from '@mui/icons-material/NavigateNext';
4 | import ItemContext from './itemContext';
5 |
6 | const Cart = ({doneAdd}) => {
7 | const {itemprop} = useContext(ItemContext);
8 | const lenofitem = itemprop.length;
9 |
10 | const visibleItems = itemprop.slice(-3);
11 | const totalQuantity = itemprop.reduce((acc, item) => acc + item.quantity, 0);
12 | return (
13 | 2 ? 'w-52':null}`}>
14 |
15 | {visibleItems.map((i, idx) => (
16 |
1 ? '-mr-4' : null}`}
19 | />
20 |
21 | ))}
22 |
23 |
24 |
25 | View cart
26 | { totalQuantity }
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
38 | export default Cart;
39 |
--------------------------------------------------------------------------------
/components/menuInterface/Confirmpage.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useContext, useRef, useMemo } from "react";
3 | import { motion } from "framer-motion";
4 | import ItemContext from "./itemContext";
5 | import ConfirmPop from "./confirm_pop";
6 |
7 | const AnimatedDiv = ({ doneAdd,userName }) => {
8 | const { itemprop, setitemprop } = useContext(ItemContext);
9 | const containerRef = useRef(null);
10 | const [placeorder,setplaceorder] = React.useState(false);
11 | const [isVisible, setIsVisible] = React.useState(true);
12 |
13 | const handlePlaceorder = () => {
14 | setplaceorder(!placeorder)
15 | }
16 |
17 |
18 | const totalPrice = useMemo(() => {
19 | return itemprop.reduce((acc, item) => {
20 | const priceValue = parseFloat(item.pr.replace(/[^0-9.]/g, ""));
21 | return acc + priceValue * item.quantity;
22 | }, 0);
23 | }, [itemprop]);
24 |
25 | return (
26 | offset.y > 50 && (setIsVisible(false), doneAdd())}
36 | >
37 |
38 |
41 |
42 |
43 |
Your Cart
44 |
45 |
46 |
e.stopPropagation()}
50 | >
51 | {itemprop.length === 0 ? (
52 |
Your cart is empty.
53 | ) : (
54 | itemprop.map(item => {
55 | const priceValue = parseFloat(item.pr.replace(/[^0-9.]/g, ""));
56 | return (
57 |
58 |
59 |
60 |
61 |
{item.title}
62 |
63 |
64 |
65 |
66 |
67 |
68 | setitemprop(prev => prev.map(it => it.id === item.id ? { ...it, quantity: it.quantity - 1 } : it).filter(it => it.quantity > 0))}>
70 | -
71 |
72 | {item.quantity}
73 | setitemprop(prev => prev.map(it => it.id === item.id ? { ...it, quantity: it.quantity + 1 } : it))}>
75 | +
76 |
77 |
78 |
₹{(priceValue * item.quantity).toFixed(2)}
79 |
80 |
81 | );
82 | })
83 | )}
84 |
85 |
86 |
87 | {placeorder ?
: null}
88 |
89 |
90 |
91 |
92 | {itemprop.length > 0 && (
93 |
94 |
95 | Total:
96 | ₹{totalPrice.toFixed(2)}
97 |
98 |
99 | Order Now
100 |
101 |
102 | )}
103 |
104 |
105 | );
106 | };
107 |
108 | export default AnimatedDiv;
109 |
--------------------------------------------------------------------------------
/components/menuInterface/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Footer = () => {
4 | return (
5 |
6 |
112 |
113 |
@Made with 💗 Softenrj
114 |
115 |
116 | );
117 | }
118 |
119 | export default Footer;
120 |
--------------------------------------------------------------------------------
/components/menuInterface/Menupopulate.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useEffect, useState } from 'react';
3 | import ItemCard from './itemCard';
4 | import axios from 'axios';
5 |
6 |
7 | const Menupopulate = ({ activeSection }) => {
8 | const [items, setItems] = useState([]);
9 |
10 | useEffect(() => {
11 | const fetchItem = async () => {
12 | try {
13 | const resp = await axios.get('/api/menuItems');
14 | setItems(resp.data);
15 | } catch (error) {
16 | console.error("Error fetching menu items:", error);
17 | }
18 | };
19 | fetchItem();
20 | }, [activeSection]);
21 |
22 | return (
23 |
24 | {items
25 | .filter((item) => activeSection === "All" || item.section === activeSection)
26 | .map((item) => (
27 |
35 | ))
36 | }
37 |
38 |
39 | );
40 | }
41 |
42 | export default Menupopulate;
43 |
--------------------------------------------------------------------------------
/components/menuInterface/SectionTitle.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const SectionTitle = ({activesection}) => {
4 | return (
5 |
7 | {activesection}
8 |
9 |
10 | );
11 | }
12 |
13 | export default SectionTitle;
14 |
--------------------------------------------------------------------------------
/components/menuInterface/ai_chat.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useEffect, useState, useRef } from "react";
3 | import { motion, AnimatePresence } from "framer-motion";
4 | import axios from "axios";
5 |
6 |
7 | const AiChat = ({ onClose }) => {
8 | const [items, setItems] = useState([]);
9 |
10 | useEffect(() => {
11 | const fetchItem = async () => {
12 | try {
13 | const resp = await axios.get('/api/menuItems');
14 | setItems(resp.data.map((i) => (
15 | i.title
16 | )));
17 | } catch (error) {
18 | console.error("Error fetching menu items:", error);
19 | }
20 | };
21 | fetchItem();
22 | }, []);
23 | const [isVisible, setIsVisible] = useState(true);
24 | const [inputMessage, setInputMessage] = useState("");
25 | const [messages, setMessages] = useState([
26 | {
27 | text: "Welcome to DineBuddy! 🍴 I can help with menu suggestions, dietary restrictions, and reservations. How can I assist you today?",
28 | isBot: true,
29 | time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
30 | }
31 | ]);
32 | const messagesEndRef = useRef(null);
33 | const inputRef = useRef(null);
34 |
35 | const generateResponse = async (userMessage) => {
36 | try {
37 | const response = await fetch("/api/gemini", {
38 | method: "POST",
39 | headers: {
40 | "Content-Type": "application/json",
41 | },
42 | body: JSON.stringify({ message: userMessage,
43 | Avitem: items
44 | }),
45 | });
46 |
47 | if (!response.ok) {
48 | throw new Error("Failed to fetch AI response");
49 | }
50 |
51 | const data = await response.json();
52 | return data.response; // Return AI-generated response
53 |
54 | } catch (error) {
55 | console.error("Error fetching response:", error);
56 | return "Sorry, I'm having trouble connecting to the kitchen. Please try again!";
57 | }
58 | };
59 |
60 |
61 |
62 | const handleSend = async () => {
63 | const trimmedMessage = inputMessage.trim();
64 | if (!trimmedMessage) return;
65 |
66 | // Add user message
67 | setMessages(prev => [...prev, {
68 | text: trimmedMessage,
69 | isBot: false,
70 | time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
71 | }]);
72 |
73 | try {
74 | // Get AI response
75 | const aiResponse = await generateResponse(trimmedMessage);
76 |
77 | // Add AI response
78 | setMessages(prev => [...prev, {
79 | text: aiResponse,
80 | isBot: true,
81 | time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
82 | }]);
83 |
84 | } catch (error) {
85 | setMessages(prev => [...prev, {
86 | text: "Our chefs are busy right now. Please try again later!",
87 | isBot: true,
88 | time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
89 | }]);
90 | }
91 |
92 | setInputMessage("");
93 | };
94 |
95 | const quickSuggestions = [
96 | "Super Starters",
97 | "Diet Options",
98 | "Chef's Specials",
99 | "Popular Dishes",
100 | "Vegetarian Choices",
101 | "Dessert Menu"
102 | ];
103 |
104 |
105 |
106 | const handleSuggestionClick = (suggestion) => {
107 | setInputMessage(suggestion);
108 | inputRef.current.focus();
109 | };
110 |
111 | useEffect(() => {
112 | messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
113 | }, [messages]);
114 |
115 | return (
116 |
117 | {isVisible && (
118 | offset.y > 50 && (setIsVisible(false), onClose())}
128 | >
129 | {/* Chat Header */}
130 |
131 |
132 |
137 |
138 |
DineBuddy
139 |
AI Dining Assistant
140 |
141 |
142 |
(setIsVisible(false), onClose())}
144 | className="ml-auto text-white hover:text-amber-100 transition-colors"
145 | aria-label="Close chat"
146 | >
147 |
148 |
149 |
150 |
151 |
152 |
153 | {/* Chat Body */}
154 |
155 |
156 | {messages.map((msg, index) => (
157 |
163 |
164 |
{msg.text}
165 |
{msg.time}
166 |
167 |
168 | ))}
169 |
170 |
171 |
172 | {/* Quick Suggestions */}
173 |
174 |
175 | {quickSuggestions.map((suggestion, index) => (
176 | handleSuggestionClick(suggestion)}
179 | className="px-3 py-2 bg-amber-50 text-rose-700 text-sm font-medium rounded-lg hover:bg-amber-100 transition-colors whitespace-nowrap"
180 | >
181 | {suggestion}
182 |
183 | ))}
184 |
185 |
186 |
187 |
188 | {/* Input Area */}
189 |
190 |
191 |
setInputMessage(e.target.value)}
196 | onKeyPress={(e) => e.key === 'Enter' && handleSend()}
197 | placeholder="Ask about menu items, dietary info, or reservations..."
198 | className="flex-1 p-2 border border-gray-300 rounded-xl focus:outline-none focus:border-rose-500 focus:ring-1 focus:ring-rose-500 text-sm"
199 | />
200 |
205 |
206 |
207 |
208 |
209 |
210 |
211 | Powered by AI - GEMINI
212 |
213 |
214 |
215 | )}
216 |
217 | );
218 | };
219 |
220 | export default AiChat;
--------------------------------------------------------------------------------
/components/menuInterface/confirm_pop.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useContext, useRef, useState } from "react";
3 | import axios from "axios";
4 | import ItemContext from "./itemContext";
5 | import Recept from "@/components/doc/recept";
6 | import html2canvas from "html2canvas";
7 | import jsPDF from "jspdf";
8 |
9 | const ConformPop = ({ onClose, doneorder, userName }) => {
10 | const { itemprop, setitemprop } = useContext(ItemContext);
11 | const receiptRef = useRef(null);
12 | const [isSuccess, setIsSuccess] = useState(false);
13 | const [isProcessing, setIsProcessing] = useState(false);
14 |
15 | const totalPrice = React.useMemo(() => {
16 | return itemprop.reduce((acc, item) => {
17 | const priceValue = parseFloat(item.pr.replace(/[^0-9.]/g, ""));
18 | return acc + priceValue * item.quantity;
19 | }, 0);
20 | }, [itemprop]);
21 |
22 | const handleOrder = async (paymentType) => {
23 | setIsProcessing(true);
24 | try {
25 | const response = await axios.post("/api/order", {
26 | table: userName,
27 | status: paymentType,
28 | items: itemprop.map(item => ({
29 | name: item.title,
30 | quantity: item.quantity
31 | })),
32 | total_Amount: totalPrice.toFixed(2)
33 | });
34 |
35 | if (response.status === 201) {
36 | setitemprop([]);
37 | try {
38 | await generateReceipt();
39 | } catch (error) {
40 | console.error("Receipt generation failed:", error);
41 | }
42 | doneorder();
43 | setIsSuccess(true);
44 | setTimeout(() => {
45 | onClose();
46 | }, 2000);
47 | }
48 | } catch (error) {
49 | console.error("Order error:", error);
50 | let errorMessage = "Failed to place order";
51 | if (error.response?.data?.message) {
52 | errorMessage = error.response.data.message;
53 | }
54 | alert(errorMessage);
55 | } finally {
56 | setIsProcessing(false);
57 | }
58 | };
59 |
60 | const generateReceipt = () => {
61 | return new Promise((resolve, reject) => {
62 | const input = receiptRef.current;
63 | if (!input) {
64 | reject(new Error("Receipt element not found"));
65 | return;
66 | }
67 |
68 | html2canvas(input, {
69 | useCORS: true,
70 | scale: 2,
71 | logging: true,
72 | allowTaint: true
73 | }).then((canvas) => {
74 | const imgData = canvas.toDataURL("image/png");
75 | const pdf = new jsPDF("p", "mm", "a4");
76 |
77 | // Get PDF page dimensions
78 | const pageWidth = pdf.internal.pageSize.getWidth();
79 | const pageHeight = pdf.internal.pageSize.getHeight();
80 |
81 | // Calculate image dimensions
82 | let imgWidth = 180; // Reduced from 190 to 180 for margins
83 | let imgHeight = (canvas.height * imgWidth) / canvas.width;
84 |
85 | // Check if image height exceeds page height
86 | const margin = 10; // 10mm top and bottom margin
87 | if (imgHeight > pageHeight - margin * 2) {
88 | // Scale down to fit page height
89 | const ratio = (pageHeight - margin * 2) / imgHeight;
90 | imgHeight = (pageHeight - margin * 2);
91 | imgWidth = imgWidth * ratio;
92 | }
93 |
94 | // Center horizontally
95 | const x = (pageWidth - imgWidth) / 2;
96 |
97 | pdf.addImage(imgData, "PNG", x, margin, imgWidth, imgHeight);
98 | pdf.save("receipt.pdf");
99 | resolve();
100 | }).catch(reject);
101 | });
102 | };
103 |
104 | return (
105 |
106 | {isSuccess ? (
107 |
108 |
109 |
110 |
111 |
112 |
113 |
Order Placed Successfully!
114 |
115 |
116 | ) : (
117 |
118 |
119 | ✖
120 |
121 |
Confirm Your Order
122 |
123 | You have selected items worth ₹{totalPrice.toFixed(2)}
124 |
125 |
126 | handleOrder("Pay Now")}
128 | disabled={isProcessing}
129 | className={`w-1/2 text-white py-2 rounded-l-xl ${
130 | isProcessing ? 'bg-gray-400' : 'bg-green-500 hover:bg-green-600'
131 | }`}
132 | >
133 | {isProcessing ? 'Processing...' : 'Pay Now'}
134 |
135 | handleOrder("Pay Later")}
137 | disabled={isProcessing}
138 | className={`w-1/2 text-white py-2 rounded-r-xl ${
139 | isProcessing ? 'bg-gray-400' : 'bg-blue-500 hover:bg-blue-600'
140 | }`}
141 | >
142 | {isProcessing ? 'Processing...' : 'Pay Later'}
143 |
144 |
145 |
146 | )}
147 |
148 | {/* Hidden Receipt - positioned off-screen */}
149 |
158 |
159 | );
160 | };
161 |
162 | export default ConformPop;
163 |
164 |
--------------------------------------------------------------------------------
/components/menuInterface/itemCard.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import React, { useContext } from "react";
3 | import ItemContext from "./itemContext";
4 |
5 | const ItemCard = ({ id, title, image, orp, pr }) => {
6 | const { itemprop, setitemprop } = useContext(ItemContext);
7 |
8 | const currentItem = (itemprop || []).find(item => item.id === id);
9 | const itemCount = currentItem ? currentItem.quantity : 0;
10 | const isInCart = itemCount > 0;
11 |
12 | const toggleCart = () => {
13 | setitemprop(prev =>
14 | isInCart
15 | ? prev.filter(item => item.id !== id)
16 | : [...prev, { id, title, image, quantity: 1, pr }]
17 | );
18 | };
19 |
20 | const addMore = () => {
21 | setitemprop(prev =>
22 | prev.map(item =>
23 | item.id === id ? { ...item, quantity: (item.quantity || 0) + 1 } : item
24 | )
25 | );
26 | };
27 |
28 | const removeOne = () => {
29 | setitemprop(prev =>
30 | prev
31 | .map(item =>
32 | item.id === id ? { ...item, quantity: (item.quantity || 1) - 1 } : item
33 | )
34 | .filter(item => item.quantity > 0)
35 | );
36 | };
37 |
38 | return (
39 |
40 |
41 |
45 | {isInCart ? 'X' : '+'}
46 |
47 |
52 |
53 |
54 | {isInCart && (
55 |
56 |
60 | -
61 |
62 | {itemCount}
63 |
67 | +
68 |
69 |
70 | )}
71 |
72 |
73 | {title}
74 |
75 |
76 |
{pr}
77 |
{orp}
78 |
79 |
80 | );
81 | };
82 |
83 | export default ItemCard;
84 |
--------------------------------------------------------------------------------
/components/menuInterface/itemContext.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { createContext } from "react";
3 |
4 | const ItemContext = createContext([]);
5 |
6 | export default ItemContext;
--------------------------------------------------------------------------------
/components/menuInterface/navbar.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import GrainIcon from '@mui/icons-material/Grain';
3 |
4 | const Navbar = () => {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | );
14 | }
15 |
16 | export default Navbar;
17 |
--------------------------------------------------------------------------------
/components/menuInterface/sections.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useState, useEffect } from 'react';
3 | import axios from 'axios';
4 |
5 | const Sections = ({setSec}) => {
6 | const [sections, setSections] = useState(["All"]);
7 |
8 | useEffect(() => {
9 | const fetchData = async () => {
10 | try {
11 | const response = await axios.get('/api/sections');
12 | const uniqueSections = new Set([...sections, ...response.data.map(s => s.label)]);
13 | setSections(Array.from(uniqueSections));
14 | } catch (error) {
15 | console.error("Error fetching sections:", error);
16 | }
17 | };
18 | fetchData();
19 | }, []);
20 |
21 | return (
22 |
23 | {sections.map((sec, idx) => (
24 |
setSec(sec)}
26 | key={idx}
27 | className="h-10 px-6 flex items-center justify-center border rounded-full min-w-max font-dmSans text-lg font-medium text-gray-700 bg-white shadow-sm hover:bg-fuchsia-100 hover:border-fuchsia-400 hover:scale-105 transition-all duration-300 cursor-pointer"
28 | >
29 | {sec}
30 |
31 | ))}
32 |
33 | );
34 | }
35 |
36 | export default Sections;
37 |
--------------------------------------------------------------------------------
/components/menuSubmitbtn.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Link from 'next/link';
3 |
4 | const MenuSubmitbtn = () => {
5 | return (
6 |
7 |
10 |
Make Begin.
23 |
27 |
31 |
32 |
33 |
34 |
35 | );
36 | }
37 |
38 | export default MenuSubmitbtn;
39 |
--------------------------------------------------------------------------------
/components/menu_ItemCard_default.js:
--------------------------------------------------------------------------------
1 | "use client"
2 | import React, { useState } from 'react';
3 |
4 | const MenuItemCard = ({onDone}) => {
5 | const [image, setImage] = useState("/qrbuilder/default-img.png");
6 | const [title, setTitle] = useState("Name of your Food");
7 | const [price, setPrice] = useState("₹449");
8 | const [originalPrice, setOriginalPrice] = useState("₹699");
9 | const [isEditing, setIsEditing] = useState({ title: false, price: false, originalPrice: false });
10 |
11 | const handleImageChange = async (event) => {
12 | const file = event.target.files[0];
13 | if(!file) return;
14 | const formData = new FormData();
15 | formData.append("image",file);
16 |
17 | try{
18 | const resp = await fetch("/api/upload",{
19 | method: "POST",
20 | body: formData
21 | });
22 | const data = await resp.json();
23 | if(resp.ok){
24 | setImage(data.imageUrl);
25 | }else{
26 | console.error(data.error);
27 | }
28 | }catch(e){
29 | console.log(e);
30 | }
31 | };
32 |
33 | const handleEdit = (field) => {
34 | setIsEditing({ ...isEditing, [field]: true });
35 | };
36 |
37 | const handleBlur = (field, value) => {
38 | setIsEditing({ ...isEditing, [field]: false });
39 | if (field === "title") setTitle(value);
40 | if (field === "price") setPrice(value);
41 | if (field === "originalPrice") setOriginalPrice(value);
42 | };
43 |
44 | return (
45 |
46 |
47 |
48 |
49 |
50 |
51 | {isEditing.title ? (
52 |
handleBlur("title", e.target.value)} />
54 | ) : (
55 |
handleEdit("title")}>{title}
56 | )}
57 |
58 |
75 |
{
76 | onDone({
77 | image,
78 | title,
79 | price,
80 | originalPrice
81 | })
82 | setImage("/qrbuilder/default-img.png")
83 | setTitle("Name of your Food")
84 | }} className=" flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-xs font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-300">
85 |
86 |
87 |
88 | Done
89 |
90 |
91 |
92 | );
93 | };
94 |
95 | export default MenuItemCard;
96 |
--------------------------------------------------------------------------------
/components/menu_builder_section.js:
--------------------------------------------------------------------------------
1 | "use client";
2 | import { useState, useEffect } from "react";
3 | import { useRouter } from "next/navigation";
4 | import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
5 | import EditIcon from "@mui/icons-material/Edit";
6 | import DoneIcon from "@mui/icons-material/Done";
7 | import DeleteForeverIcon from "@mui/icons-material/DeleteForever";
8 | import { v4 as uuidv4 } from "uuid";
9 | import MenuItemCard from "./menuCard";
10 | import MenuItemCard_default from "./menu_ItemCard_default";
11 | import axios from "axios";
12 |
13 | const Tabs = () => {
14 | const [activeTab, setActiveTab] = useState("");
15 | const [isEditing, setIsEditing] = useState(false);
16 | const [isAddingSection, setIsAddingSection] = useState(false);
17 | const [newSectionName, setNewSectionName] = useState("");
18 | const [menuSections, setMenuSections] = useState([]);
19 | const [menuItems, setMenuItems] = useState([]);
20 | const router = useRouter();
21 |
22 |
23 | useEffect(() => {
24 | const fetchData = async () => {
25 | try {
26 | const sectionsRes = await axios.get("/api/sections");
27 | const itemsRes = await axios.get("/api/menuItems");
28 | setMenuSections(sectionsRes.data);
29 | setMenuItems(itemsRes.data);
30 | if (sectionsRes.data.length > 0) {
31 | setActiveTab(sectionsRes.data[0].label);
32 | }
33 | } catch (error) {
34 | console.error("Error fetching data:", error);
35 | }
36 | };
37 | fetchData();
38 | }, []);
39 |
40 |
41 | const addSection = () => setIsAddingSection(!isAddingSection);
42 |
43 | const handleInput = async (e) => {
44 | if (e.key === "Enter" && newSectionName.trim() !== "") {
45 | try {
46 | const res = await axios.post("/api/sections", { label: newSectionName });
47 | setMenuSections((prev) => [...prev, res.data.section]);
48 | setNewSectionName("");
49 | setIsAddingSection(false);
50 | } catch (error) {
51 | console.error("Error adding section:", error);
52 | }
53 | }
54 | };
55 |
56 |
57 | const doneEditing = async () => {
58 | if (!newSectionName.trim()) return;
59 | try {
60 | await axios.put("/api/sections", { oldLabel: activeTab, newLabel: newSectionName });
61 | setMenuSections((prev) =>
62 | prev.map((section) => (section.label === activeTab ? { ...section, label: newSectionName } : section))
63 | );
64 | setMenuItems((prev) =>
65 | prev.map((item) => (item.section === activeTab ? { ...item, section: newSectionName } : item))
66 | );
67 | setActiveTab(newSectionName);
68 | setIsEditing(false);
69 | setNewSectionName("");
70 | } catch (error) {
71 | console.error("Error updating section:", error);
72 | }
73 | };
74 |
75 |
76 | const sectionDelete = async () => {
77 | try {
78 | await axios.delete(`/api/sections`, { data: { label: activeTab } });
79 | await axios.delete(`/api/menuItems`,{ data: { label: activeTab } });
80 | setMenuSections((prev) => prev.filter((item) => item.label !== activeTab));
81 | setMenuItems((prev) => prev.filter((item) => item.section !== activeTab));
82 | setActiveTab(menuSections.length > 1 ? menuSections[1].label : "");
83 | setIsEditing(!isEditing);
84 | } catch (error) {
85 | console.error("Error deleting section:", error);
86 | }
87 | };
88 |
89 |
90 | const addNewItem = async (data) => {
91 | try {
92 | const newItem = {
93 | id: uuidv4(),
94 | section: activeTab,
95 | image: data.image,
96 | originalPrice: data.originalPrice,
97 | price: data.price,
98 | title: data.title,
99 | };
100 | await axios.post("/api/menuItems", newItem);
101 | setMenuItems((prev) => [...prev, newItem]);
102 | } catch (error) {
103 | console.error("Error adding item:", error);
104 | }
105 | };
106 |
107 | const handleCardDelete = async (id) => {
108 | try{
109 | await axios.delete(`/api/menuItems?id=${id}`);
110 | setMenuItems((prev) =>
111 | prev.filter((item) => item.id !== id)
112 | )
113 | }catch(error){
114 | console.error(error)
115 | }
116 | }
117 |
118 | return (
119 |
120 | {/* Tabs Section */}
121 |
142 |
143 |
144 | {(activeTab) ?
145 |
146 |
147 |
148 |
149 | {isEditing ? (
150 | setNewSectionName(e.target.value)} value={newSectionName} />
151 | ) : (
152 | activeTab
153 | )}
154 |
155 | {isEditing ? : { setNewSectionName(activeTab); setIsEditing(true); }} />}
156 |
157 |
158 |
Add item as by just click on default item card.
159 |
160 | : null
161 | }
162 |
163 | {/* Menu Items */}
164 | {(activeTab) ?
165 |
166 | {menuItems.filter((item) => item.section === activeTab).map((item) => (
167 | handleCardDelete(item.id)} />
168 | ))}
169 |
170 |
171 | :null
172 | }
173 |
174 | );
175 | };
176 |
177 | export default Tabs;
178 |
--------------------------------------------------------------------------------
/components/navbar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import HomeNavButton from "./homeNavButton";
3 | import AccountDropdown from "./AccountDropdown";
4 |
5 | function Navbar() {
6 | return (
7 |
9 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 | );
33 | }
34 |
35 | export default Navbar;
36 |
--------------------------------------------------------------------------------
/components/service-poster.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Card = () => {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 | }
20 |
21 | export default Card;
22 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { dirname } from "path";
2 | import { fileURLToPath } from "url";
3 | import { FlatCompat } from "@eslint/eslintrc";
4 |
5 | const __filename = fileURLToPath(import.meta.url);
6 | const __dirname = dirname(__filename);
7 |
8 | const compat = new FlatCompat({
9 | baseDirectory: __dirname,
10 | });
11 |
12 | const eslintConfig = [...compat.extends("next/core-web-vitals")];
13 |
14 | export default eslintConfig;
15 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "paths": {
4 | "@/*": ["./*"]
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 |
2 | /** @type {import('next').NextConfig} */
3 | const nextConfig = {
4 | async headers(){
5 | return [
6 | {
7 | source: "/api/:path*",
8 | headers: [
9 | { key: "Access-Control-Allow-Credentials", value: "true" },
10 | { key: "Access-Control-Allow-Origin", value: "*" },
11 | { key: "Access-Control-Allow-Methods", value: "GET,DELETE,PATCH,POST,PUT" },
12 | { key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" },
13 | ]
14 | }
15 | ]
16 | }
17 | };
18 |
19 | export default nextConfig;
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "smart-menu-maker",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@emotion/react": "^11.14.0",
13 | "@emotion/styled": "^11.14.0",
14 | "@fontsource/dm-sans": "^5.1.1",
15 | "@headlessui/react": "^2.2.0",
16 | "@mui/icons-material": "^6.4.1",
17 | "@mui/material": "^6.4.1",
18 | "@nextui-org/react": "^2.6.11",
19 | "@shadcn/ui": "^0.0.4",
20 | "@tanstack/react-virtual": "^3.13.0",
21 | "axios": "^1.7.9",
22 | "express": "^4.21.2",
23 | "formidable": "^3.5.2",
24 | "framer-motion": "^12.4.3",
25 | "fs": "^0.0.1-security",
26 | "html2canvas": "^1.4.1",
27 | "jspdf": "^3.0.0",
28 | "lucide-react": "^0.475.0",
29 | "mongoose": "^8.10.0",
30 | "multer": "^1.4.5-lts.1",
31 | "next": "^15.1.7",
32 | "next-auth": "^4.24.11",
33 | "path": "^0.12.7",
34 | "qrcode": "^1.5.4",
35 | "react": "^19.0.0",
36 | "react-csv": "^2.2.2",
37 | "react-dom": "^19.0.0",
38 | "react-hook-form": "^7.54.2",
39 | "react-icons": "^5.5.0",
40 | "react-qr-code": "^2.0.15",
41 | "react-qrcode-logo": "^3.0.0",
42 | "react-simple-typewriter": "^5.0.1",
43 | "react-toastify": "^11.0.3",
44 | "recharts": "^2.15.1",
45 | "socket.io": "^4.8.1",
46 | "socket.io-client": "^4.8.1"
47 | },
48 | "devDependencies": {
49 | "@eslint/eslintrc": "^3",
50 | "eslint": "^9",
51 | "eslint-config-next": "15.1.6",
52 | "postcss": "^8",
53 | "tailwindcss": "^3.4.1"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('postcss-load-config').Config} */
2 | const config = {
3 | plugins: {
4 | tailwindcss: {},
5 | },
6 | };
7 |
8 | export default config;
9 |
--------------------------------------------------------------------------------
/public/MenuInterface/Ai.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/MenuInterface/Ai.mp4
--------------------------------------------------------------------------------
/public/MenuInterface/ailogo.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/MenuInterface/ailogo.mp4
--------------------------------------------------------------------------------
/public/MenuInterface/b1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/MenuInterface/b1.jpg
--------------------------------------------------------------------------------
/public/MenuInterface/b2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/MenuInterface/b2.jpg
--------------------------------------------------------------------------------
/public/dashboard/b1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/dashboard/b1.jpg
--------------------------------------------------------------------------------
/public/file.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/globe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/homepage/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/homepage/bg.png
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/notification.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/notification.mp3
--------------------------------------------------------------------------------
/public/profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/profile.jpg
--------------------------------------------------------------------------------
/public/qrbuilder/default-img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/qrbuilder/default-img.png
--------------------------------------------------------------------------------
/public/qrbuilder/download.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/qrbuilder/download.jpeg
--------------------------------------------------------------------------------
/public/serviceTab/poster-1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/serviceTab/poster-1.jpeg
--------------------------------------------------------------------------------
/public/serviceTab/poster-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/serviceTab/poster-2.png
--------------------------------------------------------------------------------
/public/serviceTab/poster-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/serviceTab/poster-3.png
--------------------------------------------------------------------------------
/public/serviceTab/qr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/serviceTab/qr.png
--------------------------------------------------------------------------------
/public/serviceTab/s-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/serviceTab/s-1.jpg
--------------------------------------------------------------------------------
/public/serviceTab/s-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/serviceTab/s-2.jpg
--------------------------------------------------------------------------------
/public/serviceTab/s-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/serviceTab/s-3.jpg
--------------------------------------------------------------------------------
/public/serviceTab/s-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/serviceTab/s-4.jpg
--------------------------------------------------------------------------------
/public/shared/Softcore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/shared/Softcore.png
--------------------------------------------------------------------------------
/public/shared/TNE-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/shared/TNE-logo.png
--------------------------------------------------------------------------------
/public/shared/close-up-hands-holding-smartphones.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/shared/close-up-hands-holding-smartphones.jpg
--------------------------------------------------------------------------------
/public/shared/logo.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/shared/logo.mp4
--------------------------------------------------------------------------------
/public/shared/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/shared/logo.png
--------------------------------------------------------------------------------
/public/shared/profile-icon.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/shared/profile-icon.jpeg
--------------------------------------------------------------------------------
/public/uploads/360_F_733954401_TdqG5vccceLm5JuSBIGoImDaBxyoMvOc.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/360_F_733954401_TdqG5vccceLm5JuSBIGoImDaBxyoMvOc.jpg
--------------------------------------------------------------------------------
/public/uploads/564931.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/564931.jpg
--------------------------------------------------------------------------------
/public/uploads/708311.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/708311.jpg
--------------------------------------------------------------------------------
/public/uploads/7509855.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/7509855.jpg
--------------------------------------------------------------------------------
/public/uploads/Baingan Bharta.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Baingan Bharta.jpeg
--------------------------------------------------------------------------------
/public/uploads/Boondi Raita.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Boondi Raita.jpeg
--------------------------------------------------------------------------------
/public/uploads/Butter Naan.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Butter Naan.jpeg
--------------------------------------------------------------------------------
/public/uploads/Chicken Biryani.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Chicken Biryani.jpeg
--------------------------------------------------------------------------------
/public/uploads/Chicken Butter Masala.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Chicken Butter Masala.jpeg
--------------------------------------------------------------------------------
/public/uploads/Chicken Shorba.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Chicken Shorba.jpg
--------------------------------------------------------------------------------
/public/uploads/Chicken Tikka.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Chicken Tikka.jpeg
--------------------------------------------------------------------------------
/public/uploads/Chicken Tikka.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Chicken Tikka.jpg
--------------------------------------------------------------------------------
/public/uploads/Coconut Chutney.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Coconut Chutney.jpeg
--------------------------------------------------------------------------------
/public/uploads/Dal Makhani.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Dal Makhani.jpeg
--------------------------------------------------------------------------------
/public/uploads/Fish Amritsari.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Fish Amritsari.jpg
--------------------------------------------------------------------------------
/public/uploads/Fish Curry.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Fish Curry.jpeg
--------------------------------------------------------------------------------
/public/uploads/Gajar Ka Halwa.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Gajar Ka Halwa.webp
--------------------------------------------------------------------------------
/public/uploads/Garlic Naan.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Garlic Naan.jpeg
--------------------------------------------------------------------------------
/public/uploads/Gulab Jamun.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Gulab Jamun.jpg
--------------------------------------------------------------------------------
/public/uploads/Hyderabadi Biryani.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Hyderabadi Biryani.jpeg
--------------------------------------------------------------------------------
/public/uploads/Jalebi (per plate).jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Jalebi (per plate).jpeg
--------------------------------------------------------------------------------
/public/uploads/Jeera Rice.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Jeera Rice.jpeg
--------------------------------------------------------------------------------
/public/uploads/Kachumber Salad.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Kachumber Salad.jpeg
--------------------------------------------------------------------------------
/public/uploads/Lachha Paratha.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Lachha Paratha.jpg
--------------------------------------------------------------------------------
/public/uploads/Manchow Soup.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Manchow Soup.jpeg
--------------------------------------------------------------------------------
/public/uploads/Mutton Biryani.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Mutton Biryani.jpeg
--------------------------------------------------------------------------------
/public/uploads/Mutton Rogan Josh.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Mutton Rogan Josh.jpeg
--------------------------------------------------------------------------------
/public/uploads/Mutton Seekh Kebab.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Mutton Seekh Kebab.jpg
--------------------------------------------------------------------------------
/public/uploads/OIP.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/OIP.jpg
--------------------------------------------------------------------------------
/public/uploads/OnionCucumber Raita.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/OnionCucumber Raita.jpeg
--------------------------------------------------------------------------------
/public/uploads/Pakoras (Mix Veg).jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Pakoras (Mix Veg).jpeg
--------------------------------------------------------------------------------
/public/uploads/Pakoras (Mix Veg).jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Pakoras (Mix Veg).jpg
--------------------------------------------------------------------------------
/public/uploads/Palak Paneer.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Palak Paneer.jpeg
--------------------------------------------------------------------------------
/public/uploads/Paneer Butter Masala.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Paneer Butter Masala.jpeg
--------------------------------------------------------------------------------
/public/uploads/Paneer Butter Masala.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Paneer Butter Masala.jpg
--------------------------------------------------------------------------------
/public/uploads/Peas Pulao.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Peas Pulao.jpeg
--------------------------------------------------------------------------------
/public/uploads/Rasgulla.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Rasgulla.jpeg
--------------------------------------------------------------------------------
/public/uploads/Rasmalai (2 pcs).jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Rasmalai (2 pcs).jpeg
--------------------------------------------------------------------------------
/public/uploads/Roomali Roti.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Roomali Roti.jpg
--------------------------------------------------------------------------------
/public/uploads/Steamed Basmati Rice.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Steamed Basmati Rice.jpeg
--------------------------------------------------------------------------------
/public/uploads/Sweet Corn Soup.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Sweet Corn Soup.webp
--------------------------------------------------------------------------------
/public/uploads/Tandoori Roti.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Tandoori Roti.jpeg
--------------------------------------------------------------------------------
/public/uploads/Tomato Soup.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Tomato Soup.jpeg
--------------------------------------------------------------------------------
/public/uploads/Veg Biryani.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/Veg Biryani.jpeg
--------------------------------------------------------------------------------
/public/uploads/aloo tikki.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/aloo tikki.jpeg
--------------------------------------------------------------------------------
/public/uploads/aura++.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/aura++.gif
--------------------------------------------------------------------------------
/public/uploads/baingan-bharta.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/baingan-bharta.jpg
--------------------------------------------------------------------------------
/public/uploads/cholekulche2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/cholekulche2.jpg
--------------------------------------------------------------------------------
/public/uploads/momos.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/momos.jpg
--------------------------------------------------------------------------------
/public/uploads/pain-naruto-indigo-5120x2880-10830.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/pain-naruto-indigo-5120x2880-10830.png
--------------------------------------------------------------------------------
/public/uploads/paneer-tikka.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/paneer-tikka.jpg
--------------------------------------------------------------------------------
/public/uploads/samosa.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/samosa.jpeg
--------------------------------------------------------------------------------
/public/uploads/sung-jinwoo-movie-5120x2880-20245.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/softenrj/qr-menu/a6fae416c727a387a4f0ce26839773a75245e216/public/uploads/sung-jinwoo-movie-5120x2880-20245.jpg
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/window.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tailwind.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: [
4 | "./pages/**/*.{js,ts,jsx,tsx,mdx}",
5 | "./components/**/*.{js,ts,jsx,tsx,mdx}",
6 | "./app/**/*.{js,ts,jsx,tsx,mdx}",
7 | ],
8 | theme: {
9 | extend: {
10 | fontFamily: {
11 | geist: "var(--font-geist-sans), sans-serif",
12 | geistMono: "var(--font-geist-mono), monospace",
13 | dmSans: ["dm-sans, sans-serif"],
14 | playfair: ["Playfair, serif"],
15 | },
16 | colors: {
17 | background: "var(--background)",
18 | foreground: "var(--foreground)",
19 | },
20 | },
21 | },
22 | plugins: [],
23 | };
24 |
--------------------------------------------------------------------------------