Draw winner page has: - Better user feedback - Knows how many wines are left - Separat button for starting sms prize distribution. - Edit, update and delete wine by id. RegisterWinePage: - Import wine by url. - Edit, update and delete wine by id.
357 lines
9.5 KiB
Vue
357 lines
9.5 KiB
Vue
<template>
|
|
<div class="page-container">
|
|
<h1>Trekk vinnere</h1>
|
|
|
|
<div class="draw-winner-container">
|
|
<div v-if="drawingWinner == false" class="draw-container">
|
|
<input type="number" v-model="winnersToDraw" />
|
|
<button class="vin-button no-margin" @click="startDrawingWinners">Trekk vinnere</button>
|
|
</div>
|
|
|
|
<div v-if="wines.length" class="wines-left">
|
|
<span>Antall vin igjen: {{ winnersToDraw }} av {{ wines.length }}</span>
|
|
</div>
|
|
|
|
<div v-if="drawingWinner == true">
|
|
<p>Trekker vinner {{ winners.length }} av {{ wines.length }}.</p>
|
|
<p>Neste trekning om {{ secondsLeft }} sekunder av {{ drawTime }}</p>
|
|
|
|
<div class="button-container draw-winner-actions">
|
|
<button class="vin-button danger" @click="stopDraw">
|
|
Stopp trekning
|
|
</button>
|
|
<button
|
|
class="vin-button"
|
|
:class="{ 'pulse-button': secondsLeft == 0 }"
|
|
:disabled="secondsLeft > 0"
|
|
@click="drawWinner"
|
|
>
|
|
Trekk neste
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="prize-distribution">
|
|
<h2>Prisutdeling</h2>
|
|
|
|
<div class="button-container">
|
|
<button class="vin-button" @click="startPrizeDistribution">Start automatisk prisutdeling med SMS</button>
|
|
</div>
|
|
</div>
|
|
|
|
<h2 v-if="winners.length > 0">Vinnere</h2>
|
|
<div class="winners" v-if="winners.length > 0">
|
|
<div :class="winner.color + '-raffle'" class="raffle-element" v-for="(winner, index) in winners" :key="index">
|
|
<span>{{ winner.name }}</span>
|
|
<span>Phone: {{ winner.phoneNumber }}</span>
|
|
<span>Rød: {{ winner.red }}</span>
|
|
<span>Blå: {{ winner.blue }}</span>
|
|
<span>Grønn: {{ winner.green }}</span>
|
|
<span>Gul: {{ winner.yellow }}</span>
|
|
|
|
<div class="button-container">
|
|
<button class="vin-button small" @click="editingWinner = editingWinner == winner ? false : winner">
|
|
{{ editingWinner == winner ? "Lukk" : "Rediger" }}
|
|
</button>
|
|
</div>
|
|
|
|
<div v-if="editingWinner == winner" class="edit">
|
|
<div class="label-div" v-for="key in Object.keys(winner)" :key="key">
|
|
<label>{{ key }}</label>
|
|
<input type="text" v-model="winner[key]" :placeholder="key" />
|
|
</div>
|
|
|
|
<div v-if="editingWinner == winner" class="button-container column">
|
|
<button class="vin-button small" @click="notifyWinner(winner)">
|
|
Send SMS
|
|
</button>
|
|
<button class="vin-button small warning" @click="updateWinner(winner)">
|
|
Oppdater
|
|
</button>
|
|
<button class="vin-button small danger" @click="deleteWinner(winner)">
|
|
Slett
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="button-container margin-md" v-if="winners.length > 0">
|
|
<button class="vin-button danger" v-if="winners.length > 0" @click="deleteAllWinners">
|
|
Slett virtuelle vinnere
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
wines: [],
|
|
drawingWinner: false,
|
|
secondsLeft: 20,
|
|
drawTime: 20,
|
|
winners: [],
|
|
editingWinner: undefined
|
|
};
|
|
},
|
|
created() {
|
|
this.fetchLotterWines();
|
|
this.fetchLotterWinners();
|
|
},
|
|
computed: {
|
|
winnersToDraw() {
|
|
if (this.wines.length == undefined || this.winners.length == undefined) {
|
|
return 0;
|
|
}
|
|
|
|
return this.wines.length - this.winners.length;
|
|
}
|
|
},
|
|
watch: {
|
|
winners(val) {
|
|
this.$emit("counter", val.length);
|
|
}
|
|
},
|
|
methods: {
|
|
fetchLotterWines() {
|
|
return fetch("/api/lottery/wines")
|
|
.then(resp => resp.json())
|
|
.then(response => (this.wines = response.wines));
|
|
},
|
|
fetchLotterWinners() {
|
|
return fetch("/api/lottery/winners")
|
|
.then(resp => resp.json())
|
|
.then(response => (this.winners = response.winners));
|
|
},
|
|
countdown() {
|
|
if (this.drawingWinner == false) {
|
|
return;
|
|
}
|
|
|
|
if (this.secondsLeft > 0) {
|
|
this.secondsLeft -= 1;
|
|
|
|
setTimeout(_ => {
|
|
this.countdown();
|
|
}, 1000);
|
|
} else {
|
|
if (this.winners.length == this.wines.length) {
|
|
this.drawingWinner = false;
|
|
}
|
|
}
|
|
},
|
|
startDrawingWinners() {
|
|
if (window.confirm("Er du sikker på at du vil trekke vinnere?")) {
|
|
this.drawWinner();
|
|
}
|
|
},
|
|
drawWinner() {
|
|
if (this.winnersToDraw <= 0) {
|
|
this.$toast.error({ title: "No more wines to draw" });
|
|
return;
|
|
}
|
|
this.secondsLeft = this.drawTime;
|
|
this.drawingWinner = true;
|
|
|
|
fetch("/api/lottery/draw")
|
|
.then(resp => resp.json())
|
|
.then(response => {
|
|
const { winner, color, success, message } = response;
|
|
|
|
if (success == false) {
|
|
this.$toast.error({ title: message });
|
|
return;
|
|
}
|
|
|
|
winner.color = color;
|
|
this.winners.push(winner);
|
|
this.countdown();
|
|
})
|
|
.catch(error => {
|
|
if (error) {
|
|
this.$toast.error({ title: error.message });
|
|
}
|
|
this.drawingWinner = false;
|
|
});
|
|
},
|
|
stopDraw() {
|
|
this.drawingWinner = false;
|
|
this.secondsLeft = this.drawTime;
|
|
},
|
|
startPrizeDistribution() {
|
|
if (!window.confirm("Er du sikker på at du vil starte prisutdeling?")) {
|
|
return;
|
|
}
|
|
|
|
this.drawingWinner = false;
|
|
|
|
const options = { method: "POST" };
|
|
fetch(`/api/lottery/prize-distribution/start`, options)
|
|
.then(resp => resp.json())
|
|
.then(response => {
|
|
if (response.success) {
|
|
this.$toast.info({
|
|
title: `Startet prisutdeling. SMS'er sendt ut!`
|
|
});
|
|
} else {
|
|
this.$toast.error({
|
|
title: `Klarte ikke starte prisutdeling`,
|
|
description: response.message
|
|
});
|
|
}
|
|
});
|
|
},
|
|
notifyWinner(winner) {
|
|
const options = { method: "POST" };
|
|
|
|
fetch(`/api/lottery/messages/winner/${winner.id}`, options)
|
|
.then(resp => resp.json())
|
|
.then(response => {
|
|
if (response.success) {
|
|
this.$toast.info({
|
|
title: `Sendte sms til vinner ${winner.name}.`
|
|
});
|
|
} else {
|
|
this.$toast.error({
|
|
title: `Klarte ikke sende sms til vinner ${winner.name}`,
|
|
description: response.message
|
|
});
|
|
}
|
|
});
|
|
},
|
|
updateWinner(winner) {
|
|
const options = {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ winner })
|
|
};
|
|
|
|
fetch(`/api/lottery/winner/${winner.id}`, options)
|
|
.then(resp => resp.json())
|
|
.then(response => {
|
|
if (response.success) {
|
|
this.$toast.info({
|
|
title: `Oppdaterte vinner ${winner.name}.`
|
|
});
|
|
} else {
|
|
this.$toast.error({
|
|
title: `Klarte ikke oppdatere vinner ${winner.name}`,
|
|
description: response.message
|
|
});
|
|
}
|
|
});
|
|
},
|
|
deleteWinner(winner) {
|
|
if (winner._id != null && window.confirm(`Er du sikker på at du vil slette vinner ${winner.name}?`)) {
|
|
const options = { method: "DELETE" };
|
|
|
|
fetch(`/api/lottery/winner/${winner.id}`, options)
|
|
.then(resp => resp.json())
|
|
.then(response => {
|
|
if (response.success) {
|
|
this.winners = this.winners.filter(w => w.id != winner.id);
|
|
|
|
this.$toast.info({
|
|
title: `Slettet vinner ${winner.name}.`
|
|
});
|
|
} else {
|
|
this.$toast.error({
|
|
title: `Klarte ikke slette vinner ${winner.name}`,
|
|
description: response.message
|
|
});
|
|
}
|
|
});
|
|
}
|
|
},
|
|
deleteAllWinners() {
|
|
if (window.confirm("Er du sikker på at du vil slette alle vinnere?")) {
|
|
const options = { method: "DELETE" };
|
|
|
|
fetch("/api/lottery/winners", options)
|
|
.then(resp => resp.json())
|
|
.then(response => {
|
|
if (response.success) {
|
|
this.winners = [];
|
|
this.$toast.info({
|
|
title: "Slettet alle vinnere."
|
|
});
|
|
} else {
|
|
this.$toast.error({
|
|
title: "Klarte ikke slette vinnere",
|
|
description: response.message
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.wines-left {
|
|
display: flex;
|
|
justify-content: center;
|
|
margin-top: 1rem;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.draw-container {
|
|
display: flex;
|
|
justify-content: center;
|
|
|
|
input {
|
|
font-size: 1.7rem;
|
|
padding: 7px;
|
|
margin: 0;
|
|
width: 10rem;
|
|
height: 3rem;
|
|
border: 1px solid rgba(#333333, 0.3);
|
|
}
|
|
}
|
|
|
|
.button-container {
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.draw-winner-actions {
|
|
justify-content: left;
|
|
}
|
|
|
|
.winners {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: center;
|
|
|
|
.raffle-element {
|
|
width: 220px;
|
|
height: 100%;
|
|
min-height: 250px;
|
|
font-size: 1.1rem;
|
|
padding: 1rem;
|
|
font-weight: 500;
|
|
// text-align: center;
|
|
|
|
-webkit-mask-size: cover;
|
|
-moz-mask-size: cover;
|
|
mask-size: cover;
|
|
flex-direction: column;
|
|
|
|
span:first-of-type {
|
|
font-weight: 600;
|
|
}
|
|
|
|
span.active {
|
|
margin-top: 3rem;
|
|
}
|
|
|
|
.edit {
|
|
padding: 1rem;
|
|
}
|
|
}
|
|
}
|
|
</style>
|