Skip to content

Commit

Permalink
StreamHatchet Frontend Technical Test
Browse files Browse the repository at this point in the history
  • Loading branch information
Xarlizard committed Oct 24, 2024
1 parent 9697078 commit 19ecca4
Show file tree
Hide file tree
Showing 7 changed files with 429 additions and 0 deletions.
62 changes: 62 additions & 0 deletions sites/streamhatchet_frontendtest/css/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*Responsive page, has two sizes for desktop or phone devices (just to show off how the streamhatchet header webcomponent resizes)*/
body {
margin-top: 450px;
display: flex;
flex-direction: column;
font-family: 'Nunito', Arial;
font-size: 22px;
}
.container{
margin: auto;
display: flex;
gap: 30px;
flex-direction: row;
justify-content: center;
width: 80%;
margin: auto;
height: 100%;
}

.containerr {
width: 80%;
margin: 8em auto;
}

.chart-container {
min-width: 300px;
}

#myChart {
width: 100%;
max-width: 300px;
margin: 0 auto;
}

#buttonContainer{
transform: scale(0);
margin: auto;
}

button{
margin: auto 0;
height: 100px;
}

button:hover{
cursor: pointer;
}

#getRequestContainer button{
height: 20px;
}

#buttonload {
display: none;
}

/* Specifications for desktop screen size*/
@media only screen and (min-width:800px) {
body {
margin-top: 80px;
}
}
Binary file added sites/streamhatchet_frontendtest/img/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions sites/streamhatchet_frontendtest/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Demo StreamHatchet</title>
<link rel="shortcut icon" href="img/logo.png" type="image/x-icon">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/charts.css/dist/charts.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="css/main.css">
</head>
<!--THIS ENTIRE PAGE SERVES AS THE TECHNICAL TEST REQUIRED FOR AN INTERVIEW AIMING TO JOIN STREAMHATCHET TEAM.
TO CORRECTLY USE/SEE THIS PAGE CONTENT, A VALID API MUST BE PROVIDED.-->
<body>

<header>
<!--Custom webcomponent element for the header-->
<header-streamhatchet></header-streamhatchet>
</header>

<div class="container" id="getRequestContainer">
<p>API Key:
<!--Custom webcomponent element for a clickable popup that displays relevant info for the user-->
<popup-notify>
<span slot="popupText">
-Input your 30-characters API_KEY
<br>
(Used for GET server request through Axios method, doesn't get stored)
</span>
</popup-notify>
<input type="text" id="apiInput" value="">
<!--This button calls for axios get request with above's given input value field, making this static page secure to load on a public repository without compromising any keys/secrets-->
<button id="fetchServer">Send Api Request</button>
<button id="buttonload">
<i class="fa fa-circle-o-notch fa-spin"></i>Waiting for server
</button>
</p>
</div>
<div class="container">
<!--We use chart.js library to create custom charts for StreamHatchet API data through the "canvas" HTML element-->
<div class="chart-container">
<canvas id="myChart"></canvas>
</div>
</div>

<!-- A div containing all the buttons, remains hidden until data is fetched from server and is secure to push buttons-->
<div id="buttonContainer">
<button id="airtimeHours">Airtime hours</button>
<button id="maxRank">Max Rank</button>
<button id="averageViewers">Average Viewers</button>
<button id="averageChannels">Average Channels</button>
</div>

<!--Script for Axios Fetching and creating the desired Chart with the library chart.js that was previsously lodaded on the page-->
<script src="./js/main.js"></script>
<!--Custom webcomponent file reference for the header-->
<script src="./js/popupNotify.js"></script>
<script src="js/header-streamhatchet.js"></script>
</body>
</html>
83 changes: 83 additions & 0 deletions sites/streamhatchet_frontendtest/js/header-streamhatchet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//const template = document.createElement("template"); //If more webcomponents use this, remove it and add it to the main js file ONCE.
template.innerHTML = `
<style>
ul {
list-style-type: none;
gap: 5px;
margin: 0;
padding: 10px;
overflow: hidden;
background-color: #333;
position: fixed;
top: 0;
width: 100%;
}
li {
font-size: 1.1em;
}
li > img {
height: 50px;
margin-right: 20px;
}
li > a{
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
li > a:hover:not(.active) {
background-color: #111;
border-radius: 50px;
}
.active {
color: black;
background-color: white;
border-radius: 50px;
}
@media only screen and (min-width:800px) {
li{
float: left;
}
}
</style>
<ul class="headerList">
<li class="header-item"><img src="img/logo.png" alt="logo" srcset="https://streamhatchet.com/wp-content/uploads/2020/05/SH_Icon_-primary-2.png"></li>
<li class="header-item "><a href="#channels">Channels</a></li>
<li class="header-item"><a class="active" href="#games">Games</a></li>
<li class="header-item"><a href="#campaigns">Campaigns</a></li>
<li class="header-item"><a href="#esports">Esports</a></li>
<li class="header-item"><a href="#comparison">Comparison</a></li>
<li class="header-item"><a href="#insight">Insights</a></li>
<li class="header-item"><a href="#youtube">YouTube VODs</a></li>
</ul>
`;

class HeaderStreamHatchet extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(template.content.cloneNode(true));
}


