Add sms capabilities, and online-fixing of everything
This commit is contained in:
70
api/message.js
Normal file
70
api/message.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
const request = require("request");
|
||||||
|
const path = require("path");
|
||||||
|
const config = require(path.join(__dirname + "/../config/defaults/lottery"));
|
||||||
|
|
||||||
|
async function sendMessage(winnerObject) {
|
||||||
|
console.log("sent message to ", winnerObject);
|
||||||
|
winnerObject.timestamp_sent = new Date().getTime();
|
||||||
|
winnerObject.timestamp_limit = new Date().getTime() * (600000 * 10000);
|
||||||
|
await winnerObject.save();
|
||||||
|
|
||||||
|
await sendMessageToUser(
|
||||||
|
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. /#/winner/${winnerObject.id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendMessageTooLate(winnerObject) {
|
||||||
|
await sendMessageToUser(
|
||||||
|
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.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendMessageToUser(phoneNumber, message) {
|
||||||
|
console.log("num", phoneNumber);
|
||||||
|
console.log("message", message);
|
||||||
|
|
||||||
|
request.post(
|
||||||
|
{
|
||||||
|
url: `https://gatewayapi.com/rest/mtsms?token=${config.token}`,
|
||||||
|
json: true,
|
||||||
|
body: {
|
||||||
|
sender: "Vinlottis",
|
||||||
|
message: message,
|
||||||
|
recipients: [{ msisdn: `47${phoneNumber}` }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(err, r, body) {
|
||||||
|
console.log(err ? err : body);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendUpdate(winners) {
|
||||||
|
let numbers = [];
|
||||||
|
for (let i = 0; i < winners.length; i++) {
|
||||||
|
numbers.push({ msisdn: `47${winners[i].phoneNumber}` });
|
||||||
|
}
|
||||||
|
request.post(
|
||||||
|
{
|
||||||
|
url: `https://gatewayapi.com/rest/mtsms?token=${config.token}`,
|
||||||
|
json: true,
|
||||||
|
body: {
|
||||||
|
sender: "Vinlottis",
|
||||||
|
message:
|
||||||
|
"Gratulerer som vinner av vinlottisen! Du vil snart få en SMS med oppdatering om hvordan gangen går!",
|
||||||
|
recipients: numbers
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(err, r, body) {
|
||||||
|
console.log(err ? err : body);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.sendUpdate = sendUpdate;
|
||||||
|
module.exports.sendMessage = sendMessage;
|
||||||
|
module.exports.sendMessageTooLate = sendMessageTooLate;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
const express = require("express");
|
const express = require("express");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
const crypto = require("crypto");
|
||||||
const mongoose = require("mongoose");
|
const mongoose = require("mongoose");
|
||||||
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
||||||
useNewUrlParser: true
|
useNewUrlParser: true
|
||||||
@@ -9,11 +10,13 @@ let io;
|
|||||||
const mustBeAuthenticated = require(path.join(
|
const mustBeAuthenticated = require(path.join(
|
||||||
__dirname + "/../middleware/mustBeAuthenticated"
|
__dirname + "/../middleware/mustBeAuthenticated"
|
||||||
));
|
));
|
||||||
|
const config = require(path.join(__dirname + "/../config/defaults/lottery"));
|
||||||
|
|
||||||
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 Message = require(path.join(__dirname + "/../api/message"));
|
||||||
|
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
next();
|
next();
|
||||||
@@ -133,7 +136,9 @@ router.route("/winner").get(mustBeAuthenticated, async (req, res) => {
|
|||||||
red: winner.red,
|
red: winner.red,
|
||||||
blue: winner.blue,
|
blue: winner.blue,
|
||||||
green: winner.green,
|
green: winner.green,
|
||||||
yellow: winner.yellow
|
yellow: winner.yellow,
|
||||||
|
id: sha512(winner.phoneNumber, genRandomString(10)),
|
||||||
|
timestamp_drawn: new Date().getTime()
|
||||||
});
|
});
|
||||||
|
|
||||||
await Attendee.update(
|
await Attendee.update(
|
||||||
@@ -145,6 +150,56 @@ router.route("/winner").get(mustBeAuthenticated, async (req, res) => {
|
|||||||
res.json(winner);
|
res.json(winner);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const genRandomString = function(length) {
|
||||||
|
return crypto
|
||||||
|
.randomBytes(Math.ceil(length / 2))
|
||||||
|
.toString("hex") /** convert to hexadecimal format */
|
||||||
|
.slice(0, length); /** return required number of characters */
|
||||||
|
};
|
||||||
|
|
||||||
|
const sha512 = function(password, salt) {
|
||||||
|
var hash = crypto.createHmac("md5", salt); /** Hashing algorithm sha512 */
|
||||||
|
hash.update(password);
|
||||||
|
var value = hash.digest("hex");
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
router.route("/finish").get(mustBeAuthenticated, async (req, res) => {
|
||||||
|
if (!config.token) {
|
||||||
|
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();
|
||||||
|
res.json(true);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
router.route("/attendees").get(async (req, res) => {
|
router.route("/attendees").get(async (req, res) => {
|
||||||
let attendees = await Attendee.find();
|
let attendees = await Attendee.find();
|
||||||
let attendeesRedacted = [];
|
let attendeesRedacted = [];
|
||||||
|
|||||||
173
api/virtualRegistration.js
Normal file
173
api/virtualRegistration.js
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
const express = require("express");
|
||||||
|
const path = require("path");
|
||||||
|
const router = express.Router();
|
||||||
|
const mongoose = require("mongoose");
|
||||||
|
|
||||||
|
mongoose.connect("mongodb://localhost:27017/vinlottis", {
|
||||||
|
useNewUrlParser: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const Message = require(path.join(__dirname + "/../api/message"));
|
||||||
|
const config = require(path.join(__dirname + "/../config/defaults/push"));
|
||||||
|
const VirtualWinner = require(path.join(
|
||||||
|
__dirname + "/../schemas/VirtualWinner"
|
||||||
|
));
|
||||||
|
const Highscore = require(path.join(__dirname + "/../schemas/Highscore"));
|
||||||
|
const Wine = require(path.join(__dirname + "/../schemas/Wine"));
|
||||||
|
const PreLotteryWine = require(path.join(
|
||||||
|
__dirname + "/../schemas/PreLotteryWine"
|
||||||
|
));
|
||||||
|
const lotteryConfig = require(path.join(
|
||||||
|
__dirname + "/../config/defaults/lottery"
|
||||||
|
));
|
||||||
|
|
||||||
|
router.use((req, res, next) => {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
router.route("/winner/:id").get((req, res) => {
|
||||||
|
res.redirect(`/#/winner/${req.params.id}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.route("/:id").get(async (req, res) => {
|
||||||
|
let id = req.params.id;
|
||||||
|
|
||||||
|
let foundWinner = await VirtualWinner.findOne({ id: id });
|
||||||
|
|
||||||
|
if (!foundWinner) {
|
||||||
|
res.json({
|
||||||
|
existing: false,
|
||||||
|
turn: false
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let allWinners = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
|
||||||
|
|
||||||
|
if (
|
||||||
|
allWinners[0].id != foundWinner.id ||
|
||||||
|
foundWinner.timestamp_limit == undefined ||
|
||||||
|
foundWinner.timestamp_sent == undefined
|
||||||
|
) {
|
||||||
|
res.json({ existing: true, turn: false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
existing: true,
|
||||||
|
turn: true,
|
||||||
|
name: foundWinner.name,
|
||||||
|
color: foundWinner.color
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
router.route("/:id").post(async (req, res) => {
|
||||||
|
let id = req.params.id;
|
||||||
|
let wineName = req.body.wineName;
|
||||||
|
let foundWinner = await VirtualWinner.findOne({ id: id });
|
||||||
|
|
||||||
|
if (!foundWinner) {
|
||||||
|
res.json(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundWinner.timestamp_limit < new Date().getTime()) {
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
limit: true
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let date = new Date();
|
||||||
|
|
||||||
|
let prelotteryWine = await PreLotteryWine.findOne({ name: wineName });
|
||||||
|
|
||||||
|
if (!prelotteryWine) {
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
wine: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let wonWine = await Wine.findOne({ name: prelotteryWine.name });
|
||||||
|
if (wonWine == undefined) {
|
||||||
|
let newWonWine = new Wine({
|
||||||
|
name: prelotteryWine.name,
|
||||||
|
vivinoLink: prelotteryWine.vivinoLink,
|
||||||
|
rating: prelotteryWine.rating,
|
||||||
|
occurences: 1,
|
||||||
|
image: prelotteryWine.image,
|
||||||
|
id: prelotteryWine.id
|
||||||
|
});
|
||||||
|
await newWonWine.save();
|
||||||
|
wonWine = newWonWine;
|
||||||
|
} else {
|
||||||
|
wonWine.occurences += 1;
|
||||||
|
wonWine.image = prelotteryWine.image;
|
||||||
|
wonWine.id = prelotteryWine.id;
|
||||||
|
await wonWine.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
await prelotteryWine.delete();
|
||||||
|
|
||||||
|
const person = await Highscore.findOne({
|
||||||
|
name: foundWinner.name
|
||||||
|
});
|
||||||
|
|
||||||
|
if (person == undefined) {
|
||||||
|
let newPerson = new Highscore({
|
||||||
|
name: foundWinner.name,
|
||||||
|
wins: [
|
||||||
|
{
|
||||||
|
color: foundWinner.color,
|
||||||
|
date: date,
|
||||||
|
wine: wonWine
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
await newPerson.save();
|
||||||
|
} else {
|
||||||
|
person.wins.push({
|
||||||
|
color: foundWinner.color,
|
||||||
|
date: date,
|
||||||
|
wine: wonWine
|
||||||
|
});
|
||||||
|
person.markModified("wins");
|
||||||
|
await person.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
await foundWinner.delete();
|
||||||
|
|
||||||
|
let nextWinner = await VirtualWinner.find().sort({ timestamp_drawn: 1 });
|
||||||
|
if (nextWinner.length > 0) {
|
||||||
|
Message.sendMessage(nextWinner[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
startTimeout(id);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
function startTimeout(id) {
|
||||||
|
setTimeout(async () => {
|
||||||
|
let virtualWinner = await VirtualWinner.findOne({ id: id });
|
||||||
|
if (!virtualWinner) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Message.sendMessageTooLate(virtualWinner);
|
||||||
|
|
||||||
|
virtualWinner.timestamp_drawn;
|
||||||
|
virtualWinner.timestamp_limit = null;
|
||||||
|
virtualWinner.timestamp_sent = null;
|
||||||
|
|
||||||
|
await virtualWinner.save();
|
||||||
|
}, 600000);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -11,6 +11,7 @@ try {
|
|||||||
message: "INSERT MESSAGE",
|
message: "INSERT MESSAGE",
|
||||||
date: 5,
|
date: 5,
|
||||||
hours: 15,
|
hours: 15,
|
||||||
apiUrl: "https://lottis.vin"
|
apiUrl: "https://lottis.vin",
|
||||||
|
token: undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
3
config/env/lottery.config.example.js
vendored
3
config/env/lottery.config.example.js
vendored
@@ -5,5 +5,6 @@ module.exports = {
|
|||||||
message: "VINLOTTERI",
|
message: "VINLOTTERI",
|
||||||
date: 5,
|
date: 5,
|
||||||
hours: 15,
|
hours: 15,
|
||||||
apiUrl: undefined
|
apiUrl: undefined,
|
||||||
|
token: undefined
|
||||||
};
|
};
|
||||||
|
|||||||
36
package-lock.json
generated
36
package-lock.json
generated
@@ -9302,9 +9302,9 @@
|
|||||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
||||||
},
|
},
|
||||||
"psl": {
|
"psl": {
|
||||||
"version": "1.7.0",
|
"version": "1.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||||
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
|
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||||
},
|
},
|
||||||
"public-encrypt": {
|
"public-encrypt": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
@@ -9356,7 +9356,8 @@
|
|||||||
"punycode": {
|
"punycode": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"q": {
|
"q": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
@@ -9821,9 +9822,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"request": {
|
"request": {
|
||||||
"version": "2.88.0",
|
"version": "2.88.2",
|
||||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
|
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||||
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
|
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"aws-sign2": "~0.7.0",
|
"aws-sign2": "~0.7.0",
|
||||||
"aws4": "^1.8.0",
|
"aws4": "^1.8.0",
|
||||||
@@ -9832,7 +9833,7 @@
|
|||||||
"extend": "~3.0.2",
|
"extend": "~3.0.2",
|
||||||
"forever-agent": "~0.6.1",
|
"forever-agent": "~0.6.1",
|
||||||
"form-data": "~2.3.2",
|
"form-data": "~2.3.2",
|
||||||
"har-validator": "~5.1.0",
|
"har-validator": "~5.1.3",
|
||||||
"http-signature": "~1.2.0",
|
"http-signature": "~1.2.0",
|
||||||
"is-typedarray": "~1.0.0",
|
"is-typedarray": "~1.0.0",
|
||||||
"isstream": "~0.1.2",
|
"isstream": "~0.1.2",
|
||||||
@@ -9842,7 +9843,7 @@
|
|||||||
"performance-now": "^2.1.0",
|
"performance-now": "^2.1.0",
|
||||||
"qs": "~6.5.2",
|
"qs": "~6.5.2",
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"tough-cookie": "~2.4.3",
|
"tough-cookie": "~2.5.0",
|
||||||
"tunnel-agent": "^0.6.0",
|
"tunnel-agent": "^0.6.0",
|
||||||
"uuid": "^3.3.2"
|
"uuid": "^3.3.2"
|
||||||
},
|
},
|
||||||
@@ -11394,12 +11395,19 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"tough-cookie": {
|
"tough-cookie": {
|
||||||
"version": "2.4.3",
|
"version": "2.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"psl": "^1.1.24",
|
"psl": "^1.1.28",
|
||||||
"punycode": "^1.4.1"
|
"punycode": "^2.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trim-newlines": {
|
"trim-newlines": {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
"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",
|
||||||
|
|||||||
@@ -8,7 +8,11 @@ const VirtualWinner = new Schema({
|
|||||||
green: Number,
|
green: Number,
|
||||||
blue: Number,
|
blue: Number,
|
||||||
red: Number,
|
red: Number,
|
||||||
yellow: Number
|
yellow: Number,
|
||||||
|
id: String,
|
||||||
|
timestamp_drawn: Number,
|
||||||
|
timestamp_sent: Number,
|
||||||
|
timestamp_limit: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = mongoose.model("VirtualWinner", VirtualWinner);
|
module.exports = mongoose.model("VirtualWinner", VirtualWinner);
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ 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 wineinfoApi = require(path.join(__dirname + "/api/wineinfo"));
|
||||||
const virtualApi = require(path.join(__dirname + "/api/virtualLottery"));
|
const virtualApi = require(path.join(__dirname + "/api/virtualLottery"));
|
||||||
|
const virtualRegistrationApi = require(path.join(
|
||||||
|
__dirname + "/api/virtualRegistration"
|
||||||
|
));
|
||||||
|
|
||||||
//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);
|
||||||
@@ -92,6 +95,7 @@ app.use("/api/", retrieveApi);
|
|||||||
app.use("/api/", wineinfoApi);
|
app.use("/api/", wineinfoApi);
|
||||||
app.use("/api/", chatHistory);
|
app.use("/api/", chatHistory);
|
||||||
app.use("/api/virtual/", virtualApi(io));
|
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) {
|
||||||
|
|||||||
207
src/api.js
207
src/api.js
@@ -1,58 +1,49 @@
|
|||||||
const BASE_URL = __APIURL__ || window.location.origin;
|
const BASE_URL = __APIURL__ || window.location.origin;
|
||||||
|
|
||||||
const statistics = () => {
|
const statistics = () => {
|
||||||
const url = new URL('/api/purchase/statistics', BASE_URL)
|
const url = new URL("/api/purchase/statistics", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const colorStatistics = () => {
|
const colorStatistics = () => {
|
||||||
const url = new URL("/api/purchase/statistics/color", BASE_URL)
|
const url = new URL("/api/purchase/statistics/color", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const highscoreStatistics = () => {
|
const highscoreStatistics = () => {
|
||||||
const url = new URL("/api/highscore/statistics", BASE_URL)
|
const url = new URL("/api/highscore/statistics", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const overallWineStatistics = () => {
|
const overallWineStatistics = () => {
|
||||||
const url = new URL("/api/wines/statistics/overall", BASE_URL)
|
const url = new URL("/api/wines/statistics/overall", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
|
||||||
.then(resp => resp.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return fetch(url.href).then(resp => resp.json());
|
||||||
|
};
|
||||||
|
|
||||||
const chartWinsByColor = () => {
|
const chartWinsByColor = () => {
|
||||||
const url = new URL("/api/purchase/statistics/color", BASE_URL)
|
const url = new URL("/api/purchase/statistics/color", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const chartPurchaseByColor = () => {
|
const chartPurchaseByColor = () => {
|
||||||
const url = new URL("/api/purchase/statistics", BASE_URL)
|
const url = new URL("/api/purchase/statistics", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
|
||||||
.then(resp => resp.json())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return fetch(url.href).then(resp => resp.json());
|
||||||
|
};
|
||||||
|
|
||||||
const prelottery = () => {
|
const prelottery = () => {
|
||||||
const url = new URL("/api/wines/prelottery", BASE_URL)
|
const url = new URL("/api/wines/prelottery", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const log = (sendObject) => {
|
const log = sendObject => {
|
||||||
const url = new URL("/api/log", BASE_URL)
|
const url = new URL("/api/log", BASE_URL);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -60,11 +51,10 @@ const log = (sendObject) => {
|
|||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(sendObject)
|
body: JSON.stringify(sendObject)
|
||||||
}
|
};
|
||||||
|
|
||||||
return fetch(url.href, options)
|
return fetch(url.href, options).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const addAttendee = sendObject => {
|
const addAttendee = sendObject => {
|
||||||
const url = new URL("/api/virtual/attendee", BASE_URL);
|
const url = new URL("/api/virtual/attendee", BASE_URL);
|
||||||
@@ -115,7 +105,7 @@ const deleteWinners = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return fetch(url.href, options).then(resp => resp.json());
|
return fetch(url.href, options).then(resp => resp.json());
|
||||||
}
|
};
|
||||||
|
|
||||||
const deleteAttendees = () => {
|
const deleteAttendees = () => {
|
||||||
const url = new URL("/api/virtual/attendees", BASE_URL);
|
const url = new URL("/api/virtual/attendees", BASE_URL);
|
||||||
@@ -134,10 +124,10 @@ const attendees = () => {
|
|||||||
const url = new URL("/api/virtual/attendees", BASE_URL);
|
const url = new URL("/api/virtual/attendees", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href).then(resp => resp.json());
|
return fetch(url.href).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);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -145,95 +135,115 @@ const logWines = (wines) => {
|
|||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(wines)
|
body: JSON.stringify(wines)
|
||||||
}
|
};
|
||||||
|
|
||||||
return fetch(url.href, options)
|
return fetch(url.href, options).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const wineSchema = () => {
|
const wineSchema = () => {
|
||||||
const url = new URL("/api/log/schema", BASE_URL)
|
const url = new URL("/api/log/schema", BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const barcodeToVinmonopolet = (id) => {
|
const barcodeToVinmonopolet = id => {
|
||||||
const url = new URL("/api/wineinfo/" + id, BASE_URL)
|
const url = new URL("/api/wineinfo/" + id, BASE_URL);
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href).then(async resp => {
|
||||||
.then(async (resp) => {
|
if (!resp.ok) {
|
||||||
if (!resp.ok) {
|
if (resp.status == 404) {
|
||||||
if (resp.status == 404) {
|
throw await resp.json();
|
||||||
throw await resp.json()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 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();
|
||||||
} else {
|
} else {
|
||||||
console.error("Unexpected error occured when login/register user:", resp)
|
console.error("Unexpected error occured when login/register user:", resp);
|
||||||
throw await resp.json()
|
throw await resp.json();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const login = (username, password) => {
|
const login = (username, password) => {
|
||||||
const url = new URL("/login", BASE_URL)
|
const url = new URL("/login", BASE_URL);
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ username, password })
|
body: JSON.stringify({ username, password })
|
||||||
}
|
};
|
||||||
|
|
||||||
return fetch(url.href, options)
|
return fetch(url.href, options).then(resp => {
|
||||||
.then(resp => {
|
if (resp.ok) {
|
||||||
if (resp.ok) {
|
return resp.json();
|
||||||
return resp.json()
|
} else {
|
||||||
} else {
|
return handleErrors(resp);
|
||||||
return handleErrors(resp)
|
}
|
||||||
}
|
});
|
||||||
})
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const register = (username, password) => {
|
const register = (username, password) => {
|
||||||
const url = new URL("/register", BASE_URL)
|
const url = new URL("/register", BASE_URL);
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
},
|
},
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ username, password })
|
body: JSON.stringify({ username, password })
|
||||||
}
|
};
|
||||||
|
|
||||||
return fetch(url.href, options)
|
return fetch(url.href, options).then(resp => {
|
||||||
.then(resp => {
|
if (resp.ok) {
|
||||||
if (resp.ok) {
|
return resp.json();
|
||||||
return resp.json()
|
} else {
|
||||||
} else {
|
return handleErrors(resp);
|
||||||
return handleErrors(resp)
|
}
|
||||||
}
|
});
|
||||||
})
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
const getChatHistory = (skip = null, take = null) => {
|
||||||
const getChatHistory = (skip=null, take=null) => {
|
|
||||||
const url = new URL("/api/chat/history", BASE_URL);
|
const url = new URL("/api/chat/history", BASE_URL);
|
||||||
if (!isNaN(skip))
|
if (!isNaN(skip)) url.searchParams.append("skip", skip);
|
||||||
url.searchParams.append("skip", skip);
|
if (!isNaN(take)) url.searchParams.append("take", take);
|
||||||
if (!isNaN(take))
|
|
||||||
url.searchParams.append("take", take);
|
|
||||||
|
|
||||||
return fetch(url.href)
|
return fetch(url.href).then(resp => resp.json());
|
||||||
.then(resp => resp.json())
|
};
|
||||||
|
|
||||||
}
|
const finishedDraw = () => {
|
||||||
|
const url = new URL("/api/virtual/finish", BASE_URL);
|
||||||
|
|
||||||
|
return fetch(url.href).then(resp => resp.json());
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAmIWinner = id => {
|
||||||
|
const url = new URL(`/api/virtual-registration/${id}`, BASE_URL);
|
||||||
|
return fetch(url.href).then(resp => resp.json());
|
||||||
|
};
|
||||||
|
|
||||||
|
const postWineChosen = (id, wineName) => {
|
||||||
|
const url = new URL(`/api/virtual-registration/${id}`, BASE_URL);
|
||||||
|
const options = {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ wineName: wineName })
|
||||||
|
};
|
||||||
|
|
||||||
|
return fetch(url.href, options).then(resp => {
|
||||||
|
if (resp.ok) {
|
||||||
|
return resp.json();
|
||||||
|
} else {
|
||||||
|
return handleErrors(resp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
statistics,
|
statistics,
|
||||||
@@ -257,5 +267,8 @@ export {
|
|||||||
winnersSecure,
|
winnersSecure,
|
||||||
deleteWinners,
|
deleteWinners,
|
||||||
deleteAttendees,
|
deleteAttendees,
|
||||||
getChatHistory
|
getChatHistory,
|
||||||
|
finishedDraw,
|
||||||
|
getAmIWinner,
|
||||||
|
postWineChosen
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -118,7 +118,8 @@ import {
|
|||||||
attendees,
|
attendees,
|
||||||
winnersSecure,
|
winnersSecure,
|
||||||
deleteWinners,
|
deleteWinners,
|
||||||
deleteAttendees
|
deleteAttendees,
|
||||||
|
finishedDraw
|
||||||
} from "@/api";
|
} from "@/api";
|
||||||
import RaffleGenerator from "@/ui/RaffleGenerator";
|
import RaffleGenerator from "@/ui/RaffleGenerator";
|
||||||
|
|
||||||
@@ -165,6 +166,9 @@ export default {
|
|||||||
this.socket.on("new_attendee", async msg => {
|
this.socket.on("new_attendee", async msg => {
|
||||||
this.getAttendees();
|
this.getAttendees();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
window.finishedDraw = finishedDraw;
|
||||||
|
console.log("here");
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setWithRandomColors(colors) {
|
setWithRandomColors(colors) {
|
||||||
@@ -209,6 +213,7 @@ export default {
|
|||||||
this.countdown();
|
this.countdown();
|
||||||
} else {
|
} else {
|
||||||
this.drawingWinner = false;
|
this.drawingWinner = false;
|
||||||
|
finishedDraw();
|
||||||
}
|
}
|
||||||
this.getWinners();
|
this.getWinners();
|
||||||
this.getAttendees();
|
this.getAttendees();
|
||||||
|
|||||||
102
src/components/WinnerPage.vue
Normal file
102
src/components/WinnerPage.vue
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div v-if="!posted">
|
||||||
|
<h1 v-if="name">Gratulerer {{name}}!</h1>
|
||||||
|
<p
|
||||||
|
v-if="name"
|
||||||
|
>Her er valgene for dagens lotteri, du har 10 minutter å velge etter du fikk SMS-en.</p>
|
||||||
|
<h1 v-else-if="!turn && !existing" class="sent-container">Finner ikke noen vinner her..</h1>
|
||||||
|
<h1 v-else-if="!turn" class="sent-container">Du må vente på tur..</h1>
|
||||||
|
<div class="wines-container" v-if="name">
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<Wine
|
||||||
|
:wine="wine"
|
||||||
|
v-for="wine in wines"
|
||||||
|
:key="wine"
|
||||||
|
:winner="true"
|
||||||
|
:fullscreen="true"
|
||||||
|
:inlineSlot="true"
|
||||||
|
v-on:chosen="chosenWine"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="posted" class="sent-container">
|
||||||
|
<h1>Valget ditt er sendt inn!</h1>
|
||||||
|
<p>Du får mer info om henting snarest!</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getAmIWinner, postWineChosen } from "@/api";
|
||||||
|
import Wine from "@/ui/Wine";
|
||||||
|
export default {
|
||||||
|
components: { Wine },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
id: null,
|
||||||
|
existing: false,
|
||||||
|
fetched: false,
|
||||||
|
turn: false,
|
||||||
|
name: null,
|
||||||
|
wines: [],
|
||||||
|
posted: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
this.id = this.$router.currentRoute.params.id;
|
||||||
|
|
||||||
|
let winnerObject = await getAmIWinner(this.id);
|
||||||
|
this.fetched = true;
|
||||||
|
if (!winnerObject || !winnerObject.existing) {
|
||||||
|
console.error("non existing", winnerObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.existing = true;
|
||||||
|
if (winnerObject.existing && !winnerObject.turn) {
|
||||||
|
console.error("not your turn yet", winnerObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.turn = true;
|
||||||
|
this.name = winnerObject.name;
|
||||||
|
const _wines = await fetch("/api/wines/prelottery");
|
||||||
|
this.wines = await _wines.json();
|
||||||
|
console.log(this.wines);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
chosenWine: async function(name) {
|
||||||
|
console.log("chosen a wine");
|
||||||
|
let posted = await postWineChosen(this.id, name);
|
||||||
|
console.log("response", posted);
|
||||||
|
if (posted.success) {
|
||||||
|
this.posted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "./src/styles/global";
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
.sent-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 90vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wines-container {
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -8,7 +8,7 @@ import CreatePage from "@/components/CreatePage";
|
|||||||
|
|
||||||
import AdminPage from "@/components/AdminPage";
|
import AdminPage from "@/components/AdminPage";
|
||||||
|
|
||||||
import VirtualLotteryPage from "@/components/VirtualLotteryPage";
|
import WinnerPage from "@/components/WinnerPage";
|
||||||
import LotteryPage from "@/components/LotteryPage";
|
import LotteryPage from "@/components/LotteryPage";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
@@ -43,6 +43,10 @@ const routes = [
|
|||||||
{
|
{
|
||||||
path: "/lottery/:tab",
|
path: "/lottery/:tab",
|
||||||
component: LotteryPage
|
component: LotteryPage
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/winner/:id",
|
||||||
|
component: WinnerPage
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="wine-container" :class="{ 'big': fullscreen }">
|
<div class="wine-container" :class="{ 'big': fullscreen }">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<img v-if="wine.image" :src="wine.image" class="wine-image" :class="{ 'fullscreen': fullscreen }" />
|
<img
|
||||||
|
v-if="wine.image"
|
||||||
|
:src="wine.image"
|
||||||
|
class="wine-image"
|
||||||
|
:class="{ 'fullscreen': fullscreen }"
|
||||||
|
/>
|
||||||
<img v-else class="wine-placeholder" alt="Wine image" />
|
<img v-else class="wine-placeholder" alt="Wine image" />
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<div>
|
<div>
|
||||||
<h2 v-if="wine.name">{{ wine.name }}</h2><h2 v-else>(no name)</h2>
|
<h2 v-if="wine.name">{{ wine.name }}</h2>
|
||||||
|
<h2 v-else>(no name)</h2>
|
||||||
<span v-if="wine.rating">{{ wine.rating }} rating</span>
|
<span v-if="wine.rating">{{ wine.rating }} rating</span>
|
||||||
<span v-if="wine.price">{{ wine.price }} NOK</span>
|
<span v-if="wine.price">{{ wine.price }} NOK</span>
|
||||||
<span v-if="wine.country">{{ wine.country }}</span>
|
<span v-if="wine.country">{{ wine.country }}</span>
|
||||||
|
|
||||||
<a v-if="wine.vivinoLink" :href="wine.vivinoLink" class="wine-link">Les mer på {{ hostname(wine.vivinoLink) }}</a>
|
<a
|
||||||
|
v-if="wine.vivinoLink"
|
||||||
|
:href="wine.vivinoLink"
|
||||||
|
class="wine-link"
|
||||||
|
>Les mer på {{ hostname(wine.vivinoLink) }}</a>
|
||||||
|
<button
|
||||||
|
v-if="winner"
|
||||||
|
@click="choseWine(wine.name)"
|
||||||
|
class="vin-button"
|
||||||
|
>Velg dette som din vin</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<slot v-if="shouldUseInlineSlot()"></slot>
|
<slot v-if="shouldUseInlineSlot()"></slot>
|
||||||
@@ -36,15 +51,27 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: false,
|
required: false,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
winner: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
shouldUseInlineSlot() {
|
shouldUseInlineSlot() {
|
||||||
return this.inlineSlot && window.innerWidth > 768
|
return this.inlineSlot && window.innerWidth > 768;
|
||||||
},
|
},
|
||||||
hostname(url) {
|
hostname(url) {
|
||||||
const urlHostname = new URL(url).hostname
|
const urlHostname = new URL(url).hostname;
|
||||||
return urlHostname.split(".")[(urlHostname.match(/\./g) || []).length - 1]
|
return urlHostname.split(".")[
|
||||||
|
(urlHostname.match(/\./g) || []).length - 1
|
||||||
|
];
|
||||||
|
},
|
||||||
|
choseWine(name) {
|
||||||
|
if (window.confirm(`Er du sikker på at du vil ha ${name}?`)) {
|
||||||
|
this.$emit("chosen", name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -52,6 +79,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "./src/styles/media-queries";
|
@import "./src/styles/media-queries";
|
||||||
|
@import "./src/styles/global";
|
||||||
@import "./src/styles/variables";
|
@import "./src/styles/variables";
|
||||||
|
|
||||||
.wine-image {
|
.wine-image {
|
||||||
|
|||||||
Reference in New Issue
Block a user