diff --git a/api/lottery.js b/api/lottery.js index 17d8e29..0f7d218 100644 --- a/api/lottery.js +++ b/api/lottery.js @@ -1,63 +1,66 @@ const path = require('path'); -const mongoose = require('mongoose'); -mongoose.connect('mongodb://localhost:27017/vinlottis', { - useNewUrlParser: true -}) - const Highscore = require(path.join(__dirname + '/../schemas/Highscore')); const Wine = require(path.join(__dirname + '/../schemas/Wine')); // Utils const epochToDateString = date => new Date(parseInt(date)).toDateString(); -const getHighscoreByDates = highscore => { - let groupedLotteries = {} +const sortNewestFirst = (lotteries) => { + return lotteries.sort((a, b) => parseInt(a.date) < parseInt(b.date) ? 1 : -1) +} - highscore.forEach(user => { - user.wins.map(win => { +const groupHighscoreByDate = async (highscore=undefined) => { + if (highscore == undefined) + highscore = await Highscore.find(); + + const highscoreByDate = []; + + highscore.forEach(person => { + person.wins.map(win => { const epochDate = new Date(win.date).setHours(0,0,0,0); - const obj = { - name: user.name, + const winnerObject = { + name: person.name, color: win.color, wine: win.wine, date: epochDate } - groupedLotteries[epochDate] ? - groupedLotteries[epochDate].push(obj) : groupedLotteries[epochDate] = [obj]; + const existingDateIndex = highscoreByDate.findIndex(el => el.date == epochDate) + if (existingDateIndex > -1) + highscoreByDate[existingDateIndex].winners.push(winnerObject); + else + highscoreByDate.push({ + date: epochDate, + winners: [winnerObject] + }) }) }) - return groupedLotteries + return sortNewestFirst(highscoreByDate); } -const groupedHighscoreToSortedList = groupedLotteries => { - return Object.keys(groupedLotteries).map(key => { - const winners = groupedLotteries[key]; - return { - date: parseInt(key), - dateString: epochToDateString(key), - winners - } - }).sort((a,b) => parseInt(a.date) > parseInt(b.date) ? 1 : -1) -} +const resolveWineReferences = (highscoreObject, key) => { + const listWithWines = highscoreObject[key] -const resolveWineReferences = listWithWines => { return Promise.all(listWithWines.map(element => - Wine.findById(element.wine) - .then(wine => { - element.wine = wine - return element - }) - )) + Wine.findById(element.wine) + .then(wine => { + element.wine = wine + return element + })) + ) + .then(resolvedListWithWines => { + highscoreObject[key] = resolvedListWithWines; + return highscoreObject + }) } +// end utils // Routes const all = (req, res) => { return Highscore.find() - .then(highscore => getHighscoreByDates(highscore)) - .then(groupedLotteries => groupedHighscoreToSortedList(groupedLotteries)) + .then(highscore => groupHighscoreByDate(highscore)) .then(lotteries => res.send({ message: "Lotteries by date!", lotteries @@ -65,56 +68,60 @@ const all = (req, res) => { } const latest = (req, res) => { - return Highscore.find() - .then(highscore => getHighscoreByDates(highscore)) - .then(groupedLotteries => groupedHighscoreToSortedList(groupedLotteries)) - .then(lotteries => res.send({ - message: "Latest lottery!", - lottery: lotteries.slice(-1).pop() - })) + return groupHighscoreByDate() + .then(lotteries => lotteries.shift()) // first element in list + .then(latestLottery => resolveWineReferences(latestLottery, "winners")) + .then(lottery => res.send({ + message: "Latest lottery!", + winners: lottery.winners + }) + ) } const byEpochDate = (req, res) => { - const { date } = req.params; + let { date } = req.params; + date = new Date(new Date(parseInt(date)).setHours(0,0,0,0)).getTime() const dateString = epochToDateString(date); - return Highscore.find() - .then(highscore => getHighscoreByDates(highscore)) - .then(async (lotteries) => { - const lottery = lotteries[date]; - - if (lottery != null) { - return res.send({ - message: `Lottery for date: ${dateString}`, - lottery: await resolveWineReferences(lottery) - }) + return groupHighscoreByDate() + .then(lotteries => { + const lottery = lotteries.filter(lottery => lottery.date == date) + if (lottery.length > 0) { + return lottery[0] } else { return res.status(404).send({ - message: `No lottery found for date: ${dateString}` + message: `No lottery found for date: ${ dateString }` }) } }) + .then(lottery => resolveWineReferences(lottery, "winners")) + .then(lottery => res.send({ + message: `Lottery for date: ${ dateString}`, + date, + winners: lottery.winners + })) } const byName = (req, res) => { const { name } = req.params; + const regexName = new RegExp(name, "i"); // lowercase regex of the name return Highscore.find({ name }) - .then(async (highscore) => { - highscore = highscore[0] - if (highscore) { - const highscoreWithResolvedWines = await resolveWineReferences(highscore.wins) - - return res.send({ - message: `Lottery winnings by name: ${name}`, - highscore: highscoreWithResolvedWines - }) + .then(highscore => { + if (highscore.length > 0) { + return highscore[0] } else { return res.status(404).send({ message: `Name: ${ name } not found in leaderboards.` }) } }) + .then(highscore => resolveWineReferences(highscore, "wins")) + .then(highscore => res.send({ + message: `Lottery winnings for name: ${ name }.`, + name: highscore.name, + highscore: sortNewestFirst(highscore.wins) + })) } module.exports = { diff --git a/api/message.js b/api/message.js index a763a7b..0885793 100644 --- a/api/message.js +++ b/api/message.js @@ -2,6 +2,17 @@ const https = require("https"); const path = require("path"); const config = require(path.join(__dirname + "/../config/defaults/lottery")); +const dateString = (date) => { + if (typeof(date) == "string") { + date = new Date(date); + } + const ye = new Intl.DateTimeFormat('en', { year: 'numeric' }).format(date) + const mo = new Intl.DateTimeFormat('en', { month: '2-digit' }).format(date) + const da = new Intl.DateTimeFormat('en', { day: '2-digit' }).format(date) + + return `${da}-${mo}-${ye}` +} + async function sendWineSelectMessage(winnerObject) { winnerObject.timestamp_sent = new Date().getTime(); winnerObject.timestamp_limit = new Date().getTime() * 600000; @@ -15,6 +26,12 @@ async function sendWineSelectMessage(winnerObject) { ) } +async function sendWineConfirmation(winnerObject, wineObject, date) { + date = dateString(date); + return sendMessageToUser(winnerObject.phoneNumber, + `Bekreftelse på din vin ${ winnerObject.name }.\nDato vunnet: ${ date }.\nVin valgt: ${ wineObject.name }.\nKan hentes hos ${ config.name } på kontoret. Ha en ellers fin helg!`) +} + async function sendLastWinnerMessage(winnerObject, wineObject) { console.log(`User ${winnerObject.id} is only one left, chosing wine for him/her.`); winnerObject.timestamp_sent = new Date().getTime(); @@ -23,7 +40,7 @@ async function sendLastWinnerMessage(winnerObject, wineObject) { return sendMessageToUser( winnerObject.phoneNumber, - `Gratulerer som heldig vinner av vinlotteriet ${winnerObject.name}! Du har vunnet vinen ${wineObject.name}, og vil få nærmere info om hvordan/hvor du kan hente vinen snarest. Ha en ellers fin helg!` + `Gratulerer som heldig vinner av vinlotteriet ${winnerObject.name}! Du har vunnet vinen ${wineObject.name}, vinen kan hentes hos ${ config.name } på kontoret. Ha en ellers fin helg!` ); } @@ -82,7 +99,11 @@ async function gatewayRequest(body) { res.setEncoding('utf8'); if (res.statusCode == 200) { - res.on("data", (d) => resolve(JSON.parse(d))); + res.on("data", (data) => { + console.log("Response from message gateway:", data) + + resolve(JSON.parse(data)) + }); } else { res.on("data", (data) => { data = JSON.parse(data); @@ -103,6 +124,7 @@ async function gatewayRequest(body) { module.exports = { sendWineSelectMessage, + sendWineConfirmation, sendLastWinnerMessage, sendWineSelectMessageTooLate, sendInitialMessageToWinners diff --git a/api/retrieve.js b/api/retrieve.js index 92bb2a6..a90b95f 100644 --- a/api/retrieve.js +++ b/api/retrieve.js @@ -1,8 +1,4 @@ const path = require("path"); -const mongoose = require("mongoose"); -mongoose.connect("mongodb://localhost:27017/vinlottis", { - useNewUrlParser: true -}); const Purchase = require(path.join(__dirname + "/../schemas/Purchase")); const Wine = require(path.join(__dirname + "/../schemas/Wine")); @@ -143,7 +139,9 @@ const allWinesSummary = async (req, res) => { } } - return res.json(Object.values(wines)); + wines = Object.values(wines).reverse() + + return res.json(wines); }; module.exports = { diff --git a/api/subscriptions.js b/api/subscriptions.js index 574280c..1d61a08 100644 --- a/api/subscriptions.js +++ b/api/subscriptions.js @@ -2,13 +2,8 @@ const express = require("express"); const path = require("path"); const router = express.Router(); const webpush = require("web-push"); //requiring the web-push module -const mongoose = require("mongoose"); const schedule = require("node-schedule"); -mongoose.connect("mongodb://localhost:27017/vinlottis", { - useNewUrlParser: true -}); - const mustBeAuthenticated = require(path.join( __dirname + "/../middleware/mustBeAuthenticated" )); diff --git a/api/update.js b/api/update.js index 59642dc..c3fa4d1 100644 --- a/api/update.js +++ b/api/update.js @@ -1,9 +1,5 @@ const express = require("express"); const path = require("path"); -const mongoose = require("mongoose"); -mongoose.connect("mongodb://localhost:27017/vinlottis", { - useNewUrlParser: true -}); const sub = require(path.join(__dirname + "/../api/subscriptions")); diff --git a/api/virtualLottery.js b/api/virtualLottery.js index 81107dc..74224d6 100644 --- a/api/virtualLottery.js +++ b/api/virtualLottery.js @@ -45,7 +45,7 @@ const attendees = async (req, res) => { attendee = attendees[i]; attendeesRedacted.push({ name: attendee.name, - ballots: attendee.red + attendee.blue + attendee.yellow + attendee.green, + raffles: attendee.red + attendee.blue + attendee.yellow + attendee.green, red: attendee.red, blue: attendee.blue, green: attendee.green, @@ -99,27 +99,27 @@ const drawWinner = async (req, res) => { message: "No attendees left that have not won." }); } - let ballotColors = []; + let raffleColors = []; for (let i = 0; i < allContestants.length; i++) { let currentContestant = allContestants[i]; for (let blue = 0; blue < currentContestant.blue; blue++) { - ballotColors.push("blue"); + raffleColors.push("blue"); } for (let red = 0; red < currentContestant.red; red++) { - ballotColors.push("red"); + raffleColors.push("red"); } for (let green = 0; green < currentContestant.green; green++) { - ballotColors.push("green"); + raffleColors.push("green"); } for (let yellow = 0; yellow < currentContestant.yellow; yellow++) { - ballotColors.push("yellow"); + raffleColors.push("yellow"); } } - ballotColors = shuffle(ballotColors); + raffleColors = shuffle(raffleColors); let colorToChooseFrom = - ballotColors[Math.floor(Math.random() * ballotColors.length)]; + raffleColors[Math.floor(Math.random() * raffleColors.length)]; let findObject = { winner: false }; findObject[colorToChooseFrom] = { $gt: 0 }; @@ -187,7 +187,10 @@ const drawWinner = async (req, res) => { ); await newWinnerElement.save(); - return res.json(winner); + return res.json({ + success: true, + winner + }); }; const finish = async (req, res) => { diff --git a/api/virtualRegistration.js b/api/virtualRegistration.js index 74fb652..4e5e895 100644 --- a/api/virtualRegistration.js +++ b/api/virtualRegistration.js @@ -80,6 +80,7 @@ const registerWinnerSelection = async (req, res) => { let wonWine = await _wineFunctions.findSaveWine(prelotteryWine); await prelotteryWine.delete(); await _personFunctions.findSavePerson(foundWinner, wonWine, date); + await Message.sendWineConfirmation(foundWinner, wonWine, date); await foundWinner.delete(); console.info("Saved winners choice."); @@ -95,15 +96,19 @@ const registerWinnerSelection = async (req, res) => { })) }; -const chooseLastWineForUser = (winner, prelotteryWine) => { +const chooseLastWineForUser = (winner, preLotteryWine) => { let date = new Date(); date.setHours(5, 0, 0, 0); return _wineFunctions.findSaveWine(preLotteryWine) .then(wonWine => _personFunctions.findSavePerson(winner, wonWine, date)) - .then(() => prelotteryWine.delete()) + .then(() => preLotteryWine.delete()) .then(() => Message.sendLastWinnerMessage(winner, preLotteryWine)) - .then(() => winner.delete()); + .then(() => winner.delete()) + .catch(err => { + console.log("Error thrown from chooseLastWineForUser: " + err); + throw err; + }) } const findAndNotifyNextWinner = async () => { @@ -113,10 +118,13 @@ const findAndNotifyNextWinner = async () => { let winesLeft = await PreLotteryWine.find(); if (winnersLeft.length > 1) { + console.log("multiple winners left, choose next in line") nextWinner = winnersLeft[0]; // multiple winners left, choose next in line } else if (winnersLeft.length == 1 && winesLeft.length > 1) { + console.log("one winner left, but multiple wines") nextWinner = winnersLeft[0] // one winner left, but multiple wines } else if (winnersLeft.length == 1 && winesLeft.length == 1) { + console.log("one winner and one wine left, choose for user") nextWinner = winnersLeft[0] // one winner and one wine left, choose for user wine = winesLeft[0] return chooseLastWineForUser(nextWinner, wine); diff --git a/src/Vinlottis.vue b/src/Vinlottis.vue index 61d7294..c7b7944 100644 --- a/src/Vinlottis.vue +++ b/src/Vinlottis.vue @@ -98,7 +98,7 @@ export default { } body { - background-color: #dbeede; + background-color: $primary; } diff --git a/src/api.js b/src/api.js index 05145b9..2e0a9e2 100644 --- a/src/api.js +++ b/src/api.js @@ -333,6 +333,31 @@ const historyAll = () => { }); } +const historyByDate = (date) => { + const url = new URL(`/api/lottery/by-date/${ date }`, BASE_URL); + + return fetch(url.href).then(resp => { + if (resp.ok) { + return resp.json(); + } else { + return handleErrors(resp); + } + }); +} + +const getWinnerByName = (name) => { + const encodedName = encodeURIComponent(name) + const url = new URL(`/api/lottery/by-name/${name}`, BASE_URL); + + return fetch(url.href).then(resp => { + if (resp.ok) { + return resp.json(); + } else { + return handleErrors(resp); + } + }) +} + export { statistics, colorStatistics, @@ -364,5 +389,7 @@ export { finishedDraw, getAmIWinner, postWineChosen, - historyAll + historyAll, + historyByDate, + getWinnerByName }; diff --git a/src/components/AllWinesPage.vue b/src/components/AllWinesPage.vue index f042fbd..e4c63ec 100644 --- a/src/components/AllWinesPage.vue +++ b/src/components/AllWinesPage.vue @@ -1,26 +1,32 @@ @@ -30,6 +36,7 @@ import { page, event } from "vue-analytics"; import Banner from "@/ui/Banner"; import Wine from "@/ui/Wine"; import { overallWineStatistics } from "@/api"; +import { dateString } from "@/utils"; export default { components: { @@ -41,133 +48,77 @@ export default { wines: [] }; }, + methods: { + winDateUrl(date) { + const timestamp = new Date(date).getTime(); + return `/history/${timestamp}` + }, + dateString: dateString + }, async mounted() { - const wines = await overallWineStatistics(); - this.wines = wines.sort((a, b) => - a.rating > b.rating ? -1 : 1 - ); + this.wines = await overallWineStatistics(); } }; diff --git a/src/components/GeneratePage.vue b/src/components/GeneratePage.vue index a77e780..037a03d 100644 --- a/src/components/GeneratePage.vue +++ b/src/components/GeneratePage.vue @@ -1,13 +1,13 @@ @@ -27,7 +27,7 @@ export default { data() { return { hardStart: false, - numberOfBallots: null + numberOfRaffles: null }; }, mounted() { @@ -54,13 +54,16 @@ export default { @import "../styles/variables.scss"; @import "../styles/global.scss"; @import "../styles/media-queries.scss"; + +.container { + display: flex; + flex-direction: column; + margin-top: 0; +} + h1 { cursor: pointer; } -.header-link { - color: #333333; - text-decoration: none; -} p { text-align: center; @@ -74,10 +77,4 @@ p { margin-top: 2rem; } } - -.container { - margin: auto; - display: flex; - flex-direction: column; -} diff --git a/src/components/HighscorePage.vue b/src/components/HighscorePage.vue index e082bd3..090e51d 100644 --- a/src/components/HighscorePage.vue +++ b/src/components/HighscorePage.vue @@ -1,56 +1,24 @@ @@ -58,16 +26,15 @@ diff --git a/src/components/HistoryPage.vue b/src/components/HistoryPage.vue index 372e348..2e60cf2 100644 --- a/src/components/HistoryPage.vue +++ b/src/components/HistoryPage.vue @@ -2,14 +2,15 @@

