diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..e7a697e
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "idf.pythonInstallPath": "C:\\Users\\mofai\\.espressif\\tools\\idf-python\\3.11.2\\python.exe"
+}
\ No newline at end of file
diff --git a/backend/.env.example b/backend/.env.example
new file mode 100644
index 0000000..4cc714a
--- /dev/null
+++ b/backend/.env.example
@@ -0,0 +1 @@
+DATABASE_URL=
\ No newline at end of file
diff --git a/backend/.gitignore b/backend/.gitignore
new file mode 100644
index 0000000..b485964
--- /dev/null
+++ b/backend/.gitignore
@@ -0,0 +1,5 @@
+.env
+node_modules
+db.zip
+package-lock.json
+yarn.lock
\ No newline at end of file
diff --git a/backend/Controller/userControllr.js b/backend/Controller/userControllr.js
new file mode 100644
index 0000000..c10e9b0
--- /dev/null
+++ b/backend/Controller/userControllr.js
@@ -0,0 +1,331 @@
+import prisma from "../db/db.config.js";
+
+export const registerUser = async (req, res) => {
+ const { enrollment, name, email, password, phone, gender, batch } = req.body;
+
+ console.log("Register Request Body: ", req.body);
+
+ try {
+ const findUser = await prisma.user.findUnique({
+ where: {
+ enrollment: enrollment,
+ },
+ });
+
+ if (findUser) {
+ return res.json({
+ status: 409,
+ message: "You are already registered with the portal!!",
+ });
+ }
+
+ const newUser = await prisma.user.create({
+ data: {
+ enrollment,
+ name: name,
+ email: email,
+ password: password,
+ phone,
+ gender,
+ batch,
+ },
+ });
+ return res.json({
+ status: 200,
+ message: "Student created successfully!!",
+ data: newUser,
+ });
+ } catch (error) {
+ console.log(error);
+ }
+};
+
+export const getUsers = async (req, res) => {
+ try {
+ let page = Number(req.query.page) || 1;
+ let limit = Number(req.query.limit) || 15;
+
+ if (page <= 0) {
+ page = 1;
+ }
+ if (limit <= 0 || limit > 20) {
+ limit = 10;
+ }
+ const offset = (page - 1) * limit;
+ const users = await prisma.user.findMany({
+ skip: offset,
+ take: limit,
+ select: {
+ id: true,
+ name: true,
+ email: true,
+ enrollment: true,
+ phone: true,
+ gender: true,
+ batch: true,
+ },
+ });
+
+ // get current user count
+ const tCount = await prisma.user.count();
+ const totalPages = Math.ceil(tCount / limit);
+ return res.json({
+ status: 200,
+ students: users.length,
+ data: users,
+ meta: {
+ totalPages,
+ currentPage: page,
+ limit: limit,
+ },
+ });
+ } catch (error) {
+ console.log(error);
+ }
+};
+
+export const getUserStatus = async (req, res) => {
+ const { enrollment } = req.params;
+ console.log(enrollment);
+
+ try {
+ // Find user by enrollment
+ const user = await prisma.user.findUnique({
+ where: {
+ enrollment: enrollment,
+ },
+ include: {
+ logs: {
+ orderBy: {
+ timeIn: "desc",
+ },
+ },
+ },
+ });
+
+ console.log(user);
+
+ if (!user) {
+ return res.json({
+ status: 404,
+ message: "User not found!",
+ });
+ }
+
+ // Use the user_status field from the database
+ const userStatus = user.user_status;
+
+ // Get the most recent log entry for additional context
+ const latestLog = user.logs && user.logs.length > 0 ? user.logs[0] : null;
+
+ return res.json({
+ status: 200,
+ message: "User status retrieved successfully!",
+ data: {
+ enrollment: user.enrollment,
+ name: user.name,
+ user_status: userStatus, // 0 = inside, 1 = outside
+ lastActivity: latestLog ? {
+ timeIn: latestLog.timeIn,
+ timeOut: latestLog.timeOut,
+ purpose: latestLog.purpose
+ } : null
+ },
+ });
+ } catch (error) {
+ console.log(error);
+ return res.json({
+ status: 500,
+ message: "Internal server error!",
+ });
+ }
+};
+
+export const setUserStatus = async (req, res) => {
+ const { enrollment } = req.params;
+ const { purpose } = req.body; // Optional purpose for the log entry
+
+ try {
+ // Find user by enrollment
+ const user = await prisma.user.findUnique({
+ where: {
+ enrollment: enrollment,
+ },
+ include: {
+ logs: {
+ orderBy: {
+ timeIn: "desc",
+ },
+ },
+ },
+ });
+
+ if (!user) {
+ return res.json({
+ status: 404,
+ message: "User not found!",
+ });
+ }
+
+ // Determine current status based on latest log
+ let currentStatus;
+ let latestLog = null;
+
+ if (!user.logs || user.logs.length === 0) {
+ // No logs = outside campus
+ currentStatus = 1;
+ } else {
+ latestLog = user.logs[0];
+ // Check if timeIn exists and timeOut is null = inside campus
+ // If timeIn exists and timeOut exists = outside campus
+ if (latestLog.timeIn && latestLog.timeOut === null) {
+ currentStatus = 0; // Inside campus
+ } else {
+ currentStatus = 1; // Outside campus
+ }
+ }
+
+ let newStatus;
+ let logEntry;
+
+ if (currentStatus === 0) {
+ // User is currently inside campus, so they're leaving
+ // Update the latest log with timeOut and update user_status
+ if (latestLog) {
+ logEntry = await prisma.status.update({
+ where: {
+ id: latestLog.id,
+ },
+ data: {
+ timeOut: new Date(),
+ },
+ });
+ }
+
+ // Update user status to 1 (outside)
+ await prisma.user.update({
+ where: { id: user.id },
+ data: { user_status: 1 }
+ });
+
+ newStatus = 1; // Now outside campus
+ } else {
+ // User is currently outside campus, so they're entering
+ // Create a new log entry with timeIn only and update user_status
+ logEntry = await prisma.status.create({
+ data: {
+ timeIn: new Date(),
+ purpose: purpose || null,
+ userId: user.id,
+ },
+ });
+
+ // Update user status to 0 (inside)
+ await prisma.user.update({
+ where: { id: user.id },
+ data: { user_status: 0 }
+ });
+
+ newStatus = 0; // Now inside campus
+ }
+
+ return res.json({
+ status: 200,
+ message: `User ${newStatus === 0 ? 'entered' : 'left'} campus successfully!`,
+ data: {
+ enrollment: user.enrollment,
+ name: user.name,
+ user_status: newStatus, // 0 = inside, 1 = outside
+ logEntry: {
+ id: logEntry.id,
+ timeIn: logEntry.timeIn,
+ timeOut: logEntry.timeOut,
+ purpose: logEntry.purpose,
+ },
+ },
+ });
+ } catch (error) {
+ console.log(error);
+ return res.json({
+ status: 500,
+ message: "Internal server error!",
+ });
+ }
+};
+
+export const getUser = async (req, res) => {
+ const { enrollment } = req.params;
+ try {
+ const user = await prisma.user.findUnique({
+ where: {
+ enrollment: Number(enrollment),
+ },
+ select: {
+ id: true,
+ name: true,
+ enrollment: true,
+ email: true,
+ phone: true,
+ gender: true,
+ batch: true,
+ },
+ include: {
+ logs: true,
+ },
+ });
+
+ if (!user) {
+ return res.json({ status: 404, message: "User not found!!" });
+ }
+ return res.json({
+ status: 200,
+ message: "User fetched successfully!!",
+ data: user,
+ });
+ } catch (error) {
+ console.log(error);
+ }
+};
+
+export const loginUser = async (req, res) => {
+ const { enrollment, password } = req.body;
+
+ try {
+ // Find user by enrollment number
+ const user = await prisma.user.findUnique({
+ where: {
+ enrollment: enrollment,
+ },
+ });
+
+ if (!user) {
+ return res.json({
+ status: 404,
+ message: "User not found with this enrollment number!",
+ });
+ }
+
+ // Check password (no hashing as requested for hackathon)
+ if (user.password !== password) {
+ return res.json({
+ status: 401,
+ message: "Invalid password!",
+ });
+ }
+
+ // Return user data (excluding password)
+ const { password: _, ...userWithoutPassword } = user;
+
+ return res.json({
+ status: 200,
+ message: "Login successful!",
+ data: userWithoutPassword,
+ });
+ } catch (error) {
+ console.log(error);
+ return res.json({
+ status: 500,
+ message: "Internal server error!",
+ });
+ }
+};
diff --git a/backend/db/db.config.js b/backend/db/db.config.js
new file mode 100644
index 0000000..a7c038c
--- /dev/null
+++ b/backend/db/db.config.js
@@ -0,0 +1,5 @@
+import { PrismaClient } from "@prisma/client";
+
+const prisma = new PrismaClient({})
+
+export default prisma
\ No newline at end of file
diff --git a/backend/package.json b/backend/package.json
new file mode 100644
index 0000000..b89d863
--- /dev/null
+++ b/backend/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "backend",
+ "version": "1.0.0",
+ "type": "module",
+ "main": "server.js",
+ "scripts": {
+ "test": "nodemon server.js",
+ "postinstall": "prisma generate",
+ "db:sync": "prisma db push && prisma generate",
+ "db:studio": "prisma studio",
+ "start": "node server.js"
+ },
+ "license": "MIT",
+ "dependencies": {
+ "@prisma/client": "^6.16.1",
+ "cors": "^2.8.5",
+ "dotenv": "^17.2.2",
+ "express": "^5.1.0",
+ "jsonwebtoken": "^9.0.2",
+ "nodemon": "^3.1.10",
+ "prisma": "^6.16.1"
+ }
+}
diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma
new file mode 100644
index 0000000..7819b97
--- /dev/null
+++ b/backend/prisma/schema.prisma
@@ -0,0 +1,32 @@
+generator client {
+ provider = "prisma-client-js"
+}
+
+datasource db {
+ provider = "postgresql"
+ url = env("DATABASE_URL")
+}
+
+// If person left yesterday and never scanned back qr and tried to leave campus again next day, inform admin
+
+model User {
+ id String @id @unique @default(uuid())
+ enrollment String @unique
+ name String
+ password String
+ email String? @unique
+ phone String? @unique
+ gender String?
+ user_status Int @default(0)
+ batch String?
+ logs Status[]
+}
+
+model Status {
+ id String @id @unique @default(uuid())
+ timeIn DateTime @default(now())
+ timeOut DateTime?
+ purpose String?
+ user User @relation(fields: [userId], references: [id])
+ userId String
+}
diff --git a/backend/routes/index.js b/backend/routes/index.js
new file mode 100644
index 0000000..9eae26f
--- /dev/null
+++ b/backend/routes/index.js
@@ -0,0 +1,16 @@
+import { Router } from "express";
+import { registerUser, loginUser, getUsers, getUser, getUserStatus, setUserStatus } from "../Controller/userControllr.js";
+
+const router = Router()
+
+// Auth routes
+router.post('/v1/auth/register', registerUser)
+router.post('/v1/auth/login', loginUser)
+
+// User routes
+router.get('/v1/me/status/:enrollment', getUserStatus)
+router.post('/v1/me/toggle/:enrollment', setUserStatus)
+router.get('/v1/users', getUsers)
+router.get('/v1/users/:enrollment', getUser)
+
+export default router
\ No newline at end of file
diff --git a/backend/routes/registerUser.js b/backend/routes/registerUser.js
new file mode 100644
index 0000000..d03a90c
--- /dev/null
+++ b/backend/routes/registerUser.js
@@ -0,0 +1,2 @@
+// This file is no longer needed as routes are now in index.js
+// Keeping this file empty to avoid conflicts
\ No newline at end of file
diff --git a/backend/server.js b/backend/server.js
new file mode 100644
index 0000000..9416666
--- /dev/null
+++ b/backend/server.js
@@ -0,0 +1,50 @@
+
+import express from "express";
+import cors from "cors";
+import 'dotenv/config'
+import routes from './routes/index.js'
+
+const app = express()
+const PORT = process.env.PORT || 3000
+
+app.get("/", (req, res) => {
+ res.send("Ayo👋! Hello World from NIT")
+})
+
+// CORS configuration - Explicit for development
+app.use(cors({
+ origin: function (origin, callback) {
+ // Allow requests with no origin (like mobile apps or curl requests)
+ if (!origin) return callback(null, true);
+
+ // Allow all origins for development
+ return callback(null, true);
+ },
+ credentials: true,
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
+ allowedHeaders: ['Content-Type', 'Authorization', 'ngrok-skip-browser-warning', 'x-requested-with'],
+ optionsSuccessStatus: 200
+}))
+
+// Additional CORS headers for preflight requests
+app.use((req, res, next) => {
+ res.header('Access-Control-Allow-Origin', '*');
+ res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
+ res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, ngrok-skip-browser-warning, x-requested-with');
+ res.header('Access-Control-Allow-Credentials', 'true');
+
+ if (req.method === 'OPTIONS') {
+ res.sendStatus(200);
+ } else {
+ next();
+ }
+})
+
+// Middleware
+app.use(express.json())
+app.use(express.urlencoded({ extended: false }))
+
+// Route File
+app.use(routes)
+
+app.listen(PORT, () => console.log(`CamPass Server running on port: ${PORT} `))
diff --git a/client/.gitignore b/client/.gitignore
new file mode 100644
index 0000000..64b2be4
--- /dev/null
+++ b/client/.gitignore
@@ -0,0 +1,15 @@
+# dependencies
+/node_modules
+/package-lock.json
+
+# build outputs
+/dist
+/assets
+
+# misc
+.DS_Store
+*.log
+
+
+# env
+.env
diff --git a/client/html/home.html b/client/html/home.html
new file mode 100644
index 0000000..e8ad05e
--- /dev/null
+++ b/client/html/home.html
@@ -0,0 +1,38 @@
+
+
+
+
+ QR Scanner
+
+
+
+
CampusPass Entry
+
+
+
+
+
+
+
+ Attendence for entry marked
+
+
+
+
+
+
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 0000000..bd1a2e6
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "campuspass_client",
+ "version": "1.0.0",
+ "license": "ISC",
+ "author": "sickboy",
+ "private": "true",
+ "scripts": {
+ "start": "webpack serve --open",
+ "build": "webpack --mode production"
+ },
+ "dependencies": {
+ "html5-qrcode": "^2.3.8",
+ "qr-scanner": "^1.4.2"
+ },
+ "devDependencies": {
+ "css-loader": "^7.1.2",
+ "dotenv-webpack": "^8.1.0",
+ "file-loader": "^6.2.0",
+ "html-webpack-plugin": "^5.6.3",
+ "style-loader": "^4.0.0",
+ "webpack-bundle-analyzer": "^4.10.2",
+ "webpack-cli": "^6.0.1",
+ "webpack-dev-server": "^5.2.0"
+ }
+}
diff --git a/client/public/home.bundle.js b/client/public/home.bundle.js
new file mode 100644
index 0000000..cf93fc4
--- /dev/null
+++ b/client/public/home.bundle.js
@@ -0,0 +1,663 @@
+/******/ (() => { // webpackBootstrap
+/******/ "use strict";
+/******/ var __webpack_modules__ = ({
+
+/***/ "./node_modules/css-loader/dist/cjs.js!./styles/home.css":
+/*!***************************************************************!*\
+ !*** ./node_modules/css-loader/dist/cjs.js!./styles/home.css ***!
+ \***************************************************************/
+/***/ ((module, __webpack_exports__, __webpack_require__) => {
+
+__webpack_require__.r(__webpack_exports__);
+/* harmony export */ __webpack_require__.d(__webpack_exports__, {
+/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
+/* harmony export */ });
+/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../node_modules/css-loader/dist/runtime/sourceMaps.js */ "./node_modules/css-loader/dist/runtime/sourceMaps.js");
+/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
+/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
+/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
+// Imports
+
+
+var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
+// Module
+___CSS_LOADER_EXPORT___.push([module.id, `* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-family: "Segoe UI", Arial, sans-serif;
+}
+
+body {
+ background: #f9f9f9;
+ color: #333;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
+.app-container {
+ background: #fff;
+ padding: 20px;
+ border-radius: 16px;
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+ width: 90%;
+ max-width: 400px;
+ text-align: center;
+}
+
+h2 {
+ margin-bottom: 15px;
+ font-size: 22px;
+ color: #444;
+}
+
+/* Scanner box */
+#reader {
+ width: 100%;
+ height: 300px;
+ border-radius: 12px;
+ overflow: hidden;
+ margin-bottom: 15px;
+ border: 2px solid #ddd;
+}
+
+/* Input area */
+.input-container {
+ display: flex;
+ gap: 10px;
+ margin-bottom: 10px;
+}
+
+.input-container input {
+ flex: 1;
+ padding: 10px;
+ border-radius: 8px;
+ border: 1px solid #ccc;
+ outline: none;
+}
+
+.input-container button {
+ padding: 10px 14px;
+ border: none;
+ border-radius: 8px;
+ background: #007bff;
+ color: white;
+ font-weight: 500;
+ cursor: pointer;
+ transition: background 0.2s;
+}
+
+.input-container button:hover {
+ background: #0056cc;
+}
+
+/* Output text */
+#result {
+ margin-top: 12px;
+ font-size: 14px;
+ color: #007bff;
+ word-wrap: break-word;
+}
+`, "",{"version":3,"sources":["webpack://./styles/home.css"],"names":[],"mappings":"AAAA;EACE,SAAS;EACT,UAAU;EACV,sBAAsB;EACtB,0CAA0C;AAC5C;;AAEA;EACE,mBAAmB;EACnB,WAAW;EACX,aAAa;EACb,uBAAuB;EACvB,mBAAmB;EACnB,aAAa;AACf;;AAEA;EACE,gBAAgB;EAChB,aAAa;EACb,mBAAmB;EACnB,yCAAyC;EACzC,UAAU;EACV,gBAAgB;EAChB,kBAAkB;AACpB;;AAEA;EACE,mBAAmB;EACnB,eAAe;EACf,WAAW;AACb;;AAEA,gBAAgB;AAChB;EACE,WAAW;EACX,aAAa;EACb,mBAAmB;EACnB,gBAAgB;EAChB,mBAAmB;EACnB,sBAAsB;AACxB;;AAEA,eAAe;AACf;EACE,aAAa;EACb,SAAS;EACT,mBAAmB;AACrB;;AAEA;EACE,OAAO;EACP,aAAa;EACb,kBAAkB;EAClB,sBAAsB;EACtB,aAAa;AACf;;AAEA;EACE,kBAAkB;EAClB,YAAY;EACZ,kBAAkB;EAClB,mBAAmB;EACnB,YAAY;EACZ,gBAAgB;EAChB,eAAe;EACf,2BAA2B;AAC7B;;AAEA;EACE,mBAAmB;AACrB;;AAEA,gBAAgB;AAChB;EACE,gBAAgB;EAChB,eAAe;EACf,cAAc;EACd,qBAAqB;AACvB","sourcesContent":["* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n font-family: \"Segoe UI\", Arial, sans-serif;\n}\n\nbody {\n background: #f9f9f9;\n color: #333;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n}\n\n.app-container {\n background: #fff;\n padding: 20px;\n border-radius: 16px;\n box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);\n width: 90%;\n max-width: 400px;\n text-align: center;\n}\n\nh2 {\n margin-bottom: 15px;\n font-size: 22px;\n color: #444;\n}\n\n/* Scanner box */\n#reader {\n width: 100%;\n height: 300px;\n border-radius: 12px;\n overflow: hidden;\n margin-bottom: 15px;\n border: 2px solid #ddd;\n}\n\n/* Input area */\n.input-container {\n display: flex;\n gap: 10px;\n margin-bottom: 10px;\n}\n\n.input-container input {\n flex: 1;\n padding: 10px;\n border-radius: 8px;\n border: 1px solid #ccc;\n outline: none;\n}\n\n.input-container button {\n padding: 10px 14px;\n border: none;\n border-radius: 8px;\n background: #007bff;\n color: white;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.input-container button:hover {\n background: #0056cc;\n}\n\n/* Output text */\n#result {\n margin-top: 12px;\n font-size: 14px;\n color: #007bff;\n word-wrap: break-word;\n}\n"],"sourceRoot":""}]);
+// Exports
+/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);
+
+
+/***/ }),
+
+/***/ "./node_modules/css-loader/dist/runtime/api.js":
+/*!*****************************************************!*\
+ !*** ./node_modules/css-loader/dist/runtime/api.js ***!
+ \*****************************************************/
+/***/ ((module) => {
+
+
+
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+*/
+module.exports = function (cssWithMappingToString) {
+ var list = [];
+
+ // return the list of modules as css string
+ list.toString = function toString() {
+ return this.map(function (item) {
+ var content = "";
+ var needLayer = typeof item[5] !== "undefined";
+ if (item[4]) {
+ content += "@supports (".concat(item[4], ") {");
+ }
+ if (item[2]) {
+ content += "@media ".concat(item[2], " {");
+ }
+ if (needLayer) {
+ content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");
+ }
+ content += cssWithMappingToString(item);
+ if (needLayer) {
+ content += "}";
+ }
+ if (item[2]) {
+ content += "}";
+ }
+ if (item[4]) {
+ content += "}";
+ }
+ return content;
+ }).join("");
+ };
+
+ // import a list of modules into the list
+ list.i = function i(modules, media, dedupe, supports, layer) {
+ if (typeof modules === "string") {
+ modules = [[null, modules, undefined]];
+ }
+ var alreadyImportedModules = {};
+ if (dedupe) {
+ for (var k = 0; k < this.length; k++) {
+ var id = this[k][0];
+ if (id != null) {
+ alreadyImportedModules[id] = true;
+ }
+ }
+ }
+ for (var _k = 0; _k < modules.length; _k++) {
+ var item = [].concat(modules[_k]);
+ if (dedupe && alreadyImportedModules[item[0]]) {
+ continue;
+ }
+ if (typeof layer !== "undefined") {
+ if (typeof item[5] === "undefined") {
+ item[5] = layer;
+ } else {
+ item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");
+ item[5] = layer;
+ }
+ }
+ if (media) {
+ if (!item[2]) {
+ item[2] = media;
+ } else {
+ item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");
+ item[2] = media;
+ }
+ }
+ if (supports) {
+ if (!item[4]) {
+ item[4] = "".concat(supports);
+ } else {
+ item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");
+ item[4] = supports;
+ }
+ }
+ list.push(item);
+ }
+ };
+ return list;
+};
+
+/***/ }),
+
+/***/ "./node_modules/css-loader/dist/runtime/sourceMaps.js":
+/*!************************************************************!*\
+ !*** ./node_modules/css-loader/dist/runtime/sourceMaps.js ***!
+ \************************************************************/
+/***/ ((module) => {
+
+
+
+module.exports = function (item) {
+ var content = item[1];
+ var cssMapping = item[3];
+ if (!cssMapping) {
+ return content;
+ }
+ if (typeof btoa === "function") {
+ var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping))));
+ var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
+ var sourceMapping = "/*# ".concat(data, " */");
+ return [content].concat([sourceMapping]).join("\n");
+ }
+ return [content].join("\n");
+};
+
+/***/ }),
+
+/***/ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":
+/*!****************************************************************************!*\
+ !*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***!
+ \****************************************************************************/
+/***/ ((module) => {
+
+
+
+var stylesInDOM = [];
+function getIndexByIdentifier(identifier) {
+ var result = -1;
+ for (var i = 0; i < stylesInDOM.length; i++) {
+ if (stylesInDOM[i].identifier === identifier) {
+ result = i;
+ break;
+ }
+ }
+ return result;
+}
+function modulesToDom(list, options) {
+ var idCountMap = {};
+ var identifiers = [];
+ for (var i = 0; i < list.length; i++) {
+ var item = list[i];
+ var id = options.base ? item[0] + options.base : item[0];
+ var count = idCountMap[id] || 0;
+ var identifier = "".concat(id, " ").concat(count);
+ idCountMap[id] = count + 1;
+ var indexByIdentifier = getIndexByIdentifier(identifier);
+ var obj = {
+ css: item[1],
+ media: item[2],
+ sourceMap: item[3],
+ supports: item[4],
+ layer: item[5]
+ };
+ if (indexByIdentifier !== -1) {
+ stylesInDOM[indexByIdentifier].references++;
+ stylesInDOM[indexByIdentifier].updater(obj);
+ } else {
+ var updater = addElementStyle(obj, options);
+ options.byIndex = i;
+ stylesInDOM.splice(i, 0, {
+ identifier: identifier,
+ updater: updater,
+ references: 1
+ });
+ }
+ identifiers.push(identifier);
+ }
+ return identifiers;
+}
+function addElementStyle(obj, options) {
+ var api = options.domAPI(options);
+ api.update(obj);
+ var updater = function updater(newObj) {
+ if (newObj) {
+ if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {
+ return;
+ }
+ api.update(obj = newObj);
+ } else {
+ api.remove();
+ }
+ };
+ return updater;
+}
+module.exports = function (list, options) {
+ options = options || {};
+ list = list || [];
+ var lastIdentifiers = modulesToDom(list, options);
+ return function update(newList) {
+ newList = newList || [];
+ for (var i = 0; i < lastIdentifiers.length; i++) {
+ var identifier = lastIdentifiers[i];
+ var index = getIndexByIdentifier(identifier);
+ stylesInDOM[index].references--;
+ }
+ var newLastIdentifiers = modulesToDom(newList, options);
+ for (var _i = 0; _i < lastIdentifiers.length; _i++) {
+ var _identifier = lastIdentifiers[_i];
+ var _index = getIndexByIdentifier(_identifier);
+ if (stylesInDOM[_index].references === 0) {
+ stylesInDOM[_index].updater();
+ stylesInDOM.splice(_index, 1);
+ }
+ }
+ lastIdentifiers = newLastIdentifiers;
+ };
+};
+
+/***/ }),
+
+/***/ "./node_modules/style-loader/dist/runtime/insertBySelector.js":
+/*!********************************************************************!*\
+ !*** ./node_modules/style-loader/dist/runtime/insertBySelector.js ***!
+ \********************************************************************/
+/***/ ((module) => {
+
+
+
+var memo = {};
+
+/* istanbul ignore next */
+function getTarget(target) {
+ if (typeof memo[target] === "undefined") {
+ var styleTarget = document.querySelector(target);
+
+ // Special case to return head of iframe instead of iframe itself
+ if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
+ try {
+ // This will throw an exception if access to iframe is blocked
+ // due to cross-origin restrictions
+ styleTarget = styleTarget.contentDocument.head;
+ } catch (e) {
+ // istanbul ignore next
+ styleTarget = null;
+ }
+ }
+ memo[target] = styleTarget;
+ }
+ return memo[target];
+}
+
+/* istanbul ignore next */
+function insertBySelector(insert, style) {
+ var target = getTarget(insert);
+ if (!target) {
+ throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
+ }
+ target.appendChild(style);
+}
+module.exports = insertBySelector;
+
+/***/ }),
+
+/***/ "./node_modules/style-loader/dist/runtime/insertStyleElement.js":
+/*!**********************************************************************!*\
+ !*** ./node_modules/style-loader/dist/runtime/insertStyleElement.js ***!
+ \**********************************************************************/
+/***/ ((module) => {
+
+
+
+/* istanbul ignore next */
+function insertStyleElement(options) {
+ var element = document.createElement("style");
+ options.setAttributes(element, options.attributes);
+ options.insert(element, options.options);
+ return element;
+}
+module.exports = insertStyleElement;
+
+/***/ }),
+
+/***/ "./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js":
+/*!**********************************************************************************!*\
+ !*** ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js ***!
+ \**********************************************************************************/
+/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
+
+
+
+/* istanbul ignore next */
+function setAttributesWithoutAttributes(styleElement) {
+ var nonce = true ? __webpack_require__.nc : 0;
+ if (nonce) {
+ styleElement.setAttribute("nonce", nonce);
+ }
+}
+module.exports = setAttributesWithoutAttributes;
+
+/***/ }),
+
+/***/ "./node_modules/style-loader/dist/runtime/styleDomAPI.js":
+/*!***************************************************************!*\
+ !*** ./node_modules/style-loader/dist/runtime/styleDomAPI.js ***!
+ \***************************************************************/
+/***/ ((module) => {
+
+
+
+/* istanbul ignore next */
+function apply(styleElement, options, obj) {
+ var css = "";
+ if (obj.supports) {
+ css += "@supports (".concat(obj.supports, ") {");
+ }
+ if (obj.media) {
+ css += "@media ".concat(obj.media, " {");
+ }
+ var needLayer = typeof obj.layer !== "undefined";
+ if (needLayer) {
+ css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");
+ }
+ css += obj.css;
+ if (needLayer) {
+ css += "}";
+ }
+ if (obj.media) {
+ css += "}";
+ }
+ if (obj.supports) {
+ css += "}";
+ }
+ var sourceMap = obj.sourceMap;
+ if (sourceMap && typeof btoa !== "undefined") {
+ css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
+ }
+
+ // For old IE
+ /* istanbul ignore if */
+ options.styleTagTransform(css, styleElement, options.options);
+}
+function removeStyleElement(styleElement) {
+ // istanbul ignore if
+ if (styleElement.parentNode === null) {
+ return false;
+ }
+ styleElement.parentNode.removeChild(styleElement);
+}
+
+/* istanbul ignore next */
+function domAPI(options) {
+ if (typeof document === "undefined") {
+ return {
+ update: function update() {},
+ remove: function remove() {}
+ };
+ }
+ var styleElement = options.insertStyleElement(options);
+ return {
+ update: function update(obj) {
+ apply(styleElement, options, obj);
+ },
+ remove: function remove() {
+ removeStyleElement(styleElement);
+ }
+ };
+}
+module.exports = domAPI;
+
+/***/ }),
+
+/***/ "./node_modules/style-loader/dist/runtime/styleTagTransform.js":
+/*!*********************************************************************!*\
+ !*** ./node_modules/style-loader/dist/runtime/styleTagTransform.js ***!
+ \*********************************************************************/
+/***/ ((module) => {
+
+
+
+/* istanbul ignore next */
+function styleTagTransform(css, styleElement) {
+ if (styleElement.styleSheet) {
+ styleElement.styleSheet.cssText = css;
+ } else {
+ while (styleElement.firstChild) {
+ styleElement.removeChild(styleElement.firstChild);
+ }
+ styleElement.appendChild(document.createTextNode(css));
+ }
+}
+module.exports = styleTagTransform;
+
+/***/ }),
+
+/***/ "./styles/home.css":
+/*!*************************!*\
+ !*** ./styles/home.css ***!
+ \*************************/
+/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
+
+__webpack_require__.r(__webpack_exports__);
+/* harmony export */ __webpack_require__.d(__webpack_exports__, {
+/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
+/* harmony export */ });
+/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
+/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);
+/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/styleDomAPI.js */ "./node_modules/style-loader/dist/runtime/styleDomAPI.js");
+/* harmony import */ var _node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);
+/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/insertBySelector.js */ "./node_modules/style-loader/dist/runtime/insertBySelector.js");
+/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);
+/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js */ "./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js");
+/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);
+/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/insertStyleElement.js */ "./node_modules/style-loader/dist/runtime/insertStyleElement.js");
+/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);
+/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! !../node_modules/style-loader/dist/runtime/styleTagTransform.js */ "./node_modules/style-loader/dist/runtime/styleTagTransform.js");
+/* harmony import */ var _node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5__);
+/* harmony import */ var _node_modules_css_loader_dist_cjs_js_home_css__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! !!../node_modules/css-loader/dist/cjs.js!./home.css */ "./node_modules/css-loader/dist/cjs.js!./styles/home.css");
+
+
+
+
+
+
+
+
+
+
+
+var options = {};
+
+options.styleTagTransform = (_node_modules_style_loader_dist_runtime_styleTagTransform_js__WEBPACK_IMPORTED_MODULE_5___default());
+options.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());
+options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, "head");
+options.domAPI = (_node_modules_style_loader_dist_runtime_styleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());
+options.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());
+
+var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_home_css__WEBPACK_IMPORTED_MODULE_6__["default"], options);
+
+
+
+
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_home_css__WEBPACK_IMPORTED_MODULE_6__["default"] && _node_modules_css_loader_dist_cjs_js_home_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals ? _node_modules_css_loader_dist_cjs_js_home_css__WEBPACK_IMPORTED_MODULE_6__["default"].locals : undefined);
+
+
+/***/ })
+
+/******/ });
+/************************************************************************/
+/******/ // The module cache
+/******/ var __webpack_module_cache__ = {};
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/ // Check if module is in cache
+/******/ var cachedModule = __webpack_module_cache__[moduleId];
+/******/ if (cachedModule !== undefined) {
+/******/ return cachedModule.exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = __webpack_module_cache__[moduleId] = {
+/******/ id: moduleId,
+/******/ // no module.loaded needed
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/************************************************************************/
+/******/ /* webpack/runtime/compat get default export */
+/******/ (() => {
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = (module) => {
+/******/ var getter = module && module.__esModule ?
+/******/ () => (module['default']) :
+/******/ () => (module);
+/******/ __webpack_require__.d(getter, { a: getter });
+/******/ return getter;
+/******/ };
+/******/ })();
+/******/
+/******/ /* webpack/runtime/define property getters */
+/******/ (() => {
+/******/ // define getter functions for harmony exports
+/******/ __webpack_require__.d = (exports, definition) => {
+/******/ for(var key in definition) {
+/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
+/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
+/******/ }
+/******/ }
+/******/ };
+/******/ })();
+/******/
+/******/ /* webpack/runtime/hasOwnProperty shorthand */
+/******/ (() => {
+/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
+/******/ })();
+/******/
+/******/ /* webpack/runtime/make namespace object */
+/******/ (() => {
+/******/ // define __esModule on exports
+/******/ __webpack_require__.r = (exports) => {
+/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ }
+/******/ Object.defineProperty(exports, '__esModule', { value: true });
+/******/ };
+/******/ })();
+/******/
+/******/ /* webpack/runtime/nonce */
+/******/ (() => {
+/******/ __webpack_require__.nc = undefined;
+/******/ })();
+/******/
+/************************************************************************/
+var __webpack_exports__ = {};
+// This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
+(() => {
+/*!*************************!*\
+ !*** ./scripts/home.js ***!
+ \*************************/
+__webpack_require__.r(__webpack_exports__);
+/* harmony import */ var _styles_home_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../styles/home.css */ "./styles/home.css");
+
+// import { Html5Qrcode } from "html5-qrcode";
+
+// function startScanner() {
+// const html5QrCode = new Html5Qrcode("reader");
+// const config = { fps: 10, qrbox: { width: 250, height: 250 } };
+
+// html5QrCode
+// .start(
+// { facingMode: "environment" },
+// config,
+// qrCodeMessage => {
+// document.getElementById("result").innerText =
+// "✅ QR Code: " + qrCodeMessage;
+// html5QrCode.stop().catch(err => console.error("Stop error", err));
+// },
+// () => {} // ignore scan errors
+// )
+// .catch(err => {
+// document.getElementById("result").innerText = "❌ Camera error: " + err;
+// });
+// }
+
+// document.getElementById("submitBtn").addEventListener("click", () => {
+// const purpose = document.getElementById("purpose").value;
+// alert("Purpose entered: " + purpose);
+// });
+
+// startScanner();
+
+})();
+
+/******/ })()
+;
+//# sourceMappingURL=home.bundle.js.map
\ No newline at end of file
diff --git a/client/public/home.bundle.js.map b/client/public/home.bundle.js.map
new file mode 100644
index 0000000..c645971
--- /dev/null
+++ b/client/public/home.bundle.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"home.bundle.js","mappings":";;;;;;;;;;;;;;;;;;AAAA;AAC0G;AACjB;AACzF,8BAA8B,mFAA2B,CAAC,4FAAqC;AAC/F;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,kFAAkF,UAAU,UAAU,YAAY,aAAa,OAAO,KAAK,YAAY,WAAW,UAAU,YAAY,aAAa,WAAW,MAAM,KAAK,YAAY,WAAW,YAAY,aAAa,WAAW,YAAY,aAAa,OAAO,KAAK,YAAY,WAAW,UAAU,MAAM,YAAY,MAAM,UAAU,UAAU,YAAY,aAAa,aAAa,aAAa,OAAO,UAAU,KAAK,UAAU,UAAU,YAAY,OAAO,KAAK,UAAU,UAAU,YAAY,aAAa,WAAW,MAAM,KAAK,YAAY,WAAW,YAAY,aAAa,WAAW,YAAY,WAAW,YAAY,OAAO,KAAK,YAAY,OAAO,YAAY,MAAM,YAAY,WAAW,UAAU,YAAY,6BAA6B,cAAc,eAAe,2BAA2B,iDAAiD,GAAG,UAAU,wBAAwB,gBAAgB,kBAAkB,4BAA4B,wBAAwB,kBAAkB,GAAG,oBAAoB,qBAAqB,kBAAkB,wBAAwB,8CAA8C,eAAe,qBAAqB,uBAAuB,GAAG,QAAQ,wBAAwB,oBAAoB,gBAAgB,GAAG,gCAAgC,gBAAgB,kBAAkB,wBAAwB,qBAAqB,wBAAwB,2BAA2B,GAAG,wCAAwC,kBAAkB,cAAc,wBAAwB,GAAG,4BAA4B,YAAY,kBAAkB,uBAAuB,2BAA2B,kBAAkB,GAAG,6BAA6B,uBAAuB,iBAAiB,uBAAuB,wBAAwB,iBAAiB,qBAAqB,oBAAoB,gCAAgC,GAAG,mCAAmC,wBAAwB,GAAG,gCAAgC,qBAAqB,oBAAoB,mBAAmB,0BAA0B,GAAG,qBAAqB;AACrkE;AACA,iEAAe,uBAAuB,EAAC;;;;;;;;;;;ACtF1B;;AAEb;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,qDAAqD;AACrD;AACA;AACA,gDAAgD;AAChD;AACA;AACA,qFAAqF;AACrF;AACA;AACA;AACA,qBAAqB;AACrB;AACA;AACA,qBAAqB;AACrB;AACA;AACA,qBAAqB;AACrB;AACA;AACA,KAAK;AACL;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,sBAAsB,iBAAiB;AACvC;AACA;AACA;AACA;AACA;AACA;AACA,qBAAqB,qBAAqB;AAC1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA,UAAU;AACV,sFAAsF,qBAAqB;AAC3G;AACA;AACA;AACA;AACA;AACA;AACA,UAAU;AACV,iDAAiD,qBAAqB;AACtE;AACA;AACA;AACA;AACA;AACA;AACA,UAAU;AACV,sDAAsD,qBAAqB;AAC3E;AACA;AACA;AACA;AACA;AACA;AACA;AACA,E;;;;;;;;;;ACpFa;;AAEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,uDAAuD,cAAc;AACrE;AACA;AACA;AACA;AACA,E;;;;;;;;;;ACfa;;AAEb;AACA;AACA;AACA,kBAAkB,wBAAwB;AAC1C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kBAAkB,iBAAiB;AACnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA,OAAO;AACP;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAM;AACN;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oBAAoB,4BAA4B;AAChD;AACA;AACA;AACA;AACA;AACA,qBAAqB,6BAA6B;AAClD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,E;;;;;;;;;;ACnFa;;AAEb;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,QAAQ;AACR;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,kC;;;;;;;;;;ACjCa;;AAEb;AACA;AACA;AACA;AACA;AACA;AACA;AACA,oC;;;;;;;;;;ACTa;;AAEb;AACA;AACA,cAAc,KAAwC,GAAG,sBAAiB,GAAG,CAAI;AACjF;AACA;AACA;AACA;AACA,gD;;;;;;;;;;ACTa;;AAEb;AACA;AACA;AACA;AACA,kDAAkD;AAClD;AACA;AACA,0CAA0C;AAC1C;AACA;AACA;AACA,iFAAiF;AACjF;AACA;AACA;AACA,aAAa;AACb;AACA;AACA,aAAa;AACb;AACA;AACA,aAAa;AACb;AACA;AACA;AACA,yDAAyD;AACzD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,kCAAkC;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA,wB;;;;;;;;;;AC5Da;;AAEb;AACA;AACA;AACA;AACA,IAAI;AACJ;AACA;AACA;AACA;AACA;AACA;AACA,mC;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACZA,MAA+F;AAC/F,MAAqF;AACrF,MAA4F;AAC5F,MAA+G;AAC/G,MAAwG;AACxG,MAAwG;AACxG,MAAkG;AAClG;AACA;;AAEA;;AAEA,4BAA4B,qGAAmB;AAC/C,wBAAwB,kHAAa;AACrC,iBAAiB,uGAAa;AAC9B,iBAAiB,+FAAM;AACvB,6BAA6B,sGAAkB;;AAE/C,aAAa,0GAAG,CAAC,qFAAO;;;;AAI4C;AACpE,OAAO,iEAAe,qFAAO,IAAI,qFAAO,UAAU,qFAAO,mBAAmB,EAAC;;;;;;;UCxB7E;UACA;;UAEA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;UACA;;UAEA;UACA;;UAEA;UACA;UACA;;;;;WCtBA;WACA;WACA;WACA;WACA;WACA,iCAAiC,WAAW;WAC5C;WACA,E;;;;;WCPA;WACA;WACA;WACA;WACA,yCAAyC,wCAAwC;WACjF;WACA;WACA,E;;;;;WCPA,wF;;;;;WCAA;WACA;WACA;WACA,uDAAuD,iBAAiB;WACxE;WACA,gDAAgD,aAAa;WAC7D,E;;;;;WCNA,mC;;;;;;;;;;;;ACA4B;AAC5B,YAAY,cAAc;;AAE1B;AACA;AACA,sBAAsB,kBAAkB;;AAExC;AACA;AACA,WAAW,2BAA2B;AACtC;AACA;AACA;AACA;AACA;AACA,UAAU;AACV,kBAAkB;AAClB;AACA;AACA;AACA,QAAQ;AACR;;AAEA;AACA;AACA;AACA,IAAI;;AAEJ","sources":["webpack://campuspass_client/./styles/home.css","webpack://campuspass_client/./node_modules/css-loader/dist/runtime/api.js","webpack://campuspass_client/./node_modules/css-loader/dist/runtime/sourceMaps.js","webpack://campuspass_client/./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js","webpack://campuspass_client/./node_modules/style-loader/dist/runtime/insertBySelector.js","webpack://campuspass_client/./node_modules/style-loader/dist/runtime/insertStyleElement.js","webpack://campuspass_client/./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js","webpack://campuspass_client/./node_modules/style-loader/dist/runtime/styleDomAPI.js","webpack://campuspass_client/./node_modules/style-loader/dist/runtime/styleTagTransform.js","webpack://campuspass_client/./styles/home.css?93a2","webpack://campuspass_client/webpack/bootstrap","webpack://campuspass_client/webpack/runtime/compat get default export","webpack://campuspass_client/webpack/runtime/define property getters","webpack://campuspass_client/webpack/runtime/hasOwnProperty shorthand","webpack://campuspass_client/webpack/runtime/make namespace object","webpack://campuspass_client/webpack/runtime/nonce","webpack://campuspass_client/./scripts/home.js"],"sourcesContent":["// Imports\nimport ___CSS_LOADER_API_SOURCEMAP_IMPORT___ from \"../node_modules/css-loader/dist/runtime/sourceMaps.js\";\nimport ___CSS_LOADER_API_IMPORT___ from \"../node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(___CSS_LOADER_API_SOURCEMAP_IMPORT___);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n font-family: \"Segoe UI\", Arial, sans-serif;\n}\n\nbody {\n background: #f9f9f9;\n color: #333;\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100vh;\n}\n\n.app-container {\n background: #fff;\n padding: 20px;\n border-radius: 16px;\n box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);\n width: 90%;\n max-width: 400px;\n text-align: center;\n}\n\nh2 {\n margin-bottom: 15px;\n font-size: 22px;\n color: #444;\n}\n\n/* Scanner box */\n#reader {\n width: 100%;\n height: 300px;\n border-radius: 12px;\n overflow: hidden;\n margin-bottom: 15px;\n border: 2px solid #ddd;\n}\n\n/* Input area */\n.input-container {\n display: flex;\n gap: 10px;\n margin-bottom: 10px;\n}\n\n.input-container input {\n flex: 1;\n padding: 10px;\n border-radius: 8px;\n border: 1px solid #ccc;\n outline: none;\n}\n\n.input-container button {\n padding: 10px 14px;\n border: none;\n border-radius: 8px;\n background: #007bff;\n color: white;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.input-container button:hover {\n background: #0056cc;\n}\n\n/* Output text */\n#result {\n margin-top: 12px;\n font-size: 14px;\n color: #007bff;\n word-wrap: break-word;\n}\n`, \"\",{\"version\":3,\"sources\":[\"webpack://./styles/home.css\"],\"names\":[],\"mappings\":\"AAAA;EACE,SAAS;EACT,UAAU;EACV,sBAAsB;EACtB,0CAA0C;AAC5C;;AAEA;EACE,mBAAmB;EACnB,WAAW;EACX,aAAa;EACb,uBAAuB;EACvB,mBAAmB;EACnB,aAAa;AACf;;AAEA;EACE,gBAAgB;EAChB,aAAa;EACb,mBAAmB;EACnB,yCAAyC;EACzC,UAAU;EACV,gBAAgB;EAChB,kBAAkB;AACpB;;AAEA;EACE,mBAAmB;EACnB,eAAe;EACf,WAAW;AACb;;AAEA,gBAAgB;AAChB;EACE,WAAW;EACX,aAAa;EACb,mBAAmB;EACnB,gBAAgB;EAChB,mBAAmB;EACnB,sBAAsB;AACxB;;AAEA,eAAe;AACf;EACE,aAAa;EACb,SAAS;EACT,mBAAmB;AACrB;;AAEA;EACE,OAAO;EACP,aAAa;EACb,kBAAkB;EAClB,sBAAsB;EACtB,aAAa;AACf;;AAEA;EACE,kBAAkB;EAClB,YAAY;EACZ,kBAAkB;EAClB,mBAAmB;EACnB,YAAY;EACZ,gBAAgB;EAChB,eAAe;EACf,2BAA2B;AAC7B;;AAEA;EACE,mBAAmB;AACrB;;AAEA,gBAAgB;AAChB;EACE,gBAAgB;EAChB,eAAe;EACf,cAAc;EACd,qBAAqB;AACvB\",\"sourcesContent\":[\"* {\\n margin: 0;\\n padding: 0;\\n box-sizing: border-box;\\n font-family: \\\"Segoe UI\\\", Arial, sans-serif;\\n}\\n\\nbody {\\n background: #f9f9f9;\\n color: #333;\\n display: flex;\\n justify-content: center;\\n align-items: center;\\n height: 100vh;\\n}\\n\\n.app-container {\\n background: #fff;\\n padding: 20px;\\n border-radius: 16px;\\n box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);\\n width: 90%;\\n max-width: 400px;\\n text-align: center;\\n}\\n\\nh2 {\\n margin-bottom: 15px;\\n font-size: 22px;\\n color: #444;\\n}\\n\\n/* Scanner box */\\n#reader {\\n width: 100%;\\n height: 300px;\\n border-radius: 12px;\\n overflow: hidden;\\n margin-bottom: 15px;\\n border: 2px solid #ddd;\\n}\\n\\n/* Input area */\\n.input-container {\\n display: flex;\\n gap: 10px;\\n margin-bottom: 10px;\\n}\\n\\n.input-container input {\\n flex: 1;\\n padding: 10px;\\n border-radius: 8px;\\n border: 1px solid #ccc;\\n outline: none;\\n}\\n\\n.input-container button {\\n padding: 10px 14px;\\n border: none;\\n border-radius: 8px;\\n background: #007bff;\\n color: white;\\n font-weight: 500;\\n cursor: pointer;\\n transition: background 0.2s;\\n}\\n\\n.input-container button:hover {\\n background: #0056cc;\\n}\\n\\n/* Output text */\\n#result {\\n margin-top: 12px;\\n font-size: 14px;\\n color: #007bff;\\n word-wrap: break-word;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","\"use strict\";\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n*/\nmodule.exports = function (cssWithMappingToString) {\n var list = [];\n\n // return the list of modules as css string\n list.toString = function toString() {\n return this.map(function (item) {\n var content = \"\";\n var needLayer = typeof item[5] !== \"undefined\";\n if (item[4]) {\n content += \"@supports (\".concat(item[4], \") {\");\n }\n if (item[2]) {\n content += \"@media \".concat(item[2], \" {\");\n }\n if (needLayer) {\n content += \"@layer\".concat(item[5].length > 0 ? \" \".concat(item[5]) : \"\", \" {\");\n }\n content += cssWithMappingToString(item);\n if (needLayer) {\n content += \"}\";\n }\n if (item[2]) {\n content += \"}\";\n }\n if (item[4]) {\n content += \"}\";\n }\n return content;\n }).join(\"\");\n };\n\n // import a list of modules into the list\n list.i = function i(modules, media, dedupe, supports, layer) {\n if (typeof modules === \"string\") {\n modules = [[null, modules, undefined]];\n }\n var alreadyImportedModules = {};\n if (dedupe) {\n for (var k = 0; k < this.length; k++) {\n var id = this[k][0];\n if (id != null) {\n alreadyImportedModules[id] = true;\n }\n }\n }\n for (var _k = 0; _k < modules.length; _k++) {\n var item = [].concat(modules[_k]);\n if (dedupe && alreadyImportedModules[item[0]]) {\n continue;\n }\n if (typeof layer !== \"undefined\") {\n if (typeof item[5] === \"undefined\") {\n item[5] = layer;\n } else {\n item[1] = \"@layer\".concat(item[5].length > 0 ? \" \".concat(item[5]) : \"\", \" {\").concat(item[1], \"}\");\n item[5] = layer;\n }\n }\n if (media) {\n if (!item[2]) {\n item[2] = media;\n } else {\n item[1] = \"@media \".concat(item[2], \" {\").concat(item[1], \"}\");\n item[2] = media;\n }\n }\n if (supports) {\n if (!item[4]) {\n item[4] = \"\".concat(supports);\n } else {\n item[1] = \"@supports (\".concat(item[4], \") {\").concat(item[1], \"}\");\n item[4] = supports;\n }\n }\n list.push(item);\n }\n };\n return list;\n};","\"use strict\";\n\nmodule.exports = function (item) {\n var content = item[1];\n var cssMapping = item[3];\n if (!cssMapping) {\n return content;\n }\n if (typeof btoa === \"function\") {\n var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping))));\n var data = \"sourceMappingURL=data:application/json;charset=utf-8;base64,\".concat(base64);\n var sourceMapping = \"/*# \".concat(data, \" */\");\n return [content].concat([sourceMapping]).join(\"\\n\");\n }\n return [content].join(\"\\n\");\n};","\"use strict\";\n\nvar stylesInDOM = [];\nfunction getIndexByIdentifier(identifier) {\n var result = -1;\n for (var i = 0; i < stylesInDOM.length; i++) {\n if (stylesInDOM[i].identifier === identifier) {\n result = i;\n break;\n }\n }\n return result;\n}\nfunction modulesToDom(list, options) {\n var idCountMap = {};\n var identifiers = [];\n for (var i = 0; i < list.length; i++) {\n var item = list[i];\n var id = options.base ? item[0] + options.base : item[0];\n var count = idCountMap[id] || 0;\n var identifier = \"\".concat(id, \" \").concat(count);\n idCountMap[id] = count + 1;\n var indexByIdentifier = getIndexByIdentifier(identifier);\n var obj = {\n css: item[1],\n media: item[2],\n sourceMap: item[3],\n supports: item[4],\n layer: item[5]\n };\n if (indexByIdentifier !== -1) {\n stylesInDOM[indexByIdentifier].references++;\n stylesInDOM[indexByIdentifier].updater(obj);\n } else {\n var updater = addElementStyle(obj, options);\n options.byIndex = i;\n stylesInDOM.splice(i, 0, {\n identifier: identifier,\n updater: updater,\n references: 1\n });\n }\n identifiers.push(identifier);\n }\n return identifiers;\n}\nfunction addElementStyle(obj, options) {\n var api = options.domAPI(options);\n api.update(obj);\n var updater = function updater(newObj) {\n if (newObj) {\n if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {\n return;\n }\n api.update(obj = newObj);\n } else {\n api.remove();\n }\n };\n return updater;\n}\nmodule.exports = function (list, options) {\n options = options || {};\n list = list || [];\n var lastIdentifiers = modulesToDom(list, options);\n return function update(newList) {\n newList = newList || [];\n for (var i = 0; i < lastIdentifiers.length; i++) {\n var identifier = lastIdentifiers[i];\n var index = getIndexByIdentifier(identifier);\n stylesInDOM[index].references--;\n }\n var newLastIdentifiers = modulesToDom(newList, options);\n for (var _i = 0; _i < lastIdentifiers.length; _i++) {\n var _identifier = lastIdentifiers[_i];\n var _index = getIndexByIdentifier(_identifier);\n if (stylesInDOM[_index].references === 0) {\n stylesInDOM[_index].updater();\n stylesInDOM.splice(_index, 1);\n }\n }\n lastIdentifiers = newLastIdentifiers;\n };\n};","\"use strict\";\n\nvar memo = {};\n\n/* istanbul ignore next */\nfunction getTarget(target) {\n if (typeof memo[target] === \"undefined\") {\n var styleTarget = document.querySelector(target);\n\n // Special case to return head of iframe instead of iframe itself\n if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\n try {\n // This will throw an exception if access to iframe is blocked\n // due to cross-origin restrictions\n styleTarget = styleTarget.contentDocument.head;\n } catch (e) {\n // istanbul ignore next\n styleTarget = null;\n }\n }\n memo[target] = styleTarget;\n }\n return memo[target];\n}\n\n/* istanbul ignore next */\nfunction insertBySelector(insert, style) {\n var target = getTarget(insert);\n if (!target) {\n throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");\n }\n target.appendChild(style);\n}\nmodule.exports = insertBySelector;","\"use strict\";\n\n/* istanbul ignore next */\nfunction insertStyleElement(options) {\n var element = document.createElement(\"style\");\n options.setAttributes(element, options.attributes);\n options.insert(element, options.options);\n return element;\n}\nmodule.exports = insertStyleElement;","\"use strict\";\n\n/* istanbul ignore next */\nfunction setAttributesWithoutAttributes(styleElement) {\n var nonce = typeof __webpack_nonce__ !== \"undefined\" ? __webpack_nonce__ : null;\n if (nonce) {\n styleElement.setAttribute(\"nonce\", nonce);\n }\n}\nmodule.exports = setAttributesWithoutAttributes;","\"use strict\";\n\n/* istanbul ignore next */\nfunction apply(styleElement, options, obj) {\n var css = \"\";\n if (obj.supports) {\n css += \"@supports (\".concat(obj.supports, \") {\");\n }\n if (obj.media) {\n css += \"@media \".concat(obj.media, \" {\");\n }\n var needLayer = typeof obj.layer !== \"undefined\";\n if (needLayer) {\n css += \"@layer\".concat(obj.layer.length > 0 ? \" \".concat(obj.layer) : \"\", \" {\");\n }\n css += obj.css;\n if (needLayer) {\n css += \"}\";\n }\n if (obj.media) {\n css += \"}\";\n }\n if (obj.supports) {\n css += \"}\";\n }\n var sourceMap = obj.sourceMap;\n if (sourceMap && typeof btoa !== \"undefined\") {\n css += \"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), \" */\");\n }\n\n // For old IE\n /* istanbul ignore if */\n options.styleTagTransform(css, styleElement, options.options);\n}\nfunction removeStyleElement(styleElement) {\n // istanbul ignore if\n if (styleElement.parentNode === null) {\n return false;\n }\n styleElement.parentNode.removeChild(styleElement);\n}\n\n/* istanbul ignore next */\nfunction domAPI(options) {\n if (typeof document === \"undefined\") {\n return {\n update: function update() {},\n remove: function remove() {}\n };\n }\n var styleElement = options.insertStyleElement(options);\n return {\n update: function update(obj) {\n apply(styleElement, options, obj);\n },\n remove: function remove() {\n removeStyleElement(styleElement);\n }\n };\n}\nmodule.exports = domAPI;","\"use strict\";\n\n/* istanbul ignore next */\nfunction styleTagTransform(css, styleElement) {\n if (styleElement.styleSheet) {\n styleElement.styleSheet.cssText = css;\n } else {\n while (styleElement.firstChild) {\n styleElement.removeChild(styleElement.firstChild);\n }\n styleElement.appendChild(document.createTextNode(css));\n }\n}\nmodule.exports = styleTagTransform;","\n import API from \"!../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import domAPI from \"!../node_modules/style-loader/dist/runtime/styleDomAPI.js\";\n import insertFn from \"!../node_modules/style-loader/dist/runtime/insertBySelector.js\";\n import setAttributes from \"!../node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js\";\n import insertStyleElement from \"!../node_modules/style-loader/dist/runtime/insertStyleElement.js\";\n import styleTagTransformFn from \"!../node_modules/style-loader/dist/runtime/styleTagTransform.js\";\n import content, * as namedExport from \"!!../node_modules/css-loader/dist/cjs.js!./home.css\";\n \n \n\nvar options = {};\n\noptions.styleTagTransform = styleTagTransformFn;\noptions.setAttributes = setAttributes;\noptions.insert = insertFn.bind(null, \"head\");\noptions.domAPI = domAPI;\noptions.insertStyleElement = insertStyleElement;\n\nvar update = API(content, options);\n\n\n\nexport * from \"!!../node_modules/css-loader/dist/cjs.js!./home.css\";\n export default content && content.locals ? content.locals : undefined;\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","__webpack_require__.nc = undefined;","import \"../styles/home.css\";\n// import { Html5Qrcode } from \"html5-qrcode\";\n\n// function startScanner() {\n// const html5QrCode = new Html5Qrcode(\"reader\");\n// const config = { fps: 10, qrbox: { width: 250, height: 250 } };\n\n// html5QrCode\n// .start(\n// { facingMode: \"environment\" },\n// config,\n// qrCodeMessage => {\n// document.getElementById(\"result\").innerText =\n// \"✅ QR Code: \" + qrCodeMessage;\n// html5QrCode.stop().catch(err => console.error(\"Stop error\", err));\n// },\n// () => {} // ignore scan errors\n// )\n// .catch(err => {\n// document.getElementById(\"result\").innerText = \"❌ Camera error: \" + err;\n// });\n// }\n\n// document.getElementById(\"submitBtn\").addEventListener(\"click\", () => {\n// const purpose = document.getElementById(\"purpose\").value;\n// alert(\"Purpose entered: \" + purpose);\n// });\n\n// startScanner();\n"],"names":[],"sourceRoot":""}
\ No newline at end of file
diff --git a/client/public/home.html b/client/public/home.html
new file mode 100644
index 0000000..35b8c8f
--- /dev/null
+++ b/client/public/home.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ QR Code Scanner
+
+
+
+
+
📷 QR Code Scanner
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/scripts/home.js b/client/scripts/home.js
new file mode 100644
index 0000000..7382579
--- /dev/null
+++ b/client/scripts/home.js
@@ -0,0 +1,76 @@
+import "../styles/home.css";
+import QrScanner from "qr-scanner";
+
+const STATUS_OUT = 1;
+const STATUS_IN = 0;
+// TODO: get real enrollment number and status from server
+const enroll_num = "1231241";
+const status = 0;
+let scanner = null;
+
+function showDoneOverlay(message) {
+ const overlay = document.getElementById("overlay");
+ const overlayText = document.querySelector(".overlay-text .status");
+ overlayText.textContent = message;
+ overlay.style.display = "flex";
+
+ setTimeout(() => {
+ location.reload();
+ }, 3000);
+}
+
+function showMessage(text, type = "error") {
+ const messageBox = document.getElementById("message");
+ messageBox.textContent = text;
+ messageBox.style.display = "block";
+
+ if (type === "error") {
+ messageBox.style.background = "#fee2e2";
+ messageBox.style.color = "#b91c1c";
+ messageBox.style.border = "1px solid #fca5a5";
+ } else if (type == "success") {
+ messageBox.style.background = "#dcfce7";
+ messageBox.style.color = "#166534";
+ messageBox.style.border = "1px solid #86efac";
+ }
+}
+
+function onQrScanned(qrId) {
+ const purposeInput = document.getElementById("purpose").value.trim();
+
+ const payload = {
+ enroll_num,
+ id: qrId,
+ };
+
+ if (status == STATUS_IN && !purposeInput) {
+ showMessage("⚠️ Please enter a purpose before scanning.", "error");
+ return;
+ } else {
+ payload.purpose = purposeInput;
+ }
+ scanner.pause();
+ console.log("submitting", payload);
+ if (status == STATUS_IN) showDoneOverlay("exit");
+ else showDoneOverlay("entry");
+}
+
+function main() {
+ const videoElem = document.getElementById("qr-video");
+ const statusTitle = document.querySelector(".title .status");
+ if (status == STATUS_OUT) {
+ document.querySelector(".purpose").style.display = "none";
+ statusTitle.textContent = "Exit";
+ }
+ scanner = new QrScanner(
+ videoElem,
+ result => {
+ console.log(result.data);
+ onQrScanned(result.data);
+ },
+ { highlightScanRegion: true, highlightCodeOutline: true }
+ );
+ scanner.start();
+}
+
+main();
diff --git a/client/styles/home.css b/client/styles/home.css
new file mode 100644
index 0000000..98706eb
--- /dev/null
+++ b/client/styles/home.css
@@ -0,0 +1,193 @@
+body {
+ margin: 0;
+ height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: #f9fafb; /* light gray */
+ font-family: "Segoe UI", sans-serif;
+ color: #111827;
+}
+
+.main-container {
+ .title {
+ text-align: center;
+ font-size: 2rem; /* scalable for all devices */
+ font-weight: 600; /* semi-bold */
+ color: #282828; /* light gray */
+ margin-bottom: 1.5rem;
+ letter-spacing: -0.02em;
+
+ span {
+ text-decoration: underline;
+ }
+ }
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+ align-items: center;
+ width: 100%;
+ max-width: 520px;
+ padding: 2rem 1rem;
+ background: #ffffff;
+ border-radius: 1rem;
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
+}
+
+.scanner-container {
+ position: relative;
+ width: 90vw;
+ max-width: 480px;
+ aspect-ratio: 1/1;
+ border-radius: 1rem;
+ overflow: hidden;
+ border: 2px solid #e5e7eb;
+ background: #f3f4f6;
+}
+
+#qr-video {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.purpose {
+ width: 100%;
+ display: flex;
+ gap: 0.5rem;
+}
+
+#purpose {
+ flex: 1;
+ padding: 0.75rem 1rem;
+ font-size: 1rem;
+ border: 2px solid #d1d5db;
+ border-radius: 0.75rem;
+ outline: none;
+ transition: border-color 0.2s, box-shadow 0.2s;
+}
+
+#purpose:focus {
+ border-color: #2563eb;
+ box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.3);
+}
+
+button {
+ padding: 0.75rem 1.25rem;
+ font-size: 1rem;
+ border: none;
+ border-radius: 0.75rem;
+ background: #2563eb;
+ color: #fff;
+ cursor: pointer;
+ transition: background 0.2s;
+}
+
+button:hover {
+ background: #1d4ed8;
+}
+
+#message {
+ padding: 0.75rem 1rem;
+ border-radius: 0.75rem;
+ font-size: 0.95rem;
+ display: none;
+}
+
+/* OVERLAY */
+/* Overlay hidden by default */
+#overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255, 255, 255, 0.95);
+ z-index: 9999;
+}
+
+/* Animation container */
+.overlay-content {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1rem;
+ animation: popIn 0.4s ease-out;
+}
+
+/* Checkmark animation */
+.checkmark {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 4px solid #22c55e;
+ position: relative;
+ animation: scaleUp 0.4s ease-out;
+}
+
+.checkmark::after {
+ content: "";
+ position: absolute;
+ width: 25px;
+ height: 45px;
+ border-right: 4px solid #22c55e;
+ border-bottom: 4px solid #22c55e;
+ transform: rotate(45deg) translate(-5px, -8px);
+ opacity: 0;
+ animation: drawCheck 0.6s ease forwards 0.3s;
+}
+
+/* Text */
+.overlay-text {
+ font-size: 1.2rem;
+ font-weight: 600;
+ color: #166534;
+ font-family: "Segoe UI", sans-serif;
+ .status {
+ text-decoration: underline;
+ /* color: black; */
+ }
+}
+
+/* Animations */
+@keyframes popIn {
+ from {
+ transform: scale(0.9);
+ opacity: 0;
+ }
+ to {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+@keyframes scaleUp {
+ from {
+ transform: scale(0.5);
+ opacity: 0;
+ }
+ to {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+@keyframes drawCheck {
+ from {
+ opacity: 0;
+ transform: rotate(45deg) scale(0);
+ }
+ to {
+ opacity: 1;
+ transform: rotate(45deg) scale(1);
+ }
+}
diff --git a/client/webpack.config.js b/client/webpack.config.js
new file mode 100644
index 0000000..7e55234
--- /dev/null
+++ b/client/webpack.config.js
@@ -0,0 +1,62 @@
+const path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const Dotenv = require("dotenv-webpack");
+
+module.exports = {
+ mode: "development",
+
+ entry: {
+ home: "./scripts/home.js",
+ },
+
+ output: {
+ path: path.resolve(__dirname, "public"),
+ filename: "[name].bundle.js",
+ clean: true,
+ },
+
+ module: {
+ rules: [
+ {
+ test: /\.css$/,
+ use: ["style-loader", "css-loader"],
+ },
+ {
+ test: /\.(png|jpg|jpeg|gif|svg)$/i,
+ type: "asset/resource",
+ },
+ {
+ test: /\.(ttf|woff|woff2|eot|otf)$/,
+ type: "asset/resource",
+ generator: {
+ filename: "fonts/[name][ext]",
+ },
+ },
+ ],
+ },
+
+ plugins: [
+ new Dotenv(),
+ new HtmlWebpackPlugin({
+ filename: "home.html",
+ template: "./html/home.html",
+ chunks: ["home"],
+ }),
+ ],
+
+ devtool: "source-map",
+
+ devServer: {
+ static: "./public",
+ port: 3005,
+ open: "home.html",
+ hot: true,
+ historyApiFallback: true,
+ watchFiles: [
+ "html/**/*.*",
+ "styles/**/*.*",
+ "scripts/**/*.*",
+ "assets/**/*.*",
+ ],
+ },
+};
diff --git a/endpoints.png b/endpoints.png
new file mode 100644
index 0000000..4cedf5e
Binary files /dev/null and b/endpoints.png differ