Much virtual many lotteries
This commit is contained in:
		| @@ -7,7 +7,9 @@ mongoose.connect("mongodb://localhost:27017/vinlottis", { | ||||
| }); | ||||
|  | ||||
| const sub = require(path.join(__dirname + "/../api/subscriptions")); | ||||
| const mustBeAuthenticated = require(path.join(__dirname + "/../middleware/mustBeAuthenticated")) | ||||
| const mustBeAuthenticated = require(path.join( | ||||
|   __dirname + "/../middleware/mustBeAuthenticated" | ||||
| )); | ||||
|  | ||||
| const Subscription = require(path.join(__dirname + "/../schemas/Subscription")); | ||||
| const Purchase = require(path.join(__dirname + "/../schemas/Purchase")); | ||||
| @@ -15,6 +17,9 @@ const Wine = require(path.join(__dirname + "/../schemas/Wine")); | ||||
| const PreLotteryWine = require(path.join( | ||||
|   __dirname + "/../schemas/PreLotteryWine" | ||||
| )); | ||||
| const VirtualWinner = require(path.join( | ||||
|   __dirname + "/../schemas/VirtualWinner" | ||||
| )); | ||||
| const Highscore = require(path.join(__dirname + "/../schemas/Highscore")); | ||||
|  | ||||
| router.use((req, res, next) => { | ||||
| @@ -49,15 +54,14 @@ router.route("/log/wines").post(mustBeAuthenticated, async (req, res) => { | ||||
| }); | ||||
|  | ||||
| router.route("/log/schema").get(mustBeAuthenticated, async (req, res) => { | ||||
|   let schema = {...PreLotteryWine.schema.obj}; | ||||
|   let nulledSchema = Object.keys(schema).reduce( | ||||
|     (accumulator, current) => { | ||||
|       accumulator[current] = ""; | ||||
|       return accumulator | ||||
|     }, {}); | ||||
|   let schema = { ...PreLotteryWine.schema.obj }; | ||||
|   let nulledSchema = Object.keys(schema).reduce((accumulator, current) => { | ||||
|     accumulator[current] = ""; | ||||
|     return accumulator; | ||||
|   }, {}); | ||||
|  | ||||
|   res.send(nulledSchema) | ||||
| }) | ||||
|   res.send(nulledSchema); | ||||
| }); | ||||
|  | ||||
| router.route("/log").post(mustBeAuthenticated, async (req, res) => { | ||||
|   await PreLotteryWine.deleteMany(); | ||||
|   | ||||
							
								
								
									
										180
									
								
								api/virtualLottery.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								api/virtualLottery.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| const express = require("express"); | ||||
| const path = require("path"); | ||||
| const router = express.Router(); | ||||
| const mongoose = require("mongoose"); | ||||
| mongoose.connect("mongodb://localhost:27017/vinlottis", { | ||||
|   useNewUrlParser: true | ||||
| }); | ||||
| const io = require("socket.io")(8080); | ||||
| const mustBeAuthenticated = require(path.join( | ||||
|   __dirname + "/../middleware/mustBeAuthenticated" | ||||
| )); | ||||
|  | ||||
| const Attendee = require(path.join(__dirname + "/../schemas/Attendee")); | ||||
| const VirtualWinner = require(path.join( | ||||
|   __dirname + "/../schemas/VirtualWinner" | ||||
| )); | ||||
|  | ||||
| router.use((req, res, next) => { | ||||
|   next(); | ||||
| }); | ||||
|  | ||||
| router.route("/winners").delete(mustBeAuthenticated, async (req, res) => { | ||||
|   await VirtualWinner.deleteMany(); | ||||
|   res.json(true); | ||||
| }); | ||||
|  | ||||
| router.route("/attendees").delete(mustBeAuthenticated, async (req, res) => { | ||||
|   await Attendee.deleteMany(); | ||||
|   res.json(true); | ||||
| }); | ||||
|  | ||||
| router.route("/winners").get(async (req, res) => { | ||||
|   let winners = await VirtualWinner.find(); | ||||
|   let winnersRedacted = []; | ||||
|   let winner; | ||||
|   for (let i = 0; i < winners.length; i++) { | ||||
|     winner = winners[i]; | ||||
|     winnersRedacted.push({ | ||||
|       name: winner.name, | ||||
|       color: winner.color | ||||
|     }); | ||||
|   } | ||||
|   res.json(winnersRedacted); | ||||
| }); | ||||
|  | ||||
| router.route("/winners/secure").get(mustBeAuthenticated, async (req, res) => { | ||||
|   let winners = await VirtualWinner.find(); | ||||
|  | ||||
|   res.json(winners); | ||||
| }); | ||||
|  | ||||
| router.route("/winner").get(mustBeAuthenticated, async (req, res) => { | ||||
|   let colorWinner = Math.floor(Math.random() * 4); | ||||
|   let colorToChoseFrom; | ||||
|   let findObject = {}; | ||||
|  | ||||
|   switch (colorWinner) { | ||||
|     case 0: | ||||
|       colorToChoseFrom = "red"; | ||||
|       break; | ||||
|     case 1: | ||||
|       colorToChoseFrom = "blue"; | ||||
|       break; | ||||
|     case 2: | ||||
|       colorToChoseFrom = "green"; | ||||
|       break; | ||||
|     case 3: | ||||
|       colorToChoseFrom = "yellow"; | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   io.emit("color_winner", { color: colorToChoseFrom }); | ||||
|  | ||||
|   findObject[colorToChoseFrom] = { $gt: 0 }; | ||||
|  | ||||
|   let contestantsToChoseFrom = await Attendee.find(findObject); | ||||
|   let attendeeListDemocratic = []; | ||||
|  | ||||
|   let currentContestant; | ||||
|   for (let i = 0; i < contestantsToChoseFrom.length; i++) { | ||||
|     currentContestant = contestantsToChoseFrom[i]; | ||||
|     for (let y = 0; y < currentContestant[colorToChoseFrom]; y++) { | ||||
|       attendeeListDemocratic.push({ | ||||
|         name: currentContestant.name, | ||||
|         phoneNumber: currentContestant.phoneNumber | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let winner = | ||||
|     attendeeListDemocratic[ | ||||
|       Math.floor(Math.random() * attendeeListDemocratic.length) | ||||
|     ]; | ||||
|  | ||||
|   io.emit("winner", { name: winner.name }); | ||||
|  | ||||
|   let newWinnerElement = new VirtualWinner({ | ||||
|     name: winner.name, | ||||
|     phoneNumber: winner.phoneNumber, | ||||
|     color: colorToChoseFrom | ||||
|   }); | ||||
|  | ||||
|   await Attendee.remove({ name: winner.name, phoneNumber: winner.phoneNumber }); | ||||
|  | ||||
|   await newWinnerElement.save(); | ||||
|   res.json(winner); | ||||
| }); | ||||
|  | ||||
| router.route("/attendees").get(async (req, res) => { | ||||
|   let attendees = await Attendee.find(); | ||||
|   let attendeesRedacted = []; | ||||
|   let attendee; | ||||
|   for (let i = 0; i < attendees.length; i++) { | ||||
|     attendee = attendees[i]; | ||||
|     attendeesRedacted.push({ | ||||
|       name: attendee.name, | ||||
|       ballots: attendee.red + attendee.blue + attendee.yellow + attendee.green, | ||||
|       red: attendee.red, | ||||
|       blue: attendee.blue, | ||||
|       green: attendee.green, | ||||
|       yellow: attendee.yellow | ||||
|     }); | ||||
|   } | ||||
|   res.json(attendeesRedacted); | ||||
| }); | ||||
|  | ||||
| router.route("/attendees/secure").get(mustBeAuthenticated, async (req, res) => { | ||||
|   let attendees = await Attendee.find(); | ||||
|  | ||||
|   res.json(attendees); | ||||
| }); | ||||
|  | ||||
| router.route("/attendee").post(mustBeAuthenticated, async (req, res) => { | ||||
|   const attendee = req.body; | ||||
|   let red = 0; | ||||
|   let blue = 0; | ||||
|   let green = 0; | ||||
|   let yellow = 0; | ||||
|   if (attendee.randomColors) { | ||||
|     let color; | ||||
|     for (let i = 0; i < attendee.ballots; i++) { | ||||
|       color = Math.floor(Math.random() * 4); | ||||
|       switch (color) { | ||||
|         case 0: | ||||
|           red += 1; | ||||
|           break; | ||||
|         case 1: | ||||
|           blue += 1; | ||||
|           break; | ||||
|         case 2: | ||||
|           green += 1; | ||||
|           break; | ||||
|         case 3: | ||||
|           yellow += 1; | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|   } else { | ||||
|     red = attendee.red; | ||||
|     blue = attendee.blue; | ||||
|     yellow = attendee.yellow; | ||||
|     green = attendee.green; | ||||
|   } | ||||
|   let newAttendee = new Attendee({ | ||||
|     name: attendee.name, | ||||
|     red, | ||||
|     blue, | ||||
|     green, | ||||
|     yellow, | ||||
|     phoneNumber: attendee.phoneNumber | ||||
|   }); | ||||
|   console.log(newAttendee); | ||||
|   await newAttendee.save(); | ||||
|  | ||||
|   io.emit("new_attendee", {}); | ||||
|  | ||||
|   res.send(true); | ||||
| }); | ||||
|  | ||||
| module.exports = router; | ||||
| @@ -1,13 +1,13 @@ | ||||
|  | ||||
| const mustBeAuthenticated = (req, res, next) => { | ||||
|   console.log(req.isAuthenticated()); | ||||
|   if (!req.isAuthenticated()) { | ||||
|     return res.status(401).send({ | ||||
|       success: false, | ||||
|       message: "Du må være logget inn." | ||||
|     }) | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   return next() | ||||
| } | ||||
|   return next(); | ||||
| }; | ||||
|  | ||||
| module.exports = mustBeAuthenticated; | ||||
							
								
								
									
										347
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										347
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1348,6 +1348,15 @@ | ||||
|       "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "@zxing/library": { | ||||
|       "version": "0.16.0", | ||||
|       "resolved": "https://registry.npmjs.org/@zxing/library/-/library-0.16.0.tgz", | ||||
|       "integrity": "sha512-z3+kNQDQXXuS1O0Q5AHVFXK9O2j/rG4vCIEnvjW4vTrkyA6HuJKQR3BIKqkbZeiMf4VjDPCbrPk3n8ZSw7n/xw==", | ||||
|       "requires": { | ||||
|         "text-encoding": "^0.7.0", | ||||
|         "ts-custom-error": "^3.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "abbrev": { | ||||
|       "version": "1.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", | ||||
| @@ -1374,6 +1383,11 @@ | ||||
|       "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "after": { | ||||
|       "version": "0.8.2", | ||||
|       "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", | ||||
|       "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" | ||||
|     }, | ||||
|     "agent-base": { | ||||
|       "version": "4.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", | ||||
| @@ -1565,6 +1579,11 @@ | ||||
|       "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "arraybuffer.slice": { | ||||
|       "version": "0.0.7", | ||||
|       "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", | ||||
|       "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" | ||||
|     }, | ||||
|     "asn1": { | ||||
|       "version": "0.2.4", | ||||
|       "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", | ||||
| @@ -1650,8 +1669,7 @@ | ||||
|     "async-limiter": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", | ||||
|       "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" | ||||
|     }, | ||||
|     "async-listener": { | ||||
|       "version": "0.6.10", | ||||
| @@ -1740,6 +1758,11 @@ | ||||
|         "object.assign": "^4.1.0" | ||||
|       } | ||||
|     }, | ||||
|     "backo2": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", | ||||
|       "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" | ||||
|     }, | ||||
|     "balanced-match": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", | ||||
| @@ -1800,11 +1823,21 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "base64-arraybuffer": { | ||||
|       "version": "0.1.5", | ||||
|       "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", | ||||
|       "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" | ||||
|     }, | ||||
|     "base64-js": { | ||||
|       "version": "1.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", | ||||
|       "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" | ||||
|     }, | ||||
|     "base64id": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", | ||||
|       "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" | ||||
|     }, | ||||
|     "batch": { | ||||
|       "version": "0.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", | ||||
| @@ -1819,6 +1852,14 @@ | ||||
|         "tweetnacl": "^0.14.3" | ||||
|       } | ||||
|     }, | ||||
|     "better-assert": { | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", | ||||
|       "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", | ||||
|       "requires": { | ||||
|         "callsite": "1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "bfj": { | ||||
|       "version": "6.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/bfj/-/bfj-6.1.2.tgz", | ||||
| @@ -1867,6 +1908,11 @@ | ||||
|       "integrity": "sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "blob": { | ||||
|       "version": "0.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", | ||||
|       "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" | ||||
|     }, | ||||
|     "block-stream": { | ||||
|       "version": "0.0.9", | ||||
|       "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", | ||||
| @@ -2227,6 +2273,11 @@ | ||||
|         "caller-callsite": "^2.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "callsite": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", | ||||
|       "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" | ||||
|     }, | ||||
|     "callsites": { | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", | ||||
| @@ -2635,12 +2686,22 @@ | ||||
|       "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "component-bind": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", | ||||
|       "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" | ||||
|     }, | ||||
|     "component-emitter": { | ||||
|       "version": "1.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", | ||||
|       "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "component-inherit": { | ||||
|       "version": "0.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", | ||||
|       "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" | ||||
|     }, | ||||
|     "compressible": { | ||||
|       "version": "2.0.18", | ||||
|       "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", | ||||
| @@ -3756,6 +3817,92 @@ | ||||
|         "once": "^1.4.0" | ||||
|       } | ||||
|     }, | ||||
|     "engine.io": { | ||||
|       "version": "3.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz", | ||||
|       "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==", | ||||
|       "requires": { | ||||
|         "accepts": "~1.3.4", | ||||
|         "base64id": "2.0.0", | ||||
|         "cookie": "0.3.1", | ||||
|         "debug": "~4.1.0", | ||||
|         "engine.io-parser": "~2.2.0", | ||||
|         "ws": "^7.1.2" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "cookie": { | ||||
|           "version": "0.3.1", | ||||
|           "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", | ||||
|           "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" | ||||
|         }, | ||||
|         "debug": { | ||||
|           "version": "4.1.1", | ||||
|           "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", | ||||
|           "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", | ||||
|           "requires": { | ||||
|             "ms": "^2.1.1" | ||||
|           } | ||||
|         }, | ||||
|         "ws": { | ||||
|           "version": "7.2.3", | ||||
|           "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz", | ||||
|           "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "engine.io-client": { | ||||
|       "version": "3.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz", | ||||
|       "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==", | ||||
|       "requires": { | ||||
|         "component-emitter": "1.2.1", | ||||
|         "component-inherit": "0.0.3", | ||||
|         "debug": "~4.1.0", | ||||
|         "engine.io-parser": "~2.2.0", | ||||
|         "has-cors": "1.1.0", | ||||
|         "indexof": "0.0.1", | ||||
|         "parseqs": "0.0.5", | ||||
|         "parseuri": "0.0.5", | ||||
|         "ws": "~6.1.0", | ||||
|         "xmlhttprequest-ssl": "~1.5.4", | ||||
|         "yeast": "0.1.2" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "component-emitter": { | ||||
|           "version": "1.2.1", | ||||
|           "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", | ||||
|           "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" | ||||
|         }, | ||||
|         "debug": { | ||||
|           "version": "4.1.1", | ||||
|           "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", | ||||
|           "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", | ||||
|           "requires": { | ||||
|             "ms": "^2.1.1" | ||||
|           } | ||||
|         }, | ||||
|         "ws": { | ||||
|           "version": "6.1.4", | ||||
|           "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", | ||||
|           "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", | ||||
|           "requires": { | ||||
|             "async-limiter": "~1.0.0" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "engine.io-parser": { | ||||
|       "version": "2.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", | ||||
|       "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", | ||||
|       "requires": { | ||||
|         "after": "0.8.2", | ||||
|         "arraybuffer.slice": "~0.0.7", | ||||
|         "base64-arraybuffer": "0.1.5", | ||||
|         "blob": "0.0.5", | ||||
|         "has-binary2": "~1.0.2" | ||||
|       } | ||||
|     }, | ||||
|     "enhanced-resolve": { | ||||
|       "version": "4.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", | ||||
| @@ -5533,6 +5680,26 @@ | ||||
|         "ansi-regex": "^2.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "has-binary2": { | ||||
|       "version": "1.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", | ||||
|       "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", | ||||
|       "requires": { | ||||
|         "isarray": "2.0.1" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "isarray": { | ||||
|           "version": "2.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", | ||||
|           "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "has-cors": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", | ||||
|       "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" | ||||
|     }, | ||||
|     "has-flag": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", | ||||
| @@ -6053,6 +6220,11 @@ | ||||
|       "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "indexof": { | ||||
|       "version": "0.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", | ||||
|       "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" | ||||
|     }, | ||||
|     "infer-owner": { | ||||
|       "version": "1.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", | ||||
| @@ -7593,6 +7765,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", | ||||
|       "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" | ||||
|     }, | ||||
|     "object-component": { | ||||
|       "version": "0.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", | ||||
|       "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" | ||||
|     }, | ||||
|     "object-copy": { | ||||
|       "version": "0.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", | ||||
| @@ -7945,6 +8122,22 @@ | ||||
|       "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "parseqs": { | ||||
|       "version": "0.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", | ||||
|       "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", | ||||
|       "requires": { | ||||
|         "better-assert": "~1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "parseuri": { | ||||
|       "version": "0.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", | ||||
|       "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", | ||||
|       "requires": { | ||||
|         "better-assert": "~1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "parseurl": { | ||||
|       "version": "1.3.3", | ||||
|       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", | ||||
| @@ -10323,6 +10516,130 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "socket.io": { | ||||
|       "version": "2.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz", | ||||
|       "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==", | ||||
|       "requires": { | ||||
|         "debug": "~4.1.0", | ||||
|         "engine.io": "~3.4.0", | ||||
|         "has-binary2": "~1.0.2", | ||||
|         "socket.io-adapter": "~1.1.0", | ||||
|         "socket.io-client": "2.3.0", | ||||
|         "socket.io-parser": "~3.4.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "debug": { | ||||
|           "version": "4.1.1", | ||||
|           "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", | ||||
|           "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", | ||||
|           "requires": { | ||||
|             "ms": "^2.1.1" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "socket.io-adapter": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz", | ||||
|       "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==" | ||||
|     }, | ||||
|     "socket.io-client": { | ||||
|       "version": "2.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz", | ||||
|       "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==", | ||||
|       "requires": { | ||||
|         "backo2": "1.0.2", | ||||
|         "base64-arraybuffer": "0.1.5", | ||||
|         "component-bind": "1.0.0", | ||||
|         "component-emitter": "1.2.1", | ||||
|         "debug": "~4.1.0", | ||||
|         "engine.io-client": "~3.4.0", | ||||
|         "has-binary2": "~1.0.2", | ||||
|         "has-cors": "1.1.0", | ||||
|         "indexof": "0.0.1", | ||||
|         "object-component": "0.0.3", | ||||
|         "parseqs": "0.0.5", | ||||
|         "parseuri": "0.0.5", | ||||
|         "socket.io-parser": "~3.3.0", | ||||
|         "to-array": "0.1.4" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "component-emitter": { | ||||
|           "version": "1.2.1", | ||||
|           "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", | ||||
|           "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" | ||||
|         }, | ||||
|         "debug": { | ||||
|           "version": "4.1.1", | ||||
|           "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", | ||||
|           "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", | ||||
|           "requires": { | ||||
|             "ms": "^2.1.1" | ||||
|           } | ||||
|         }, | ||||
|         "isarray": { | ||||
|           "version": "2.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", | ||||
|           "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" | ||||
|         }, | ||||
|         "socket.io-parser": { | ||||
|           "version": "3.3.0", | ||||
|           "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", | ||||
|           "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", | ||||
|           "requires": { | ||||
|             "component-emitter": "1.2.1", | ||||
|             "debug": "~3.1.0", | ||||
|             "isarray": "2.0.1" | ||||
|           }, | ||||
|           "dependencies": { | ||||
|             "debug": { | ||||
|               "version": "3.1.0", | ||||
|               "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", | ||||
|               "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", | ||||
|               "requires": { | ||||
|                 "ms": "2.0.0" | ||||
|               } | ||||
|             }, | ||||
|             "ms": { | ||||
|               "version": "2.0.0", | ||||
|               "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", | ||||
|               "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "socket.io-parser": { | ||||
|       "version": "3.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz", | ||||
|       "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==", | ||||
|       "requires": { | ||||
|         "component-emitter": "1.2.1", | ||||
|         "debug": "~4.1.0", | ||||
|         "isarray": "2.0.1" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "component-emitter": { | ||||
|           "version": "1.2.1", | ||||
|           "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", | ||||
|           "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" | ||||
|         }, | ||||
|         "debug": { | ||||
|           "version": "4.1.1", | ||||
|           "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", | ||||
|           "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", | ||||
|           "requires": { | ||||
|             "ms": "^2.1.1" | ||||
|           } | ||||
|         }, | ||||
|         "isarray": { | ||||
|           "version": "2.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", | ||||
|           "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "sockjs": { | ||||
|       "version": "0.3.19", | ||||
|       "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", | ||||
| @@ -10930,6 +11247,12 @@ | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "text-encoding": { | ||||
|       "version": "0.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz", | ||||
|       "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==", | ||||
|       "optional": true | ||||
|     }, | ||||
|     "through2": { | ||||
|       "version": "2.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", | ||||
| @@ -10967,6 +11290,11 @@ | ||||
|       "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "to-array": { | ||||
|       "version": "0.1.4", | ||||
|       "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", | ||||
|       "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" | ||||
|     }, | ||||
|     "to-arraybuffer": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", | ||||
| @@ -11060,6 +11388,11 @@ | ||||
|       "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "ts-custom-error": { | ||||
|       "version": "3.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.1.1.tgz", | ||||
|       "integrity": "sha512-f/syoy+pTE4z82qaiRuthEeZtCGNKzlfs0Zc8jpQFcz/CYMaFSwFSdfFt1sSFnPlDLOEm7RCROdIxZ44N8UlwA==" | ||||
|     }, | ||||
|     "tslib": { | ||||
|       "version": "1.10.0", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", | ||||
| @@ -12132,6 +12465,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", | ||||
|       "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" | ||||
|     }, | ||||
|     "xmlhttprequest-ssl": { | ||||
|       "version": "1.5.5", | ||||
|       "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", | ||||
|       "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" | ||||
|     }, | ||||
|     "xregexp": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", | ||||
| @@ -12202,6 +12540,11 @@ | ||||
|           "dev": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "yeast": { | ||||
|       "version": "0.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", | ||||
|       "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -35,6 +35,8 @@ | ||||
|     "passport-local-mongoose": "^6.0.1", | ||||
|     "qrcode": "^1.4.4", | ||||
|     "referrer-policy": "^1.2.0", | ||||
|     "socket.io": "^2.3.0", | ||||
|     "socket.io-client": "^2.3.0", | ||||
|     "vue": "~2.6", | ||||
|     "vue-analytics": "^5.22.1", | ||||
|     "vue-router": "~3.0", | ||||
|   | ||||
							
								
								
									
										13
									
								
								schemas/Attendee.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								schemas/Attendee.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| const mongoose = require("mongoose"); | ||||
| const Schema = mongoose.Schema; | ||||
|  | ||||
| const Attendee = new Schema({ | ||||
|   name: String, | ||||
|   phoneNumber: String, | ||||
|   green: Number, | ||||
|   blue: Number, | ||||
|   red: Number, | ||||
|   yellow: Number | ||||
| }); | ||||
|  | ||||
| module.exports = mongoose.model("Attendee", Attendee); | ||||
							
								
								
									
										10
									
								
								schemas/VirtualWinner.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								schemas/VirtualWinner.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| const mongoose = require("mongoose"); | ||||
| const Schema = mongoose.Schema; | ||||
|  | ||||
| const VirtualWinner = new Schema({ | ||||
|   name: String, | ||||
|   phoneNumber: String, | ||||
|   color: String | ||||
| }); | ||||
|  | ||||
| module.exports = mongoose.model("VirtualWinner", VirtualWinner); | ||||
| @@ -9,6 +9,7 @@ const retrieveApi = require(path.join(__dirname + "/api/retrieve")); | ||||
| const subscriptionApi = require(path.join(__dirname + "/api/subscriptions")); | ||||
| const loginApi = require(path.join(__dirname + "/api/login")); | ||||
| const wineinfoApi = require(path.join(__dirname + "/api/wineinfo")); | ||||
| const virtualApi = require(path.join(__dirname + "/api/virtualLottery")); | ||||
| const bodyParser = require("body-parser"); | ||||
|  | ||||
| const mongoose = require("mongoose"); | ||||
| @@ -82,6 +83,7 @@ app.use("/", loginApi); | ||||
| app.use("/api/", updateApi); | ||||
| app.use("/api/", retrieveApi); | ||||
| app.use("/api/", wineinfoApi); | ||||
| app.use("/api/virtual/", virtualApi); | ||||
| app.use("/subscription", subscriptionApi); | ||||
|  | ||||
| app.get("/dagens", function(req, res) { | ||||
|   | ||||
							
								
								
									
										82
									
								
								src/api.js
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								src/api.js
									
									
									
									
									
								
							| @@ -66,6 +66,76 @@ const log = (sendObject) => { | ||||
|     .then(resp => resp.json()) | ||||
| } | ||||
|  | ||||
| const addAttendee = sendObject => { | ||||
|   const url = new URL("/api/virtual/attendee", BASE_URL); | ||||
|  | ||||
|   const options = { | ||||
|     headers: { | ||||
|       "Content-Type": "application/json" | ||||
|     }, | ||||
|     method: "POST", | ||||
|     body: JSON.stringify(sendObject) | ||||
|   }; | ||||
|  | ||||
|   return fetch(url.href, options).then(resp => resp.json()); | ||||
| }; | ||||
|  | ||||
| const getVirtualWinner = () => { | ||||
|   const url = new URL("/api/virtual/winner", BASE_URL); | ||||
|  | ||||
|   return fetch(url.href).then(resp => resp.json()); | ||||
| }; | ||||
|  | ||||
| const attendeesSecure = () => { | ||||
|   const url = new URL("/api/virtual/attendees/secure", BASE_URL); | ||||
|  | ||||
|   return fetch(url.href).then(resp => resp.json()); | ||||
| }; | ||||
|  | ||||
| const winnersSecure = () => { | ||||
|   const url = new URL("/api/virtual/winners/secure", BASE_URL); | ||||
|  | ||||
|   return fetch(url.href).then(resp => resp.json()); | ||||
| }; | ||||
|  | ||||
| const winners = () => { | ||||
|   const url = new URL("/api/virtual/winners", BASE_URL); | ||||
|  | ||||
|   return fetch(url.href).then(resp => resp.json()); | ||||
| }; | ||||
|  | ||||
| const deleteWinners = () => { | ||||
|   const url = new URL("/api/virtual/winners", BASE_URL); | ||||
|  | ||||
|   const options = { | ||||
|     headers: { | ||||
|       "Content-Type": "application/json" | ||||
|     }, | ||||
|     method: "DELETE" | ||||
|   }; | ||||
|  | ||||
|   return fetch(url.href, options).then(resp => resp.json()); | ||||
| } | ||||
|  | ||||
| const deleteAttendees = () => { | ||||
|   const url = new URL("/api/virtual/attendees", BASE_URL); | ||||
|  | ||||
|   const options = { | ||||
|     headers: { | ||||
|       "Content-Type": "application/json" | ||||
|     }, | ||||
|     method: "DELETE" | ||||
|   }; | ||||
|  | ||||
|   return fetch(url.href, options).then(resp => resp.json()); | ||||
| }; | ||||
|  | ||||
| const attendees = () => { | ||||
|   const url = new URL("/api/virtual/attendees", BASE_URL); | ||||
|  | ||||
|   return fetch(url.href).then(resp => resp.json()); | ||||
| } | ||||
|  | ||||
| const logWines = (wines) => { | ||||
|   const url = new URL("/api/log/wines", BASE_URL) | ||||
|  | ||||
| @@ -165,5 +235,13 @@ export { | ||||
|   wineSchema, | ||||
|   barcodeToVinmonopolet, | ||||
|   login, | ||||
|   register | ||||
| } | ||||
|   register, | ||||
|   addAttendee, | ||||
|   getVirtualWinner, | ||||
|   attendeesSecure, | ||||
|   attendees, | ||||
|   winners, | ||||
|   winnersSecure, | ||||
|   deleteWinners, | ||||
|   deleteAttendees | ||||
| }; | ||||
|   | ||||
| @@ -1,10 +1,23 @@ | ||||
| <template> | ||||
|   <div class="page-container"> | ||||
|     <h1>Registrering</h1> | ||||
|  | ||||
|     <router-link to="virtual-register" class="generate-link" | ||||
|       >Virtuelt-lotteri | ||||
|       <span class="subtext generator-link">Her</span></router-link | ||||
|     > | ||||
|     <br /> | ||||
|     <br /> | ||||
|     <div class="notification-element"> | ||||
|       <div class="label-div"> | ||||
|         <label for="notification">Push-melding</label> | ||||
|         <textarea id="notification" type="text" rows="3" v-model="pushMessage" placeholder="Push meldingtekst" /> | ||||
|         <textarea | ||||
|           id="notification" | ||||
|           type="text" | ||||
|           rows="3" | ||||
|           v-model="pushMessage" | ||||
|           placeholder="Push meldingtekst" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="button-container"> | ||||
| @@ -15,24 +28,31 @@ | ||||
|  | ||||
|     <h2 id="addwine-title">Prelottery</h2> | ||||
|  | ||||
|     <ScanToVinmonopolet @wine="wineFromVinmonopoletScan" v-if="showCamera"/> | ||||
|     <ScanToVinmonopolet @wine="wineFromVinmonopoletScan" v-if="showCamera" /> | ||||
|  | ||||
|     <div class="button-container"> | ||||
|       <button class="vin-button" @click="showCamera = !showCamera"> | ||||
|         {{ showCamera ? "Skjul camera" : "Legg til vin med camera" }} | ||||
|       </button> | ||||
|  | ||||
|       <button class="vin-button" @click="addWine">Legg til en vin manuelt</button> | ||||
|       <button class="vin-button" @click="addWine"> | ||||
|         Legg til en vin manuelt | ||||
|       </button> | ||||
|     </div> | ||||
|  | ||||
|     <div v-if="wines.length > 0" class="edit-container"> | ||||
|       <wine v-for="wine in wines" :key="key" :wine="wine"> | ||||
|         <div class="edit"> | ||||
|           <div class="button-container row"> | ||||
|             <button class="vin-button" @click="editWine = amIBeingEdited(wine) ? false : wine"> | ||||
|             <button | ||||
|               class="vin-button" | ||||
|               @click="editWine = amIBeingEdited(wine) ? false : wine" | ||||
|             > | ||||
|               {{ amIBeingEdited(wine) ? "Lukk" : "Rediger" }} | ||||
|             </button> | ||||
|             <button class="red vin-button" @click="deleteWine(wine)">Slett</button> | ||||
|             <button class="red vin-button" @click="deleteWine(wine)"> | ||||
|               Slett | ||||
|             </button> | ||||
|           </div> | ||||
|  | ||||
|           <div v-if="amIBeingEdited(wine)" class="wine-edit"> | ||||
| @@ -54,25 +74,37 @@ | ||||
|  | ||||
|     <h3>Legg til lodd kjøpt</h3> | ||||
|     <div class="colors"> | ||||
|       <div v-for="color in lotteryColorBoxes" :class="color.css + ' colors-box'" :key="color"> | ||||
|        <div class="colors-overlay"> | ||||
|       <div | ||||
|         v-for="color in lotteryColorBoxes" | ||||
|         :class="color.css + ' colors-box'" | ||||
|         :key="color" | ||||
|       > | ||||
|         <div class="colors-overlay"> | ||||
|           <p>{{ color.name }} kjøpt</p> | ||||
|           <input v-model="color.value" | ||||
|                  min="0" | ||||
|                  :placeholder="0" | ||||
|                  type="number" /> | ||||
|           <input v-model="color.value" min="0" :placeholder="0" type="number" /> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
|       <div class="label-div"> | ||||
|         <label>Totalt kjøpt for:</label> | ||||
|         <input v-model="payed" placeholder="NOK" type="number" :step="price || 1" min="0" /> | ||||
|         <input | ||||
|           v-model="payed" | ||||
|           placeholder="NOK" | ||||
|           type="number" | ||||
|           :step="price || 1" | ||||
|           min="0" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <h3>Vinnere</h3> | ||||
|     <div class="winner-container" v-if="winners.length > 0"> | ||||
|       <wine v-for="winner in winners" :key="winner" :wine="winner.wine" :inlineSlot="true"> | ||||
|       <wine | ||||
|         v-for="winner in winners" | ||||
|         :key="winner" | ||||
|         :wine="winner.wine" | ||||
|         :inlineSlot="true" | ||||
|       > | ||||
|         <div class="winner-element"> | ||||
|           <div class="color-selector"> | ||||
|             <div class="label-div"> | ||||
| @@ -80,273 +112,289 @@ | ||||
|             </div> | ||||
|             <button | ||||
|               class="blue" | ||||
|               :class="{'active': winner.color == 'blue' }" | ||||
|               :class="{ active: winner.color == 'blue' }" | ||||
|               @click="winner.color = 'blue'" | ||||
|             ></button> | ||||
|             <button | ||||
|               class="red" | ||||
|               :class="{'active': winner.color == 'red' }" | ||||
|               :class="{ active: winner.color == 'red' }" | ||||
|               @click="winner.color = 'red'" | ||||
|             ></button> | ||||
|             <button | ||||
|               class="green" | ||||
|               :class="{'active': winner.color == 'green' }" | ||||
|               :class="{ active: winner.color == 'green' }" | ||||
|               @click="winner.color = 'green'" | ||||
|             ></button> | ||||
|             <button | ||||
|               class="yellow" | ||||
|               :class="{'active': winner.color == 'yellow' }" | ||||
|               :class="{ active: winner.color == 'yellow' }" | ||||
|               @click="winner.color = 'yellow'" | ||||
|             ></button> | ||||
|           </div> | ||||
|  | ||||
|           <div class="label-div"> | ||||
|             <label for="winner-name">Navn vinner</label> | ||||
|             <input id="winner-name" type="text" placeholder="Navn" v-model="winner.name"/> | ||||
|             <input | ||||
|               id="winner-name" | ||||
|               type="text" | ||||
|               placeholder="Navn" | ||||
|               v-model="winner.name" | ||||
|             /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </wine> | ||||
|  | ||||
|       <div class="button-container"> | ||||
|         <button class="vin-button" @click="sendInfo">Send inn vinnere</button> | ||||
|         <button class="vin-button" @click="resetWinnerDataInStorage">Reset local wines</button> | ||||
|         <button class="vin-button" @click="resetWinnerDataInStorage"> | ||||
|           Reset local wines | ||||
|         </button> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|   <TextToast v-if="showToast" | ||||
|              :text="toastText" | ||||
|              v-on:closeToast="showToast = false" /> | ||||
|     <TextToast | ||||
|       v-if="showToast" | ||||
|       :text="toastText" | ||||
|       v-on:closeToast="showToast = false" | ||||
|     /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import { prelottery, log, logWines, wineSchema } from "@/api"; | ||||
|   import TextToast from "@/ui/TextToast"; | ||||
|   import Wine from "@/ui/Wine"; | ||||
|   import ScanToVinmonopolet from "@/ui/ScanToVinmonopolet"; | ||||
| import { prelottery, log, logWines, wineSchema } from "@/api"; | ||||
| import TextToast from "@/ui/TextToast"; | ||||
| import Wine from "@/ui/Wine"; | ||||
| import ScanToVinmonopolet from "@/ui/ScanToVinmonopolet"; | ||||
|  | ||||
|   export default { | ||||
|     components: { TextToast, Wine, ScanToVinmonopolet }, | ||||
|     data() { | ||||
|       return { | ||||
|         red: null, | ||||
|         blue: null, | ||||
|         green: null, | ||||
|         yellow: null, | ||||
|         payed: undefined, | ||||
|         winners: [], | ||||
|         wines: [], | ||||
|         pushMessage: "", | ||||
|         toastText: undefined, | ||||
|         showToast: false, | ||||
|         showCamera: false, | ||||
|         editWine: false, | ||||
|         price: __PRICE__ | ||||
|       }; | ||||
|     }, | ||||
|     created() { | ||||
|       this.fetchAndAddPrelotteryWines() | ||||
|         .then(this.getWinnerdataFromStorage) | ||||
| export default { | ||||
|   components: { TextToast, Wine, ScanToVinmonopolet }, | ||||
|   data() { | ||||
|     return { | ||||
|       red: null, | ||||
|       blue: null, | ||||
|       green: null, | ||||
|       yellow: null, | ||||
|       payed: undefined, | ||||
|       winners: [], | ||||
|       wines: [], | ||||
|       pushMessage: "", | ||||
|       toastText: undefined, | ||||
|       showToast: false, | ||||
|       showCamera: false, | ||||
|       editWine: false, | ||||
|       price: __PRICE__ | ||||
|     }; | ||||
|   }, | ||||
|   created() { | ||||
|     this.fetchAndAddPrelotteryWines().then(this.getWinnerdataFromStorage); | ||||
|  | ||||
|       window.addEventListener("unload", this.setWinnerdataToStorage); | ||||
|     }, | ||||
|     beforeDestroy() { | ||||
|       this.setWinnerdataToStorage() | ||||
|     }, | ||||
|     computed: { | ||||
|       lotteryColorBoxes() { | ||||
|         return [{ value: this.blue, name: "Blå", css: "blue" }, | ||||
|     window.addEventListener("unload", this.setWinnerdataToStorage); | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     this.setWinnerdataToStorage(); | ||||
|   }, | ||||
|   computed: { | ||||
|     lotteryColorBoxes() { | ||||
|       return [ | ||||
|         { value: this.blue, name: "Blå", css: "blue" }, | ||||
|         { value: this.red, name: "Rød", css: "red" }, | ||||
|         { value: this.green, name: "Grønn", css: "green" }, | ||||
|         { value: this.yellow, name: "Gul", css: "yellow" }] | ||||
|       } | ||||
|         { value: this.yellow, name: "Gul", css: "yellow" } | ||||
|       ]; | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     amIBeingEdited(wine) { | ||||
|       return this.editWine.id == wine.id && this.editWine.name == wine.name; | ||||
|     }, | ||||
|     methods: { | ||||
|       amIBeingEdited(wine) { | ||||
|         return this.editWine.id == wine.id && this.editWine.name == wine.name; | ||||
|       }, | ||||
|       async fetchAndAddPrelotteryWines() { | ||||
|         const wines = await prelottery() | ||||
|     async fetchAndAddPrelotteryWines() { | ||||
|       const wines = await prelottery(); | ||||
|  | ||||
|         for (let i = 0; i < wines.length; i++) { | ||||
|           let wine = wines[i]; | ||||
|           this.winners.push({ | ||||
|             name: "", | ||||
|             color: "", | ||||
|             wine: { | ||||
|               name: wine.name, | ||||
|               vivinoLink: wine.vivinoLink, | ||||
|               rating: wine.rating, | ||||
|               image: wine.image, | ||||
|               id: wine.id | ||||
|             } | ||||
|           }); | ||||
|         } | ||||
|       }, | ||||
|       wineFromVinmonopoletScan(wineResponse) { | ||||
|         if (this.wines.map(wine => wine.name).includes(wineResponse.name)) { | ||||
|           this.toastText = "Vinen er allerede lagt til." | ||||
|           this.showToast = true | ||||
|           return | ||||
|         } | ||||
|  | ||||
|         this.toastText = "Fant og la til vin:<br>" + wineResponse.name  | ||||
|         this.showToast = true; | ||||
|  | ||||
|         this.wines.unshift(wineResponse) | ||||
|       }, | ||||
|       sendPush: async function() { | ||||
|         let _response = await fetch("/subscription/send-notification", { | ||||
|           headers: { | ||||
|             "Content-Type": "application/json" | ||||
|             // 'Content-Type': 'application/x-www-form-urlencoded', | ||||
|           }, | ||||
|           method: "POST", | ||||
|           body: JSON.stringify({ message: this.pushMessage }) | ||||
|         }); | ||||
|         let response = await _response.json(); | ||||
|         if (response) { | ||||
|           alert("Sendt!"); | ||||
|         } else { | ||||
|           alert("Noe gikk galt!"); | ||||
|         } | ||||
|       }, | ||||
|       addWine: async function(event) { | ||||
|         const wine = await wineSchema() | ||||
|  | ||||
|         this.editWine = wine; | ||||
|         this.wines.unshift(wine); | ||||
|       }, | ||||
|       deleteWine(deletedWine) { | ||||
|         this.wines = this.wines.filter(wine => wine.name != deletedWine.name) | ||||
|       }, | ||||
|       sendWines: async function() { | ||||
|         let response = await logWines(this.wines) | ||||
|         if (response == true) { | ||||
|           alert("Sendt!"); | ||||
|           window.location.reload(); | ||||
|         } else { | ||||
|           alert("Noe gikk galt under innsending"); | ||||
|         } | ||||
|       }, | ||||
|       addWinner: function(event) { | ||||
|       for (let i = 0; i < wines.length; i++) { | ||||
|         let wine = wines[i]; | ||||
|         this.winners.push({ | ||||
|           name: "", | ||||
|           color: "", | ||||
|           wine: { | ||||
|             name: "", | ||||
|             vivinoLink: "", | ||||
|             rating: "" | ||||
|             name: wine.name, | ||||
|             vivinoLink: wine.vivinoLink, | ||||
|             rating: wine.rating, | ||||
|             image: wine.image, | ||||
|             id: wine.id | ||||
|           } | ||||
|         }); | ||||
|       }, | ||||
|       sendInfo: async function(event) { | ||||
|         let sendObject = { | ||||
|           purchase: { | ||||
|             date: new Date(), | ||||
|             blue: this.blue, | ||||
|             red: this.red, | ||||
|             yellow: this.yellow, | ||||
|             green: this.green | ||||
|           }, | ||||
|           winners: this.winners | ||||
|         }; | ||||
|  | ||||
|         if (sendObject.purchase.red == undefined) { | ||||
|           alert("Rød må defineres"); | ||||
|           return; | ||||
|         } | ||||
|         if (sendObject.purchase.green == undefined) { | ||||
|           alert("Grønn må defineres"); | ||||
|           return; | ||||
|         } | ||||
|         if (sendObject.purchase.yellow == undefined) { | ||||
|           alert("Gul må defineres"); | ||||
|           return; | ||||
|         } | ||||
|         if (sendObject.purchase.blue == undefined) { | ||||
|           alert("Blå må defineres"); | ||||
|           return; | ||||
|         } | ||||
|  | ||||
|         sendObject.purchase.bought = | ||||
|           parseInt(this.blue) + | ||||
|           parseInt(this.red) + | ||||
|           parseInt(this.green) + | ||||
|           parseInt(this.yellow); | ||||
|         const stolen = sendObject.purchase.bought - parseInt(this.payed) / 10; | ||||
|         if (isNaN(stolen) || stolen == undefined) { | ||||
|           alert("Betalt må registreres"); | ||||
|           return; | ||||
|         } | ||||
|         sendObject.purchase.stolen = stolen; | ||||
|  | ||||
|         if (sendObject.winners.length == 0) { | ||||
|           alert("Det må være med vinnere"); | ||||
|           return; | ||||
|         } | ||||
|         for (let i = 0; i < sendObject.winners.length; i++) { | ||||
|           let currentWinner = sendObject.winners[i]; | ||||
|  | ||||
|           if (currentWinner.name == undefined || currentWinner.name == "") { | ||||
|             alert("Navn må defineres"); | ||||
|             return; | ||||
|           } | ||||
|           if (currentWinner.color == undefined || currentWinner.color == "") { | ||||
|             alert("Farge må defineres"); | ||||
|             return; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         let response = await log(sendObject) | ||||
|         if (response == true) { | ||||
|           alert("Sendt!"); | ||||
|           window.location.reload(); | ||||
|         } else { | ||||
|           alert(response.message || "Noe gikk galt under innsending"); | ||||
|         } | ||||
|       }, | ||||
|       getWinnerdataFromStorage() { | ||||
|         let localWinners = localStorage.getItem("winners"); | ||||
|         if (localWinners && this.winners.length) { | ||||
|           localWinners = JSON.parse(localWinners); | ||||
|  | ||||
|           this.winners = this.winners.map(winner => { | ||||
|             const localWinnerMatch = localWinners.filter(localWinner => localWinner.wine.name == winner.wine.name || localWinner.wine.id == winner.wine.id) | ||||
|  | ||||
|             if (localWinnerMatch.length > 0) { | ||||
|               winner.name = localWinnerMatch[0].name || winner.name | ||||
|               winner.color = localWinnerMatch[0].color || winner.name | ||||
|             } | ||||
|        | ||||
|             return winner | ||||
|           }) | ||||
|         } | ||||
|  | ||||
|         let localColors = localStorage.getItem("colorValues"); | ||||
|         if (localColors) { | ||||
|           localColors = localColors.split(",") | ||||
|           this.lotteryColorBoxes.forEach((color, i) => { | ||||
|             const localColorValue = Number(localColors[i]) | ||||
|             color.value = localColorValue == 0 ? null : localColorValue | ||||
|           }) | ||||
|         } | ||||
|       }, | ||||
|       setWinnerdataToStorage() { | ||||
|         console.log("saving localstorage") | ||||
|         localStorage.setItem("winners", JSON.stringify(this.winners)) | ||||
|         localStorage.setItem("colorValues", this.lotteryColorBoxes.map(color => Number(color.value))) | ||||
|         window.removeEventListener("unload", this.setWinnerdataToStorage) | ||||
|       }, | ||||
|       resetWinnerDataInStorage() { | ||||
|         this.winners = [] | ||||
|         this.fetchAndAddPrelotteryWines() | ||||
|           .then(resp => this.winners = resp) | ||||
|         this.lotteryColorBoxes.map(color => color.value = null) | ||||
|         window.location.reload(); | ||||
|       } | ||||
|     }, | ||||
|     wineFromVinmonopoletScan(wineResponse) { | ||||
|       if (this.wines.map(wine => wine.name).includes(wineResponse.name)) { | ||||
|         this.toastText = "Vinen er allerede lagt til."; | ||||
|         this.showToast = true; | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       this.toastText = "Fant og la til vin:<br>" + wineResponse.name; | ||||
|       this.showToast = true; | ||||
|  | ||||
|       this.wines.unshift(wineResponse); | ||||
|     }, | ||||
|     sendPush: async function() { | ||||
|       let _response = await fetch("/subscription/send-notification", { | ||||
|         headers: { | ||||
|           "Content-Type": "application/json" | ||||
|           // 'Content-Type': 'application/x-www-form-urlencoded', | ||||
|         }, | ||||
|         method: "POST", | ||||
|         body: JSON.stringify({ message: this.pushMessage }) | ||||
|       }); | ||||
|       let response = await _response.json(); | ||||
|       if (response) { | ||||
|         alert("Sendt!"); | ||||
|       } else { | ||||
|         alert("Noe gikk galt!"); | ||||
|       } | ||||
|     }, | ||||
|     addWine: async function(event) { | ||||
|       const wine = await wineSchema(); | ||||
|  | ||||
|       this.editWine = wine; | ||||
|       this.wines.unshift(wine); | ||||
|     }, | ||||
|     deleteWine(deletedWine) { | ||||
|       this.wines = this.wines.filter(wine => wine.name != deletedWine.name); | ||||
|     }, | ||||
|     sendWines: async function() { | ||||
|       let response = await logWines(this.wines); | ||||
|       if (response == true) { | ||||
|         alert("Sendt!"); | ||||
|         window.location.reload(); | ||||
|       } else { | ||||
|         alert("Noe gikk galt under innsending"); | ||||
|       } | ||||
|     }, | ||||
|     addWinner: function(event) { | ||||
|       this.winners.push({ | ||||
|         name: "", | ||||
|         color: "", | ||||
|         wine: { | ||||
|           name: "", | ||||
|           vivinoLink: "", | ||||
|           rating: "" | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|     sendInfo: async function(event) { | ||||
|       let sendObject = { | ||||
|         purchase: { | ||||
|           date: new Date(), | ||||
|           blue: this.blue, | ||||
|           red: this.red, | ||||
|           yellow: this.yellow, | ||||
|           green: this.green | ||||
|         }, | ||||
|         winners: this.winners | ||||
|       }; | ||||
|  | ||||
|       if (sendObject.purchase.red == undefined) { | ||||
|         alert("Rød må defineres"); | ||||
|         return; | ||||
|       } | ||||
|       if (sendObject.purchase.green == undefined) { | ||||
|         alert("Grønn må defineres"); | ||||
|         return; | ||||
|       } | ||||
|       if (sendObject.purchase.yellow == undefined) { | ||||
|         alert("Gul må defineres"); | ||||
|         return; | ||||
|       } | ||||
|       if (sendObject.purchase.blue == undefined) { | ||||
|         alert("Blå må defineres"); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       sendObject.purchase.bought = | ||||
|         parseInt(this.blue) + | ||||
|         parseInt(this.red) + | ||||
|         parseInt(this.green) + | ||||
|         parseInt(this.yellow); | ||||
|       const stolen = sendObject.purchase.bought - parseInt(this.payed) / 10; | ||||
|       if (isNaN(stolen) || stolen == undefined) { | ||||
|         alert("Betalt må registreres"); | ||||
|         return; | ||||
|       } | ||||
|       sendObject.purchase.stolen = stolen; | ||||
|  | ||||
|       if (sendObject.winners.length == 0) { | ||||
|         alert("Det må være med vinnere"); | ||||
|         return; | ||||
|       } | ||||
|       for (let i = 0; i < sendObject.winners.length; i++) { | ||||
|         let currentWinner = sendObject.winners[i]; | ||||
|  | ||||
|         if (currentWinner.name == undefined || currentWinner.name == "") { | ||||
|           alert("Navn må defineres"); | ||||
|           return; | ||||
|         } | ||||
|         if (currentWinner.color == undefined || currentWinner.color == "") { | ||||
|           alert("Farge må defineres"); | ||||
|           return; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       let response = await log(sendObject); | ||||
|       if (response == true) { | ||||
|         alert("Sendt!"); | ||||
|         window.location.reload(); | ||||
|       } else { | ||||
|         alert(response.message || "Noe gikk galt under innsending"); | ||||
|       } | ||||
|     }, | ||||
|     getWinnerdataFromStorage() { | ||||
|       let localWinners = localStorage.getItem("winners"); | ||||
|       if (localWinners && this.winners.length) { | ||||
|         localWinners = JSON.parse(localWinners); | ||||
|  | ||||
|         this.winners = this.winners.map(winner => { | ||||
|           const localWinnerMatch = localWinners.filter( | ||||
|             localWinner => | ||||
|               localWinner.wine.name == winner.wine.name || | ||||
|               localWinner.wine.id == winner.wine.id | ||||
|           ); | ||||
|  | ||||
|           if (localWinnerMatch.length > 0) { | ||||
|             winner.name = localWinnerMatch[0].name || winner.name; | ||||
|             winner.color = localWinnerMatch[0].color || winner.name; | ||||
|           } | ||||
|  | ||||
|           return winner; | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       let localColors = localStorage.getItem("colorValues"); | ||||
|       if (localColors) { | ||||
|         localColors = localColors.split(","); | ||||
|         this.lotteryColorBoxes.forEach((color, i) => { | ||||
|           const localColorValue = Number(localColors[i]); | ||||
|           color.value = localColorValue == 0 ? null : localColorValue; | ||||
|         }); | ||||
|       } | ||||
|     }, | ||||
|     setWinnerdataToStorage() { | ||||
|       console.log("saving localstorage"); | ||||
|       localStorage.setItem("winners", JSON.stringify(this.winners)); | ||||
|       localStorage.setItem( | ||||
|         "colorValues", | ||||
|         this.lotteryColorBoxes.map(color => Number(color.value)) | ||||
|       ); | ||||
|       window.removeEventListener("unload", this.setWinnerdataToStorage); | ||||
|     }, | ||||
|     resetWinnerDataInStorage() { | ||||
|       this.winners = []; | ||||
|       this.fetchAndAddPrelotteryWines().then(resp => (this.winners = resp)); | ||||
|       this.lotteryColorBoxes.map(color => (color.value = null)); | ||||
|       window.location.reload(); | ||||
|     } | ||||
|   }; | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @@ -363,7 +411,7 @@ h2 { | ||||
|   width: 100%; | ||||
|   text-align: center; | ||||
|   font-size: 1.6rem; | ||||
|   font-family: knowit, Arial | ||||
|   font-family: knowit, Arial; | ||||
| } | ||||
|  | ||||
| hr { | ||||
| @@ -417,6 +465,14 @@ hr { | ||||
|   align-items: flex-start; | ||||
| } | ||||
|  | ||||
| .generate-link { | ||||
|   color: #333333; | ||||
|   text-decoration: none; | ||||
|   display: block; | ||||
|   text-align: center; | ||||
|   margin-bottom: 0px; | ||||
| } | ||||
|  | ||||
| .wine-edit { | ||||
|   width: 100%; | ||||
|   margin-top: 1.5rem; | ||||
| @@ -535,8 +591,8 @@ hr { | ||||
|     text-transform: uppercase; | ||||
|     font-weight: 600; | ||||
|     position: absolute; | ||||
|     top: .4rem; | ||||
|     left: .5rem; | ||||
|     top: 0.4rem; | ||||
|     left: 0.5rem; | ||||
|   } | ||||
|  | ||||
|   input { | ||||
| @@ -551,22 +607,26 @@ hr { | ||||
|   } | ||||
| } | ||||
|  | ||||
| .green, .green .colors-overlay > input { | ||||
| .green, | ||||
| .green .colors-overlay > input { | ||||
|   background-color: $light-green; | ||||
|   color: $green; | ||||
| } | ||||
|  | ||||
| .blue, .blue .colors-overlay > input { | ||||
| .blue, | ||||
| .blue .colors-overlay > input { | ||||
|   background-color: $light-blue; | ||||
|   color: $blue; | ||||
| } | ||||
|  | ||||
| .yellow, .yellow .colors-overlay > input { | ||||
| .yellow, | ||||
| .yellow .colors-overlay > input { | ||||
|   background-color: $light-yellow; | ||||
|   color: $yellow; | ||||
| } | ||||
|  | ||||
| .red, .red .colors-overlay > input { | ||||
| .red, | ||||
| .red .colors-overlay > input { | ||||
|   background-color: $light-red; | ||||
|   color: $red; | ||||
| } | ||||
|   | ||||
							
								
								
									
										216
									
								
								src/components/VirtualLotteryPage.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								src/components/VirtualLotteryPage.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <h1>Virtuelt lotteri</h1> | ||||
|     <div class="current-draw" v-if="currentWinnerDrawn"> | ||||
|       <h2>NY VINNER:</h2> | ||||
|       <div | ||||
|         :class="currentWinnerColor + '-ballot'" | ||||
|         class="ballot-element center-new-winner" | ||||
|       > | ||||
|         <span v-if="currentWinnerName">{{ currentWinnerName }}</span> | ||||
|         <span v-if="!currentWinnerName">{{ secondsNameLeft }}</span> | ||||
|       </div> | ||||
|       <br /> | ||||
|       <br /> | ||||
|       <br /> | ||||
|       <br /> | ||||
|       <br /> | ||||
|       <div v-if="countdownStarted && attendees.length > 0"> | ||||
|         <h2>Trekker ny om: {{ secondsLeft }}</h2> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <h2 v-if="winners.length > 0">Vinnere</h2> | ||||
|     <div class="winners" v-if="winners.length > 0"> | ||||
|       <div class="winner" v-for="(winner, index) in winners" :key="index"> | ||||
|         <div :class="winner.color + '-ballot'" class="ballot-element"> | ||||
|           {{ winner.name }} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="attendees" v-if="attendees.length > 0"> | ||||
|       <h2>Deltakere</h2> | ||||
|       <div class="attendee" v-for="(attendee, index) in attendees" :key="index"> | ||||
|         <span class="attendee-name">{{ attendee.name }}</span> | ||||
|         <div class="red-ballot ballot-element small">{{ attendee.red }}</div> | ||||
|         <div class="blue-ballot ballot-element small">{{ attendee.blue }}</div> | ||||
|         <div class="green-ballot ballot-element small"> | ||||
|           {{ attendee.green }} | ||||
|         </div> | ||||
|         <div class="yellow-ballot ballot-element small"> | ||||
|           {{ attendee.yellow }} | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { attendees, winners } from "@/api"; | ||||
| import io from "socket.io-client"; | ||||
|  | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       attendees: [], | ||||
|       winners: [], | ||||
|       currentWinnerDrawn: false, | ||||
|       currentWinnerName: null, | ||||
|       currentWinnerColor: null, | ||||
|       countdownStarted: false, | ||||
|       secondsLeft: 15, | ||||
|       secondsNameLeft: 5, | ||||
|       socket: null | ||||
|     }; | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.getAttendees(); | ||||
|     this.getWinners(); | ||||
|     this.socket = io(window.location.hostname + ":8080"); | ||||
|     this.socket.on("color_winner", msg => { | ||||
|       this.currentWinnerDrawn = true; | ||||
|       this.currentWinnerColor = msg.color; | ||||
|     }); | ||||
|  | ||||
|     this.socket.on("winner", async msg => { | ||||
|       this.currentWinnerDrawn = true; | ||||
|       this.countdown(); | ||||
|       setTimeout(() => { | ||||
|         this.currentWinnerName = msg.name; | ||||
|         this.getWinners(); | ||||
|         this.getAttendees(); | ||||
|       }, 5000); | ||||
|  | ||||
|       setTimeout(() => { | ||||
|         this.currentWinnerColor = null; | ||||
|         this.currentWinnerName = null; | ||||
|         this.currentWinnerDrawn = false; | ||||
|       }, 15000); | ||||
|     }); | ||||
|  | ||||
|     this.socket.on("new_attendee", async msg => { | ||||
|       this.getAttendees(); | ||||
|     }); | ||||
|   }, | ||||
|   beforeDestroy() { | ||||
|     this.socket.disconnect(); | ||||
|     this.socket = null; | ||||
|   }, | ||||
|   methods: { | ||||
|     countdown: function() { | ||||
|       this.secondsLeft -= 1; | ||||
|       this.secondsNameLeft -= 1; | ||||
|       this.countdownStarted = true; | ||||
|       if (this.secondsLeft <= 0) { | ||||
|         this.secondsLeft = 15; | ||||
|         this.secondsNameLeft = 5; | ||||
|         this.countdownStarted = false; | ||||
|         return; | ||||
|       } | ||||
|       setTimeout(() => { | ||||
|         this.countdown(); | ||||
|       }, 1000); | ||||
|     }, | ||||
|     getWinners: async function() { | ||||
|       let response = await winners(); | ||||
|       if (response) { | ||||
|         this.winners = response; | ||||
|       } | ||||
|     }, | ||||
|     getAttendees: async function() { | ||||
|       let response = await attendees(); | ||||
|       if (response) { | ||||
|         this.attendees = response; | ||||
|         if (attendees <= 0) { | ||||
|           this.secondsLeft = 0; | ||||
|           this.secondsNameLeft = 0; | ||||
|           this.countdown(); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "../styles/global.scss"; | ||||
| @import "../styles/variables.scss"; | ||||
| @import "../styles/media-queries.scss"; | ||||
|  | ||||
| h1, | ||||
| h2 { | ||||
|   text-align: center; | ||||
| } | ||||
| .current-draw { | ||||
|   margin: auto; | ||||
| } | ||||
|  | ||||
| .attendee-name { | ||||
|   width: 60%; | ||||
| } | ||||
|  | ||||
| .center-new-winner { | ||||
|   margin: auto !important; | ||||
| } | ||||
|  | ||||
| .ballot-element { | ||||
|   width: 140px; | ||||
|   height: 150px; | ||||
|   margin: 20px 0; | ||||
|   -webkit-mask-image: url(/../../public/assets/images/lodd.svg); | ||||
|   background-repeat: no-repeat; | ||||
|   mask-image: url(/../../public/assets/images/lodd.svg); | ||||
|   -webkit-mask-repeat: no-repeat; | ||||
|   mask-repeat: no-repeat; | ||||
|   color: #333333; | ||||
|   font-size: 0.75rem; | ||||
|   font-weight: bold; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   text-align: center; | ||||
|  | ||||
|   &.small { | ||||
|     width: 45px; | ||||
|     height: 45px; | ||||
|     font-size: 1rem; | ||||
|   } | ||||
|  | ||||
|   &.green-ballot { | ||||
|     background-color: $light-green; | ||||
|   } | ||||
|  | ||||
|   &.blue-ballot { | ||||
|     background-color: $light-blue; | ||||
|   } | ||||
|  | ||||
|   &.yellow-ballot { | ||||
|     background-color: $light-yellow; | ||||
|   } | ||||
|  | ||||
|   &.red-ballot { | ||||
|     background-color: $light-red; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .winners { | ||||
|   display: flex; | ||||
|   justify-content: space-around; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| .attendees { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| .attendee { | ||||
|   display: flex; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   width: 50%; | ||||
|   margin: 0 auto; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										368
									
								
								src/components/VirtualLotteryRegistrationPage.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										368
									
								
								src/components/VirtualLotteryRegistrationPage.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,368 @@ | ||||
| <template> | ||||
|   <div class="page-container"> | ||||
|     <h1>Virtuelt lotteri registrering</h1> | ||||
|     <br /> | ||||
|     <div class="draw-winner-container" v-if="attendees.length > 0"> | ||||
|       <span v-if="drawingWinner" | ||||
|         >Vent {{ secondsLeft }} sekunder til å trekke på nytt</span | ||||
|       > | ||||
|       <button class="vin-button" v-if="!drawingWinner" @click="drawWinner"> | ||||
|         Trekk en vinner | ||||
|       </button> | ||||
|     </div> | ||||
|     <h2 v-if="winners.length > 0">Vinnere</h2> | ||||
|     <div class="winners" v-if="winners.length > 0"> | ||||
|       <div class="winner" v-for="(winner, index) in winners" :key="index"> | ||||
|         <div :class="winner.color + '-ballot'" class="ballot-element"> | ||||
|           <span>{{ winner.name }}</span> | ||||
|           <span>{{ winner.phoneNumber }}</span> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div | ||||
|       class="delete-buttons" | ||||
|       v-if="attendees.length > 0 || winners.length > 0" | ||||
|     > | ||||
|       <button | ||||
|         class="vin-button" | ||||
|         v-if="winners.length > 0" | ||||
|         @click="deleteAllWinners" | ||||
|       > | ||||
|         Slett virtuelle vinnere | ||||
|       </button> | ||||
|       <button | ||||
|         class="vin-button" | ||||
|         v-if="attendees.length > 0" | ||||
|         @click="deleteAllAttendees" | ||||
|       > | ||||
|         Slett virtuelle deltakere | ||||
|       </button> | ||||
|     </div> | ||||
|     <div class="attendees" v-if="attendees.length > 0"> | ||||
|       <h2>Deltakere hittil</h2> | ||||
|       <div class="attendee" v-for="(attendee, index) in attendees" :key="index"> | ||||
|         <div class="name-and-phone"> | ||||
|           <span class="name">{{ attendee.name }}</span> | ||||
|           <span class="phoneNumber">{{ attendee.phoneNumber }}</span> | ||||
|         </div> | ||||
|         <div class="ballots-container"> | ||||
|           <div class="red-ballot ballot-element small">{{ attendee.red }}</div> | ||||
|           <div class="blue-ballot ballot-element small"> | ||||
|             {{ attendee.blue }} | ||||
|           </div> | ||||
|           <div class="green-ballot ballot-element small"> | ||||
|             {{ attendee.green }} | ||||
|           </div> | ||||
|           <div class="yellow-ballot ballot-element small"> | ||||
|             {{ attendee.yellow }} | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div class="attendee-registration-container"> | ||||
|       <h2>Legg til deltaker</h2> | ||||
|       <div class="label-div"> | ||||
|         <label for="name">Navn</label> | ||||
|         <input id="name" type="text" placeholder="Navn" v-model="name" /> | ||||
|       </div> | ||||
|       <br /> | ||||
|       <div class="label-div"> | ||||
|         <label for="phoneNumber">Telefonnummer</label> | ||||
|         <input | ||||
|           id="phoneNumber" | ||||
|           type="text" | ||||
|           placeholder="Telefonnummer" | ||||
|           v-model="phoneNumber" | ||||
|         /> | ||||
|       </div> | ||||
|       <br /> | ||||
|       <br /> | ||||
|       <div class="label-div"> | ||||
|         <label for="randomColors">Tilfeldig farger?</label> | ||||
|         <input | ||||
|           id="randomColors" | ||||
|           type="checkbox" | ||||
|           placeholder="Tilfeldig farger" | ||||
|           v-model="randomColors" | ||||
|         /> | ||||
|       </div> | ||||
|       <br /> | ||||
|       <br /> | ||||
|       <div v-if="!randomColors"> | ||||
|         <div class="label-div"> | ||||
|           <label for="red">Rød</label> | ||||
|           <input id="red" type="number" placeholder="Rød" v-model="red" /> | ||||
|         </div> | ||||
|         <div class="label-div"> | ||||
|           <label for="blue">Blå</label> | ||||
|           <input id="blue" type="number" placeholder="Blå" v-model="blue" /> | ||||
|         </div> | ||||
|         <div class="label-div"> | ||||
|           <label for="green">Grønn</label> | ||||
|           <input id="green" type="number" placeholder="Grønn" v-model="green" /> | ||||
|         </div> | ||||
|         <div class="label-div"> | ||||
|           <label for="yellow">Gul</label> | ||||
|           <input id="yellow" type="number" placeholder="Gul" v-model="yellow" /> | ||||
|         </div> | ||||
|       </div> | ||||
|       <div v-if="randomColors"> | ||||
|         <div class="label-div"> | ||||
|           <label for="ballots">Antall lodd</label> | ||||
|           <input | ||||
|             id="ballots" | ||||
|             type="number" | ||||
|             placeholder="Antall lodd" | ||||
|             v-model="ballots" | ||||
|           /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|     <br /> | ||||
|     <button class="vin-button" @click="sendAttendee">Send deltaker</button> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { | ||||
|   addAttendee, | ||||
|   getVirtualWinner, | ||||
|   attendeesSecure, | ||||
|   attendees, | ||||
|   winnersSecure, | ||||
|   deleteWinners, | ||||
|   deleteAttendees | ||||
| } from "@/api"; | ||||
|  | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       name: null, | ||||
|       phoneNumber: null, | ||||
|       red: 0, | ||||
|       blue: 0, | ||||
|       green: 0, | ||||
|       yellow: 0, | ||||
|       ballots: 0, | ||||
|       randomColors: false, | ||||
|       attendees: [], | ||||
|       winners: [], | ||||
|       drawingWinner: false, | ||||
|       secondsLeft: 15 | ||||
|     }; | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.getAttendees(); | ||||
|     this.getWinners(); | ||||
|   }, | ||||
|   methods: { | ||||
|     sendAttendee: async function() { | ||||
|       let response = await addAttendee({ | ||||
|         name: this.name, | ||||
|         phoneNumber: this.phoneNumber, | ||||
|         red: this.red, | ||||
|         blue: this.blue, | ||||
|         green: this.green, | ||||
|         yellow: this.yellow, | ||||
|         ballots: this.ballots, | ||||
|         randomColors: this.randomColors | ||||
|       }); | ||||
|       if (response == true) { | ||||
|         alert("Sendt inn deltaker!"); | ||||
|         this.name = null; | ||||
|         this.phoneNumber = null; | ||||
|         this.red = 0; | ||||
|         this.blue = 0; | ||||
|  | ||||
|         this.getAttendees(); | ||||
|       } else { | ||||
|         alert("Klarte ikke sende inn.. Er du logget inn?"); | ||||
|       } | ||||
|     }, | ||||
|     getAttendees: async function() { | ||||
|       let response = await attendeesSecure(); | ||||
|       this.attendees = response; | ||||
|     }, | ||||
|     drawWinner: async function() { | ||||
|       this.drawingWinner = true; | ||||
|       let response = await getVirtualWinner(); | ||||
|       if (response) { | ||||
|         this.countdown(); | ||||
|         this.getWinners(); | ||||
|         this.getAttendees(); | ||||
|       } else { | ||||
|         this.drawingWinner = false; | ||||
|         alert("Noe gikk galt under trekningen..!"); | ||||
|       } | ||||
|     }, | ||||
|     countdown: function() { | ||||
|       this.secondsLeft -= 1; | ||||
|       if (this.secondsLeft <= 0) { | ||||
|         this.secondsLeft = 15; | ||||
|         this.drawingWinner = false; | ||||
|         return; | ||||
|       } | ||||
|       setTimeout(() => { | ||||
|         this.countdown(); | ||||
|       }, 1000); | ||||
|     }, | ||||
|     deleteAllWinners: async function() { | ||||
|       let response = await deleteWinners(); | ||||
|       if (response) { | ||||
|         this.getWinners(); | ||||
|       } else { | ||||
|         alert("Klarte ikke hente ut vinnere"); | ||||
|       } | ||||
|     }, | ||||
|     deleteAllAttendees: async function() { | ||||
|       let response = await deleteAttendees(); | ||||
|       if (response) { | ||||
|         this.getAttendees(); | ||||
|       } else { | ||||
|         alert("Klarte ikke hente ut vinnere"); | ||||
|       } | ||||
|     }, | ||||
|     getWinners: async function() { | ||||
|       let response = await winnersSecure(); | ||||
|       if (response) { | ||||
|         this.winners = response; | ||||
|       } else { | ||||
|         alert("Klarte ikke hente ut vinnere"); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @import "../styles/global.scss"; | ||||
| @import "../styles/media-queries.scss"; | ||||
|  | ||||
| .draw-winner-container, | ||||
| .delete-buttons { | ||||
|   margin-bottom: 20px; | ||||
| } | ||||
|  | ||||
| .delete-buttons { | ||||
|   display: flex; | ||||
| } | ||||
|  | ||||
| h1 { | ||||
|   width: 100%; | ||||
|   text-align: center; | ||||
|   font-family: knowit, Arial; | ||||
| } | ||||
|  | ||||
| h2 { | ||||
|   width: 100%; | ||||
|   text-align: center; | ||||
|   font-size: 1.6rem; | ||||
|   font-family: knowit, Arial; | ||||
| } | ||||
|  | ||||
| hr { | ||||
|   width: 90%; | ||||
|   margin: 2rem auto; | ||||
|   color: grey; | ||||
| } | ||||
|  | ||||
| .page-container { | ||||
|   padding: 0 1.5rem 3rem; | ||||
|  | ||||
|   @include desktop { | ||||
|     max-width: 60vw; | ||||
|     margin: 0 auto; | ||||
|   } | ||||
| } | ||||
|  | ||||
| #randomColors { | ||||
|   width: 40px; | ||||
|   height: 40px; | ||||
|   &:checked { | ||||
|     background: green; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .ballot-element { | ||||
|   width: 140px; | ||||
|   height: 150px; | ||||
|   margin: 20px 0; | ||||
|   -webkit-mask-image: url(/../../public/assets/images/lodd.svg); | ||||
|   background-repeat: no-repeat; | ||||
|   mask-image: url(/../../public/assets/images/lodd.svg); | ||||
|   -webkit-mask-repeat: no-repeat; | ||||
|   mask-repeat: no-repeat; | ||||
|   color: #333333; | ||||
|   font-size: 0.75rem; | ||||
|   font-weight: bold; | ||||
|   display: flex; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
|   text-align: center; | ||||
|   flex-direction: column; | ||||
|  | ||||
|   &.small { | ||||
|     width: 45px; | ||||
|     height: 45px; | ||||
|     font-size: 1rem; | ||||
|   } | ||||
|  | ||||
|   &.green-ballot { | ||||
|     background-color: $light-green; | ||||
|   } | ||||
|  | ||||
|   &.blue-ballot { | ||||
|     background-color: $light-blue; | ||||
|   } | ||||
|  | ||||
|   &.yellow-ballot { | ||||
|     background-color: $light-yellow; | ||||
|   } | ||||
|  | ||||
|   &.red-ballot { | ||||
|     background-color: $light-red; | ||||
|   } | ||||
| } | ||||
|  | ||||
| button { | ||||
|   display: flex !important; | ||||
|   margin: auto !important; | ||||
| } | ||||
|  | ||||
| .winners { | ||||
|   display: flex; | ||||
|   justify-content: space-around; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| .attendees { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   justify-content: center; | ||||
|   align-items: center; | ||||
| } | ||||
|  | ||||
| .attendee { | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
|   justify-content: space-between; | ||||
|   align-items: center; | ||||
|   width: 50%; | ||||
|   margin: 0 auto; | ||||
|  | ||||
|   & .name-and-phone, | ||||
|   & .ballots-container { | ||||
|     display: flex; | ||||
|     justify-content: center; | ||||
|     align-items: center; | ||||
|   } | ||||
|  | ||||
|   & .name-and-phone { | ||||
|     flex-direction: column; | ||||
|   } | ||||
|  | ||||
|   & .ballots-container { | ||||
|     flex-direction: row; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
| @@ -7,6 +7,9 @@ import LoginPage from "@/components/LoginPage"; | ||||
| import RegisterPage from "@/components/RegisterPage"; | ||||
| import CreatePage from "@/components/CreatePage"; | ||||
|  | ||||
| import VirtualLotteryRegistrationPage from "@/components/VirtualLotteryRegistrationPage"; | ||||
| import VirtualLotteryPage from "@/components/VirtualLotteryPage"; | ||||
|  | ||||
| const routes = [ | ||||
|   { | ||||
|     path: "*", | ||||
| @@ -35,6 +38,14 @@ const routes = [ | ||||
|   { | ||||
|     path: "/create", | ||||
|     component: CreatePage | ||||
|   }, | ||||
|   { | ||||
|     path: "/virtual-register", | ||||
|     component: VirtualLotteryRegistrationPage | ||||
|   }, | ||||
|   { | ||||
|     path: "/virtual", | ||||
|     component: VirtualLotteryPage | ||||
|   } | ||||
| ]; | ||||
|  | ||||
|   | ||||
| @@ -70,7 +70,8 @@ self.addEventListener("fetch", event => { | ||||
|     event.request.url.includes("/update") || | ||||
|     event.request.url.includes("/register") || | ||||
|     event.request.method == "POST" || | ||||
|     event.request.url.includes("/api/wines/prelottery") | ||||
|     event.request.url.includes("/api/wines/prelottery") || | ||||
|     event.request.url.includes("/api/virtual") | ||||
|   ) { | ||||
|     event.respondWith(fetch(event.request)); | ||||
|     return; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user