Refactor/Virtual lottery #70
@@ -166,8 +166,16 @@ const drawWinner = async (req, res) => {
|
||||
Math.floor(Math.random() * attendeeListDemocratic.length)
|
||||
];
|
||||
|
||||
let winners = await VirtualWinner.find({ timestamp_sent: undefined }).sort({
|
||||
timestamp_drawn: 1
|
||||
});
|
||||
|
||||
var io = req.app.get('socketio');
|
||||
io.emit("winner", { color: colorToChooseFrom, name: winner.name });
|
||||
io.emit("winner", {
|
||||
color: colorToChooseFrom,
|
||||
name: winner.name,
|
||||
winner_count: winners.length + 1
|
||||
});
|
||||
|
||||
let newWinnerElement = new VirtualWinner({
|
||||
name: winner.name,
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<router-link to="/lottery/game" class="participate-button">
|
||||
<router-link to="/lottery" class="participate-button">
|
||||
<i class="icon icon--arrow-right"></i>
|
||||
<p>Trykk her for å delta</p>
|
||||
</router-link>
|
||||
|
||||
<router-link to="/lottery/generate" class="see-details-link">
|
||||
<router-link to="/generate" class="see-details-link">
|
||||
Se vipps detaljer og QR-kode
|
||||
</router-link>
|
||||
|
||||
|
||||
@@ -1,48 +1,60 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1 class="title">Virtuelt lotteri</h1>
|
||||
<h2
|
||||
v-if="
|
||||
attendees.length <= 0 &&
|
||||
winners.length <= 0 &&
|
||||
attendeesFetched &&
|
||||
winnersFetched
|
||||
"
|
||||
>Her var det lite.. Sikker på at det er en virtuell trekning nå?</h2>
|
||||
<div class="title-info">
|
||||
<h2>Send vipps med melding "Vinlotteri" for å bli registrert til virtuelt lotteri</h2>
|
||||
<p>Send gjerne melding om fargeønsker også</p>
|
||||
</div>
|
||||
|
||||
<router-link to="/dagens" class="generate-link" v-if="todayExists">
|
||||
Lurer du på dagens fangst?
|
||||
<span class="subtext generator-link">Se her</span>
|
||||
</router-link>
|
||||
|
||||
<hr />
|
||||
|
||||
<h2>Live oversikt av lodd kjøp i dag</h2>
|
||||
<div class="colors">
|
||||
<div v-for="color in Object.keys(ticketsBought)" :class="color + ' colors-box'" :key="color">
|
||||
<div class="colors-overlay">
|
||||
<p>{{ ticketsBought[color] }} kjøpt</p>
|
||||
<header ref="header">
|
||||
<div class="container">
|
||||
<div class="instructions">
|
||||
<h1 class="title">Virtuelt lotteri</h1>
|
||||
<ol>
|
||||
<li>Vurder om du ønsker å bruke <router-link to="/generate" class="vin-link">loddgeneratoren</router-link>, eller sjekke ut <router-link to="/dagens" class="vin-link">dagens fangst.</router-link></li>
|
||||
<li>Send vipps med melding "Vinlotteri" for å bli registrert til lotteriet.</li>
|
||||
<li>Send gjerne melding om fargeønske også.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<Vipps :amount="1" class="vipps-qr desktop-only" />
|
||||
|
||||
<VippsPill class="vipps-pill mobile-only" />
|
||||
|
||||
<p class="call-to-action">
|
||||
<span class="vin-link">Følg med på utviklingen</span> og <span class="vin-link">chat om trekningen</span>
|
||||
<i class="icon icon--arrow-left" @click="scrollToContent"></i></p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="container" ref="content">
|
||||
<WinnerDraw
|
||||
:currentWinnerDrawn="currentWinnerDrawn"
|
||||
:currentWinner="currentWinner"
|
||||
:attendees="attendees"
|
||||
/>
|
||||
|
||||
<div class="todays-raffles">
|
||||
<h2>Liste av lodd kjøpt i dag</h2>
|
||||
|
||||
<div class="raffle-container">
|
||||
<div v-for="color in Object.keys(ticketsBought)" :class="color + '-raffle raffle-element'" :key="color">
|
||||
<span>{{ ticketsBought[color] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Winners :winners="winners" class="winners" :drawing="currentWinner" />
|
||||
|
||||
<div class="container-attendees">
|
||||
<h2>Deltakere ({{ attendees.length }})</h2>
|
||||
<Attendees :attendees="attendees" class="attendees" />
|
||||
</div>
|
||||
|
||||
<div class="container-chat">
|
||||
<h2>Chat</h2>
|
||||
<Chat class="chat" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WinnerDraw
|
||||
:currentWinnerDrawn="currentWinnerDrawn"
|
||||
:currentWinner="currentWinner"
|
||||
:attendees="attendees"
|
||||
/>
|
||||
|
||||
<Winners :winners="winners" />
|
||||
<hr />
|
||||
<div class="middle-elements">
|
||||
<Attendees :attendees="attendees" class="outer-attendees" />
|
||||
<Chat class="outer-chat" />
|
||||
<div class="container wines-container">
|
||||
<h2>Dagens fangst</h2>
|
||||
<Wine :wine="wine" v-for="wine in wines" :key="wine" />
|
||||
</div>
|
||||
<Vipps class="vipps" :amount="1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -50,22 +62,24 @@
|
||||
import { attendees, winners, prelottery } from "@/api";
|
||||
import Chat from "@/ui/Chat";
|
||||
import Vipps from "@/ui/Vipps";
|
||||
import VippsPill from "@/ui/VippsPill";
|
||||
import Attendees from "@/ui/Attendees";
|
||||
import Wine from "@/ui/Wine";
|
||||
import Winners from "@/ui/Winners";
|
||||
import WinnerDraw from "@/ui/WinnerDraw";
|
||||
import io from "socket.io-client";
|
||||
|
||||
export default {
|
||||
components: { Chat, Attendees, Winners, WinnerDraw, Vipps },
|
||||
components: { Chat, Attendees, Winners, WinnerDraw, Vipps, VippsPill, Wine },
|
||||
data() {
|
||||
return {
|
||||
attendees: [],
|
||||
winners: [],
|
||||
wines: [],
|
||||
currentWinnerDrawn: false,
|
||||
currentWinner: {},
|
||||
currentWinner: null,
|
||||
socket: null,
|
||||
attendeesFetched: false,
|
||||
winnersFetched: false,
|
||||
wasDisconnected: false,
|
||||
ticketsBought: {}
|
||||
};
|
||||
@@ -73,6 +87,7 @@ export default {
|
||||
mounted() {
|
||||
this.track();
|
||||
this.getAttendees();
|
||||
this.getTodaysWines();
|
||||
this.getWinners();
|
||||
this.socket = io(window.location.origin);
|
||||
this.socket.on("color_winner", msg => {});
|
||||
@@ -83,14 +98,18 @@ export default {
|
||||
|
||||
this.socket.on("winner", async msg => {
|
||||
this.currentWinnerDrawn = true;
|
||||
this.currentWinner = { name: msg.name, color: msg.color };
|
||||
this.currentWinner = {
|
||||
name: msg.name,
|
||||
color: msg.color,
|
||||
winnerCount: msg.winner_count
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
this.getWinners();
|
||||
this.getAttendees();
|
||||
this.currentWinner = null;
|
||||
this.currentWinnerDrawn = false;
|
||||
}, 12000);
|
||||
}, 19250);
|
||||
});
|
||||
this.socket.on("refresh_data", async msg => {
|
||||
this.getAttendees();
|
||||
@@ -104,20 +123,20 @@ export default {
|
||||
this.socket.disconnect();
|
||||
this.socket = null;
|
||||
},
|
||||
computed: {
|
||||
todayExists: () => {
|
||||
return prelottery()
|
||||
.then(wines => wines.length > 0)
|
||||
.catch(() => false);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getWinners: async function() {
|
||||
let response = await winners();
|
||||
if (response) {
|
||||
this.winners = response;
|
||||
}
|
||||
this.winnersFetched = true;
|
||||
},
|
||||
getTodaysWines() {
|
||||
prelottery()
|
||||
.then(wines => {
|
||||
this.wines = wines;
|
||||
this.todayExists = wines.length > 0;
|
||||
})
|
||||
.catch(_ => this.todayExists = false)
|
||||
},
|
||||
getAttendees: async function() {
|
||||
let response = await attendees();
|
||||
@@ -139,6 +158,20 @@ export default {
|
||||
}
|
||||
this.attendeesFetched = true;
|
||||
},
|
||||
scrollToContent() {
|
||||
console.log(window.scrollY)
|
||||
const intersectingHeaderHeight = this.$refs.header.getBoundingClientRect().bottom - 50;
|
||||
const { scrollY } = window;
|
||||
let scrollHeight = intersectingHeaderHeight;
|
||||
if (scrollY > 0) {
|
||||
scrollHeight = intersectingHeaderHeight + scrollY;
|
||||
}
|
||||
|
||||
window.scrollTo({
|
||||
top: scrollHeight,
|
||||
behavior: "smooth"
|
||||
});
|
||||
},
|
||||
track() {
|
||||
window.ga('send', 'pageview', '/lottery/game');
|
||||
}
|
||||
@@ -146,241 +179,197 @@ export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- TODO move link styling to global with more generic name -->
|
||||
<style lang="scss" scoped>
|
||||
@import "../styles/global.scss";
|
||||
|
||||
@import "../styles/variables.scss";
|
||||
@import "../styles/media-queries.scss";
|
||||
.generate-link {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
margin-bottom: 0px;
|
||||
|
||||
.container {
|
||||
width: 80vw;
|
||||
padding: 0 10vw;
|
||||
|
||||
@include mobile {
|
||||
width: 60vw;
|
||||
margin: auto;
|
||||
width: 90vw;
|
||||
padding: 0 5vw;
|
||||
}
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
|
||||
> div, > section {
|
||||
@include mobile {
|
||||
grid-column: span 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.vipps-image {
|
||||
width: 250px;
|
||||
margin: auto;
|
||||
display: block;
|
||||
margin-top: 30px;
|
||||
h2 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 1.75rem;
|
||||
}
|
||||
|
||||
.generator-link {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid $link-color;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../styles/global.scss";
|
||||
@import "../styles/variables.scss";
|
||||
@import "../styles/media-queries.scss";
|
||||
.color-selector {
|
||||
margin-bottom: 0.65rem;
|
||||
margin-right: 1rem;
|
||||
|
||||
@include desktop {
|
||||
min-width: 175px;
|
||||
}
|
||||
|
||||
@include mobile {
|
||||
max-width: 25vw;
|
||||
}
|
||||
|
||||
.active {
|
||||
border: 2px solid unset;
|
||||
|
||||
&.green {
|
||||
border-color: $green;
|
||||
}
|
||||
&.blue {
|
||||
border-color: $dark-blue;
|
||||
}
|
||||
&.red {
|
||||
border-color: $red;
|
||||
}
|
||||
&.yellow {
|
||||
border-color: $dark-yellow;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
border: 2px solid transparent;
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
height: 2.5rem;
|
||||
width: 2.5rem;
|
||||
|
||||
// disable-dbl-tap-zoom
|
||||
touch-action: manipulation;
|
||||
header {
|
||||
h1 {
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
font-size: 3rem;
|
||||
margin: 4rem 0 2rem;
|
||||
|
||||
@include mobile {
|
||||
margin: 2px;
|
||||
margin-top: 1rem;
|
||||
font-size: 2.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
background-color: $primary;
|
||||
padding-bottom: 3rem;
|
||||
margin-bottom: 3rem;
|
||||
|
||||
.instructions {
|
||||
grid-column: 1 / 4;
|
||||
|
||||
@include mobile {
|
||||
grid-column: span 5;
|
||||
}
|
||||
}
|
||||
|
||||
.vipps-qr {
|
||||
grid-column: 4;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.vipps-pill {
|
||||
margin: 0 auto 2rem;
|
||||
max-width: 80vw;
|
||||
}
|
||||
|
||||
.call-to-action {
|
||||
grid-column: span 5;
|
||||
}
|
||||
|
||||
ol {
|
||||
font-size: 1.4rem;
|
||||
line-height: 3rem;
|
||||
color: $matte-text-color;
|
||||
|
||||
@include mobile {
|
||||
line-height: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.4rem;
|
||||
line-height: 2rem;
|
||||
margin-top: 0;
|
||||
position: relative;
|
||||
|
||||
.vin-link {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&.green {
|
||||
background: #c8f9df;
|
||||
}
|
||||
&.blue {
|
||||
background: #d4f2fe;
|
||||
}
|
||||
&.red {
|
||||
background: #fbd7de;
|
||||
}
|
||||
&.yellow {
|
||||
background: #fff6d6;
|
||||
.icon {
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
color: $link-color;
|
||||
margin-left: 0.5rem;
|
||||
display: inline-block;
|
||||
transform: rotate(-90deg);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.vin-link {
|
||||
font-weight: 400;
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.colors {
|
||||
.todays-raffles {
|
||||
grid-column: 1;
|
||||
|
||||
@include mobile {
|
||||
order: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.raffle-container {
|
||||
width: 165px;
|
||||
height: 175px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
justify-content: space-between;
|
||||
|
||||
@include mobile {
|
||||
margin: 1.8rem auto 0;
|
||||
}
|
||||
|
||||
.label-div {
|
||||
margin-top: 0.5rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.colors-box {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin: 20px;
|
||||
-webkit-mask-image: url(/public/assets/images/lodd.svg);
|
||||
background-repeat: no-repeat;
|
||||
mask-image: url(/public/assets/images/lodd.svg);
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
.raffle-element {
|
||||
font-size: 1.6rem;
|
||||
color: $matte-text-color;
|
||||
height: 75px;
|
||||
width: 75px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
@include mobile {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.colors-overlay {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 0.25rem;
|
||||
position: relative;
|
||||
|
||||
p {
|
||||
width: 70%;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
font-size: 1.5rem;
|
||||
height: unset;
|
||||
max-height: unset;
|
||||
|
||||
@include mobile {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.green,
|
||||
.green .colors-overlay > input {
|
||||
background-color: $light-green;
|
||||
color: $green;
|
||||
}
|
||||
|
||||
.blue,
|
||||
.blue .colors-overlay > input {
|
||||
background-color: $light-blue;
|
||||
color: $blue;
|
||||
}
|
||||
|
||||
.yellow,
|
||||
.yellow .colors-overlay > input {
|
||||
background-color: $light-yellow;
|
||||
color: $yellow;
|
||||
}
|
||||
|
||||
.red,
|
||||
.red .colors-overlay > input {
|
||||
background-color: $light-red;
|
||||
color: $red;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../styles/global.scss";
|
||||
@import "../styles/variables.scss";
|
||||
@import "../styles/media-queries.scss";
|
||||
hr {
|
||||
width: 80%;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
.current-draw {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.title-info {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.outer-chat {
|
||||
margin: 0 60px 0 10px;
|
||||
@include mobile {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.outer-attendees {
|
||||
margin: 0 10px 0 45px;
|
||||
.winners {
|
||||
grid-column: 2 / 5;
|
||||
|
||||
@include mobile {
|
||||
margin: 0;
|
||||
order: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.center-new-winner {
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
.middle-elements {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 400px;
|
||||
.container-attendees {
|
||||
grid-column: 1 / 3;
|
||||
margin-right: 1rem;
|
||||
margin-top: 2rem;
|
||||
|
||||
@include mobile {
|
||||
height: auto;
|
||||
flex-direction: column;
|
||||
margin-right: 0;
|
||||
order: 4;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding: 1rem;
|
||||
|
||||
-webkit-box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.15);
|
||||
-moz-box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.vipps {
|
||||
margin-top: 70px;
|
||||
display: flex;
|
||||
padding-bottom: 50px;
|
||||
justify-content: center;
|
||||
.container-chat {
|
||||
grid-column: 3 / 5;
|
||||
margin-left: 1rem;
|
||||
margin-top: 2rem;
|
||||
|
||||
@include mobile {
|
||||
flex-direction: column;
|
||||
margin-left: 0;
|
||||
order: 3;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding: 1rem;
|
||||
|
||||
-webkit-box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.15);
|
||||
-moz-box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.wines-container {
|
||||
margin-bottom: 4rem;
|
||||
|
||||
h2 {
|
||||
grid-column: 1 / 5;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const VinlottisPage = () => import(
|
||||
/* webpackChunkName: "landing-page" */
|
||||
"@/components/VinlottisPage");
|
||||
const LotteryPage = () => import(
|
||||
const VirtualLotteryPage = () => import(
|
||||
/* webpackChunkName: "landing-page" */
|
||||
"@/components/LotteryPage");
|
||||
"@/components/VirtualLotteryPage");
|
||||
const GeneratePage = () => import(
|
||||
/* webpackChunkName: "landing-page" */
|
||||
"@/components/GeneratePage");
|
||||
@@ -54,7 +54,7 @@ const routes = [
|
||||
{
|
||||
path: "/lottery",
|
||||
name: "Lotteri",
|
||||
component: LotteryPage
|
||||
component: VirtualLotteryPage
|
||||
},
|
||||
{
|
||||
path: "/dagens",
|
||||
@@ -82,8 +82,8 @@ const routes = [
|
||||
component: AdminPage
|
||||
},
|
||||
{
|
||||
path: "/lottery/:tab",
|
||||
component: LotteryPage
|
||||
path: "/generate/",
|
||||
component: GeneratePage
|
||||
},
|
||||
{
|
||||
path: "/winner/:id",
|
||||
|
||||
@@ -112,10 +112,9 @@ textarea {
|
||||
|
||||
.vin-button {
|
||||
font-family: Arial;
|
||||
$color: #b7debd;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
background: $color;
|
||||
background: $primary;
|
||||
color: #333;
|
||||
padding: 10px 30px;
|
||||
margin: 0;
|
||||
@@ -188,7 +187,7 @@ textarea {
|
||||
.vin-link {
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid $link-color;
|
||||
font-size: 1rem;
|
||||
font-size: inherit;
|
||||
cursor: pointer;
|
||||
|
||||
text-decoration: none;
|
||||
|
||||
@@ -25,4 +25,16 @@ $desktop-max: 2004px;
|
||||
@media (min-width: #{$desktop-max + 1px}){
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
.desktop-only {
|
||||
@include mobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-only {
|
||||
@include tablet {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="attendees" v-if="attendees.length > 0">
|
||||
<h2>Deltakere ({{ attendees.length }})</h2>
|
||||
<div class="attendees-container" ref="attendees">
|
||||
<div class="attendee" v-for="(attendee, index) in flipList(attendees)" :key="index">
|
||||
<span class="attendee-name">{{ attendee.name }}</span>
|
||||
@@ -42,10 +41,16 @@ export default {
|
||||
@import "../styles/global.scss";
|
||||
@import "../styles/variables.scss";
|
||||
@import "../styles/media-queries.scss";
|
||||
|
||||
.attendee-name {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 2px solid black;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.raffle-element {
|
||||
font-size: 0.75rem;
|
||||
width: 45px;
|
||||
@@ -56,20 +61,24 @@ export default {
|
||||
font-weight: bold;
|
||||
font-size: 0.75rem;
|
||||
text-align: center;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.attendees {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 65%;
|
||||
height: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.attendees-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
max-height: 550px;
|
||||
}
|
||||
|
||||
.attendee {
|
||||
@@ -78,5 +87,9 @@ export default {
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
|
||||
&:not(:last-of-type) {
|
||||
border-bottom: 2px solid #d7d8d7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="chat-container">
|
||||
<hr />
|
||||
<h2>Chat</h2>
|
||||
<span class="logged-in-username" v-if="username">Logget inn som: <span class="username">{{ username }}</span> <button @click="removeUsername">Logg ut</button></span>
|
||||
|
||||
<div class="history" ref="history" v-if="chatHistory.length > 0">
|
||||
<div class="opaque-skirt"></div>
|
||||
<div v-if="hasMorePages" class="fetch-older-history">
|
||||
@@ -19,11 +19,12 @@
|
||||
<span class="message">{{ history.message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="username" class="input">
|
||||
|
||||
<div v-if="username" class="user-actions">
|
||||
<input @keyup.enter="sendMessage" type="text" v-model="message" placeholder="Melding.." />
|
||||
<button @click="sendMessage">Send</button>
|
||||
<button @click="removeUsername">Logg ut</button>
|
||||
</div>
|
||||
|
||||
<div v-else class="username-dialog">
|
||||
<input
|
||||
type="text"
|
||||
@@ -53,7 +54,7 @@ export default {
|
||||
hasMorePages: true,
|
||||
message: "",
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
pageSize: 100,
|
||||
temporaryUsername: null,
|
||||
username: null,
|
||||
validationError: undefined
|
||||
@@ -206,33 +207,37 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/media-queries.scss";
|
||||
@import "@/styles/variables.scss";
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: none;
|
||||
|
||||
@include mobile {
|
||||
display: block;
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
.chat-container {
|
||||
height: 100%;
|
||||
width: 50%;
|
||||
position: relative;
|
||||
|
||||
@include mobile {
|
||||
width: 100%;
|
||||
}
|
||||
transform: translate3d(0,0,0);
|
||||
}
|
||||
|
||||
input {
|
||||
width: 80%;
|
||||
width: 100%;
|
||||
height: 3.25rem;
|
||||
}
|
||||
|
||||
.input {
|
||||
.logged-in-username {
|
||||
position: absolute;
|
||||
top: 0.75rem;
|
||||
left: 1rem;
|
||||
color: $matte-text-color;
|
||||
width: calc(100% - 2rem);
|
||||
|
||||
button {
|
||||
width: unset;
|
||||
padding: 5px 10px;
|
||||
position: absolute;
|
||||
right: 0rem;
|
||||
}
|
||||
|
||||
.username {
|
||||
border-bottom: 2px solid $link-color;
|
||||
}
|
||||
}
|
||||
|
||||
.user-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -241,6 +246,8 @@ input {
|
||||
height: 75%;
|
||||
overflow-y: scroll;
|
||||
position: relative;
|
||||
max-height: 550px;
|
||||
margin-top: 2rem;
|
||||
|
||||
&-message {
|
||||
display: flex;
|
||||
@@ -265,9 +272,9 @@ input {
|
||||
}
|
||||
|
||||
& .opaque-skirt {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
height: 1rem;
|
||||
width: calc(100% - 2rem);
|
||||
position: fixed;
|
||||
height: 2rem;
|
||||
z-index: 1;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
@@ -332,5 +339,9 @@ button {
|
||||
transition: transform 0.5s ease;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
touch-action: manipulation;
|
||||
|
||||
@include mobile {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
<template>
|
||||
<footer>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/KevinMidboe/vinlottis" class="github">
|
||||
<span>Open-sourced at github</span>
|
||||
<img src="/public/assets/images/logo-github.png" alt="github logo">
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="mailto:questions@vinlottis.no" class="mail">
|
||||
<span class="vin-link">questions@vinlottis.no</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<router-link to="/" class="company-logo">
|
||||
<img src="/public/assets/images/knowit.svg" alt="knowit logo">
|
||||
</router-link>
|
||||
@@ -13,20 +28,70 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../styles/variables.scss";
|
||||
@import "../styles/media-queries.scss";
|
||||
|
||||
footer {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: #f4f4f4;
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin-left: 5rem;
|
||||
|
||||
li:not(:first-of-type) {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $matte-text-color;
|
||||
}
|
||||
|
||||
.github {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
|
||||
img {
|
||||
margin-left: 0.5rem;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.mail {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
margin-left: 0.5rem;
|
||||
height: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.company-logo{
|
||||
padding: 0 5em 0 0;
|
||||
img{
|
||||
margin-right: 5em;
|
||||
|
||||
img {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
@include mobile {
|
||||
$margin: 1rem;
|
||||
ul {
|
||||
margin-left: $margin;
|
||||
}
|
||||
|
||||
.company-logo {
|
||||
margin-right: $margin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -170,7 +170,7 @@ export default {
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
@include raffle;
|
||||
|
||||
|
||||
.win-percentage {
|
||||
margin-left: 30px;
|
||||
font-size: 50px;
|
||||
|
||||
81
frontend/ui/VippsPill.vue
Normal file
81
frontend/ui/VippsPill.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div aria-label="button" role="button" @click="openVipps" tabindex="0">
|
||||
<img src="public/assets/images/vipps-pay_with_vipps_pill.png" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
amount: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
phone: __PHONE__,
|
||||
name: __NAME__,
|
||||
price: __PRICE__,
|
||||
message: __MESSAGE__
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isMobile: function() {
|
||||
return this.isMobileFunction();
|
||||
},
|
||||
priceToPay: function() {
|
||||
return this.amount * (this.price * 100);
|
||||
},
|
||||
vippsUrlBasedOnUserAgent: function() {
|
||||
if (navigator.userAgent.includes("iPhone")) {
|
||||
return (
|
||||
"https://qr.vipps.no/28/2/01/031/47" +
|
||||
this.phone.replace(/ /g, "") +
|
||||
"?v=1&m=" +
|
||||
this.message +
|
||||
"&a=" +
|
||||
this.priceToPay
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
"https://qr.vipps.no/28/2/01/031/47" +
|
||||
this.phone.replace(/ /g, "") +
|
||||
"?v=1&m=" +
|
||||
this.message
|
||||
);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openVipps() {
|
||||
if (!this.isMobileFunction()) {
|
||||
return;
|
||||
}
|
||||
window.location.assign(this.vippsUrlBasedOnUserAgent);
|
||||
},
|
||||
isMobileFunction() {
|
||||
if (
|
||||
navigator.userAgent.match(/Android/i) ||
|
||||
navigator.userAgent.match(/webOS/i) ||
|
||||
navigator.userAgent.match(/iPhone/i) ||
|
||||
navigator.userAgent.match(/iPad/i) ||
|
||||
navigator.userAgent.match(/iPod/i) ||
|
||||
navigator.userAgent.match(/BlackBerry/i) ||
|
||||
navigator.userAgent.match(/Windows Phone/i)
|
||||
) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
img {
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,36 +1,18 @@
|
||||
<template>
|
||||
<div class="current-drawn-container">
|
||||
<div class="current-draw" v-if="drawing">
|
||||
<h2>TREKKER</h2>
|
||||
<div
|
||||
:class="currentColor + '-raffle'"
|
||||
class="raffle-element center-new-winner"
|
||||
:style="{ transform: 'rotate(' + getRotation() + 'deg)' }"
|
||||
>
|
||||
<span v-if="currentName && colorDone">{{ currentName }}</span>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
|
||||
<div class="current-draw" v-if="drawingDone">
|
||||
<h2>VINNER</h2>
|
||||
<div
|
||||
:class="currentColor + '-raffle'"
|
||||
class="raffle-element center-new-winner"
|
||||
:style="{ transform: 'rotate(' + getRotation() + 'deg)' }"
|
||||
>
|
||||
<span v-if="currentName && colorDone">{{ currentName }}</span>
|
||||
</div>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<div class="current-drawn-container" v-if="drawing">
|
||||
<h2 v-if="winnersNameDrawn !== true">TREKKER {{ ordinalNumber() }} VINNER</h2>
|
||||
<h2 v-else>VINNER</h2>
|
||||
|
||||
<div
|
||||
:class="currentColor + '-raffle'"
|
||||
class="raffle-element"
|
||||
:style="{ transform: 'rotate(' + getRotation() + 'deg)' }"
|
||||
>
|
||||
<span v-if="currentName && colorDone">{{ currentName }}</span>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -61,14 +43,13 @@ export default {
|
||||
nameTimeout: null,
|
||||
colorDone: false,
|
||||
drawing: false,
|
||||
drawingDone: false,
|
||||
winnersNameDrawn: false,
|
||||
winnerQueue: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
currentWinner: function(currentWinner) {
|
||||
if (currentWinner == null) {
|
||||
this.drawingDone = false;
|
||||
return;
|
||||
}
|
||||
if (this.drawing) {
|
||||
@@ -76,6 +57,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.drawing = true;
|
||||
this.winnersNameDrawn = false;
|
||||
this.currentName = null;
|
||||
this.currentColor = null;
|
||||
this.nameRounds = 0;
|
||||
@@ -99,8 +81,7 @@ export default {
|
||||
this.drawColor(this.currentWinnerLocal.color);
|
||||
return;
|
||||
}
|
||||
this.drawing = false;
|
||||
this.drawingDone = true;
|
||||
this.winnersNameDrawn = true;
|
||||
this.startConfetti(this.currentName);
|
||||
return;
|
||||
}
|
||||
@@ -114,7 +95,7 @@ export default {
|
||||
}, 50);
|
||||
},
|
||||
drawColor: function(winnerColor) {
|
||||
this.drawingDone = false;
|
||||
this.winnersNameDrawn = false;
|
||||
if (this.colorRounds == 100) {
|
||||
this.currentColor = winnerColor;
|
||||
this.colorDone = true;
|
||||
@@ -129,7 +110,7 @@ export default {
|
||||
clearTimeout(this.colorTimeout);
|
||||
this.colorTimeout = setTimeout(() => {
|
||||
this.drawColor(winnerColor);
|
||||
}, 50);
|
||||
}, 70);
|
||||
},
|
||||
getRotation: function() {
|
||||
if (this.colorDone) {
|
||||
@@ -151,8 +132,8 @@ export default {
|
||||
return "yellow";
|
||||
}
|
||||
},
|
||||
startConfetti(currentName){
|
||||
//duration is computed as x * 1000 miliseconds, in this case 7*1000 = 7000 miliseconds ==> 7 seconds.
|
||||
startConfetti(currentName) {
|
||||
//duration is computed as x * 1000 miliseconds, in this case 7*1000 = 7000 miliseconds ==> 7 seconds.
|
||||
var duration = 7 * 1000;
|
||||
var animationEnd = Date.now() + duration;
|
||||
var defaults = { startVelocity: 50, spread: 160, ticks: 50, zIndex: 0, particleCount: 20};
|
||||
@@ -161,22 +142,25 @@ export default {
|
||||
function randomInRange(min, max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
|
||||
const self = this;
|
||||
var interval = setInterval(function() {
|
||||
var timeLeft = animationEnd - Date.now();
|
||||
if (timeLeft <= 0) {
|
||||
self.drawing = false;
|
||||
console.time("drawing finished")
|
||||
return clearInterval(interval);
|
||||
}
|
||||
if(currentName == "Amund Brandsrud"){
|
||||
}
|
||||
if (currentName == "Amund Brandsrud") {
|
||||
runCannon(uberDefaults, {x: 1, y: 1 }, {angle: 135});
|
||||
runCannon(uberDefaults, {x: 0, y: 1 }, {angle: 45});
|
||||
runCannon(uberDefaults, {x: 0, y: 1 }, {angle: 45});
|
||||
runCannon(uberDefaults, {y: 1 }, {angle: 90});
|
||||
runCannon(uberDefaults, {x: 0 }, {angle: 45});
|
||||
runCannon(uberDefaults, {x: 1 }, {angle: 135});
|
||||
}else{
|
||||
runCannon(uberDefaults, {x: 1 }, {angle: 135});
|
||||
} else {
|
||||
runCannon(defaults, {x: 0 }, {angle: 45});
|
||||
runCannon(defaults, {x: 1 }, {angle: 135});
|
||||
runCannon(defaults, {y: 1 }, {angle: 90});
|
||||
|
||||
}
|
||||
}, 250);
|
||||
|
||||
@@ -184,6 +168,23 @@ export default {
|
||||
confetti(Object.assign({}, confettiDefaultValues, {origin: originPoint }, launchAngle))
|
||||
}
|
||||
},
|
||||
ordinalNumber(number=this.currentWinnerLocal.winnerCount) {
|
||||
const dictonary = {
|
||||
1: "første",
|
||||
2: "andre",
|
||||
3: "tredje",
|
||||
4: "fjerde",
|
||||
5: "femte",
|
||||
6: "sjette",
|
||||
7: "syvende",
|
||||
8: "åttende",
|
||||
9: "niende",
|
||||
10: "tiende",
|
||||
11: "ellevte",
|
||||
12: "tolvte"
|
||||
};
|
||||
return number in dictonary ? dictonary[number] : number;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -196,22 +197,27 @@ export default {
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.current-drawn-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
grid-column: 1 / 5;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.raffle-element {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
font-size: 1.2rem;
|
||||
width: 280px;
|
||||
height: 300px;
|
||||
font-size: 2rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 0.75rem;
|
||||
text-align: center;
|
||||
|
||||
-webkit-mask-size: cover;
|
||||
-moz-mask-size: cover;
|
||||
mask-size: cover;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 v-if="winners.length > 0"> {{ title ? title : 'Vinnere' }}</h2>
|
||||
<div class="winners" v-if="winners.length > 0">
|
||||
<section>
|
||||
<h2>{{ title ? title : 'Vinnere' }}</h2>
|
||||
<div class="winning-raffles" v-if="winners.length > 0">
|
||||
<div v-for="(winner, index) in winners" :key="index">
|
||||
<router-link :to="`/highscore/${ encodeURIComponent(winner.name) }`">
|
||||
<div :class="winner.color + '-raffle'" class="raffle-element">{{ winner.name }}</div>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="drawing" class="container">
|
||||
<h3>Trekningen er igang!</h3>
|
||||
</div>
|
||||
|
||||
<div v-else class="container">
|
||||
<h3>Trekningen har ikke startet enda <button>⏰</button></h3>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -17,6 +25,9 @@ export default {
|
||||
winners: {
|
||||
type: Array
|
||||
},
|
||||
drawing: {
|
||||
type: Boolean,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false
|
||||
@@ -30,11 +41,28 @@ export default {
|
||||
@import "../styles/variables.scss";
|
||||
@import "../styles/media-queries.scss";
|
||||
|
||||
section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 0.6rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.winners {
|
||||
h3 {
|
||||
margin: auto;
|
||||
color: $matte-text-color;
|
||||
font-size: 1.6rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.winning-raffles {
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
justify-content: space-around;
|
||||
@@ -52,4 +80,21 @@ h2 {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
||||
button {
|
||||
-webkit-appearance: unset;
|
||||
background-color: unset;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: inherit;
|
||||
border: unset;
|
||||
height: auto;
|
||||
width: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
BIN
public/assets/images/logo-github.png
Normal file
BIN
public/assets/images/logo-github.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/assets/images/vipps-pay_with_vipps_pill.png
Normal file
BIN
public/assets/images/vipps-pay_with_vipps_pill.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Reference in New Issue
Block a user