//function to change between header titles/pages
headerSelect() {
const headerItem = this.shadowRoot.querySelector('.header-item');
return true;
}

connectedCallback() {
this.shadowRoot.querySelector('.header-item').addEventListener('click', () => {
this.headerSelect()
})
}
}

window.customElements.define("header-streamhatchet", HeaderStreamHatchet);
120 changes: 120 additions & 0 deletions sites/streamhatchet_frontendtest/js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//We initialize all the variables needed later on server GET Request
let API_KEY = ""; //API will be provided by user input, we don't store API's on our client-side app's.
let {game1, game2, game3, game4, game5} = {}; //Variables to initialize empty chart to later be populated by server response.data
const template = document.createElement("template"); //We load the template that will be used by the WEB-COMPONENTS (for now we have 2, a custom HeaderStreamHatchet and a popupNotify for user interaction)

//Buttons that dynamically change the chart data
airtimeHours.addEventListener("click", () => {
chart1.data.datasets[0].label = 'Airtime-hours';
chart1.data.datasets[0].data = [game1.airtime_hours, game2.airtime_hours, game3.airtime_hours, game4.airtime_hours, game5.airtime_hours, ]
chart1.update();
});

maxRank.addEventListener("click", () => {
chart1.data.datasets[0].label = 'Max_rank';
chart1.data.datasets[0].data = [game1.max_rank, game2.max_rank, game3.max_rank, game4.max_rank, game5.max_rank, ]
chart1.update();
});

averageViewers.addEventListener("click", () => {
chart1.data.datasets[0].label = 'Average_viewers';
chart1.data.datasets[0].data = [game1.average_viewers, game2.average_viewers, game3.average_viewers, game4.average_viewers, game5.average_viewers, ]
chart1.update();
});

averageChannels.addEventListener("click", () => {
chart1.data.datasets[0].label = 'Average_channels';
chart1.data.datasets[0].data = [game1.average_channels, game2.average_channels, game3.average_channels, game4.average_channels, game5.average_channels]
chart1.update();
});

//Chart creation with chart.js library -> TODO: Make it a standalone webcomponent as we did with custom header-streamhatchet, this way a constructor() could be setted for future charts.
//first we fetch de document element for the chart, a canvas inside a div with id #myChart
const ctx = document.getElementById('myChart');
//Then we prepare the data set that is going to get passedd to the chart constructor
const data = {
//Instead of using generic labels we put the basic instructions for the user to read
labels: [
'You need to input a valid api',
'to get this working, anyways chart',
'animations do work meanwhile, you',
'just need to click on {here} or even',
'<---{here} if you want to see it.'
],
datasets: [{
//Label showed on-hover
label: 'Input a valid API',
//dummy-data for first page load
data: [1, 1, 1, 1, 1],
backgroundColor: [
'rgb(255, 99, 132)',
'rgb(54, 162, 235)',
'rgb(255, 205, 86)',
'rgb(255, 205, 0)',
'rgb(255, 0, 86)'
],
hoverOffset: 4,
//this is the doughnut circumference, we want only half for this chart
circumference: 180,
//by default the half-doughnut is rotated vertically, we need to adjust it by 90degrees increments (270 in this case)
rotation: 270
}]
};
//We load the initial chart (Axios hasnt fetched data from server, dummy/demo datasets should be provided as goodpractices)
const chart1 = new Chart(ctx, {
type: 'doughnut',
data: data,
options: {
aspectRatio: 1,
plugins: {
tooltip: {enabled:true},
legend: {display: true}
}
}
});

//Server Requests
//fetch that loads when providing an API through the screen input field + button
fetchServer.addEventListener("click", () => {

//Store the user
API_KEY = document.getElementById("apiInput").value;
document.getElementById("apiInput").value = "";

//Only when API provided by user has accepted length we fetch/call the server
if ( API_KEY.length == 30 ) {
//When the API is valid, we disable GET button and show a button animation
document.getElementById("fetchServer").style.display = "none";
document.getElementById("buttonload").style.display = "inline-flex";

axios
.get(`https://api.streamhatchet.com/discovery/games/month/2024-01?token=${API_KEY}&sort_by=average_viewers&order=desc&limit=50&offset=0`)
.then((response) => {
//First things first, after server request is successful we disable API Input element's display
document.getElementById("getRequestContainer").style.display = "none";
//We store the server response on games const, if we ever want to change the displayed games with a future button
const games = response.data.games;
game1 = games[0];
game2 = games[1];
game3 = games[2];
game4 = games[3];
game5 = games[4];

//initial data population for the first server load
chart1.data.labels = [game1.game, game2.game, game3.game, game4.game, game5.game]
chart1.data.datasets[0].label = 'Airtime-hours';
chart1.data.datasets[0].data = [game1.airtime_hours, game2.airtime_hours, game3.airtime_hours, game4.airtime_hours, game5.airtime_hours, ]
chart1.update();
buttonContainer.style.transform = 'scale(1)';
})
.catch((error) => {
console.log(error);
//When the API_KEY is not valid, we enable GET button again
document.getElementById("fetchServer").style.display = "inline-flex";
document.getElementById("buttonload").style.display = "none";
});
}else{
//Promp a warning indicating wrong input length/type

}
});
Loading

0 comments on commit 19ecca4

Please sign in to comment.