From 7955b73af4c9b327706cce0421329cb3e7e5298f Mon Sep 17 00:00:00 2001 From: sickboy Date: Sat, 13 Sep 2025 05:33:44 +0000 Subject: [PATCH 1/4] initial commit --- client/.gitignore | 14 + client/html/home.html | 38 ++ client/package.json | 25 ++ client/public/home.bundle.js | 663 +++++++++++++++++++++++++++++++ client/public/home.bundle.js.map | 1 + client/public/home.html | 26 ++ client/scripts/home.js | 76 ++++ client/styles/home.css | 193 +++++++++ client/webpack.config.js | 62 +++ 9 files changed, 1098 insertions(+) create mode 100644 client/.gitignore create mode 100644 client/html/home.html create mode 100644 client/package.json create mode 100644 client/public/home.bundle.js create mode 100644 client/public/home.bundle.js.map create mode 100644 client/public/home.html create mode 100644 client/scripts/home.js create mode 100644 client/styles/home.css create mode 100644 client/webpack.config.js diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..5d87084 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,14 @@ +# 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/**/*.*", + ], + }, +}; From 459e6758a4aae21fa5925eb54453dbba33582ff8 Mon Sep 17 00:00:00 2001 From: sickboy Date: Sat, 13 Sep 2025 05:47:54 +0000 Subject: [PATCH 2/4] initial commit --- client/.gitignore | 14 ++++++++++++++ endpoints.png | Bin 0 -> 50793 bytes 2 files changed, 14 insertions(+) create mode 100644 client/.gitignore create mode 100644 endpoints.png diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..5d87084 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,14 @@ +# dependencies +/node_modules +/package-lock.json + +# build outputs +/dist +/assets + +# misc +.DS_Store +*.log + +# env +.env diff --git a/endpoints.png b/endpoints.png new file mode 100644 index 0000000000000000000000000000000000000000..4cedf5e0ad5a7e7769919018a283bbb10b99ee6c GIT binary patch literal 50793 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

{vLU(tp!{ZMg$_AxC<=P#tH2dH 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 LvSjJo_h0@GNdg6X literal 0 HcmV?d00001 From dcdb20fc56ebfded93b340e8495f000a37f2e69d Mon Sep 17 00:00:00 2001 From: sickboy Date: Sat, 13 Sep 2025 05:49:28 +0000 Subject: [PATCH 3/4] initial commit --- client/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/client/.gitignore b/client/.gitignore index 5d87084..64b2be4 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -10,5 +10,6 @@ .DS_Store *.log + # env .env From 14bdbf08c3e45384bbcebc5cefc16dc0de3f8177 Mon Sep 17 00:00:00 2001 From: Mohammad Faizan Date: Mon, 15 Sep 2025 12:50:58 +0530 Subject: [PATCH 4/4] Backend v1 --- .vscode/settings.json | 3 + backend/.env.example | 1 + backend/.gitignore | 5 + backend/Controller/userControllr.js | 331 ++++++++++++++++++++++++++++ backend/db/db.config.js | 5 + backend/package.json | 23 ++ backend/prisma/schema.prisma | 32 +++ backend/routes/index.js | 16 ++ backend/routes/registerUser.js | 2 + backend/server.js | 50 +++++ 10 files changed, 468 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 backend/.env.example create mode 100644 backend/.gitignore create mode 100644 backend/Controller/userControllr.js create mode 100644 backend/db/db.config.js create mode 100644 backend/package.json create mode 100644 backend/prisma/schema.prisma create mode 100644 backend/routes/index.js create mode 100644 backend/routes/registerUser.js create mode 100644 backend/server.js 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} `))