Add sms capabilities, and online-fixing of everything
This commit is contained in:
207
src/api.js
207
src/api.js
@@ -1,58 +1,49 @@
|
||||
const BASE_URL = __APIURL__ || window.location.origin;
|
||||
|
||||
const statistics = () => {
|
||||
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 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)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
return fetch(url.href).then(resp => resp.json());
|
||||
};
|
||||
|
||||
const highscoreStatistics = () => {
|
||||
const url = new URL("/api/highscore/statistics", BASE_URL)
|
||||
const url = new URL("/api/highscore/statistics", BASE_URL);
|
||||
|
||||
return fetch(url.href)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
return fetch(url.href).then(resp => resp.json());
|
||||
};
|
||||
|
||||
const overallWineStatistics = () => {
|
||||
const url = new URL("/api/wines/statistics/overall", BASE_URL)
|
||||
|
||||
return fetch(url.href)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
const url = new URL("/api/wines/statistics/overall", BASE_URL);
|
||||
|
||||
return fetch(url.href).then(resp => resp.json());
|
||||
};
|
||||
|
||||
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)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
return fetch(url.href).then(resp => resp.json());
|
||||
};
|
||||
|
||||
const chartPurchaseByColor = () => {
|
||||
const url = new URL("/api/purchase/statistics", BASE_URL)
|
||||
|
||||
return fetch(url.href)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
const url = new URL("/api/purchase/statistics", BASE_URL);
|
||||
|
||||
return fetch(url.href).then(resp => resp.json());
|
||||
};
|
||||
|
||||
const prelottery = () => {
|
||||
const url = new URL("/api/wines/prelottery", BASE_URL)
|
||||
const url = new URL("/api/wines/prelottery", BASE_URL);
|
||||
|
||||
return fetch(url.href)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
return fetch(url.href).then(resp => resp.json());
|
||||
};
|
||||
|
||||
const log = (sendObject) => {
|
||||
const url = new URL("/api/log", BASE_URL)
|
||||
const log = sendObject => {
|
||||
const url = new URL("/api/log", BASE_URL);
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
@@ -60,11 +51,10 @@ const log = (sendObject) => {
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(sendObject)
|
||||
}
|
||||
};
|
||||
|
||||
return fetch(url.href, options)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
return fetch(url.href, options).then(resp => resp.json());
|
||||
};
|
||||
|
||||
const addAttendee = sendObject => {
|
||||
const url = new URL("/api/virtual/attendee", BASE_URL);
|
||||
@@ -115,7 +105,7 @@ const deleteWinners = () => {
|
||||
};
|
||||
|
||||
return fetch(url.href, options).then(resp => resp.json());
|
||||
}
|
||||
};
|
||||
|
||||
const deleteAttendees = () => {
|
||||
const url = new URL("/api/virtual/attendees", BASE_URL);
|
||||
@@ -134,10 +124,10 @@ const attendees = () => {
|
||||
const url = new URL("/api/virtual/attendees", BASE_URL);
|
||||
|
||||
return fetch(url.href).then(resp => resp.json());
|
||||
}
|
||||
};
|
||||
|
||||
const logWines = (wines) => {
|
||||
const url = new URL("/api/log/wines", BASE_URL)
|
||||
const logWines = wines => {
|
||||
const url = new URL("/api/log/wines", BASE_URL);
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
@@ -145,95 +135,115 @@ const logWines = (wines) => {
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify(wines)
|
||||
}
|
||||
};
|
||||
|
||||
return fetch(url.href, options)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
return fetch(url.href, options).then(resp => resp.json());
|
||||
};
|
||||
|
||||
const wineSchema = () => {
|
||||
const url = new URL("/api/log/schema", BASE_URL)
|
||||
const url = new URL("/api/log/schema", BASE_URL);
|
||||
|
||||
return fetch(url.href)
|
||||
.then(resp => resp.json())
|
||||
}
|
||||
return fetch(url.href).then(resp => resp.json());
|
||||
};
|
||||
|
||||
const barcodeToVinmonopolet = (id) => {
|
||||
const url = new URL("/api/wineinfo/" + id, BASE_URL)
|
||||
const barcodeToVinmonopolet = id => {
|
||||
const url = new URL("/api/wineinfo/" + id, BASE_URL);
|
||||
|
||||
return fetch(url.href)
|
||||
.then(async (resp) => {
|
||||
if (!resp.ok) {
|
||||
if (resp.status == 404) {
|
||||
throw await resp.json()
|
||||
}
|
||||
} else {
|
||||
return resp.json()
|
||||
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)) {
|
||||
throw await resp.json()
|
||||
throw await resp.json();
|
||||
} else {
|
||||
console.error("Unexpected error occured when login/register user:", resp)
|
||||
throw await resp.json()
|
||||
console.error("Unexpected error occured when login/register user:", resp);
|
||||
throw await resp.json();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const login = (username, password) => {
|
||||
const url = new URL("/login", BASE_URL)
|
||||
const url = new URL("/login", BASE_URL);
|
||||
const options = {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify({ username, password })
|
||||
}
|
||||
};
|
||||
|
||||
return fetch(url.href, options)
|
||||
.then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json()
|
||||
} else {
|
||||
return handleErrors(resp)
|
||||
}
|
||||
})
|
||||
}
|
||||
return fetch(url.href, options).then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json();
|
||||
} else {
|
||||
return handleErrors(resp);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const register = (username, password) => {
|
||||
const url = new URL("/register", BASE_URL)
|
||||
const url = new URL("/register", BASE_URL);
|
||||
const options = {
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
method: "POST",
|
||||
body: JSON.stringify({ username, password })
|
||||
}
|
||||
};
|
||||
|
||||
return fetch(url.href, options)
|
||||
.then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json()
|
||||
} else {
|
||||
return handleErrors(resp)
|
||||
}
|
||||
})
|
||||
}
|
||||
return fetch(url.href, options).then(resp => {
|
||||
if (resp.ok) {
|
||||
return resp.json();
|
||||
} else {
|
||||
return handleErrors(resp);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const getChatHistory = (skip=null, take=null) => {
|
||||
const getChatHistory = (skip = null, take = null) => {
|
||||
const url = new URL("/api/chat/history", BASE_URL);
|
||||
if (!isNaN(skip))
|
||||
url.searchParams.append("skip", skip);
|
||||
if (!isNaN(take))
|
||||
url.searchParams.append("take", take);
|
||||
if (!isNaN(skip)) url.searchParams.append("skip", skip);
|
||||
if (!isNaN(take)) url.searchParams.append("take", take);
|
||||
|
||||
return fetch(url.href)
|
||||
.then(resp => resp.json())
|
||||
return fetch(url.href).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 {
|
||||
statistics,
|
||||
@@ -257,5 +267,8 @@ export {
|
||||
winnersSecure,
|
||||
deleteWinners,
|
||||
deleteAttendees,
|
||||
getChatHistory
|
||||
getChatHistory,
|
||||
finishedDraw,
|
||||
getAmIWinner,
|
||||
postWineChosen
|
||||
};
|
||||
|
||||
@@ -118,7 +118,8 @@ import {
|
||||
attendees,
|
||||
winnersSecure,
|
||||
deleteWinners,
|
||||
deleteAttendees
|
||||
deleteAttendees,
|
||||
finishedDraw
|
||||
} from "@/api";
|
||||
import RaffleGenerator from "@/ui/RaffleGenerator";
|
||||
|
||||
@@ -165,6 +166,9 @@ export default {
|
||||
this.socket.on("new_attendee", async msg => {
|
||||
this.getAttendees();
|
||||
});
|
||||
|
||||
window.finishedDraw = finishedDraw;
|
||||
console.log("here");
|
||||
},
|
||||
methods: {
|
||||
setWithRandomColors(colors) {
|
||||
@@ -209,6 +213,7 @@ export default {
|
||||
this.countdown();
|
||||
} else {
|
||||
this.drawingWinner = false;
|
||||
finishedDraw();
|
||||
}
|
||||
this.getWinners();
|
||||
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 VirtualLotteryPage from "@/components/VirtualLotteryPage";
|
||||
import WinnerPage from "@/components/WinnerPage";
|
||||
import LotteryPage from "@/components/LotteryPage";
|
||||
|
||||
const routes = [
|
||||
@@ -43,6 +43,10 @@ const routes = [
|
||||
{
|
||||
path: "/lottery/:tab",
|
||||
component: LotteryPage
|
||||
},
|
||||
{
|
||||
path: "/winner/:id",
|
||||
component: WinnerPage
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
<template>
|
||||
<div class="wine-container" :class="{ 'big': fullscreen }">
|
||||
<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" />
|
||||
</div>
|
||||
<div class="right">
|
||||
<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.price">{{ wine.price }} NOK</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>
|
||||
|
||||
<slot v-if="shouldUseInlineSlot()"></slot>
|
||||
@@ -36,15 +51,27 @@ export default {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
winner: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
shouldUseInlineSlot() {
|
||||
return this.inlineSlot && window.innerWidth > 768
|
||||
return this.inlineSlot && window.innerWidth > 768;
|
||||
},
|
||||
hostname(url) {
|
||||
const urlHostname = new URL(url).hostname
|
||||
return urlHostname.split(".")[(urlHostname.match(/\./g) || []).length - 1]
|
||||
const urlHostname = new URL(url).hostname;
|
||||
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>
|
||||
@import "./src/styles/media-queries";
|
||||
@import "./src/styles/global";
|
||||
@import "./src/styles/variables";
|
||||
|
||||
.wine-image {
|
||||
|
||||
Reference in New Issue
Block a user