Strings "Følg med på utviklingen" and "chat om trekningen" scrolls the page content into view if clicked.
		
			
				
	
	
		
			399 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			399 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <div>
 | 
						|
    <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" @click="scrollToContent">Følg med på utviklingen</span> og
 | 
						|
          <span class="vin-link" @click="scrollToContent">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>
 | 
						|
 | 
						|
    <div class="todays-wines">
 | 
						|
      <h2>Dagens fangst ({{ wines.length }})</h2>
 | 
						|
      <div class="wines-container">
 | 
						|
        <Wine :wine="wine" v-for="wine in wines" :key="wine" />
 | 
						|
      </div>
 | 
						|
    </div>
 | 
						|
  </div>
 | 
						|
</template>
 | 
						|
 | 
						|
<script>
 | 
						|
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, VippsPill, Wine },
 | 
						|
  data() {
 | 
						|
    return {
 | 
						|
      attendees: [],
 | 
						|
      attendeesFetched: false,
 | 
						|
      winners: [],
 | 
						|
      wines: [],
 | 
						|
      currentWinnerDrawn: false,
 | 
						|
      currentWinner: null,
 | 
						|
      socket: null,
 | 
						|
      wasDisconnected: false,
 | 
						|
      ticketsBought: {
 | 
						|
        red: 0,
 | 
						|
        blue: 0,
 | 
						|
        green: 0,
 | 
						|
        yellow: 0
 | 
						|
      }
 | 
						|
    };
 | 
						|
  },
 | 
						|
  mounted() {
 | 
						|
    this.track();
 | 
						|
    this.getAttendees();
 | 
						|
    this.getTodaysWines();
 | 
						|
    this.getWinners();
 | 
						|
    this.socket = io(window.location.origin);
 | 
						|
    this.socket.on("color_winner", msg => {});
 | 
						|
 | 
						|
    this.socket.on("disconnect", msg => {
 | 
						|
      this.wasDisconnected = true;
 | 
						|
    });
 | 
						|
 | 
						|
    this.socket.on("winner", async msg => {
 | 
						|
      this.currentWinnerDrawn = true;
 | 
						|
      this.currentWinner = {
 | 
						|
        name: msg.name,
 | 
						|
        color: msg.color,
 | 
						|
        winnerCount: msg.winner_count
 | 
						|
      };
 | 
						|
 | 
						|
      setTimeout(() => {
 | 
						|
        this.getWinners();
 | 
						|
        this.getAttendees();
 | 
						|
        this.currentWinner = null;
 | 
						|
        this.currentWinnerDrawn = false;
 | 
						|
      }, 19250);
 | 
						|
    });
 | 
						|
    this.socket.on("refresh_data", async msg => {
 | 
						|
      this.getAttendees();
 | 
						|
      this.getWinners();
 | 
						|
    });
 | 
						|
    this.socket.on("new_attendee", async msg => {
 | 
						|
      this.getAttendees();
 | 
						|
    });
 | 
						|
  },
 | 
						|
  beforeDestroy() {
 | 
						|
    this.socket.disconnect();
 | 
						|
    this.socket = null;
 | 
						|
  },
 | 
						|
  methods: {
 | 
						|
    getWinners() {
 | 
						|
      fetch("/api/lottery/winners")
 | 
						|
        .then(resp => resp.json())
 | 
						|
        .then(response => (this.winners = response.winners));
 | 
						|
    },
 | 
						|
    getTodaysWines() {
 | 
						|
      fetch("/api/lottery/wines")
 | 
						|
        .then(resp => resp.json())
 | 
						|
        .then(response => response.wines)
 | 
						|
        .then(wines => {
 | 
						|
          this.wines = wines;
 | 
						|
          this.todayExists = wines.length > 0;
 | 
						|
        })
 | 
						|
        .catch(_ => (this.todayExists = false));
 | 
						|
    },
 | 
						|
    getAttendees() {
 | 
						|
      fetch("/api/lottery/attendees")
 | 
						|
        .then(resp => resp.json())
 | 
						|
        .then(response => {
 | 
						|
          const { attendees } = response;
 | 
						|
          this.attendees = attendees || [];
 | 
						|
 | 
						|
          if (attendees == undefined || attendees.length == 0) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
 | 
						|
          const addValueOfListObjectByKey = (list, key) => list.map(object => object[key]).reduce((a, b) => a + b);
 | 
						|
 | 
						|
          this.ticketsBought = {
 | 
						|
            red: addValueOfListObjectByKey(attendees, "red"),
 | 
						|
            blue: addValueOfListObjectByKey(attendees, "blue"),
 | 
						|
            green: addValueOfListObjectByKey(attendees, "green"),
 | 
						|
            yellow: addValueOfListObjectByKey(attendees, "yellow")
 | 
						|
          };
 | 
						|
        })
 | 
						|
        .finally(_ => (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");
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
</script>
 | 
						|
 | 
						|
<style lang="scss" scoped>
 | 
						|
@import "../styles/variables.scss";
 | 
						|
@import "../styles/media-queries.scss";
 | 
						|
 | 
						|
.container {
 | 
						|
  width: 80vw;
 | 
						|
  padding: 0 10vw;
 | 
						|
 | 
						|
  @include mobile {
 | 
						|
    width: 90vw;
 | 
						|
    padding: 0 5vw;
 | 
						|
  }
 | 
						|
 | 
						|
  display: grid;
 | 
						|
  grid-template-columns: repeat(4, 1fr);
 | 
						|
 | 
						|
  > div,
 | 
						|
  > section {
 | 
						|
    @include mobile {
 | 
						|
      grid-column: span 5;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
h2 {
 | 
						|
  font-size: 1.1rem;
 | 
						|
  margin-bottom: 1.75rem;
 | 
						|
}
 | 
						|
 | 
						|
header {
 | 
						|
  h1 {
 | 
						|
    text-align: left;
 | 
						|
    font-weight: 500;
 | 
						|
    font-size: 3rem;
 | 
						|
    margin: 4rem 0 2rem;
 | 
						|
 | 
						|
    @include mobile {
 | 
						|
      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;
 | 
						|
    }
 | 
						|
 | 
						|
    .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;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
.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: space-between;
 | 
						|
 | 
						|
  @include mobile {
 | 
						|
    width: 100%;
 | 
						|
    height: 100%;
 | 
						|
  }
 | 
						|
 | 
						|
  .raffle-element {
 | 
						|
    font-size: 1.6rem;
 | 
						|
    color: $matte-text-color;
 | 
						|
    height: 75px;
 | 
						|
    width: 75px;
 | 
						|
    display: flex;
 | 
						|
    justify-content: center;
 | 
						|
    align-items: center;
 | 
						|
 | 
						|
    margin: 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
.winners {
 | 
						|
  grid-column: 2 / 5;
 | 
						|
 | 
						|
  @include mobile {
 | 
						|
    order: 1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
.container-attendees {
 | 
						|
  grid-column: 1 / 3;
 | 
						|
  margin-right: 1rem;
 | 
						|
  margin-top: 2rem;
 | 
						|
 | 
						|
  @include mobile {
 | 
						|
    margin-right: 0;
 | 
						|
    order: 4;
 | 
						|
  }
 | 
						|
 | 
						|
  > div {
 | 
						|
    padding: 1rem;
 | 
						|
    max-height: 638px;
 | 
						|
    overflow-y: scroll;
 | 
						|
 | 
						|
    -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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
.container-chat {
 | 
						|
  grid-column: 3 / 5;
 | 
						|
  margin-left: 1rem;
 | 
						|
  margin-top: 2rem;
 | 
						|
 | 
						|
  @include mobile {
 | 
						|
    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);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
.todays-wines {
 | 
						|
  width: 80vw;
 | 
						|
  padding: 0 10vw;
 | 
						|
 | 
						|
  @include mobile {
 | 
						|
    width: 90vw;
 | 
						|
    padding: 0 5vw;
 | 
						|
  }
 | 
						|
 | 
						|
  h2 {
 | 
						|
    width: 100%;
 | 
						|
    grid-column: 1 / 5;
 | 
						|
  }
 | 
						|
 | 
						|
  .wine {
 | 
						|
    margin-right: 1rem;
 | 
						|
    margin-bottom: 1rem;
 | 
						|
  }
 | 
						|
}
 | 
						|
</style>
 |