Historie fra tidligere lotteri

-
- +
+
diff --git a/src/components/PersonalHighscorePage.vue b/src/components/PersonalHighscorePage.vue new file mode 100644 index 0000000..9604831 --- /dev/null +++ b/src/components/PersonalHighscorePage.vue @@ -0,0 +1,255 @@ + + + + + \ No newline at end of file diff --git a/src/components/RegisterPage.vue b/src/components/RegisterPage.vue index 0cebbe9..52345d4 100644 --- a/src/components/RegisterPage.vue +++ b/src/components/RegisterPage.vue @@ -85,7 +85,7 @@

Vinnere

Refresh data fra virtuelt lotteri
- +
@@ -500,6 +500,10 @@ hr { color: grey; } +.button-container { + margin-top: 1rem; +} + .page-container { padding: 0 1.5rem 3rem; @@ -537,10 +541,10 @@ hr { } .winner-element { display: flex; - flex-direction: row; + flex-direction: column; - @include desktop { - margin-top: 1.5rem; + > div { + margin-bottom: 1rem; } @include mobile { diff --git a/src/components/TodaysPage.vue b/src/components/TodaysPage.vue index 0dfdf12..9ba6921 100644 --- a/src/components/TodaysPage.vue +++ b/src/components/TodaysPage.vue @@ -1,16 +1,15 @@ @@ -39,79 +37,10 @@ export default { height: 250px; } -h1 { - font-family: knowit, Arial; - margin-bottom: 25px; -} - .wines-container { display: flex; flex-wrap: wrap; - justify-content: space-between; - margin: 0 2rem; - - @media (min-width: 1500px) { - max-width: 1500px; - margin: 0 auto; - } - - @include mobile { - flex-direction: column; - } -} - -h3 { - max-width: 30vw; - - @include mobile { - max-width: 50vw; - } -} - -.inner-wine-container { - display: flex; - flex-direction: row; - margin: auto; - width: 500px; - font-family: Arial; - margin-bottom: 30px; - - @include desktop { - justify-content: center; - } - - @include mobile { - width: auto; - } -} - -.right { - display: flex; - flex-direction: column; - margin-bottom: 150px; - margin-left: 50px; - - @include mobile { - margin-left: 2rem; - } -} - -a, -a:focus, -a:hover, -a:visited { - color: #333333; - font-family: Arial; - text-decoration: none; - font-weight: bold; -} - -.wine-link { - color: #333333; - font-family: Arial; - text-decoration: none; - font-weight: bold; - border-bottom: 1px solid $link-color; - width: fit-content; + justify-content: space-evenly; + align-items: flex-start; } diff --git a/src/components/VinlottisPage.vue b/src/components/VinlottisPage.vue index 638a78c..9581fe7 100644 --- a/src/components/VinlottisPage.vue +++ b/src/components/VinlottisPage.vue @@ -31,7 +31,7 @@ -
+
@@ -312,11 +312,11 @@ h1 { margin-bottom: 0; } -.container{ +.content-container { display: grid; grid-template-columns: repeat(12, 1fr); row-gap: 5em; - + .scroll-info { display: flex; align-items: center; diff --git a/src/components/VirtualLotteryRegistrationPage.vue b/src/components/VirtualLotteryRegistrationPage.vue index cff8fe8..24145a1 100644 --- a/src/components/VirtualLotteryRegistrationPage.vue +++ b/src/components/VirtualLotteryRegistrationPage.vue @@ -18,7 +18,7 @@

Vinnere

-
+
{{ winner.name }} {{ winner.phoneNumber }} Rød: {{ winner.red }} @@ -47,11 +47,11 @@ {{ attendee.name }} {{ attendee.phoneNumber }}
-
-
{{ attendee.red }}
-
{{ attendee.blue }}
-
{{ attendee.green }}
-
{{ attendee.yellow }}
+
+
{{ attendee.red }}
+
{{ attendee.blue }}
+
{{ attendee.green }}
+
{{ attendee.yellow }}
@@ -140,7 +140,7 @@ export default { blue: 0, green: 0, yellow: 0, - ballots: 0, + raffles: 0, randomColors: false, attendees: [], winners: [], @@ -197,7 +197,7 @@ export default { blue: this.blue, green: this.green, yellow: this.yellow, - ballots: this.ballots + raffles: this.raffles }); if (response == true) { @@ -229,7 +229,8 @@ export default { this.drawingWinner = true; let response = await getVirtualWinner(); - if (response) { + if (response.success) { + console.log("Winner:", response.winner); if (this.currentWinners < this.numberOfWinners) { this.countdown(); } else { @@ -245,7 +246,7 @@ export default { this.getAttendees(); } else { this.drawingWinner = false; - alert("Noe gikk galt under trekningen..!"); + alert("Noe gikk galt under trekningen..! " + response["message"]); } } }, @@ -354,7 +355,7 @@ hr { } } -.ballot-element { +.raffle-element { width: 140px; height: 150px; margin: 20px 0; @@ -378,19 +379,19 @@ hr { font-size: 1rem; } - &.green-ballot { + &.green-raffle { background-color: $light-green; } - &.blue-ballot { + &.blue-raffle { background-color: $light-blue; } - &.yellow-ballot { + &.yellow-raffle { background-color: $light-yellow; } - &.red-ballot { + &.red-raffle { background-color: $light-red; } } @@ -422,7 +423,7 @@ button { margin: 0 auto; & .name-and-phone, - & .ballots-container { + & .raffles-container { display: flex; justify-content: center; } @@ -431,7 +432,7 @@ button { flex-direction: column; } - & .ballots-container { + & .raffles-container { flex-direction: row; } } diff --git a/src/components/WinnerPage.vue b/src/components/WinnerPage.vue index 434f9d7..ffc928d 100644 --- a/src/components/WinnerPage.vue +++ b/src/components/WinnerPage.vue @@ -2,23 +2,18 @@

Gratulerer {{name}}!

-

Her er valgene for dagens lotteri, du har 10 minutter å velge etter du fikk SMS-en.

+

+ Her er valgene for dagens lotteri, du har 10 minutter å velge etter du fikk SMS-en. +

Finner ikke noen vinner her..

Du må vente på tur..

-
-
- + + +
@@ -29,7 +24,7 @@ @@ -98,6 +118,7 @@ ol { padding-left: 5px; } + .winner-icon { grid-row: 1; grid-column: 3; diff --git a/src/ui/RaffleGenerator.vue b/src/ui/RaffleGenerator.vue index 83ccadb..02e8c4a 100644 --- a/src/ui/RaffleGenerator.vue +++ b/src/ui/RaffleGenerator.vue @@ -1,5 +1,5 @@