Merge branch 'master' of github.com:KevinMidboe/vinlottis into feat/navigation-in-header
This commit is contained in:
@@ -1,21 +1,13 @@
|
|||||||
const express = require('express');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const router = express.Router();
|
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
mongoose.connect('mongodb://localhost:27017/vinlottis', {
|
mongoose.connect('mongodb://localhost:27017/vinlottis', {
|
||||||
useNewUrlParser: true
|
useNewUrlParser: true
|
||||||
})
|
})
|
||||||
|
|
||||||
const mustBeAuthenticated = require(path.join(
|
|
||||||
__dirname + '/../middleware/mustBeAuthenticated'
|
|
||||||
));
|
|
||||||
const config = require(path.join(__dirname + '/../config/defaults/lottery'));
|
|
||||||
|
|
||||||
const Highscore = require(path.join(__dirname + '/../schemas/Highscore'));
|
const Highscore = require(path.join(__dirname + '/../schemas/Highscore'));
|
||||||
const Wine = require(path.join(__dirname + '/../schemas/Wine'));
|
const Wine = require(path.join(__dirname + '/../schemas/Wine'));
|
||||||
|
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
const epochToDateString = date => new Date(parseInt(date)).toDateString();
|
const epochToDateString = date => new Date(parseInt(date)).toDateString();
|
||||||
|
|
||||||
@@ -62,7 +54,7 @@ const resolveWineReferences = listWithWines => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
router.route('/all').get((req, res) => {
|
const all = (req, res) => {
|
||||||
return Highscore.find()
|
return Highscore.find()
|
||||||
.then(highscore => getHighscoreByDates(highscore))
|
.then(highscore => getHighscoreByDates(highscore))
|
||||||
.then(groupedLotteries => groupedHighscoreToSortedList(groupedLotteries))
|
.then(groupedLotteries => groupedHighscoreToSortedList(groupedLotteries))
|
||||||
@@ -70,9 +62,19 @@ router.route('/all').get((req, res) => {
|
|||||||
message: "Lotteries by date!",
|
message: "Lotteries by date!",
|
||||||
lotteries
|
lotteries
|
||||||
}))
|
}))
|
||||||
})
|
}
|
||||||
|
|
||||||
router.route('/by-date/:date').get((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()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
const byEpochDate = (req, res) => {
|
||||||
const { date } = req.params;
|
const { date } = req.params;
|
||||||
const dateString = epochToDateString(date);
|
const dateString = epochToDateString(date);
|
||||||
|
|
||||||
@@ -92,10 +94,10 @@ router.route('/by-date/:date').get((req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
router.route("/by-name").get((req, res) => {
|
const byName = (req, res) => {
|
||||||
const { name } = req.query;
|
const { name } = req.params;
|
||||||
|
|
||||||
return Highscore.find({ name })
|
return Highscore.find({ name })
|
||||||
.then(async (highscore) => {
|
.then(async (highscore) => {
|
||||||
@@ -113,6 +115,11 @@ router.route("/by-name").get((req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = {
|
||||||
|
all,
|
||||||
|
latest,
|
||||||
|
byEpochDate,
|
||||||
|
byName
|
||||||
|
};
|
||||||
|
|||||||
129
api/message.js
129
api/message.js
@@ -1,92 +1,109 @@
|
|||||||
const request = require("request");
|
const https = require("https");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const config = require(path.join(__dirname + "/../config/defaults/lottery"));
|
const config = require(path.join(__dirname + "/../config/defaults/lottery"));
|
||||||
|
|
||||||
async function sendMessage(winnerObject) {
|
async function sendWineSelectMessage(winnerObject) {
|
||||||
winnerObject.timestamp_sent = new Date().getTime();
|
winnerObject.timestamp_sent = new Date().getTime();
|
||||||
winnerObject.timestamp_limit = new Date().getTime() * 600000;
|
winnerObject.timestamp_limit = new Date().getTime() * 600000;
|
||||||
await winnerObject.save();
|
await winnerObject.save();
|
||||||
|
|
||||||
let url = new URL(`/#/winner/${winnerObject.id}`, "https://lottis.vin");
|
let url = new URL(`/#/winner/${winnerObject.id}`, "https://lottis.vin");
|
||||||
|
|
||||||
await sendMessageToUser(
|
return sendMessageToUser(
|
||||||
winnerObject.phoneNumber,
|
winnerObject.phoneNumber,
|
||||||
`Gratulerer som heldig vinner av vinlotteriet ${winnerObject.name}! Her er linken for å velge hva slags vin du vil ha, du har 10 minutter på å velge ut noe før du blir lagt bakerst i køen. ${url.href}. (Hvis den siden kommer opp som tom må du prøve å refreshe siden noen ganger.)`
|
`Gratulerer som heldig vinner av vinlotteriet ${winnerObject.name}! Her er linken for å velge hva slags vin du vil ha, du har 10 minutter på å velge ut noe før du blir lagt bakerst i køen. ${url.href}. (Hvis den siden kommer opp som tom må du prøve å refreshe siden noen ganger.)`
|
||||||
);
|
)
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendWonWineMessage(winnerObject, wineObject) {
|
async function sendLastWinnerMessage(winnerObject, wineObject) {
|
||||||
console.log(
|
console.log(`User ${winnerObject.id} is only one left, chosing wine for him/her.`);
|
||||||
`User ${winnerObject.id} is only one left, chosing wine for him/her.`
|
|
||||||
);
|
|
||||||
winnerObject.timestamp_sent = new Date().getTime();
|
winnerObject.timestamp_sent = new Date().getTime();
|
||||||
winnerObject.timestamp_limit = new Date().getTime();
|
winnerObject.timestamp_limit = new Date().getTime();
|
||||||
await winnerObject.save();
|
await winnerObject.save();
|
||||||
|
|
||||||
await sendMessageToUser(
|
return sendMessageToUser(
|
||||||
winnerObject.phoneNumber,
|
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}, og vil få nærmere info om hvordan/hvor du kan hente vinen snarest. Ha en ellers fin helg!`
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendMessageTooLate(winnerObject) {
|
async function sendWineSelectMessageTooLate(winnerObject) {
|
||||||
await sendMessageToUser(
|
return sendMessageToUser(
|
||||||
winnerObject.phoneNumber,
|
winnerObject.phoneNumber,
|
||||||
`Hei ${winnerObject.name}, du har dessverre brukt mer enn 10 minutter på å velge premie og blir derfor puttet bakerst i køen. Du vil få en ny SMS når det er din tur igjen.`
|
`Hei ${winnerObject.name}, du har dessverre brukt mer enn 10 minutter på å velge premie og blir derfor puttet bakerst i køen. Du vil få en ny SMS når det er din tur igjen.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendMessageToUser(phoneNumber, message) {
|
async function sendMessageToUser(phoneNumber, message) {
|
||||||
try {
|
console.log(`Attempting to send message to ${ phoneNumber }.`)
|
||||||
request.post(
|
|
||||||
{
|
const body = {
|
||||||
url: `https://gatewayapi.com/rest/mtsms?token=${config.gatewayToken}`,
|
sender: "Vinlottis",
|
||||||
json: true,
|
message: message,
|
||||||
body: {
|
recipients: [{ msisdn: `47${ phoneNumber }`}]
|
||||||
sender: "Vinlottis",
|
};
|
||||||
message: message,
|
|
||||||
recipients: [{ msisdn: `47${phoneNumber}` }]
|
return gatewayRequest(body);
|
||||||
}
|
|
||||||
},
|
|
||||||
function(err, r, body) {
|
|
||||||
console.log(err ? err : body);
|
|
||||||
if(err) {
|
|
||||||
console.log(phoneNumber, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch(e) {
|
|
||||||
console.log(phoneNumber, message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendUpdate(winners) {
|
|
||||||
|
async function sendInitialMessageToWinners(winners) {
|
||||||
let numbers = [];
|
let numbers = [];
|
||||||
for (let i = 0; i < winners.length; i++) {
|
for (let i = 0; i < winners.length; i++) {
|
||||||
numbers.push({ msisdn: `47${winners[i].phoneNumber}` });
|
numbers.push({ msisdn: `47${winners[i].phoneNumber}` });
|
||||||
}
|
}
|
||||||
request.post(
|
|
||||||
{
|
const body = {
|
||||||
url: `https://gatewayapi.com/rest/mtsms?token=${config.gatewayToken}`,
|
sender: "Vinlottis",
|
||||||
json: true,
|
message:
|
||||||
body: {
|
"Gratulerer som vinner av vinlottisen! Du vil snart få en SMS med oppdatering om hvordan gangen går!",
|
||||||
sender: "Vinlottis",
|
recipients: numbers
|
||||||
message:
|
}
|
||||||
"Gratulerer som vinner av vinlottisen! Du vil snart få en SMS med oppdatering om hvordan gangen går!",
|
|
||||||
recipients: numbers
|
return gatewayRequest(body);
|
||||||
}
|
|
||||||
},
|
|
||||||
function(err, r, body) {
|
|
||||||
console.log(err ? err : body);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.sendUpdate = sendUpdate;
|
async function gatewayRequest(body) {
|
||||||
module.exports.sendMessage = sendMessage;
|
return new Promise((resolve, reject) => {
|
||||||
module.exports.sendMessageTooLate = sendMessageTooLate;
|
const options = {
|
||||||
module.exports.sendWonWineMessage = sendWonWineMessage;
|
hostname: "gatewayapi.com",
|
||||||
|
post: 443,
|
||||||
|
path: `/rest/mtsms?token=${ config.gatewayToken }`,
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
|
console.log(`statusCode: ${ res.statusCode }`);
|
||||||
|
console.log(`statusMessage: ${ res.statusMessage }`);
|
||||||
|
|
||||||
|
res.setEncoding('utf8');
|
||||||
|
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
res.on("data", (d) => resolve(JSON.parse(d)));
|
||||||
|
} else {
|
||||||
|
res.on("data", (data) => {
|
||||||
|
data = JSON.parse(data);
|
||||||
|
return reject('Gateway error: ' + data['message'] || data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
req.on("error", (error) => {
|
||||||
|
console.error(`Error from sms service: ${ error }`);
|
||||||
|
reject(`Error from sms service: ${ error }`);
|
||||||
|
})
|
||||||
|
|
||||||
|
req.write(JSON.stringify(body));
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sendWineSelectMessage,
|
||||||
|
sendLastWinnerMessage,
|
||||||
|
sendWineSelectMessageTooLate,
|
||||||
|
sendInitialMessageToWinners
|
||||||
|
}
|
||||||
|
|||||||
69
api/request.js
Normal file
69
api/request.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const path = require("path");
|
||||||
|
const RequestedWine = require(path.join(
|
||||||
|
__dirname + "/../schemas/RequestedWine"
|
||||||
|
));
|
||||||
|
const Wine = require(path.join(
|
||||||
|
__dirname + "/../schemas/Wine"
|
||||||
|
));
|
||||||
|
|
||||||
|
const deleteRequestedWineById = async (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
if(id == null){
|
||||||
|
return res.json({
|
||||||
|
message: "Id er ikke definert",
|
||||||
|
success: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await RequestedWine.deleteOne({wineId: id})
|
||||||
|
return res.json({
|
||||||
|
message: `Slettet vin med id: ${id}`,
|
||||||
|
success: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllRequestedWines = async (req, res) => {
|
||||||
|
const allWines = await RequestedWine.find({}).populate("wine");
|
||||||
|
|
||||||
|
return res.json(allWines);
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestNewWine = async (req, res) => {
|
||||||
|
const {wine} = req.body
|
||||||
|
|
||||||
|
let thisWineIsLOKO = await Wine.findOne({id: wine.id})
|
||||||
|
|
||||||
|
if(thisWineIsLOKO == undefined){
|
||||||
|
thisWineIsLOKO = new Wine({
|
||||||
|
name: wine.name,
|
||||||
|
vivinoLink: wine.vivinoLink,
|
||||||
|
rating: null,
|
||||||
|
occurences: null,
|
||||||
|
image: wine.image,
|
||||||
|
id: wine.id
|
||||||
|
});
|
||||||
|
await thisWineIsLOKO.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
let requestedWine = await RequestedWine.findOne({ "wineId": wine.id})
|
||||||
|
|
||||||
|
if(requestedWine == undefined){
|
||||||
|
requestedWine = new RequestedWine({
|
||||||
|
count: 1,
|
||||||
|
wineId: wine.id,
|
||||||
|
wine: thisWineIsLOKO
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
requestedWine.count += 1;
|
||||||
|
}
|
||||||
|
await requestedWine.save()
|
||||||
|
|
||||||
|
return res.send(requestedWine);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
requestNewWine,
|
||||||
|
getAllRequestedWines,
|
||||||
|
deleteRequestedWineById
|
||||||
|
};
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
const express = require("express");
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const router = express.Router();
|
|
||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
||||||
useNewUrlParser: true
|
useNewUrlParser: true
|
||||||
@@ -13,23 +11,19 @@ const PreLotteryWine = require(path.join(
|
|||||||
__dirname + "/../schemas/PreLotteryWine"
|
__dirname + "/../schemas/PreLotteryWine"
|
||||||
));
|
));
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
const prelotteryWines = async (req, res) => {
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
router.route("/wines/prelottery").get(async (req, res) => {
|
|
||||||
let wines = await PreLotteryWine.find();
|
let wines = await PreLotteryWine.find();
|
||||||
res.json(wines);
|
return res.json(wines);
|
||||||
});
|
};
|
||||||
|
|
||||||
router.route("/purchase/statistics").get(async (req, res) => {
|
const allPurchase = async (req, res) => {
|
||||||
let purchases = await Purchase.find()
|
let purchases = await Purchase.find()
|
||||||
.populate("wines")
|
.populate("wines")
|
||||||
.sort({ date: 1 });
|
.sort({ date: 1 });
|
||||||
res.json(purchases);
|
return res.json(purchases);
|
||||||
});
|
};
|
||||||
|
|
||||||
router.route("/purchase/statistics/color").get(async (req, res) => {
|
const purchaseByColor = async (req, res) => {
|
||||||
const countColor = await Purchase.find();
|
const countColor = await Purchase.find();
|
||||||
let red = 0;
|
let red = 0;
|
||||||
let blue = 0;
|
let blue = 0;
|
||||||
@@ -75,7 +69,7 @@ router.route("/purchase/statistics/color").get(async (req, res) => {
|
|||||||
|
|
||||||
const total = red + yellow + blue + green;
|
const total = red + yellow + blue + green;
|
||||||
|
|
||||||
res.json({
|
return res.json({
|
||||||
red: {
|
red: {
|
||||||
total: red,
|
total: red,
|
||||||
win: redWin
|
win: redWin
|
||||||
@@ -95,21 +89,21 @@ router.route("/purchase/statistics/color").get(async (req, res) => {
|
|||||||
stolen: stolen,
|
stolen: stolen,
|
||||||
total: total
|
total: total
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
router.route("/highscore/statistics").get(async (req, res) => {
|
const highscore = async (req, res) => {
|
||||||
const highscore = await Highscore.find().populate("wins.wine");
|
const highscore = await Highscore.find().populate("wins.wine");
|
||||||
|
|
||||||
res.json(highscore);
|
return res.json(highscore);
|
||||||
});
|
};
|
||||||
|
|
||||||
router.route("/wines/statistics").get(async (req, res) => {
|
const allWines = async (req, res) => {
|
||||||
const wines = await Wine.find();
|
const wines = await Wine.find();
|
||||||
|
|
||||||
res.json(wines);
|
return res.json(wines);
|
||||||
});
|
};
|
||||||
|
|
||||||
router.route("/wines/statistics/overall").get(async (req, res) => {
|
const allWinesSummary = async (req, res) => {
|
||||||
const highscore = await Highscore.find().populate("wins.wine");
|
const highscore = await Highscore.find().populate("wins.wine");
|
||||||
let wines = {};
|
let wines = {};
|
||||||
|
|
||||||
@@ -149,7 +143,14 @@ router.route("/wines/statistics/overall").get(async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(Object.values(wines));
|
return res.json(Object.values(wines));
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = {
|
||||||
|
prelotteryWines,
|
||||||
|
allPurchase,
|
||||||
|
purchaseByColor,
|
||||||
|
highscore,
|
||||||
|
allWines,
|
||||||
|
allWinesSummary
|
||||||
|
};
|
||||||
|
|||||||
70
api/router.js
Normal file
70
api/router.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
const mustBeAuthenticated = require(__dirname + "/../middleware/mustBeAuthenticated");
|
||||||
|
const setAdminHeaderIfAuthenticated = require(__dirname + "/../middleware/setAdminHeaderIfAuthenticated");
|
||||||
|
|
||||||
|
const update = require(path.join(__dirname + "/update"));
|
||||||
|
const retrieve = require(path.join(__dirname + "/retrieve"));
|
||||||
|
const request = require(path.join(__dirname + "/request"));
|
||||||
|
const subscriptionApi = require(path.join(__dirname + "/subscriptions"));
|
||||||
|
const loginApi = require(path.join(__dirname + "/login"));
|
||||||
|
const wineinfo = require(path.join(__dirname + "/wineinfo"));
|
||||||
|
const virtualApi = require(path.join(__dirname + "/virtualLottery"));
|
||||||
|
const virtualRegistrationApi = require(path.join(
|
||||||
|
__dirname + "/virtualRegistration"
|
||||||
|
));
|
||||||
|
const lottery = require(path.join(__dirname + "/lottery"));
|
||||||
|
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get("/wineinfo/search", wineinfo.wineSearch);
|
||||||
|
|
||||||
|
router.get("/request/all", setAdminHeaderIfAuthenticated, request.getAllRequestedWines);
|
||||||
|
router.post("/request/new-wine", request.requestNewWine);
|
||||||
|
router.delete("/request/:id", request.deleteRequestedWineById);
|
||||||
|
|
||||||
|
router.get("/wineinfo/schema", mustBeAuthenticated, update.schema);
|
||||||
|
router.get("/wineinfo/:ean", wineinfo.byEAN);
|
||||||
|
|
||||||
|
router.post("/log/wines", mustBeAuthenticated, update.submitWines);
|
||||||
|
router.post("/lottery", update.submitLottery);
|
||||||
|
router.post("/lottery/wines", update.submitWinesToLottery);
|
||||||
|
// router.delete("/lottery/wine/:id", update.deleteWineFromLottery);
|
||||||
|
router.post("/lottery/winners", update.submitWinnersToLottery);
|
||||||
|
|
||||||
|
router.get("/wines/prelottery", retrieve.prelotteryWines);
|
||||||
|
router.get("/purchase/statistics", retrieve.allPurchase);
|
||||||
|
router.get("/purchase/statistics/color", retrieve.purchaseByColor);
|
||||||
|
router.get("/highscore/statistics", retrieve.highscore)
|
||||||
|
router.get("/wines/statistics", retrieve.allWines);
|
||||||
|
router.get("/wines/statistics/overall", retrieve.allWinesSummary);
|
||||||
|
|
||||||
|
router.get("/lottery/all", lottery.all);
|
||||||
|
router.get("/lottery/latest", lottery.latest);
|
||||||
|
router.get("/lottery/by-date/:date", lottery.byEpochDate);
|
||||||
|
router.get("/lottery/by-name/:name", lottery.byName);
|
||||||
|
|
||||||
|
router.delete('/virtual/winner/all', mustBeAuthenticated, virtualApi.deleteWinners);
|
||||||
|
router.delete('/virtual/attendee/all', mustBeAuthenticated, virtualApi.deleteAttendees);
|
||||||
|
router.get('/virtual/winner/draw', virtualApi.drawWinner);
|
||||||
|
router.get('/virtual/winner/all', virtualApi.winners);
|
||||||
|
router.get('/virtual/winner/all/secure', mustBeAuthenticated, virtualApi.winnersSecure);
|
||||||
|
router.post('/virtual/finish', mustBeAuthenticated, virtualApi.finish);
|
||||||
|
router.get('/virtual/attendee/all', virtualApi.attendees);
|
||||||
|
router.get('/virtual/attendee/all/secure', mustBeAuthenticated, virtualApi.attendeesSecure);
|
||||||
|
router.post('/virtual/attendee/add', mustBeAuthenticated, virtualApi.addAttendee);
|
||||||
|
|
||||||
|
router.post('/winner/notify/:id', virtualRegistrationApi.sendNotificationToWinnerById);
|
||||||
|
router.get('/winner/:id', virtualRegistrationApi.getWinesToWinnerById);
|
||||||
|
router.post('/winner/:id', virtualRegistrationApi.registerWinnerSelection);
|
||||||
|
|
||||||
|
// router.use("/api/", updateApi);
|
||||||
|
// router.use("/api/", retrieveApi);
|
||||||
|
// router.use("/api/", wineinfoApi);
|
||||||
|
// router.use("/api/lottery", lottery);
|
||||||
|
// router.use("/virtual-registration/", virtualRegistrationApi);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
142
api/update.js
142
api/update.js
@@ -1,29 +1,21 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const router = express.Router();
|
|
||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
||||||
useNewUrlParser: true
|
useNewUrlParser: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const sub = require(path.join(__dirname + "/../api/subscriptions"));
|
const sub = require(path.join(__dirname + "/../api/subscriptions"));
|
||||||
const mustBeAuthenticated = require(path.join(
|
|
||||||
__dirname + "/../middleware/mustBeAuthenticated"
|
|
||||||
));
|
|
||||||
|
|
||||||
const _wineFunctions = require(path.join(__dirname + "/../api/wine"));
|
const _wineFunctions = require(path.join(__dirname + "/../api/wine"));
|
||||||
const _personFunctions = require(path.join(__dirname + "/../api/person"));
|
const _personFunctions = require(path.join(__dirname + "/../api/person"));
|
||||||
const Subscription = require(path.join(__dirname + "/../schemas/Subscription"));
|
const Subscription = require(path.join(__dirname + "/../schemas/Subscription"));
|
||||||
const Purchase = require(path.join(__dirname + "/../schemas/Purchase"));
|
const Lottery = require(path.join(__dirname + "/../schemas/Purchase"));
|
||||||
const PreLotteryWine = require(path.join(
|
const PreLotteryWine = require(path.join(
|
||||||
__dirname + "/../schemas/PreLotteryWine"
|
__dirname + "/../schemas/PreLotteryWine"
|
||||||
));
|
));
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
const submitWines = async (req, res) => {
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
router.route("/log/wines").post(mustBeAuthenticated, async (req, res) => {
|
|
||||||
const wines = req.body;
|
const wines = req.body;
|
||||||
for (let i = 0; i < wines.length; i++) {
|
for (let i = 0; i < wines.length; i++) {
|
||||||
let wine = wines[i];
|
let wine = wines[i];
|
||||||
@@ -40,69 +32,115 @@ router.route("/log/wines").post(mustBeAuthenticated, async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let subs = await Subscription.find();
|
let subs = await Subscription.find();
|
||||||
|
console.log("Sending new wines w/ push notification to all subscribers.")
|
||||||
for (let i = 0; i < subs.length; i++) {
|
for (let i = 0; i < subs.length; i++) {
|
||||||
let subscription = subs[i]; //get subscription from your databse here.
|
let subscription = subs[i]; //get subscription from your databse here.
|
||||||
|
|
||||||
const message = JSON.stringify({
|
const message = JSON.stringify({
|
||||||
message: "Dagens vin er lagt til, se den på lottis.vin/dagens!",
|
message: "Dagens vin er lagt til, se den på lottis.vin/dagens!",
|
||||||
title: "Ny vin!",
|
title: "Ny vin!",
|
||||||
link: "/#/dagens"
|
link: "/#/dagens"
|
||||||
});
|
});
|
||||||
sub.sendNotification(subscription, message);
|
|
||||||
|
try {
|
||||||
|
sub.sendNotification(subscription, message);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error when trying to send push notification to subscriber.");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.send(true);
|
return res.send({
|
||||||
});
|
message: "Submitted and notified push subscribers of new wines!",
|
||||||
|
success: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
router.route("/log/schema").get(mustBeAuthenticated, async (req, res) => {
|
const schema = async (req, res) => {
|
||||||
let schema = { ...PreLotteryWine.schema.obj };
|
let schema = { ...PreLotteryWine.schema.obj };
|
||||||
let nulledSchema = Object.keys(schema).reduce((accumulator, current) => {
|
let nulledSchema = Object.keys(schema).reduce((accumulator, current) => {
|
||||||
accumulator[current] = "";
|
accumulator[current] = "";
|
||||||
return accumulator;
|
return accumulator
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
res.send(nulledSchema);
|
return res.send(nulledSchema);
|
||||||
});
|
}
|
||||||
|
|
||||||
router.route("/log").post(mustBeAuthenticated, async (req, res) => {
|
// TODO IMPLEMENT WITH FRONTEND (unused)
|
||||||
await PreLotteryWine.deleteMany();
|
const submitWinesToLottery = async (req, res) => {
|
||||||
|
const { lottery } = req.body;
|
||||||
|
const { date, wines } = lottery;
|
||||||
|
const wineObjects = await Promise.all(wines.map(async (wine) => await _wineFunctions.findSaveWine(wine)))
|
||||||
|
|
||||||
const purchaseBody = req.body.purchase;
|
return Lottery.findOneAndUpdate({ date: date }, {
|
||||||
const winnersBody = req.body.winners;
|
date: date,
|
||||||
|
wines: wineObjects
|
||||||
|
}, {
|
||||||
|
upsert: true
|
||||||
|
}).then(_ => res.send(true))
|
||||||
|
.catch(err => res.status(500).send({ message: 'Unexpected error while updating/saving wine to lottery.',
|
||||||
|
success: false,
|
||||||
|
exception: err.message }));
|
||||||
|
}
|
||||||
|
|
||||||
const date = purchaseBody.date;
|
/**
|
||||||
const blue = purchaseBody.blue;
|
* @apiParam (Request body) {Array} winners List of winners
|
||||||
const red = purchaseBody.red;
|
*/
|
||||||
const yellow = purchaseBody.yellow;
|
const submitWinnersToLottery = async (req, res) => {
|
||||||
const green = purchaseBody.green;
|
const { lottery } = req.body;
|
||||||
|
const { winners, date } = lottery;
|
||||||
|
|
||||||
const bought = purchaseBody.bought;
|
for (let i = 0; i < winners.length; i++) {
|
||||||
const stolen = purchaseBody.stolen;
|
let currentWinner = winners[i];
|
||||||
|
let wonWine = await _wineFunctions.findSaveWine(currentWinner.wine); // TODO rename to findAndSaveWineToLottery
|
||||||
const winesThisDate = [];
|
await _personFunctions.findSavePerson(currentWinner, wonWine, date); // TODO rename to findAndSaveWineToPerson
|
||||||
|
|
||||||
for (let i = 0; i < winnersBody.length; i++) {
|
|
||||||
let currentWinner = winnersBody[i];
|
|
||||||
|
|
||||||
let wonWine = await _wineFunctions.findSaveWine(currentWinner);
|
|
||||||
winesThisDate.push(wonWine);
|
|
||||||
|
|
||||||
await _personFunctions.findSavePerson(currentWinner, wonWine, date);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let purchase = new Purchase({
|
return res.json(true);
|
||||||
date: date,
|
}
|
||||||
blue: blue,
|
|
||||||
yellow: yellow,
|
|
||||||
red: red,
|
|
||||||
green: green,
|
|
||||||
wines: winesThisDate,
|
|
||||||
bought: bought,
|
|
||||||
stolen: stolen
|
|
||||||
});
|
|
||||||
|
|
||||||
await purchase.save();
|
/**
|
||||||
|
* @apiParam (Request body) {Date} date Date of lottery
|
||||||
|
* @apiParam (Request body) {Number} blue Number of blue tickets
|
||||||
|
* @apiParam (Request body) {Number} red Number of red tickets
|
||||||
|
* @apiParam (Request body) {Number} green Number of green tickets
|
||||||
|
* @apiParam (Request body) {Number} yellow Number of yellow tickets
|
||||||
|
* @apiParam (Request body) {Number} bought Number of tickets bought
|
||||||
|
* @apiParam (Request body) {Number} stolen Number of tickets stolen
|
||||||
|
*/
|
||||||
|
const submitLottery = async (req, res) => {
|
||||||
|
const { lottery } = req.body
|
||||||
|
|
||||||
res.send(true);
|
const { date,
|
||||||
});
|
blue,
|
||||||
|
red,
|
||||||
|
yellow,
|
||||||
|
green,
|
||||||
|
bought,
|
||||||
|
stolen } = lottery;
|
||||||
|
|
||||||
module.exports = router;
|
return Lottery.findOneAndUpdate({ date: date }, {
|
||||||
|
date: date,
|
||||||
|
blue: blue,
|
||||||
|
yellow: yellow,
|
||||||
|
red: red,
|
||||||
|
green: green,
|
||||||
|
bought: bought,
|
||||||
|
stolen: stolen
|
||||||
|
}, {
|
||||||
|
upsert: true
|
||||||
|
}).then(_ => res.send(true))
|
||||||
|
.catch(err => res.status(500).send({ message: 'Unexpected error while updating/saving lottery.',
|
||||||
|
success: false,
|
||||||
|
exception: err.message }));
|
||||||
|
|
||||||
|
return res.send(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
submitWines,
|
||||||
|
schema,
|
||||||
|
submitLottery,
|
||||||
|
submitWinnersToLottery,
|
||||||
|
submitWinesToLottery
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,44 +1,16 @@
|
|||||||
const express = require("express");
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const router = express.Router();
|
|
||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const mongoose = require("mongoose");
|
|
||||||
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
|
||||||
useNewUrlParser: true
|
|
||||||
});
|
|
||||||
let io;
|
|
||||||
const mustBeAuthenticated = require(path.join(
|
|
||||||
__dirname + "/../middleware/mustBeAuthenticated"
|
|
||||||
));
|
|
||||||
const config = require(path.join(__dirname + "/../config/defaults/lottery"));
|
const config = require(path.join(__dirname + "/../config/defaults/lottery"));
|
||||||
|
const Message = require(path.join(__dirname + "/message"));
|
||||||
|
const { findAndNotifyNextWinner } = require(path.join(__dirname + "/virtualRegistration"));
|
||||||
|
|
||||||
const Attendee = require(path.join(__dirname + "/../schemas/Attendee"));
|
const Attendee = require(path.join(__dirname + "/../schemas/Attendee"));
|
||||||
const VirtualWinner = require(path.join(
|
const VirtualWinner = require(path.join(__dirname + "/../schemas/VirtualWinner"));
|
||||||
__dirname + "/../schemas/VirtualWinner"
|
const PreLotteryWine = require(path.join(__dirname + "/../schemas/PreLotteryWine"));
|
||||||
));
|
|
||||||
const PreLotteryWine = require(path.join(
|
|
||||||
__dirname + "/../schemas/PreLotteryWine"
|
|
||||||
));
|
|
||||||
|
|
||||||
const Message = require(path.join(__dirname + "/../api/message"));
|
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
const winners = async (req, res) => {
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
router.route("/winners").delete(mustBeAuthenticated, async (req, res) => {
|
|
||||||
await VirtualWinner.deleteMany();
|
|
||||||
io.emit("refresh_data", {});
|
|
||||||
res.json(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
router.route("/attendees").delete(mustBeAuthenticated, async (req, res) => {
|
|
||||||
await Attendee.deleteMany();
|
|
||||||
io.emit("refresh_data", {});
|
|
||||||
res.json(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
router.route("/winners").get(async (req, res) => {
|
|
||||||
let winners = await VirtualWinner.find();
|
let winners = await VirtualWinner.find();
|
||||||
let winnersRedacted = [];
|
let winnersRedacted = [];
|
||||||
let winner;
|
let winner;
|
||||||
@@ -50,19 +22,82 @@ router.route("/winners").get(async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
res.json(winnersRedacted);
|
res.json(winnersRedacted);
|
||||||
});
|
};
|
||||||
|
|
||||||
router.route("/winners/secure").get(mustBeAuthenticated, async (req, res) => {
|
const winnersSecure = async (req, res) => {
|
||||||
let winners = await VirtualWinner.find();
|
let winners = await VirtualWinner.find();
|
||||||
|
|
||||||
res.json(winners);
|
return res.json(winners);
|
||||||
});
|
};
|
||||||
|
|
||||||
router.route("/winner").get(mustBeAuthenticated, async (req, res) => {
|
const deleteWinners = async (req, res) => {
|
||||||
|
await VirtualWinner.deleteMany();
|
||||||
|
var io = req.app.get('socketio');
|
||||||
|
io.emit("refresh_data", {});
|
||||||
|
return res.json(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const attendees = 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
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return res.json(attendeesRedacted);
|
||||||
|
};
|
||||||
|
|
||||||
|
const attendeesSecure = async (req, res) => {
|
||||||
|
let attendees = await Attendee.find();
|
||||||
|
|
||||||
|
return res.json(attendees);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addAttendee = async (req, res) => {
|
||||||
|
const attendee = req.body;
|
||||||
|
const { red, blue, yellow, green } = attendee;
|
||||||
|
|
||||||
|
let newAttendee = new Attendee({
|
||||||
|
name: attendee.name,
|
||||||
|
red,
|
||||||
|
blue,
|
||||||
|
green,
|
||||||
|
yellow,
|
||||||
|
phoneNumber: attendee.phoneNumber,
|
||||||
|
winner: false
|
||||||
|
});
|
||||||
|
await newAttendee.save();
|
||||||
|
|
||||||
|
|
||||||
|
var io = req.app.get('socketio');
|
||||||
|
io.emit("new_attendee", {});
|
||||||
|
|
||||||
|
return res.send(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteAttendees = async (req, res) => {
|
||||||
|
await Attendee.deleteMany();
|
||||||
|
var io = req.app.get('socketio');
|
||||||
|
io.emit("refresh_data", {});
|
||||||
|
return res.json(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const drawWinner = async (req, res) => {
|
||||||
let allContestants = await Attendee.find({ winner: false });
|
let allContestants = await Attendee.find({ winner: false });
|
||||||
|
|
||||||
if (allContestants.length == 0) {
|
if (allContestants.length == 0) {
|
||||||
res.json(false);
|
return res.json({
|
||||||
return;
|
success: false,
|
||||||
|
message: "No attendees left that have not won."
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let ballotColors = [];
|
let ballotColors = [];
|
||||||
for (let i = 0; i < allContestants.length; i++) {
|
for (let i = 0; i < allContestants.length; i++) {
|
||||||
@@ -131,6 +166,7 @@ router.route("/winner").get(mustBeAuthenticated, async (req, res) => {
|
|||||||
Math.floor(Math.random() * attendeeListDemocratic.length)
|
Math.floor(Math.random() * attendeeListDemocratic.length)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var io = req.app.get('socketio');
|
||||||
io.emit("winner", { color: colorToChooseFrom, name: winner.name });
|
io.emit("winner", { color: colorToChooseFrom, name: winner.name });
|
||||||
|
|
||||||
let newWinnerElement = new VirtualWinner({
|
let newWinnerElement = new VirtualWinner({
|
||||||
@@ -151,8 +187,40 @@ router.route("/winner").get(mustBeAuthenticated, async (req, res) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await newWinnerElement.save();
|
await newWinnerElement.save();
|
||||||
res.json(winner);
|
return res.json(winner);
|
||||||
});
|
};
|
||||||
|
|
||||||
|
const finish = async (req, res) => {
|
||||||
|
if (!config.gatewayToken) {
|
||||||
|
return res.json({
|
||||||
|
message: "Missing api token for sms gateway.",
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let winners = await VirtualWinner.find({ timestamp_sent: undefined }).sort({
|
||||||
|
timestamp_drawn: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
if (winners.length == 0) {
|
||||||
|
return res.json({
|
||||||
|
message: "No winners to draw from.",
|
||||||
|
success: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Message.sendInitialMessageToWinners(winners.slice(1));
|
||||||
|
|
||||||
|
return findAndNotifyNextWinner()
|
||||||
|
.then(() => res.json({
|
||||||
|
success: true,
|
||||||
|
message: "Sent wine select message to first winner and update message to rest of winners."
|
||||||
|
}))
|
||||||
|
.catch(error => res.json({
|
||||||
|
message: error["message"] || "Unable to send message to first winner.",
|
||||||
|
success: false
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
const genRandomString = function(length) {
|
const genRandomString = function(length) {
|
||||||
return crypto
|
return crypto
|
||||||
@@ -168,87 +236,6 @@ const sha512 = function(password, salt) {
|
|||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
router.route("/finish").get(mustBeAuthenticated, async (req, res) => {
|
|
||||||
if (!config.gatewayToken) {
|
|
||||||
res.json(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let winners = await VirtualWinner.find({ timestamp_sent: undefined }).sort({
|
|
||||||
timestamp_drawn: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
if (winners.length == 0) {
|
|
||||||
res.json(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let firstWinner = winners[0];
|
|
||||||
messageSent = false;
|
|
||||||
try {
|
|
||||||
let messageSent = await Message.sendMessage(firstWinner);
|
|
||||||
Message.sendUpdate(winners.slice(1));
|
|
||||||
if (!messageSent) {
|
|
||||||
res.json(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
res.json(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstWinner.timestamp_sent = new Date().getTime();
|
|
||||||
firstWinner.timestamp_limit = new Date().getTime() + 600000;
|
|
||||||
|
|
||||||
await firstWinner.save();
|
|
||||||
startTimeout(firstWinner.id);
|
|
||||||
res.json(true);
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
|
||||||
const { red, blue, yellow, green } = attendee;
|
|
||||||
|
|
||||||
let newAttendee = new Attendee({
|
|
||||||
name: attendee.name,
|
|
||||||
red,
|
|
||||||
blue,
|
|
||||||
green,
|
|
||||||
yellow,
|
|
||||||
phoneNumber: attendee.phoneNumber,
|
|
||||||
winner: false
|
|
||||||
});
|
|
||||||
await newAttendee.save();
|
|
||||||
|
|
||||||
io.emit("new_attendee", {});
|
|
||||||
|
|
||||||
res.send(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
function shuffle(array) {
|
function shuffle(array) {
|
||||||
let currentIndex = array.length,
|
let currentIndex = array.length,
|
||||||
temporaryValue,
|
temporaryValue,
|
||||||
@@ -269,43 +256,15 @@ function shuffle(array) {
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
function startTimeout(id) {
|
deleteWinners,
|
||||||
console.log(`Starting timeout for user ${id}.`);
|
deleteAttendees,
|
||||||
setTimeout(async () => {
|
winners,
|
||||||
let virtualWinner = await VirtualWinner.findOne({ id: id });
|
winnersSecure,
|
||||||
if (!virtualWinner) {
|
drawWinner,
|
||||||
console.log(
|
finish,
|
||||||
`Timeout done for user ${id}, but user has already sent data.`
|
attendees,
|
||||||
);
|
attendeesSecure,
|
||||||
return;
|
addAttendee
|
||||||
}
|
|
||||||
console.log(`Timeout done for user ${id}, sending update to user.`);
|
|
||||||
|
|
||||||
Message.sendMessageTooLate(virtualWinner);
|
|
||||||
|
|
||||||
virtualWinner.timestamp_drawn = new Date().getTime();
|
|
||||||
virtualWinner.timestamp_limit = null;
|
|
||||||
virtualWinner.timestamp_sent = null;
|
|
||||||
|
|
||||||
await virtualWinner.save();
|
|
||||||
|
|
||||||
let prelotteryWine = await PreLotteryWine.find();
|
|
||||||
let nextWinner = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
|
|
||||||
if (nextWinner.length == 1 && prelotteryWine.length == 1) {
|
|
||||||
chooseForUser(nextWinner[0], prelotteryWine[0]);
|
|
||||||
} else {
|
|
||||||
nextWinner[0].timestamp_sent = new Date().getTime();
|
|
||||||
nextWinner[0].timestamp_limit = new Date().getTime() + 600000;
|
|
||||||
await nextWinner[0].save();
|
|
||||||
Message.sendMessage(nextWinner[0]);
|
|
||||||
startTimeout(nextWinner[0].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, 600000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(_io) {
|
|
||||||
io = _io;
|
|
||||||
return router;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,11 +1,4 @@
|
|||||||
const express = require("express");
|
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const router = express.Router();
|
|
||||||
const mongoose = require("mongoose");
|
|
||||||
|
|
||||||
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
|
||||||
useNewUrlParser: true
|
|
||||||
});
|
|
||||||
|
|
||||||
const _wineFunctions = require(path.join(__dirname + "/../api/wine"));
|
const _wineFunctions = require(path.join(__dirname + "/../api/wine"));
|
||||||
const _personFunctions = require(path.join(__dirname + "/../api/person"));
|
const _personFunctions = require(path.join(__dirname + "/../api/person"));
|
||||||
@@ -17,72 +10,70 @@ const PreLotteryWine = require(path.join(
|
|||||||
__dirname + "/../schemas/PreLotteryWine"
|
__dirname + "/../schemas/PreLotteryWine"
|
||||||
));
|
));
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
router.route("/winner/:id").get((req, res) => {
|
const getWinesToWinnerById = async (req, res) => {
|
||||||
res.redirect(`/#/winner/${req.params.id}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
router.route("/:id").get(async (req, res) => {
|
|
||||||
let id = req.params.id;
|
let id = req.params.id;
|
||||||
let foundWinner = await VirtualWinner.findOne({ id: id });
|
let foundWinner = await VirtualWinner.findOne({ id: id });
|
||||||
|
|
||||||
if (!foundWinner) {
|
if (!foundWinner) {
|
||||||
res.json({
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
message: "No winner with this id.",
|
||||||
existing: false,
|
existing: false,
|
||||||
turn: false
|
turn: false
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let allWinners = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
|
let allWinners = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
|
||||||
|
|
||||||
if (
|
if (
|
||||||
allWinners[0].id != foundWinner.id ||
|
allWinners[0].id != foundWinner.id ||
|
||||||
foundWinner.timestamp_limit == undefined ||
|
foundWinner.timestamp_limit == undefined ||
|
||||||
foundWinner.timestamp_sent == undefined
|
foundWinner.timestamp_sent == undefined
|
||||||
) {
|
) {
|
||||||
res.json({ existing: true, turn: false });
|
return res.json({
|
||||||
return;
|
success: false,
|
||||||
|
message: "Not the winner next in line!",
|
||||||
|
existing: true,
|
||||||
|
turn: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
return res.json({
|
||||||
|
success: true,
|
||||||
existing: true,
|
existing: true,
|
||||||
turn: true,
|
turn: true,
|
||||||
name: foundWinner.name,
|
name: foundWinner.name,
|
||||||
color: foundWinner.color
|
color: foundWinner.color
|
||||||
});
|
});
|
||||||
return;
|
};
|
||||||
});
|
|
||||||
|
|
||||||
router.route("/:id").post(async (req, res) => {
|
const registerWinnerSelection = async (req, res) => {
|
||||||
let id = req.params.id;
|
let id = req.params.id;
|
||||||
let wineName = req.body.wineName;
|
let wineName = req.body.wineName;
|
||||||
let foundWinner = await VirtualWinner.findOne({ id: id });
|
let foundWinner = await VirtualWinner.findOne({ id: id });
|
||||||
|
|
||||||
if (!foundWinner) {
|
if (!foundWinner) {
|
||||||
res.json(false);
|
return res.json({
|
||||||
return;
|
success: false,
|
||||||
|
message: "No winner with this id."
|
||||||
|
})
|
||||||
|
} else if (foundWinner.timestamp_limit < new Date().getTime()) {
|
||||||
|
return res.json({
|
||||||
|
success: false,
|
||||||
|
message: "Timelimit expired, you will receive a wine after other users have chosen.",
|
||||||
|
limit: true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundWinner.timestamp_limit < new Date().getTime()) {
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
limit: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let date = new Date();
|
let date = new Date();
|
||||||
date.setHours(5, 0, 0, 0);
|
date.setHours(5, 0, 0, 0);
|
||||||
|
|
||||||
let prelotteryWine = await PreLotteryWine.findOne({ name: wineName });
|
let prelotteryWine = await PreLotteryWine.findOne({ name: wineName });
|
||||||
|
|
||||||
if (!prelotteryWine) {
|
if (!prelotteryWine) {
|
||||||
res.json({
|
return res.json({
|
||||||
success: false,
|
success: false,
|
||||||
wine: true
|
message: "No wine with this name.",
|
||||||
|
wine: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,31 +82,78 @@ router.route("/:id").post(async (req, res) => {
|
|||||||
await _personFunctions.findSavePerson(foundWinner, wonWine, date);
|
await _personFunctions.findSavePerson(foundWinner, wonWine, date);
|
||||||
|
|
||||||
await foundWinner.delete();
|
await foundWinner.delete();
|
||||||
|
console.info("Saved winners choice.");
|
||||||
|
|
||||||
let nextWineBottle = await PreLotteryWine.find();
|
return findAndNotifyNextWinner()
|
||||||
let nextWinner = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
|
.then(() => res.json({
|
||||||
if (nextWinner.length > 1 && nextWineBottle.length > 1) {
|
message: "Choice saved and next in line notified.",
|
||||||
Message.sendMessage(nextWinner[0]);
|
success: true
|
||||||
startTimeout(id);
|
}))
|
||||||
} else if (nextWinner.length == 1 && nextWineBottle.length == 1) {
|
.catch(error => res.json({
|
||||||
chooseForUser(nextWinner[0], nextWineBottle[0]);
|
message: error["message"] || "Error when notifing next winner.",
|
||||||
}
|
success: false
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
res.json({
|
const chooseLastWineForUser = (winner, prelotteryWine) => {
|
||||||
success: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
async function chooseForUser(winner, prelotteryWine) {
|
|
||||||
let date = new Date();
|
let date = new Date();
|
||||||
date.setHours(5, 0, 0, 0);
|
date.setHours(5, 0, 0, 0);
|
||||||
let wonWine = await _wineFunctions.findSaveWine(prelotteryWine);
|
|
||||||
await _personFunctions.findSavePerson(winner, wonWine, date);
|
|
||||||
|
|
||||||
await prelotteryWine.delete();
|
return _wineFunctions.findSaveWine(preLotteryWine)
|
||||||
await Message.sendWonWineMessage(winner, prelotteryWine);
|
.then(wonWine => _personFunctions.findSavePerson(winner, wonWine, date))
|
||||||
await winner.delete();
|
.then(() => prelotteryWine.delete())
|
||||||
|
.then(() => Message.sendLastWinnerMessage(winner, preLotteryWine))
|
||||||
|
.then(() => winner.delete());
|
||||||
|
}
|
||||||
|
|
||||||
|
const findAndNotifyNextWinner = async () => {
|
||||||
|
let nextWinner = undefined;
|
||||||
|
|
||||||
|
let winnersLeft = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
|
||||||
|
let winesLeft = await PreLotteryWine.find();
|
||||||
|
|
||||||
|
if (winnersLeft.length > 1) {
|
||||||
|
nextWinner = winnersLeft[0]; // multiple winners left, choose next in line
|
||||||
|
} else if (winnersLeft.length == 1 && winesLeft.length > 1) {
|
||||||
|
nextWinner = winnersLeft[0] // one winner left, but multiple wines
|
||||||
|
} else if (winnersLeft.length == 1 && winesLeft.length == 1) {
|
||||||
|
nextWinner = winnersLeft[0] // one winner and one wine left, choose for user
|
||||||
|
wine = winesLeft[0]
|
||||||
|
return chooseLastWineForUser(nextWinner, wine);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextWinner) {
|
||||||
|
return Message.sendWineSelectMessage(nextWinner)
|
||||||
|
.then(messageResponse => startTimeout(nextWinner.id))
|
||||||
|
} else {
|
||||||
|
console.info("All winners notified. Could start cleanup here.");
|
||||||
|
return Promise.resolve({
|
||||||
|
message: "All winners notified."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendNotificationToWinnerById = async (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
let winner = await VirtualWinner.findOne({ id: id });
|
||||||
|
|
||||||
|
if (!winner) {
|
||||||
|
return res.json({
|
||||||
|
message: "No winner with this id.",
|
||||||
|
success: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return Message.sendWineSelectMessage(winner)
|
||||||
|
.then(success => res.json({
|
||||||
|
success: success,
|
||||||
|
message: `Message sent to winner ${id} successfully!`
|
||||||
|
}))
|
||||||
|
.catch(err => res.json({
|
||||||
|
success: false,
|
||||||
|
message: "Error while trying to send sms.",
|
||||||
|
error: err
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function startTimeout(id) {
|
function startTimeout(id) {
|
||||||
@@ -123,27 +161,28 @@ function startTimeout(id) {
|
|||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
let virtualWinner = await VirtualWinner.findOne({ id: id });
|
let virtualWinner = await VirtualWinner.findOne({ id: id });
|
||||||
if (!virtualWinner) {
|
if (!virtualWinner) {
|
||||||
console.log(
|
console.log(`Timeout done for user ${id}, but user has already sent data.`);
|
||||||
`Timeout done for user ${id}, but user has already sent data.`
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(`Timeout done for user ${id}, sending update to user.`);
|
console.log(`Timeout done for user ${id}, sending update to user.`);
|
||||||
|
|
||||||
Message.sendMessageTooLate(virtualWinner);
|
Message.sendWineSelectMessageTooLate(virtualWinner);
|
||||||
|
|
||||||
virtualWinner.timestamp_drawn = new Date().getTime();
|
virtualWinner.timestamp_drawn = new Date().getTime();
|
||||||
virtualWinner.timestamp_limit = null;
|
virtualWinner.timestamp_limit = null;
|
||||||
virtualWinner.timestamp_sent = null;
|
virtualWinner.timestamp_sent = null;
|
||||||
|
|
||||||
await virtualWinner.save();
|
await virtualWinner.save();
|
||||||
|
|
||||||
let prelotteryWine = await PreLotteryWine.find();
|
findAndNotifyNextWinner();
|
||||||
let nextWinner = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
|
}, 60000);
|
||||||
if (nextWinner.length == 1 && prelotteryWine.length == 1) {
|
|
||||||
chooseForUser(nextWinner[0], prelotteryWine[0]);
|
return Promise.resolve()
|
||||||
}
|
|
||||||
}, 600000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = {
|
||||||
|
getWinesToWinnerById,
|
||||||
|
registerWinnerSelection,
|
||||||
|
findAndNotifyNextWinner,
|
||||||
|
|
||||||
|
sendNotificationToWinnerById
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,15 +1,53 @@
|
|||||||
const express = require("express");
|
|
||||||
const path = require("path");
|
|
||||||
const router = express.Router();
|
|
||||||
const fetch = require('node-fetch')
|
const fetch = require('node-fetch')
|
||||||
|
const path = require('path')
|
||||||
|
const config = require(path.join(__dirname + "/../config/env/lottery.config"));
|
||||||
|
|
||||||
const mustBeAuthenticated = require(path.join(__dirname + "/../middleware/mustBeAuthenticated"))
|
const convertToOurWineObject = wine => {
|
||||||
|
if(wine.basic.ageLimit === "18"){
|
||||||
|
return {
|
||||||
|
name: wine.basic.productShortName,
|
||||||
|
vivinoLink: "https://www.vinmonopolet.no/p/" + wine.basic.productId,
|
||||||
|
rating: wine.basic.alcoholContent,
|
||||||
|
occurences: 0,
|
||||||
|
id: wine.basic.productId,
|
||||||
|
image: `https://bilder.vinmonopolet.no/cache/500x500-0/${wine.basic.productId}-1.jpg`,
|
||||||
|
price: wine.prices[0].salesPrice.toString(),
|
||||||
|
country: wine.origins.origin.country
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
const wineSearch = async (req, res) => {
|
||||||
next();
|
const {query} = req.query
|
||||||
});
|
let url = new URL(`https://apis.vinmonopolet.no/products/v0/details-normal?productShortNameContains=test&maxResults=15`)
|
||||||
|
url.searchParams.set('productShortNameContains', query)
|
||||||
|
|
||||||
router.route("/wineinfo/:ean").get(async (req, res) => {
|
const vinmonopoletResponse = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
"Ocp-Apim-Subscription-Key": config.vinmonopoletToken
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(resp => resp.json())
|
||||||
|
.catch(err => console.error(err))
|
||||||
|
|
||||||
|
|
||||||
|
if (vinmonopoletResponse.errors != null) {
|
||||||
|
return vinmonopoletResponse.errors.map(error => {
|
||||||
|
if (error.type == "UnknownProductError") {
|
||||||
|
return res.status(404).json({
|
||||||
|
message: error.message
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const winesConverted = vinmonopoletResponse.map(convertToOurWineObject).filter(Boolean)
|
||||||
|
|
||||||
|
return res.send(winesConverted);
|
||||||
|
}
|
||||||
|
|
||||||
|
const byEAN = async (req, res) => {
|
||||||
const vinmonopoletResponse = await fetch("https://app.vinmonopolet.no/vmpws/v2/vmp/products/barCodeSearch/" + req.params.ean)
|
const vinmonopoletResponse = await fetch("https://app.vinmonopolet.no/vmpws/v2/vmp/products/barCodeSearch/" + req.params.ean)
|
||||||
.then(resp => resp.json())
|
.then(resp => resp.json())
|
||||||
|
|
||||||
@@ -25,7 +63,10 @@ router.route("/wineinfo/:ean").get(async (req, res) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
res.send(vinmonopoletResponse);
|
return res.send(vinmonopoletResponse);
|
||||||
});
|
};
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = {
|
||||||
|
byEAN,
|
||||||
|
wineSearch
|
||||||
|
};
|
||||||
|
|||||||
3
config/env/lottery.config.example.js
vendored
3
config/env/lottery.config.example.js
vendored
@@ -6,5 +6,6 @@ module.exports = {
|
|||||||
date: 5,
|
date: 5,
|
||||||
hours: 15,
|
hours: 15,
|
||||||
apiUrl: undefined,
|
apiUrl: undefined,
|
||||||
gatewayToken: undefined
|
gatewayToken: undefined,
|
||||||
|
vinmonopoletToken: undefined
|
||||||
};
|
};
|
||||||
5052
package-lock.json
generated
5052
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
"dev": "cross-env NODE_ENV=development webpack-dev-server --progress",
|
"dev": "cross-env NODE_ENV=development webpack-dev-server",
|
||||||
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
|
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
@@ -35,7 +35,6 @@
|
|||||||
"passport-local-mongoose": "^6.0.1",
|
"passport-local-mongoose": "^6.0.1",
|
||||||
"qrcode": "^1.4.4",
|
"qrcode": "^1.4.4",
|
||||||
"referrer-policy": "^1.2.0",
|
"referrer-policy": "^1.2.0",
|
||||||
"request": "^2.88.2",
|
|
||||||
"socket.io": "^2.3.0",
|
"socket.io": "^2.3.0",
|
||||||
"socket.io-client": "^2.3.0",
|
"socket.io-client": "^2.3.0",
|
||||||
"vue": "~2.6",
|
"vue": "~2.6",
|
||||||
|
|||||||
13
schemas/RequestedWine.js
Normal file
13
schemas/RequestedWine.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const mongoose = require("mongoose");
|
||||||
|
const Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
const RequestedWine = new Schema({
|
||||||
|
count: Number,
|
||||||
|
wineId: String,
|
||||||
|
wine: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: "Wine"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = mongoose.model("RequestedWine", RequestedWine);
|
||||||
24
server.js
24
server.js
@@ -6,16 +6,10 @@ const path = require("path");
|
|||||||
const session = require("express-session");
|
const session = require("express-session");
|
||||||
const User = require(path.join(__dirname + "/schemas/User"));
|
const User = require(path.join(__dirname + "/schemas/User"));
|
||||||
|
|
||||||
const updateApi = require(path.join(__dirname + "/api/update"));
|
const apiRouter = require(path.join(__dirname + "/api/router.js"));
|
||||||
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 loginApi = require(path.join(__dirname + "/api/login"));
|
||||||
const wineinfoApi = require(path.join(__dirname + "/api/wineinfo"));
|
const subscriptionApi = require(path.join(__dirname + "/api/subscriptions"));
|
||||||
const virtualApi = require(path.join(__dirname + "/api/virtualLottery"));
|
|
||||||
const virtualRegistrationApi = require(path.join(
|
|
||||||
__dirname + "/api/virtualRegistration"
|
|
||||||
));
|
|
||||||
const lottery = require(path.join(__dirname + "/api/lottery"));
|
|
||||||
|
|
||||||
//This is required for the chat to work
|
//This is required for the chat to work
|
||||||
const chat = require(path.join(__dirname + "/api/chat"))(io);
|
const chat = require(path.join(__dirname + "/api/chat"))(io);
|
||||||
@@ -76,6 +70,8 @@ app.use(
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
app.set('socketio', io);
|
||||||
|
|
||||||
const passport = require("passport");
|
const passport = require("passport");
|
||||||
const LocalStrategy = require("passport-local");
|
const LocalStrategy = require("passport-local");
|
||||||
|
|
||||||
@@ -91,18 +87,16 @@ passport.deserializeUser(User.deserializeUser());
|
|||||||
app.use("/public", express.static(path.join(__dirname, "public")));
|
app.use("/public", express.static(path.join(__dirname, "public")));
|
||||||
app.use("/dist", express.static(path.join(__dirname, "public/dist")));
|
app.use("/dist", express.static(path.join(__dirname, "public/dist")));
|
||||||
app.use("/", loginApi);
|
app.use("/", loginApi);
|
||||||
app.use("/api/", updateApi);
|
|
||||||
app.use("/api/", retrieveApi);
|
|
||||||
app.use("/api/", wineinfoApi);
|
|
||||||
app.use("/api/", chatHistory);
|
app.use("/api/", chatHistory);
|
||||||
app.use("/api/lottery", lottery);
|
app.use("/api/", apiRouter);
|
||||||
app.use("/api/virtual/", virtualApi(io));
|
|
||||||
app.use("/api/virtual-registration/", virtualRegistrationApi);
|
|
||||||
app.use("/subscription", subscriptionApi);
|
app.use("/subscription", subscriptionApi);
|
||||||
|
|
||||||
app.get("/dagens", function(req, res) {
|
app.get("/dagens", function(req, res) {
|
||||||
res.redirect("/#/dagens");
|
res.redirect("/#/dagens");
|
||||||
});
|
});
|
||||||
|
app.get("/winner/:id", function(req, res) {
|
||||||
|
res.redirect("/#/winner/" + req.params.id);
|
||||||
|
});
|
||||||
|
|
||||||
app.use("/service-worker.js", function(req, res) {
|
app.use("/service-worker.js", function(req, res) {
|
||||||
res.sendFile(path.join(__dirname, "public/sw/serviceWorker.js"));
|
res.sendFile(path.join(__dirname, "public/sw/serviceWorker.js"));
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@import "./styles/global.scss";
|
@import "./styles/global.scss";
|
||||||
|
@import "./styles/positioning.scss";
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "knowit";
|
font-family: "knowit";
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
111
src/api.js
111
src/api.js
@@ -1,3 +1,5 @@
|
|||||||
|
import fetch from "node-fetch";
|
||||||
|
|
||||||
const BASE_URL = __APIURL__ || window.location.origin;
|
const BASE_URL = __APIURL__ || window.location.origin;
|
||||||
|
|
||||||
const statistics = () => {
|
const statistics = () => {
|
||||||
@@ -24,6 +26,16 @@ const overallWineStatistics = () => {
|
|||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const allRequestedWines = () => {
|
||||||
|
const url = new URL("/api/request/all", BASE_URL);
|
||||||
|
|
||||||
|
return fetch(url.href)
|
||||||
|
.then(resp => {
|
||||||
|
const isAdmin = resp.headers.get("Vinlottis-Admin") || false;
|
||||||
|
return Promise.all([resp.json(), isAdmin]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const chartWinsByColor = () => {
|
const chartWinsByColor = () => {
|
||||||
const url = new URL("/api/purchase/statistics/color", BASE_URL);
|
const url = new URL("/api/purchase/statistics/color", BASE_URL);
|
||||||
|
|
||||||
@@ -42,8 +54,22 @@ const prelottery = () => {
|
|||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
const log = sendObject => {
|
const sendLottery = sendObject => {
|
||||||
const url = new URL("/api/log", BASE_URL);
|
const url = new URL("/api/lottery", 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 sendLotteryWinners = sendObject => {
|
||||||
|
const url = new URL("/api/lottery/winners", BASE_URL);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -57,7 +83,7 @@ const log = sendObject => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const addAttendee = sendObject => {
|
const addAttendee = sendObject => {
|
||||||
const url = new URL("/api/virtual/attendee", BASE_URL);
|
const url = new URL("/api/virtual/attendee/add", BASE_URL);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -71,31 +97,46 @@ const addAttendee = sendObject => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getVirtualWinner = () => {
|
const getVirtualWinner = () => {
|
||||||
const url = new URL("/api/virtual/winner", BASE_URL);
|
const url = new URL("/api/virtual/winner/draw", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
const attendeesSecure = () => {
|
const attendeesSecure = () => {
|
||||||
const url = new URL("/api/virtual/attendees/secure", BASE_URL);
|
const url = new URL("/api/virtual/attendee/all/secure", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
const winnersSecure = () => {
|
const winnersSecure = () => {
|
||||||
const url = new URL("/api/virtual/winners/secure", BASE_URL);
|
const url = new URL("/api/virtual/winner/all/secure", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
const winners = () => {
|
const winners = () => {
|
||||||
const url = new URL("/api/virtual/winners", BASE_URL);
|
const url = new URL("/api/virtual/winner/all", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteRequestedWine = wineToBeDeleted => {
|
||||||
|
|
||||||
|
const url = new URL("api/request/"+ wineToBeDeleted._id, BASE_URL);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
method: "DELETE",
|
||||||
|
body: JSON.stringify(wineToBeDeleted)
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetch(url.href, options).then(resp => resp.json())
|
||||||
|
}
|
||||||
|
|
||||||
const deleteWinners = () => {
|
const deleteWinners = () => {
|
||||||
const url = new URL("/api/virtual/winners", BASE_URL);
|
const url = new URL("/api/virtual/winner/all", BASE_URL);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -108,7 +149,7 @@ const deleteWinners = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const deleteAttendees = () => {
|
const deleteAttendees = () => {
|
||||||
const url = new URL("/api/virtual/attendees", BASE_URL);
|
const url = new URL("/api/virtual/attendee/all", BASE_URL);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -121,11 +162,28 @@ const deleteAttendees = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const attendees = () => {
|
const attendees = () => {
|
||||||
const url = new URL("/api/virtual/attendees", BASE_URL);
|
const url = new URL("/api/virtual/attendee/all", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const requestNewWine = (wine) => {
|
||||||
|
const options = {
|
||||||
|
body: JSON.stringify({
|
||||||
|
wine: wine
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
method: "post"
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL("/api/request/new-wine", BASE_URL)
|
||||||
|
|
||||||
|
return fetch(url.href, options).then(resp => resp.json())
|
||||||
|
}
|
||||||
|
|
||||||
const logWines = wines => {
|
const logWines = wines => {
|
||||||
const url = new URL("/api/log/wines", BASE_URL);
|
const url = new URL("/api/log/wines", BASE_URL);
|
||||||
|
|
||||||
@@ -141,7 +199,7 @@ const logWines = wines => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const wineSchema = () => {
|
const wineSchema = () => {
|
||||||
const url = new URL("/api/log/schema", BASE_URL);
|
const url = new URL("/api/wineinfo/schema", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
@@ -160,6 +218,21 @@ const barcodeToVinmonopolet = id => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const searchForWine = searchString => {
|
||||||
|
const url = new URL("/api/wineinfo/search?query=" + searchString, BASE_URL);
|
||||||
|
|
||||||
|
return fetch(url.href).then(async resp => {
|
||||||
|
if (!resp.ok) {
|
||||||
|
if (resp.status == 404) {
|
||||||
|
throw await resp.json();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return resp.json();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleErrors = async resp => {
|
const handleErrors = async resp => {
|
||||||
if ([400, 409].includes(resp.status)) {
|
if ([400, 409].includes(resp.status)) {
|
||||||
throw await resp.json();
|
throw await resp.json();
|
||||||
@@ -217,17 +290,20 @@ const getChatHistory = (skip = null, take = null) => {
|
|||||||
|
|
||||||
const finishedDraw = () => {
|
const finishedDraw = () => {
|
||||||
const url = new URL("/api/virtual/finish", BASE_URL);
|
const url = new URL("/api/virtual/finish", BASE_URL);
|
||||||
|
const options = {
|
||||||
|
method: 'POST'
|
||||||
|
}
|
||||||
|
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href, options).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAmIWinner = id => {
|
const getAmIWinner = id => {
|
||||||
const url = new URL(`/api/virtual-registration/${id}`, BASE_URL);
|
const url = new URL(`/api/winner/${id}`, BASE_URL);
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).then(resp => resp.json());
|
||||||
};
|
};
|
||||||
|
|
||||||
const postWineChosen = (id, wineName) => {
|
const postWineChosen = (id, wineName) => {
|
||||||
const url = new URL(`/api/virtual-registration/${id}`, BASE_URL);
|
const url = new URL(`/api/winner/${id}`, BASE_URL);
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
@@ -265,10 +341,14 @@ export {
|
|||||||
chartWinsByColor,
|
chartWinsByColor,
|
||||||
chartPurchaseByColor,
|
chartPurchaseByColor,
|
||||||
prelottery,
|
prelottery,
|
||||||
log,
|
sendLottery,
|
||||||
|
sendLotteryWinners,
|
||||||
logWines,
|
logWines,
|
||||||
wineSchema,
|
wineSchema,
|
||||||
barcodeToVinmonopolet,
|
barcodeToVinmonopolet,
|
||||||
|
searchForWine,
|
||||||
|
requestNewWine,
|
||||||
|
allRequestedWines,
|
||||||
login,
|
login,
|
||||||
register,
|
register,
|
||||||
addAttendee,
|
addAttendee,
|
||||||
@@ -279,6 +359,7 @@ export {
|
|||||||
winnersSecure,
|
winnersSecure,
|
||||||
deleteWinners,
|
deleteWinners,
|
||||||
deleteAttendees,
|
deleteAttendees,
|
||||||
|
deleteRequestedWine,
|
||||||
getChatHistory,
|
getChatHistory,
|
||||||
finishedDraw,
|
finishedDraw,
|
||||||
getAmIWinner,
|
getAmIWinner,
|
||||||
|
|||||||
52
src/components/AllRequestedWines.vue
Normal file
52
src/components/AllRequestedWines.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<h1>
|
||||||
|
Alle foreslåtte viner
|
||||||
|
</h1>
|
||||||
|
<section class="requested-wines-container">
|
||||||
|
<p v-if="wines == undefined || wines.length == 0">Ingen har foreslått noe enda!</p>
|
||||||
|
<RequestedWineCard v-for="requestedEl in wines" :key="requestedEl.id" :requestedElement="requestedEl" @wineDeleted="filterOutDeletedWine" :showDeleteButton="isAdmin"/>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { allRequestedWines } from "@/api";
|
||||||
|
import RequestedWineCard from "@/ui/RequestedWineCard";
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
RequestedWineCard
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return{
|
||||||
|
wines: undefined,
|
||||||
|
canRequest: true,
|
||||||
|
isAdmin: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
filterOutDeletedWine(wine){
|
||||||
|
this.wines = this.wines.filter(item => item.wine._id !== wine._id)
|
||||||
|
},
|
||||||
|
async refreshData(){
|
||||||
|
[this.wines, this.isAdmin] = await allRequestedWines() || [[], false]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.refreshData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
h1{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.requested-wines-container{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
align-items: stretch
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -78,6 +78,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="button-container">
|
||||||
|
<button class="vin-button" @click="submitLottery">Send inn lotteri</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3>Vinnere</h3>
|
<h3>Vinnere</h3>
|
||||||
<a class="wine-link" @click="fetchColorsAndWinners()">Refresh data fra virtuelt lotteri</a>
|
<a class="wine-link" @click="fetchColorsAndWinners()">Refresh data fra virtuelt lotteri</a>
|
||||||
<div class="winner-container" v-if="winners.length > 0">
|
<div class="winner-container" v-if="winners.length > 0">
|
||||||
@@ -130,8 +134,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</wine>
|
</wine>
|
||||||
|
|
||||||
<div class="button-container">
|
<div class="button-container column">
|
||||||
<button class="vin-button" @click="sendInfo">Send inn vinnere</button>
|
<button class="vin-button" @click="submitLotteryWinners">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>
|
||||||
</div>
|
</div>
|
||||||
@@ -142,9 +146,11 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import eventBus from "@/mixins/EventBus";
|
import eventBus from "@/mixins/EventBus";
|
||||||
|
import { dateString } from '@/utils'
|
||||||
import {
|
import {
|
||||||
prelottery,
|
prelottery,
|
||||||
log,
|
sendLotteryWinners,
|
||||||
|
sendLottery,
|
||||||
logWines,
|
logWines,
|
||||||
wineSchema,
|
wineSchema,
|
||||||
winnersSecure,
|
winnersSecure,
|
||||||
@@ -306,7 +312,7 @@ export default {
|
|||||||
},
|
},
|
||||||
sendWines: async function() {
|
sendWines: async function() {
|
||||||
let response = await logWines(this.wines);
|
let response = await logWines(this.wines);
|
||||||
if (response == true) {
|
if (response.success == true) {
|
||||||
alert("Sendt!");
|
alert("Sendt!");
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
} else {
|
} else {
|
||||||
@@ -324,7 +330,7 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
sendInfo: async function(event) {
|
submitLottery: async function(event) {
|
||||||
const colors = {
|
const colors = {
|
||||||
red: this.lotteryColors.filter(c => c.css == "red")[0].value,
|
red: this.lotteryColors.filter(c => c.css == "red")[0].value,
|
||||||
green: this.lotteryColors.filter(c => c.css == "green")[0].value,
|
green: this.lotteryColors.filter(c => c.css == "green")[0].value,
|
||||||
@@ -333,48 +339,63 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let sendObject = {
|
let sendObject = {
|
||||||
purchase: {
|
lottery: {
|
||||||
date: new Date(),
|
date: dateString(new Date()),
|
||||||
...colors
|
...colors
|
||||||
},
|
}
|
||||||
winners: this.winners
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (sendObject.purchase.red == undefined) {
|
if (sendObject.lottery.red == undefined) {
|
||||||
alert("Rød må defineres");
|
alert("Rød må defineres");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sendObject.purchase.green == undefined) {
|
if (sendObject.lottery.green == undefined) {
|
||||||
alert("Grønn må defineres");
|
alert("Grønn må defineres");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sendObject.purchase.yellow == undefined) {
|
if (sendObject.lottery.yellow == undefined) {
|
||||||
alert("Gul må defineres");
|
alert("Gul må defineres");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sendObject.purchase.blue == undefined) {
|
if (sendObject.lottery.blue == undefined) {
|
||||||
alert("Blå må defineres");
|
alert("Blå må defineres");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendObject.purchase.bought =
|
sendObject.lottery.bought =
|
||||||
parseInt(colors.blue) +
|
parseInt(colors.blue) +
|
||||||
parseInt(colors.red) +
|
parseInt(colors.red) +
|
||||||
parseInt(colors.green) +
|
parseInt(colors.green) +
|
||||||
parseInt(colors.yellow);
|
parseInt(colors.yellow);
|
||||||
const stolen = sendObject.purchase.bought - parseInt(this.payed) / 10;
|
const stolen = sendObject.lottery.bought - parseInt(this.payed) / 10;
|
||||||
if (isNaN(stolen) || stolen == undefined) {
|
if (isNaN(stolen) || stolen == undefined) {
|
||||||
alert("Betalt må registreres");
|
alert("Betalt må registreres");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sendObject.purchase.stolen = stolen;
|
sendObject.lottery.stolen = stolen;
|
||||||
|
|
||||||
if (sendObject.winners.length == 0) {
|
let response = await sendLottery(sendObject);
|
||||||
|
if (response == true) {
|
||||||
|
alert("Sendt!");
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
alert(response.message || "Noe gikk galt under innsending");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submitLotteryWinners: async function(event) {
|
||||||
|
let sendObject = {
|
||||||
|
lottery: {
|
||||||
|
date: dateString(new Date()),
|
||||||
|
winners: this.winners
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendObject.lottery.winners.length == 0) {
|
||||||
alert("Det må være med vinnere");
|
alert("Det må være med vinnere");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let i = 0; i < sendObject.winners.length; i++) {
|
for (let i = 0; i < sendObject.lottery.winners.length; i++) {
|
||||||
let currentWinner = sendObject.winners[i];
|
let currentWinner = sendObject.lottery.winners[i];
|
||||||
|
|
||||||
if (currentWinner.name == undefined || currentWinner.name == "") {
|
if (currentWinner.name == undefined || currentWinner.name == "") {
|
||||||
alert("Navn må defineres");
|
alert("Navn må defineres");
|
||||||
@@ -386,7 +407,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = await log(sendObject);
|
let response = await sendLotteryWinners(sendObject);
|
||||||
if (response == true) {
|
if (response == true) {
|
||||||
alert("Sendt!");
|
alert("Sendt!");
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
@@ -613,7 +634,7 @@ hr {
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
margin: 3rem auto 0;
|
margin: 3rem auto 1rem;
|
||||||
|
|
||||||
@include mobile {
|
@include mobile {
|
||||||
margin: 1.8rem auto 0;
|
margin: 1.8rem auto 0;
|
||||||
|
|||||||
195
src/components/RequestWine.vue
Normal file
195
src/components/RequestWine.vue
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
<template>
|
||||||
|
<main>
|
||||||
|
<h1>
|
||||||
|
Foreslå en vin!
|
||||||
|
</h1>
|
||||||
|
<Modal
|
||||||
|
v-if="showModal"
|
||||||
|
modalText="Ønsket ditt har blitt lagt til"
|
||||||
|
:buttons="modalButtons"
|
||||||
|
@click="emitFromModalButton"
|
||||||
|
></Modal>
|
||||||
|
<section>
|
||||||
|
<section class="search-section">
|
||||||
|
<input type="text" v-model="searchString" @keyup.enter="fetchWineFromVin()" placeholder="Søk etter en vin du liker her!🍷">
|
||||||
|
<button :disabled="!searchString" @click="fetchWineFromVin()" class="vin-button">Søk</button>
|
||||||
|
</section>
|
||||||
|
<section v-for="(wine, index) in this.wines" :key="index" class="search-results-container">
|
||||||
|
<img
|
||||||
|
v-if="wine.image"
|
||||||
|
:src="wine.image"
|
||||||
|
class="wine-image"
|
||||||
|
:class="{ 'fullscreen': fullscreen }"
|
||||||
|
/>
|
||||||
|
<img v-else class="wine-placeholder" alt="Wine image" />
|
||||||
|
<section class="wine-info">
|
||||||
|
<h2 v-if="wine.name">{{ wine.name }}</h2>
|
||||||
|
<h2 v-else>(no name)</h2>
|
||||||
|
<div class="__details">
|
||||||
|
<span v-if="wine.rating">{{ wine.rating }}%</span>
|
||||||
|
<span v-if="wine.price">{{ wine.price }} NOK</span>
|
||||||
|
<span v-if="wine.country">{{ wine.country }}</span>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="buttons">
|
||||||
|
<button class="vin-button" @click="request(wine)">Foreslå denne</button>
|
||||||
|
<a
|
||||||
|
v-if="wine.vivinoLink"
|
||||||
|
:href="wine.vivinoLink"
|
||||||
|
class="wine-link"
|
||||||
|
>Les mer på polet</a>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
<p v-if="this.wines && this.wines.length == 0">
|
||||||
|
Fant ingen viner med det navnet!
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { searchForWine, requestNewWine } from "@/api";
|
||||||
|
import Wine from "@/ui/Wine";
|
||||||
|
import Modal from "@/ui/Modal";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
Wine,
|
||||||
|
Modal
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchString: undefined,
|
||||||
|
wines: undefined,
|
||||||
|
showModal: false,
|
||||||
|
modalButtons: [
|
||||||
|
{
|
||||||
|
text: "Legg til flere viner",
|
||||||
|
action: "stay"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Se alle viner",
|
||||||
|
action: "move"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
fetchWineFromVin(){
|
||||||
|
if(this.searchString){
|
||||||
|
this.wines = []
|
||||||
|
let localSearchString = this.searchString.replace(/ /g,"_");
|
||||||
|
searchForWine(localSearchString)
|
||||||
|
.then(res => this.wines = res)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
request(wine){
|
||||||
|
requestNewWine(wine)
|
||||||
|
.then(() => this.showModal = true)
|
||||||
|
},
|
||||||
|
emitFromModalButton(action){
|
||||||
|
if(action == "stay"){
|
||||||
|
this.showModal = false
|
||||||
|
} else {
|
||||||
|
this.$router.push("/requested-wines");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "./src/styles/media-queries";
|
||||||
|
@import "./src/styles/global";
|
||||||
|
@import "./src/styles/variables";
|
||||||
|
|
||||||
|
main{
|
||||||
|
margin: auto;
|
||||||
|
width: 80%;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
color: black;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: 1px solid black;
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-section{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-flow: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results-container{
|
||||||
|
display: flex;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 1px;
|
||||||
|
box-shadow: 0px 0px 0px 1px rgba(0,0,0,0.3);
|
||||||
|
margin: 1rem 0;
|
||||||
|
justify-content: space-around;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
|
||||||
|
.wine-image {
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wine-placeholder {
|
||||||
|
height: 100px;
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wine-info{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.__details{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.wine-link {
|
||||||
|
color: #333333;
|
||||||
|
font-family: Arial;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px solid #ff5fff;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
order: 2;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 40%;
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
@include mobile {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
.wine-image {
|
||||||
|
height: 100px;
|
||||||
|
width: 50px;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
.buttons{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-self: center;
|
||||||
|
margin: 1em;
|
||||||
|
.wine-link{
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -106,6 +106,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<br />
|
<br />
|
||||||
<button class="vin-button" @click="sendAttendee">Send deltaker</button>
|
<button class="vin-button" @click="sendAttendee">Send deltaker</button>
|
||||||
|
|
||||||
|
<TextToast v-if="showToast" :text="toastText" v-on:closeToast="showToast = false" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -119,13 +121,16 @@ import {
|
|||||||
winnersSecure,
|
winnersSecure,
|
||||||
deleteWinners,
|
deleteWinners,
|
||||||
deleteAttendees,
|
deleteAttendees,
|
||||||
finishedDraw
|
finishedDraw,
|
||||||
|
prelottery
|
||||||
} from "@/api";
|
} from "@/api";
|
||||||
|
import TextToast from "@/ui/TextToast";
|
||||||
import RaffleGenerator from "@/ui/RaffleGenerator";
|
import RaffleGenerator from "@/ui/RaffleGenerator";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
RaffleGenerator
|
RaffleGenerator,
|
||||||
|
TextToast
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -144,7 +149,9 @@ export default {
|
|||||||
drawTime: 20,
|
drawTime: 20,
|
||||||
currentWinners: 1,
|
currentWinners: 1,
|
||||||
numberOfWinners: 4,
|
numberOfWinners: 4,
|
||||||
socket: null
|
socket: null,
|
||||||
|
toastText: undefined,
|
||||||
|
showToast: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -168,13 +175,21 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
window.finishedDraw = finishedDraw;
|
window.finishedDraw = finishedDraw;
|
||||||
console.log("here");
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setWithRandomColors(colors) {
|
setWithRandomColors(colors) {
|
||||||
Object.keys(colors).forEach(color => (this[color] = colors[color]));
|
Object.keys(colors).forEach(color => (this[color] = colors[color]));
|
||||||
},
|
},
|
||||||
sendAttendee: async function() {
|
sendAttendee: async function() {
|
||||||
|
if (this.red == 0 && this.blue == 0 && this.green == 0 && this.yellow == 0) {
|
||||||
|
alert('Ingen farger valgt!')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.name == 0 && this.phoneNumber) {
|
||||||
|
alert('Ingen navn eller tlf satt!')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let response = await addAttendee({
|
let response = await addAttendee({
|
||||||
name: this.name,
|
name: this.name,
|
||||||
phoneNumber: this.phoneNumber,
|
phoneNumber: this.phoneNumber,
|
||||||
@@ -184,10 +199,15 @@ export default {
|
|||||||
yellow: this.yellow,
|
yellow: this.yellow,
|
||||||
ballots: this.ballots
|
ballots: this.ballots
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response == true) {
|
if (response == true) {
|
||||||
alert("Sendt inn deltaker!");
|
this.toastText = `Sendt inn deltaker: ${this.name}`;
|
||||||
|
this.showToast = true;
|
||||||
|
|
||||||
this.name = null;
|
this.name = null;
|
||||||
this.phoneNumber = null;
|
this.phoneNumber = null;
|
||||||
|
this.yellow = 0;
|
||||||
|
this.green = 0;
|
||||||
this.red = 0;
|
this.red = 0;
|
||||||
this.blue = 0;
|
this.blue = 0;
|
||||||
|
|
||||||
@@ -208,6 +228,7 @@ export default {
|
|||||||
if (window.confirm("Er du sikker på at du vil trekke vinnere?")) {
|
if (window.confirm("Er du sikker på at du vil trekke vinnere?")) {
|
||||||
this.drawingWinner = true;
|
this.drawingWinner = true;
|
||||||
let response = await getVirtualWinner();
|
let response = await getVirtualWinner();
|
||||||
|
|
||||||
if (response) {
|
if (response) {
|
||||||
if (this.currentWinners < this.numberOfWinners) {
|
if (this.currentWinners < this.numberOfWinners) {
|
||||||
this.countdown();
|
this.countdown();
|
||||||
|
|||||||
@@ -62,11 +62,9 @@ export default {
|
|||||||
this.name = winnerObject.name;
|
this.name = winnerObject.name;
|
||||||
const _wines = await fetch("/api/wines/prelottery");
|
const _wines = await fetch("/api/wines/prelottery");
|
||||||
this.wines = await _wines.json();
|
this.wines = await _wines.json();
|
||||||
console.log(this.wines);
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
chosenWine: async function(name) {
|
chosenWine: async function(name) {
|
||||||
console.log("chosen a wine");
|
|
||||||
let posted = await postWineChosen(this.id, name);
|
let posted = await postWineChosen(this.id, name);
|
||||||
console.log("response", posted);
|
console.log("response", posted);
|
||||||
if (posted.success) {
|
if (posted.success) {
|
||||||
@@ -82,7 +80,8 @@ export default {
|
|||||||
.container {
|
.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 4rem;
|
margin-top: 2rem;
|
||||||
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
.sent-container {
|
.sent-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import WinnerPage from "@/components/WinnerPage";
|
|||||||
import LotteryPage from "@/components/LotteryPage";
|
import LotteryPage from "@/components/LotteryPage";
|
||||||
import HistoryPage from "@/components/HistoryPage";
|
import HistoryPage from "@/components/HistoryPage";
|
||||||
|
|
||||||
|
import RequestWine from "@/components/RequestWine";
|
||||||
|
import AllRequestedWines from "@/components/AllRequestedWines";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
path: "*",
|
path: "*",
|
||||||
@@ -52,6 +55,14 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: "/history",
|
path: "/history",
|
||||||
component: HistoryPage
|
component: HistoryPage
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/request",
|
||||||
|
component: RequestWine
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/requested-wines",
|
||||||
|
component: AllRequestedWines
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -74,6 +74,16 @@ body {
|
|||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.column {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
margin-right: unset;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include mobile {
|
@include mobile {
|
||||||
&:not(.row) {
|
&:not(.row) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -108,6 +118,7 @@ textarea {
|
|||||||
border: 0;
|
border: 0;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
font-size: 1.3rem;
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.3rem;
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
max-height: 4rem;
|
max-height: 4rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -133,13 +144,17 @@ textarea {
|
|||||||
0 16px 32px rgba(0, 0, 0, 0.07), 0 32px 64px rgba(0, 0, 0, 0.07);
|
0 16px 32px rgba(0, 0, 0, 0.07), 0 32px 64px rgba(0, 0, 0, 0.07);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover:not(:disabled) {
|
||||||
transform: scale(1.02) translateZ(0);
|
transform: scale(1.02) translateZ(0);
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&:disabled{
|
||||||
|
opacity: 0.25;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-margin {
|
.no-margin {
|
||||||
|
|||||||
7
src/styles/positioning.scss
Normal file
7
src/styles/positioning.scss
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
&-column {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
101
src/ui/Modal.vue
Normal file
101
src/ui/Modal.vue
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<transition name="modal-fade">
|
||||||
|
<main class="modal-backdrop">
|
||||||
|
<section class="modal">
|
||||||
|
<header class="modal-header" v-if="headerText">
|
||||||
|
{{headerText}}
|
||||||
|
</header>
|
||||||
|
<section class="modal-body">
|
||||||
|
<p>
|
||||||
|
{{modalText}}
|
||||||
|
</p>
|
||||||
|
<section class="button-container">
|
||||||
|
<button v-for="(button, index) in buttons" :key="index" @click="modalButtonClicked(button.action)" class="vin-button">
|
||||||
|
{{button.text}}
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
headerText: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
modalText: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
modalButtonClicked(action){
|
||||||
|
this.$emit('click', action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "../styles/global.scss";
|
||||||
|
|
||||||
|
.modal-fade-enter,
|
||||||
|
.modal-fade-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-fade-enter-active,
|
||||||
|
.modal-fade-leave-active {
|
||||||
|
transition: opacity .5s ease
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-backdrop {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
background: #FFFFFF;
|
||||||
|
-webkit-box-shadow: 0px 0px 22px 1px rgba(0, 0, 0, 0.65);
|
||||||
|
-moz-box-shadow: 0px 0px 22px 1px rgba(0, 0, 0, 0.65);
|
||||||
|
box-shadow: 0px 0px 22px 1px rgba(0, 0, 0, 0.65);
|
||||||
|
overflow-x: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
border-bottom: 1px solid #eeeeee;
|
||||||
|
color: #4AAE9B;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
position: relative;
|
||||||
|
padding: 20px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
80
src/ui/RequestedWineCard.vue
Normal file
80
src/ui/RequestedWineCard.vue
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<template>
|
||||||
|
<div class="requested-wine">
|
||||||
|
<img
|
||||||
|
v-if="wine.image"
|
||||||
|
:src="wine.image"
|
||||||
|
class="wine-image"
|
||||||
|
:class="{ 'fullscreen': fullscreen }"
|
||||||
|
/>
|
||||||
|
<img v-else class="wine-placeholder" alt="Wine image" />
|
||||||
|
<section class="wine-info">
|
||||||
|
<h3 v-if="wine.name">{{ wine.name }}</h3>
|
||||||
|
<h3 v-else>(no name)</h3>
|
||||||
|
<p>Antall ganger denne har blitt foreslått: {{requestedElement.count}}</p>
|
||||||
|
<section class="buttons">
|
||||||
|
<button class="vin-button" @click="request(wine)" v-if="!locallyRequested">Foreslå denne</button>
|
||||||
|
<a
|
||||||
|
v-if="wine.vivinoLink"
|
||||||
|
:href="wine.vivinoLink"
|
||||||
|
class="wine-link"
|
||||||
|
>Les mer på polet</a>
|
||||||
|
</section>
|
||||||
|
<button @click="deleteWine(wine)" v-if="showDeleteButton == true">
|
||||||
|
Slett vinen
|
||||||
|
</button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { deleteRequestedWine, requestNewWine } from "@/api";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data(){
|
||||||
|
return {
|
||||||
|
wine: this.requestedElement.wine,
|
||||||
|
locallyRequested: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
requestedElement: {
|
||||||
|
required: true,
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
|
showDeleteButton: {
|
||||||
|
required: false,
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
request(wine){
|
||||||
|
this.locallyRequested = true
|
||||||
|
this.requestedElement.count = this.requestedElement.count +1
|
||||||
|
requestNewWine(wine)
|
||||||
|
},
|
||||||
|
async deleteWine(wine) {
|
||||||
|
if (window.confirm("Er du sikker på at du vil slette vinen?")) {
|
||||||
|
let response = await deleteRequestedWine(wine);
|
||||||
|
if (response['success'] == true) {
|
||||||
|
this.$emit('wineDeleted', wine);
|
||||||
|
} else {
|
||||||
|
alert("Klarte ikke slette vinen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
.requested-wine{
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 1px;
|
||||||
|
margin: 1rem 0;
|
||||||
|
-webkit-box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.65);
|
||||||
|
-moz-box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.65);
|
||||||
|
box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.65);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
:href="wine.vivinoLink"
|
:href="wine.vivinoLink"
|
||||||
class="wine-link"
|
class="wine-link"
|
||||||
>Les mer på {{ hostname(wine.vivinoLink) }}</a>
|
>Les mer på {{ hostname(wine.vivinoLink) }}</a>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
v-if="winner"
|
v-if="winner"
|
||||||
@click="choseWine(wine.name)"
|
@click="choseWine(wine.name)"
|
||||||
@@ -174,4 +175,8 @@ a:visited {
|
|||||||
color: $red;
|
color: $red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vin-button {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -34,8 +34,10 @@ h2 {
|
|||||||
|
|
||||||
.winners {
|
.winners {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-flow: wrap;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ballot-element {
|
.ballot-element {
|
||||||
|
|||||||
12
src/utils.js
Normal file
12
src/utils.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
const dateString = (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 `${ye}-${mo}-${da}`
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
dateString
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import VueRouter from "vue-router";
|
import VueRouter from "vue-router";
|
||||||
import { routes } from "@/routes/vinlottisRouter";
|
import { routes } from "@/router.js";
|
||||||
import Vinlottis from "@/Vinlottis";
|
import Vinlottis from "@/Vinlottis";
|
||||||
import VueAnalytics from "vue-analytics";
|
import VueAnalytics from "vue-analytics";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user