Pulled feature branch up-to-date w/ master.

This commit is contained in:
2020-11-20 19:15:46 +01:00
32 changed files with 1511 additions and 1985 deletions

View File

@@ -2,6 +2,7 @@
<div class="app-container">
<banner :routes="routes"/>
<router-view />
<Footer />
<UpdateToast
v-if="showToast"
:text="toastText"
@@ -14,11 +15,12 @@
<script>
import ServiceWorkerMixin from "@/mixins/ServiceWorkerMixin";
import banner from "@/ui/Banner";
import Footer from "@/ui/Footer";
import UpdateToast from "@/ui/UpdateToast";
export default {
name: "vinlottis",
components: { banner, UpdateToast },
components: { banner, UpdateToast, Footer },
props: {},
data() {
return {
@@ -77,8 +79,10 @@ export default {
</script>
<style lang="scss">
@import "./styles/global.scss";
@import "./styles/positioning.scss";
@import "styles/global.scss";
@import "styles/positioning.scss";
@import "styles/vinlottis-icons";
@font-face {
font-family: "knowit";
font-weight: 600;
@@ -100,9 +104,16 @@ body {
}
</style>
<style scoped>
<style lang="scss" scoped>
.app-container {
background-color: white;
min-height: 100vh;
display: grid;
grid-template-rows: 80px auto 100px;
.main-container{
height: 100%;
width: 100%;
}
}
</style>

View File

@@ -122,7 +122,7 @@ const winners = () => {
const deleteRequestedWine = wineToBeDeleted => {
const url = new URL("api/request/"+ wineToBeDeleted._id, BASE_URL);
const url = new URL("api/request/"+ wineToBeDeleted.id, BASE_URL);
const options = {
headers: {

View File

@@ -1,11 +1,11 @@
<template>
<main>
<h1>
Alle foreslåtte viner
</h1>
<main class="container">
<h1>Alle foreslåtte viner</h1>
<section class="requested-wines-container">
<p v-if="wines == undefined || wines.length == 0">Ingen har foreslått noe enda!</p>
<RequestedWineCard v-for="requestedEl in wines" :key="requestedEl.id" :requestedElement="requestedEl" @wineDeleted="filterOutDeletedWine" :showDeleteButton="isAdmin"/>
<RequestedWineCard v-for="requestedEl in wines" :key="requestedEl.wine._id" :requestedElement="requestedEl" @wineDeleted="filterOutDeletedWine" :showDeleteButton="isAdmin"/>
</section>
</main>
</template>
@@ -40,41 +40,25 @@ export default {
<style lang="scss" scoped>
@import "../styles/media-queries.scss";
@import "./src/styles/variables.scss";
.container {
width: 90vw;
margin: 3rem auto;
margin-bottom: 0;
padding-bottom: 3rem;
}
h1 {
font-size: 3rem;
font-family: "knowit";
color: $matte-text-color;
font-weight: normal;
}
.requested-wines-container{
display: grid;
grid-gap: 1.5rem;
justify-items: center;
@include mobile {
display: flex;
justify-content: space-around;
flex-flow: row wrap;
row-gap: 1.5rem;
margin: 2rem;
}
@include tablet {
margin: 1em;
grid: 1fr / 1fr 1fr;
justify-items: center;
}
@include desktop {
margin: 1em;
grid: 1fr / repeat(4, 1fr);
}
@include widescreen {
width: 80%;
margin: auto;
grid: 1fr / repeat(5, 1fr);
justify-items: center;
}
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-gap: 2rem;
}
h1{
text-align: center;
}
</style>

View File

@@ -66,27 +66,34 @@ export default {
@import "./src/styles/variables";
.container {
max-width: unset;
width: 90vw;
margin: 3rem auto;
margin-bottom: 0;
padding-bottom: 4rem;
}
h1 {
font-size: 3rem;
font-family: "knowit";
font-weight: normal;
font-family: knowit, Arial;
margin-bottom: 25px;
}
.label {
font-weight: 600;
}
#wines-container {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
align-items: flex-start;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-gap: 2rem;
> div {
justify-content: flex-start;
}
}
.winners-container {
display: flex;
flex-direction: column;
margin-top: 1rem;
> div:not(:last-of-type) {
margin-bottom: 1rem;
margin-bottom: 2rem;
}
}

View File

@@ -1,6 +1,6 @@
<template>
<div class="container">
<h1 class="text-center title" @click="startCountdown">Loddgenerator</h1>
<h1 class="title" @click="startCountdown">Loddgenerator</h1>
<p class="subtext">
Velg hvilke farger du vil ha, fyll inn antall lodd og klikk 'generer'
</p>
@@ -54,16 +54,13 @@ export default {
@import "../styles/variables.scss";
@import "../styles/global.scss";
@import "../styles/media-queries.scss";
.container {
display: flex;
flex-direction: column;
margin-top: 0;
}
h1 {
cursor: pointer;
}
.header-link {
color: #333333;
text-decoration: none;
}
p {
text-align: center;
@@ -77,4 +74,10 @@ p {
margin-top: 2rem;
}
}
.container {
margin: auto;
display: flex;
flex-direction: column;
}
</style>

View File

@@ -101,12 +101,35 @@ $elementSpacing: 3.5rem;
margin-bottom: $elementSpacing;
}
.container {
width: 90vw;
margin: 3rem auto;
max-width: 1200px;
margin-bottom: 0;
padding-bottom: 3rem;
@include desktop {
width: 80vw;
}
}
h1 {
font-size: 3rem;
font-family: "knowit";
color: $matte-text-color;
font-weight: normal;
}
.filter input {
font-size: 1rem;
width: 30%;
width: 100%;
border-color: black;
border-width: 1.5px;
padding: 0.75rem 1rem;
@include desktop {
width: 30%;
}
}
.highscore-header {

View File

@@ -14,14 +14,14 @@
<h4 class="margin-bottom-0">Vinnende farger:</h4>
<div class="raffle-container el-spacing">
<div class="raffle-element" :class="color + `-raffle`" v-for="[color, occurences] in Object.entries(winningColors)">
<div class="raffle-element" :class="color + `-raffle`" v-for="[color, occurences] in Object.entries(winningColors)" :key="color">
{{ occurences }}
</div>
</div>
<h4 class="el-spacing">Flasker vunnet:</h4>
<div v-for="win in winner.highscore">
<div v-for="win in winner.highscore" :key="win._id">
<router-link :to="winDateUrl(win.date)" class="days-ago">
{{ humanReadableDate(win.date) }} - {{ daysAgo(win.date) }} dager siden
</router-link>
@@ -141,6 +141,24 @@ $elementSpacing: 3rem;
border-color: gray;
}
.container {
width: 90vw;
margin: 3rem auto;
margin-bottom: 0;
padding-bottom: 3rem;
max-width: 1200px;
@include desktop {
width: 80vw;
}
}
h1 {
font-size: 3rem;
font-family: "knowit";
font-weight: normal;
}
.name {
text-transform: capitalize;
font-size: 3.5rem;

View File

@@ -106,7 +106,6 @@ h1{
text-align: center;
}
.main-container{
margin: auto;
max-width: 1200px;

View File

@@ -37,10 +37,79 @@ export default {
height: 250px;
}
h1 {
font-family: knowit, Arial;
margin-bottom: 25px;
}
.wines-container {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
align-items: flex-start;
margin: 0 2rem;
@media (min-width: 1500px) {
max-width: 1500px;
margin: 0 auto;
}
@include mobile {
flex-direction: column;
}
}
h3 {
max-width: 30vw;
@include mobile {
max-width: 50vw;
}
}
.inner-wine-container {
display: flex;
flex-direction: row;
margin: auto;
width: 500px;
font-family: Arial;
margin-bottom: 30px;
@include desktop {
justify-content: center;
}
@include mobile {
width: auto;
}
}
.right {
display: flex;
flex-direction: column;
margin-bottom: 150px;
margin-left: 50px;
@include mobile {
margin-left: 2rem;
}
}
a,
a:focus,
a:hover,
a:visited {
color: #333333;
font-family: Arial;
text-decoration: none;
font-weight: bold;
}
.wine-link {
color: #333333;
font-family: Arial;
text-decoration: none;
font-weight: bold;
border-bottom: 1px solid $link-color;
width: fit-content;
}
</style>

View File

@@ -1,31 +1,68 @@
<template>
<div class="outer">
<div class="container">
<section class="header-and-notification">
<h1 @click="startCountdown">Vinlotteri</h1>
<img
src="/assets/images/notification.svg"
alt="Notification-bell"
@click="requestNotificationAccess"
class="notification-request-button"
role="button"
v-if="notificationAllowed"
/>
</section>
<div class="to-lottery-container">
<a href="#/lottery" class="to-lottery">Vil du til lotteriet?<span class="vin-link">Trykk her</span></a>
<main class="main-container">
<section class="top-container">
<div class="want-to-win">
<h1>
Vil du også vinne?
</h1>
<img
src="/public/assets/images/notification.svg"
alt="Notification-bell"
@click="requestNotificationAccess"
class="notification-request-button"
role="button"
v-if="notificationAllowed"
/>
</div>
<router-link to="/lottery/game" 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">
Se vipps detaljer og QR-kode
</router-link>
<div class="icons-container">
<i class="icon icon--heart-sparks"></i>
<i class="icon icon--face-1"></i>
<i class="icon icon--face-3"></i>
<i class="icon icon--ballon"></i>
<i class="icon icon--bottle"></i>
<i class="icon icon--bottle"></i>
<i class="icon icon--bottle"></i>
<i class="icon icon--bottle"></i>
<i class="icon icon--bottle"></i>
</div>
</section>
<section class="content-container">
<div class="scroll-info">
<i class ="icon icon--arrow-long-right"></i>
<p>Scroll for å se vinnere og annen gøy statistikk</p>
</div>
<Highscore class="highscore"/>
<TotalBought class="total-bought" />
<section class="chart-container">
<PurchaseGraph class="purchase" />
<WinGraph class="win" />
</section>
<TotalBought class="total-bought" />
<Vipps class="vipps-icon" />
<Highscore class="highscore"/>
<Wines class="wines-container" />
</div>
</section>
<Countdown :hardEnable="hardStart" @countdown="changeEnabled" />
</div>
</main>
</template>
<script>
@@ -95,9 +132,180 @@ export default {
<style lang="scss" scoped>
@import "../styles/media-queries.scss";
@import "../styles/variables.scss";
.top-container {
height: 30em;
background-color: $primary;
width: 100vw;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(12, 1fr);
align-items: center;
justify-items: start;
@include mobile{
padding-bottom: 2em;
height: 15em;
grid-template-rows: repeat(7, 1fr);
}
.want-to-win {
grid-row: 2 / 4;
grid-column: 2 / -1;
display: flex;
h1{
font-size: 2em;
font-weight: 400;
}
@include tablet {
h1{
font-size: 3em;
}
grid-row: 2 / 4;
grid-column: 3 / -3;
}
}
.notification-request-button{
cursor: pointer;
}
.participate-button {
grid-row: 4 / 6;
grid-column: 2 / -1;
background: inherit;
border: 4px solid black;
padding: 0 1em 0 1em;
display: flex;
width: 12.5em;
align-items: center;
text-decoration: none;
color: black;
i {
color: $link-color;
margin-left: 5px;
}
p {
font-size: 16px;
margin-left: 15px;
}
@include tablet {
grid-row: 4 / 6;
grid-column: 3 / -3;
}
}
.see-details-link {
grid-row: 6 / 8;
grid-column: 2 / -1;
@include tablet {
grid-row: 6 / 8;
grid-column: 2 / 10;
}
@include tablet {
grid-column: 3 / -3;
}
font-weight: bold;
color: black;
font-weight: 200;
font-size: 1.3em;
text-decoration: underline;
text-decoration-color: $link-color;
text-underline-position: under;
}
.icons-container {
grid-column: 1 / -1;
grid-row: 7 / -1;
@include mobile{
margin-top: 2em;
display: none;
}
@include tablet {
grid-row: 6 / -1;
grid-column: 7 / -1;
}
@include desktop{
grid-row: 4 / -3;
grid-column: 7 / 11;
}
@include widescreen {
grid-column: 6 / 10;
}
width: 100%;
min-width: 375px;
height: 100%;
display: grid;
grid: repeat(6, 1fr) / repeat(12, 1fr);
i {
font-size: 5em;
&.icon--heart-sparks{
grid-column: 2 / 4;
grid-row: 2 / 4;
align-self: center;
justify-self: center;
}
&.icon--face-1{
grid-column: 4 / 7;
grid-row: 2 / 4;
justify-self: center;
}
&.icon--face-3{
grid-column: 7 / 10;
grid-row: 1 / 4;
align-self: center;
}
&.icon--ballon{
grid-column: 9 / 11;
grid-row: 3 / 5;
}
&.icon--bottle{
grid-row: 4 / -1;
&:nth-of-type(5) {
grid-column: 4 / 5;
align-self: center;
}
&:nth-of-type(6) {
grid-column: 5 / 6;
}
&:nth-of-type(7) {
grid-column: 6 / 7;
align-self: center;
}
&:nth-of-type(8) {
grid-column: 7 / 8;
}
&:nth-of-type(9){
grid-column: 8 / 9;
align-self: center;
}
}
}
}
.outer {
margin: 1em;
}
h1 {
@@ -113,131 +321,63 @@ h1 {
margin-bottom: 0;
}
.container{
display: flex;
flex-direction: column;
width: 100%;
.content-container {
display: grid;
grid-template-columns: repeat(12, 1fr);
row-gap: 5em;
.header-and-notification{
display: flex;
flex-direction: row;
margin: auto;
.scroll-info {
display: flex;
align-items: center;
column-gap: 10px;
grid-column: 2 / -2;
}
.vipps-icon{
margin: 1em;
.chart-container {
display: flex;
width: 100%;
flex-direction: column;
grid-column: 2 / -2;
}
.total-bought {
grid-column: 2 / -2;
}
.highscore {
grid-column: 2 / -2;
}
.wines-container {
grid-column: 2 / -2;
}
.icon--arrow-long-right {
transform: rotate(90deg);
color: $link-color;
}
@include tablet {
margin: .5em;
.chart-container {
display: flex;
width: 100%;
}
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto-flow min-content;
grid-template-areas: "top-top top-top top-top"
"top-bot top-bot top-bot"
"middle-top middle-top middle-top"
"middle-bot-left middle-bot-left middle-bot-right"
"bot-left bot-right bot-right";
.header-and-notification {
grid-area: top-top;
}
.to-lottery-container{
grid-area: top-bot;
.scroll-info{
grid-column: 3 / -3;
}
.chart-container {
grid-area: middle-top;
grid-column: 3 / -3;
flex-direction: row;
}
.total-bought {
grid-area: middle-bot-left;
grid-column: 3 / -3;
}
.highscore {
border-top: 1px solid rgb(237, 237, 237);
grid-area: bot-left;
align-self: baseline;
grid-column: 3 / -3;
}
.wines-container {
border-top: 1px solid rgb(237, 237, 237);
padding-left: 1em;
border-left: 1px solid rgb(237, 237, 237);
grid-area: bot-right;
}
.vipps-icon {
padding-left: 1em;
align-self: center;
grid-area: middle-bot-right;
border-left: 1px solid rgb(237, 237, 237);
}
}
@include desktop {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: auto-flow min-content;
grid-template-areas: "top-top top-top top-top top-top"
"top-bot top-bot top-bot top-bot"
"middle-top middle-top middle-top middle-top"
"middle-bot middle-bot middle-bot aside"
"bot-left bot-right bot-right aside";
grid-gap: 1em;
align-items: center;
.header-and-notification {
grid-area: top-top;
}
.to-lottery-container {
grid-area: top-bot;
}
.chart-container {
grid-area: middle-top;
}
.total-bought {
grid-area: middle-bot;
border-bottom: 1px solid rgb(237, 237, 237);
}
.highscore {
border: none;
grid-area: bot-left;
}
.wines-container {
border: none;
grid-area: bot-right;
}
.vipps-icon {
grid-area: aside;
padding-left: 3em;
border-left: 1px solid rgb(237, 237, 237);
}
}
@include widescreen {
width: 70%;
max-width: 1800px;
margin: auto;
.vipps-icon {
padding-left: 6em;
grid-column: 3 / -3;
}
}
}
</style>

View File

@@ -77,11 +77,6 @@ export default {
margin-top: 2rem;
padding: 2rem;
}
h1 {
color: $matte-text-color;
}
.sent-container {
width: 100%;
height: 90vh;

View File

@@ -2,6 +2,9 @@
@import "./variables.scss";
.top-banner{
position: sticky;
top: 0;
z-index: 1;
display: grid;
grid-template-columns: 0.5fr 1fr 0.5fr;
grid-template-areas: "menu logo clock";
@@ -9,9 +12,16 @@
align-items: center;
justify-items: center;
background-color: $primary;
-webkit-box-shadow: 0px 0px 22px -8px rgba(0, 0, 0, 0.65);
-moz-box-shadow: 0px 0px 22px -8px rgba(0, 0, 0, 0.65);
box-shadow: 0px 0px 22px -8px rgba(0, 0, 0, 0.65);
// ios homescreen app whitespace above header fix.
&::before {
content: '';
width: 100%;
height: 3rem;
position: absolute;
top: -3rem;
background-color: inherit;
}
}
.company-logo{

View File

@@ -37,33 +37,6 @@ a {
}
}
.container {
width: 90vw;
margin: 3rem auto;
margin-bottom: 0;
padding-bottom: 3rem;
max-width: 1700px;
@include desktop {
width: 80vw;
}
h1 {
font-family: "knowit";
font-weight: normal;
font-size: 2rem;
@include desktop {
font-size: 3rem;
}
}
}
.text-center {
text-align: center;
}
.subtext {
margin-top: 0.5rem;
font-size: 1.22rem;
@@ -195,6 +168,10 @@ textarea {
opacity: 0.25;
cursor: not-allowed;
}
&.small {
height: min-content;
}
}
@@ -268,6 +245,26 @@ textarea {
}
}
.width {
&-100 {
width: 100%;
}
&-75 {
width: 75%;
}
&-50 {
width: 50%;
}
&-25 {
width: 25%;
}
}
.cursor {
&-pointer {
cursor: pointer;
}
}
.no-margin {
margin: 0 !important;
@@ -299,6 +296,28 @@ textarea {
}
}
@mixin raffle {
padding-bottom: 50px;
&::before, &::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 25px;
height: 50px;
background: radial-gradient(closest-side, #fff, #fff 50%, transparent 50%);
background-size: 50px 50px;
background-position: 0 25px;
background-repeat: repeat-x;
}
&::after{
background: radial-gradient(closest-side, transparent, transparent 50%, #fff 50%);
background-size: 50px 50px;
background-position: 25px -25px;
bottom: -25px
}
}
.desktop-only {
@include mobile {
display: none;

View File

@@ -0,0 +1,122 @@
@font-face {
font-family: 'vinlottis-icons';
src:
url('/public/assets/fonts/vinlottis-icons/vinlottis-icons.ttf?95xu5r') format('truetype'),
url('/public/assets/fonts/vinlottis-icons/vinlottis-icons.woff?95xu5r') format('woff'),
url('/public/assets/fonts/vinlottis-icons/vinlottis-icons.svg?95xu5r#vinlottis-icons') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
}
.icon {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'vinlottis-icons' !important;
speak: never;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon--arrow-long-right:before {
content: "\e907";
}
.icon--arrow-long-left:before {
content: "\e908";
}
.icon--arrow-right:before {
content: "\e909";
}
.icon--arrow-left:before {
content: "\e900";
}
.icon--ballon:before {
content: "\e90b";
}
.icon--bars:before {
content: "\e90c";
}
.icon--bottle:before {
content: "\e90d";
}
.icon--cake-chart:before {
content: "\e90f";
}
.icon--stopwatch:before {
content: "\e911";
}
.icon--cloud:before {
content: "\e912";
}
.icon--dart:before {
content: "\e914";
}
.icon--eye-1:before {
content: "\e919";
}
.icon--eye-2:before {
content: "\e91a";
}
.icon--eye-3:before {
content: "\e91b";
}
.icon--eye-4:before {
content: "\e91c";
}
.icon--eye-5:before {
content: "\e91d";
}
.icon--eye-6:before {
content: "\e91e";
}
.icon--eye-7:before {
content: "\e91f";
}
.icon--eye-8:before {
content: "\e920";
}
.icon--face-1:before {
content: "\e922";
}
.icon--face-2:before {
content: "\e923";
}
.icon--face-3:before {
content: "\e924";
}
.icon--heart-sparks:before {
content: "\e928";
}
.icon--heart:before {
content: "\e929";
}
.icon--medal:before {
content: "\e936";
}
.icon--megaphone:before {
content: "\e937";
}
.icon--phone:before {
content: "\e93a";
}
.icon--plus:before {
content: "\e93e";
}
.icon--spark:before {
content: "\e946";
}
.icon--tag:before {
content: "\e949";
}
.icon--talk:before {
content: "\e94b";
}
.icon--cross:before {
content: "\e952";
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="top-banner">
<header class="top-banner">
<!-- Mobile -->
<router-link to="/" class="company-logo">
<img src="/assets/images/knowit.svg" alt="knowit logo" />
@@ -26,7 +26,7 @@
</h2>
<h2 v-if="twoMinutesLeft || tenMinutesOver">Lotteriet er i gang!</h2>
</div>
</div>
</header>
</template>
<script>

32
src/ui/Footer.vue Normal file
View File

@@ -0,0 +1,32 @@
<template>
<footer>
<router-link to="/" class="company-logo">
<img src="/public/assets/images/knowit.svg" alt="knowit logo">
</router-link>
</footer>
</template>
<script>
export default {
name: 'WineFooter'
}
</script>
<style lang="scss" scoped>
footer {
width: 100%;
height: 100px;
display: flex;
justify-content: flex-end;
align-items: center;
background: #f4f4f4;
.company-logo{
padding: 0 5em 0 0;
img{
width: 100px;
}
}
}
</style>

View File

@@ -1,13 +1,19 @@
<template>
<div class="highscores" v-if="highscore.length > 0">
<h3>
<router-link to="highscore">
Topp 10 vinnere <span class="vin-link">Se alle &gt;</span>
<section class="heading">
<h3>
Topp 5 vinnere
</h3>
<router-link to="highscore" class="">
<span class="vin-link">Se alle vinnere</span>
</router-link>
</h3>
<ol>
<li v-for="person in highscore" :key="person">
<b>{{ person.rank }}.</b> {{ person.name }} - {{ person.wins.length }}
</section>
<ol class="winner-list-container">
<li v-for="(person, index) in highscore" :key="person._id" class="single-winner">
<span class="placement">{{index + 1}}.</span>
<i class="icon icon--medal"></i>
<p class="winner-name">{{ person.name }}</p>
</li>
</ol>
</div>
@@ -24,7 +30,7 @@ export default {
async mounted() {
let response = await highscoreStatistics();
response.sort((a, b) => a.wins.length < b.wins.length ? 1 : -1)
this.highscore = this.generateScoreBoard(response.slice(0, 10));
this.highscore = this.generateScoreBoard(response.slice(0, 5));
},
methods: {
generateScoreBoard(highscore=this.highscore) {
@@ -48,43 +54,66 @@ export default {
</script>
<style lang="scss" scoped>
@import "../styles/media-queries.scss";
div {
margin: 0;
font-family: Arial;
display: inline-flex;
flex-direction: column;
@import "../styles/variables.scss";
.heading {
display: flex;
justify-content: space-between;
align-items: center;
}
h3 {
text-align: left;
a {
text-decoration: none;
color: #333333;
& a {
&:focus,
&:active,
&:visited {
text-decoration: none;
color: #333333;
&:focus,
&:active,
&:visited {
text-decoration: none;
color: #333333;
}
}
}
ol {
padding-left: 1.375rem !important;
list-style-type: none;
margin-left: 0;
margin: 0 0 1.5em;
padding: 0;
counter-reset: item;
& > li {
padding: 2.5px 0;
margin: 0 0 0 -1.25rem;
list-style-type: none;
}
@include mobile {
padding: 5px 0;
.winner-list-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(12.5em, 1fr));
gap: 5%;
.single-winner {
box-sizing: border-box;
width: 100%;
background: $primary;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
align-items: center;
padding: 1em;
i {
font-size: 3em;
width: max-content;
justify-self: end;
}
.placement {
grid-row: 1;
grid-column: 1 / 3;
font-size: 3em;
}
.winner-name {
grid-row: 2;
grid-column: 1 / -1;
}
.winner-icon {
grid-row: 1;
grid-column: 3;
}
}
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="flex column">
<div class="container">
<div class="input-line">
<label for="redCheckbox">
<input type="checkbox" id="redCheckbox" v-model="redCheckbox" @click="generateColors"/>
@@ -35,11 +35,10 @@
type="number"
placeholder="Antall lodd"
@keyup.enter="generateColors"
v-model="numberOfBallots"
v-model="numberOfRaffles"
/>
<button class="vin-button" @click="generateColors">Generer</button>
</div>
<div class="colors">
<div
v-for="color in colors"
@@ -69,7 +68,7 @@ export default {
},
data() {
return {
numberOfBallots: 4,
numberOfRaffles: 4,
colors: [],
blue: 0,
red: 0,
@@ -85,14 +84,14 @@ export default {
};
},
beforeMount() {
this.$emit("numberOfBallots", this.numberOfBallots);
this.$emit("numberOfRaffles", this.numberOfRaffles);
if (this.generateOnInit) {
this.generateColors();
}
},
watch: {
numberOfBallots: function() {
this.$emit("numberOfBallots", this.numberOfBallots);
numberOfRaffles: function() {
this.$emit("numberOfRaffles", this.numberOfRaffles);
this.generateColors();
}
},
@@ -102,7 +101,7 @@ export default {
if (time == 5) {
this.generating = false;
this.generated = true;
if (this.numberOfBallots > 1 &&
if (this.numberOfRaffles > 1 &&
[this.redCheckbox, this.greenCheckbox, this.yellowCheckbox, this.blueCheckbox].filter(value => value == true).length == 1) {
return
}
@@ -117,7 +116,7 @@ export default {
return;
}
this.$ga.event({
eventCategory: "Ballots",
eventCategory: "Raffles",
eventAction: "Generate",
eventValue: JSON.stringify(this.colors)
});
@@ -148,8 +147,8 @@ export default {
alert("Du må velge MINST 1 farge");
return;
}
if (this.numberOfBallots > 0) {
for (let i = 0; i < this.numberOfBallots; i++) {
if (this.numberOfRaffles > 0) {
for (let i = 0; i < this.numberOfRaffles; i++) {
let color =
randomArray[Math.floor(Math.random() * randomArray.length)];
this.colors.push(color);
@@ -208,6 +207,12 @@ export default {
@import "../styles/global.scss";
@import "../styles/media-queries.scss";
.container {
margin: auto;
display: flex;
flex-direction: column;
}
.input-line {
margin: auto;
display: flex;

View File

@@ -1,33 +1,40 @@
<template>
<div class="requested-wine">
<img
v-if="wine.image"
:src="wine.image"
class="wine-image"
:class="{ 'fullscreen': fullscreen }"
/>
<img v-else class="wine-placeholder" alt="Wine image" />
<h3 v-if="wine.name">{{ wine.name }}</h3>
<h3 v-else>(no name)</h3>
<p class="requested-amount">Foreslått: <strong>{{requestedElement.count}}</strong></p>
<Wine :wine="wine">
<template v-slot:top>
<div class="flex justify-end">
<div class="requested-count cursor-pointer" @click="request">
<span>{{ requestedElement.count }}</span>
<i class="icon icon--heart" :class="{ 'active': locallyRequested }" />
</div>
</div>
</template>
<button class="vin-button" @click="request(wine)" v-if="!locallyRequested">Foreslå denne</button>
<a
v-if="wine.vivinoLink"
:href="wine.vivinoLink"
class="wine-link">
Les mer
</a>
<button @click="deleteWine(wine)" v-if="showDeleteButton == true" class="vin-button danger">
Slett vinen
</button>
</div>
<template v-slot:default>
<button @click="deleteWine(wine)" v-if="showDeleteButton == true" class="vin-button small danger width-100">
Slett vinen
</button>
</template>
<template v-slot:bottom>
<div class="float-left request">
<i class="icon icon--heart request-icon" :class="{ 'active': locallyRequested }"></i>
<a aria-role="button" tabindex="0" class="link" @click="request"
:class="{ 'active': locallyRequested }">
{{ locallyRequested ? 'Anbefalt' : 'Anbefal' }}
</a>
</div>
</template>
</Wine>
</template>
<script>
import { deleteRequestedWine, requestNewWine } from "@/api";
import Wine from "@/ui/Wine";
export default {
components: {
Wine
},
data(){
return {
wine: this.requestedElement.wine,
@@ -46,12 +53,16 @@ export default {
}
},
methods: {
request(wine){
request(){
if (this.locallyRequested)
return
console.log("requesting", this.wine)
this.locallyRequested = true
this.requestedElement.count = this.requestedElement.count +1
requestNewWine(wine)
requestNewWine(this.wine)
},
async deleteWine(wine) {
async deleteWine() {
const wine = this.wine
if (window.confirm("Er du sikker på at du vil slette vinen?")) {
let response = await deleteRequestedWine(wine);
if (response['success'] == true) {
@@ -65,62 +76,49 @@ export default {
}
</script>
<style lang="scss" scoped>
@import "../styles/global.scss";
<style lang="scss" scoped>
@import "./src/styles/variables";
.requested-wine{
-webkit-box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.65);
-moz-box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.65);
box-shadow: 0px 0px 10px 1px rgba(0, 0, 0, 0.65);
text-align: center;
display: grid;
grid-template-areas: "top top"
"middle-left middle-right-top"
"middle-left middle-right-bot"
"bottom-top bottom-top"
"bottom-bot bottom-bot";
grid-gap: 1em;
justify-items: center;
.requested-count {
display: flex;
align-items: center;
width: 100%;
h3{
grid-area: top;
word-break: keep-all;
width: 90%;
margin-top: -0.5rem;
background-color: rgb(244,244,244);
border-radius: 1.1rem;
padding: 0.25rem 1rem;
font-size: 1.25em;
span {
padding-right: 0.5rem;
line-height: 1.25em;
}
img{
height: 13em;
grid-area: middle-left;
}
.requested-amount{
grid-area: middle-right-top;
width: 90%;
word-break: keep-all;
}
.wine-link{
grid-area: middle-right-bot;
color: #333333;
font-family: Arial;
text-decoration: none;
font-weight: bold;
border-bottom: 1px solid $link-color;
height: 1em;
}
.vin-button{
grid-area: bottom-top;
margin-bottom: 1em;
&.danger{
grid-area: bottom-bot;
background-color: $light-red;
color: $red;
}
.icon--heart{
color: grey;
}
}
.active {
&.link {
border-color: $link-color
}
&.icon--heart {
color: $link-color;
}
}
.request {
display: flex;
align-items: center;
&-icon {
font-size: 1.5rem;
color: grey;
}
a {
margin-left: 0.5rem;
}
}
</style>

View File

@@ -1,6 +1,16 @@
<template>
<div class="outer-bought">
<section class="outer-bought">
<h3>Loddstatistikk</h3>
<div class="total-raffles">
Totalt&nbsp;
<span class="total">{{ total }}</span>
&nbsp;kjøpte,&nbsp;
<span>{{ totalWin }}&nbsp;vinn og&nbsp;</span>
<span> {{ stolen }} stjålet </span>
</div>
<div class="bought-container">
<div
v-for="color in colors"
@@ -8,34 +18,19 @@
color.name +
'-container ' +
color.name +
'-raffle inner-bought-container raffle-element'
'-raffle raffle-element-local'
"
:key="color.name"
>
<div class="number-container">
<span class="color-total bought-number-span">
{{ color.total }}
</span>
<span>kjøpte</span>
</div>
<div class="inner-text-container">
<div>{{ color.win }} vinn</div>
<div>{{ color.totalPercentage }}% vinn</div>
</div>
</div>
<div class="inner-bought-container total-raffles">
<div class="total-container">
Totalt&nbsp;
<div>
<span class="total">{{ total }}</span> kjøpte
</div>
<div>{{ totalWin }} vinn</div>
<div>{{ stolen }} stjålet</div>
</div>
>
<p class="winner-chance">
{{translate(color.name)}} vinnersjanse
</p>
<span class="win-percentage">{{ color.totalPercentage }}% </span>
<p class="total-bought-color">{{ color.total }} kjøpte</p>
<p class="amount-of-wins"> {{ color.win }} vinn </p>
</div>
</div>
</div>
</section>
</template>
<script>
import { colorStatistics } from "@/api";
@@ -60,11 +55,13 @@ export default {
},
async mounted() {
let response = await colorStatistics();
this.red = response.red;
this.blue = response.blue;
this.green = response.green;
this.yellow = response.yellow;
this.total = response.total;
this.totalWin =
this.red.win + this.yellow.win + this.blue.win + this.green.win;
this.stolen = response.stolen;
@@ -114,119 +111,106 @@ export default {
this.colors = this.colors.sort((a, b) => (a.win > b.win ? -1 : 1));
},
methods: {
translate(color){
switch(color) {
case "blue":
return "Blå"
break;
case "red":
return "Rød"
break;
case "green":
return "Grønn"
break;
case "yellow":
return "Gul"
break;
break;
}
},
getPercentage: function(win, total) {
return this.round(win == 0 ? 0 : (win / total) * 100);
},
round: function(number) {
return Math.round(number * 100) / 100;
//this can make the odds added together more than 100%, maybe rework?
let actualPercentage = Math.round(number * 100) / 100;
let rounded = actualPercentage.toFixed(0);
return rounded;
}
}
};
</script>
<style lang="scss" scoped>
@import "../styles/global.scss";
@import "../styles/variables.scss";
@import "../styles/media-queries.scss";
@import "../styles/global.scss";
.inner-bought-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.raffle-element {
width: 140px;
height: 150px;
margin: 20px 0;
}
.number-container {
display: flex;
align-items: flex-end;
& span:last-child {
padding-bottom: 5px;
padding-left: 5px;
}
}
.inner-text-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
// TODO fix styling for displaying in columns
@include mobile {
& div {
padding: 0 5px;
}
@include mobile{
section {
margin-top: 5em;
}
}
.total-raffles {
width: 150px;
height: 150px;
margin: 20px 0;
}
.total-container {
align-items: flex-start;
}
@include mobile {
.total-container {
> div:nth-of-type(2) {
margin-top: auto;
padding-bottom: 4px;
padding-left: 5px;
}
}
}
.bought-number-span {
display: inline-flex;
}
.outer-bought {
@include mobile {
padding: 0 20px;
}
display: flex;
}
.bought-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
padding-bottom: 3rem;
max-width: 1400px;
margin: auto;
justify-content: space-between;
font-family: Arial;
margin-top: 2em;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-gap: 50px;
@include mobile {
padding-bottom: 0px;
}
}
.raffle-element-local {
height: 250px;
width: 100%;
display: flex;
flex-direction: column;
position: relative;
@include raffle;
.win-percentage {
margin-left: 30px;
font-size: 50px;
}
.color-total,
.total {
font-size: 2rem;
font-weight: bold;
}
p {
margin-left: 30px;
&.winner-chance {
margin-top: 40px;
}
.small {
font-weight: bold;
font-size: 1.25rem;
display: inline-block;
}
&.total-bought-color{
font-weight: bold;
margin-top: 25px;
}
@include mobile {
.bought-container {
flex-wrap: wrap;
&.amount-of-wins {
font-weight: bold;
}
}
h3 {
margin-left: 15px;
}
&.green-raffle {
background-color: $light-green;
}
&.blue-raffle {
background-color: $light-blue;
}
&.yellow-raffle {
background-color: $light-yellow;
}
&.red-raffle {
background-color: $light-red;
}
}
}
</style>

View File

@@ -1,5 +1,6 @@
<template>
<div class="wine">
<slot name="top"></slot>
<div class="wine-image">
<img
v-if="wine.image && loadImage"
@@ -9,8 +10,8 @@
</div>
<div class="wine-details">
<h2 v-if="wine.name">{{ wine.name }}</h2>
<span v-if="wine.rating"><b>Rating:</b> {{ wine.rating }} rating</span>
<span v-if="wine.name" class="wine-name">{{ wine.name }}</span>
<span v-if="wine.rating"><b>Rating:</b> {{ wine.rating }}</span>
<span v-if="wine.price"><b>Pris:</b> {{ wine.price }} NOK</span>
<span v-if="wine.country"><b>Land:</b> {{ wine.country }}</span>
</div>
@@ -18,6 +19,7 @@
<slot></slot>
<div class="bottom-section">
<slot name="bottom"></slot>
<a v-if="wine.vivinoLink" :href="wine.vivinoLink" class="link float-right">
Les mer
</a>
@@ -61,12 +63,11 @@ export default {
<style lang="scss" scoped>
@import "./src/styles/media-queries";
@import "./src/styles/global";
@import "./src/styles/variables";
.wine {
padding: 2rem;
margin: 1rem 0rem;
padding: 1rem;
box-sizing: border-box;
position: relative;
-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);
@@ -75,13 +76,13 @@ export default {
@include tablet {
width: 250px;
height: 100%;
margin: 1rem 2rem;
}
}
.wine-image {
display: flex;
justify-content: center;
margin-top: 10px;
img {
height: 250px;
@@ -96,6 +97,7 @@ export default {
}
}
.wine-details {
display: flex;
flex-direction: column;
@@ -105,6 +107,16 @@ export default {
}
}
.wine-name{
font-size: 20px;
margin: 1em 0;
}
.wine-details {
display: flex;
flex-direction: column;
}
.bottom-section {
width: 100%;
margin-top: 1rem;

View File

@@ -1,21 +1,25 @@
<template>
<div v-if="wines.length > 0">
<h3>
<router-link to="viner"
>Topp 10 viner <span class="vin-link">Se alle &gt;</span></router-link
>
</h3>
<ol class="list-container">
<li v-for="(wine, index) in wines" :key="wine" class="single-item">
<span class="wine-occurences">{{ index + 1}}.</span>
<span class="wine-name">{{ wine.name }}</span>
<span class="wine-win-info"> {{ wine.occurences }} {{amount(wine.occurences)}}</span>
<a
class="wine-link"
:href="wine.vivinoLink"
>Les mer</a>
</li>
</ol>
<div v-if="wines.length > 0" class="wines-main-container">
<div class="info-and-link">
<h3>
Topp 5 viner
</h3>
<router-link to="viner">
<span class="vin-link">Se alle viner </span>
</router-link>
</div>
<div class="wine-container">
<Wine v-for="wine in wines" :key="wine" :wine="wine">
<template v-slot:top>
<div class="flex justify-end">
<div class="requested-count cursor-pointer">
<span> {{ wine.occurences }} </span>
<i class="icon icon--heart" />
</div>
</div>
</template>
</Wine>
</div>
</div>
</template>
@@ -29,7 +33,10 @@ export default {
Wine
},
data() {
return { wines: [], clickedWine: null, wineOpen: false };
return {
wines: [],
clickedWine: null,
};
},
async mounted() {
let response = await overallWineStatistics();
@@ -49,12 +56,9 @@ export default {
}
)
);
this.wines = response.slice(0, 10);
this.wines = response.slice(0, 5);
},
methods: {
amount(occurences){
return occurences > 1 ? "ganger" : "gang";
},
predicate: function() {
var fields = [],
n_fields = arguments.length,
@@ -124,40 +128,40 @@ export default {
@import "./src/styles/global.scss";
@import "../styles/media-queries.scss";
h3 {
& a {
text-decoration: none;
color: #333333;
}
.wines-main-container {
margin-bottom: 10em;
}
.list-container{
.info-and-link{
display: flex;
justify-content: space-between;
}
.wine-container {
display: grid;
grid: auto-flow min-content / 1fr;
row-gap: 5px;
padding: 0;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
grid-gap: 2rem;
.single-item{
display: grid;
grid: 1fr / .1fr 1fr .3fr .3fr;
.requested-count {
display: flex;
align-items: center;
margin-top: -0.5rem;
background-color: rgb(244,244,244);
border-radius: 1.1rem;
padding: 0.25rem 1rem;
font-size: 1.25em;
.wine-occurences{
font-weight: bold;
span {
padding-right: 0.5rem;
line-height: 1.25em;
}
.wine-name{
display: inline-block;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.wine-link {
color: #333333;
text-decoration: underline 1px solid $link-color;
font-weight: bold;
cursor: pointer;
.icon--heart{
font-size: 1.5rem;
color: $link-color;
}
}
}
</style>

View File

@@ -4,13 +4,26 @@ import { routes } from "@/router.js";
import Vinlottis from "@/Vinlottis";
import VueAnalytics from "vue-analytics";
import "core-js/stable"; // babel polyfill
import * as Sentry from "@sentry/browser";
import { Vue as VueIntegration } from "@sentry/integrations";
import { Integrations } from "@sentry/tracing";
Vue.use(VueRouter);
Vue.use(VueAnalytics, {
id: "UA-156846886-1"
});
Sentry.init({
dsn: "https://7debc951f0074fb68d7a76a1e3ace6fa@o364834.ingest.sentry.io/4905091",
integrations: [
new VueIntegration({
Vue,
tracing: true
}),
new Integrations.BrowserTracing(),
]
})
const router = new VueRouter({
routes: routes
});
@@ -22,3 +35,4 @@ new Vue({
template: "<Vinlottis/>",
render: h => h(Vinlottis)
});