├── .eslintrc.json
├── .github
└── workflows
│ └── linters.yml
├── .gitignore
├── .hintrc
├── .stylelintrc.json
├── README.md
├── dist
├── index.html
└── main.js
├── package-lock.json
├── package.json
├── src
├── index.html
├── index.js
├── modules
│ └── scoreUI.js
└── style.css
└── webpack.config.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "jest": true
6 | },
7 | "parser": "babel-eslint",
8 | "parserOptions": {
9 | "ecmaVersion": 2018,
10 | "sourceType": "module"
11 | },
12 | "extends": ["airbnb-base"],
13 | "rules": {
14 | "no-shadow": "off",
15 | "no-param-reassign": "off",
16 | "eol-last": "off",
17 | "import/extensions": [ 1, {
18 | "js": "always", "json": "always"
19 | }]
20 | },
21 | "ignorePatterns": [
22 | "dist/",
23 | "build/",
24 | "src/"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/.github/workflows/linters.yml:
--------------------------------------------------------------------------------
1 | name: Linters
2 |
3 | on: pull_request
4 |
5 | env:
6 | FORCE_COLOR: 1
7 |
8 | jobs:
9 | lighthouse:
10 | name: Lighthouse
11 | runs-on: ubuntu-18.04
12 | steps:
13 | - uses: actions/checkout@v2
14 | - uses: actions/setup-node@v1
15 | with:
16 | node-version: "12.x"
17 | - name: Setup Lighthouse
18 | run: npm install -g @lhci/cli@0.7.x
19 | - name: Lighthouse Report
20 | run: lhci autorun --upload.target=temporary-public-storage --collect.staticDistDir=.
21 | webhint:
22 | name: Webhint
23 | runs-on: ubuntu-18.04
24 | steps:
25 | - uses: actions/checkout@v2
26 | - uses: actions/setup-node@v1
27 | with:
28 | node-version: "12.x"
29 | - name: Setup Webhint
30 | run: |
31 | npm install --save-dev hint@6.x
32 | [ -f .hintrc ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.hintrc
33 | - name: Webhint Report
34 | run: npx hint --telemetry=off .
35 | stylelint:
36 | name: Stylelint
37 | runs-on: ubuntu-18.04
38 | steps:
39 | - uses: actions/checkout@v2
40 | - uses: actions/setup-node@v1
41 | with:
42 | node-version: "12.x"
43 | - name: Setup Stylelint
44 | run: |
45 | npm install --save-dev stylelint@13.x stylelint-scss@3.x stylelint-config-standard@21.x stylelint-csstree-validator@1.x
46 | [ -f .stylelintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.stylelintrc.json
47 | - name: Stylelint Report
48 | run: npx stylelint "**/*.{css,scss}"
49 | eslint:
50 | name: ESLint
51 | runs-on: ubuntu-18.04
52 | steps:
53 | - uses: actions/checkout@v2
54 | - uses: actions/setup-node@v1
55 | with:
56 | node-version: "12.x"
57 | - name: Setup ESLint
58 | run: |
59 | npm install --save-dev eslint@7.x eslint-config-airbnb-base@14.x eslint-plugin-import@2.x babel-eslint@10.x
60 | [ -f .eslintrc.json ] || wget https://raw.githubusercontent.com/microverseinc/linters-config/master/html-css-js/.eslintrc.json
61 | - name: ESLint Report
62 | run: npx eslint .
63 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # .gitignore
2 | node_modules/
--------------------------------------------------------------------------------
/.hintrc:
--------------------------------------------------------------------------------
1 | {
2 | "connector": {
3 | "name": "local",
4 | "options": {
5 | "pattern": ["**", "!.git/**", "!node_modules/**"]
6 | }
7 | },
8 | "extends": ["development"],
9 | "formatters": ["stylish"],
10 | "hints": [
11 | "button-type",
12 | "disown-opener",
13 | "html-checker",
14 | "meta-charset-utf-8",
15 | "meta-viewport",
16 | "no-inline-styles:error"
17 | ]
18 | }
--------------------------------------------------------------------------------
/.stylelintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["stylelint-config-standard"],
3 | "plugins": ["stylelint-scss", "stylelint-csstree-validator"],
4 | "rules": {
5 | "at-rule-no-unknown": null,
6 | "scss/at-rule-no-unknown": true,
7 | "csstree/validator": true
8 | },
9 | "ignoreFiles": ["build/**", "dist/**", "**/reset*.css", "**/bootstrap*.css", "**/*.js", "**/*.jsx"]
10 | }
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Leaderboard
2 |
3 | > The leaderboard website displays scores submitted by different players. It also allows you to submit your score. All data is preserved thanks to the external [Leaderboard API service](https://www.notion.so/microverse/Leaderboard-API-service-24c0c3c116974ac49488d4eb0267ade3).
4 |
5 | ## How it looks like :
6 |
7 | 
8 |
9 |
10 |
11 | ## Built With
12 |
13 | - Major languages: Javascript, HTML & CSS
14 | - Frameworks: none
15 | - Technologies used: Webpack, Leaderboard API service
16 |
17 | ## Live Demo is avaible here
18 |
19 | [Live Demo Link](https://anicetfantomas.github.io/Leaderboard/)
20 |
21 |
22 | ## Getting Started
23 |
24 | ```
25 | To get a local copy up and running follow these simple example steps.
26 |
27 | git clone
28 |
29 | https://github.com/AnicetFantomas/Leaderboard.git
30 |
31 | ```
32 | ### To run this project locacally
33 | ```
34 | - mpm install
35 | - npm start
36 | ```
37 |
38 | ## Prerequisites
39 |
40 | The basic requirements for building the javascript are:
41 | ```
42 | - A working browser application (Google chrome, Mozilla Firefox, Microsoft edge ...)
43 | - VSCode or any other equivalent code editor
44 | - Node Package Manager (For installing packages like Lighthous, webhint & stylelint used for checking and or debugging bad codes before deployment)
45 | ```
46 |
47 | ### Deployment
48 |
49 | https://anicetfantomas.github.io/Leaderboard/
50 |
51 | ## Authors
52 |
53 | 👤 **Anicet Murhula**
54 |
55 | - GitHub: [@AnicetFantomas](https://github.com/AnicetFantomas)
56 |
57 | - LinkedIn: [Anicet Murhula](https://www.linkedin.com/in/anicet-murhula-13a1b0220/)
58 |
59 |
60 | ## 🤝 Contributing
61 |
62 | Contributions, issues, and feature requests are welcome!
63 |
64 | Feel free to check the [issues page](../../issues/).
65 |
66 | ## Show your support
67 |
68 | Give a ⭐️ if you like this project!
69 |
70 | ## Acknowledgments
71 |
72 | - Hat tip to anyone whose code was used
73 | - Inspiration
74 | - etc
75 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
Wbpack ExerciseHello webpack!
--------------------------------------------------------------------------------
/dist/main.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";var e,t,r,n,o,a,i,c,s,u,p,l,d,f,v={426:(e,t,r)=>{r.d(t,{Z:()=>c});var n=r(81),o=r.n(n),a=r(645),i=r.n(a)()(o());i.push([e.id,"",""]);const c=i},645:e=>{e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var r="",n=void 0!==t[5];return t[4]&&(r+="@supports (".concat(t[4],") {")),t[2]&&(r+="@media ".concat(t[2]," {")),n&&(r+="@layer".concat(t[5].length>0?" ".concat(t[5]):""," {")),r+=e(t),n&&(r+="}"),t[2]&&(r+="}"),t[4]&&(r+="}"),r})).join("")},t.i=function(e,r,n,o,a){"string"==typeof e&&(e=[[null,e,void 0]]);var i={};if(n)for(var c=0;c0?" ".concat(p[5]):""," {").concat(p[1],"}")),p[5]=a),r&&(p[2]?(p[1]="@media ".concat(p[2]," {").concat(p[1],"}"),p[2]=r):p[2]=r),o&&(p[4]?(p[1]="@supports (".concat(p[4],") {").concat(p[1],"}"),p[4]=o):p[4]="".concat(o)),t.push(p))}},t}},81:e=>{e.exports=function(e){return e[1]}},379:e=>{var t=[];function r(e){for(var r=-1,n=0;n{var t={};e.exports=function(e,r){var n=function(e){if(void 0===t[e]){var r=document.querySelector(e);if(window.HTMLIFrameElement&&r instanceof window.HTMLIFrameElement)try{r=r.contentDocument.head}catch(e){r=null}t[e]=r}return t[e]}(e);if(!n)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");n.appendChild(r)}},216:e=>{e.exports=function(e){var t=document.createElement("style");return e.setAttributes(t,e.attributes),e.insert(t,e.options),t}},565:(e,t,r)=>{e.exports=function(e){var t=r.nc;t&&e.setAttribute("nonce",t)}},795:e=>{e.exports=function(e){var t=e.insertStyleElement(e);return{update:function(r){!function(e,t,r){var n="";r.supports&&(n+="@supports (".concat(r.supports,") {")),r.media&&(n+="@media ".concat(r.media," {"));var o=void 0!==r.layer;o&&(n+="@layer".concat(r.layer.length>0?" ".concat(r.layer):""," {")),n+=r.css,o&&(n+="}"),r.media&&(n+="}"),r.supports&&(n+="}");var a=r.sourceMap;a&&"undefined"!=typeof btoa&&(n+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),t.styleTagTransform(n,e,t.options)}(t,e,r)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(t)}}}},589:e=>{e.exports=function(e,t){if(t.styleSheet)t.styleSheet.cssText=e;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(e))}}}},m={};function h(e){var t=m[e];if(void 0!==t)return t.exports;var r=m[e]={id:e,exports:{}};return v[e](r,r.exports,h),r.exports}h.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return h.d(t,{a:t}),t},h.d=(e,t)=>{for(var r in t)h.o(t,r)&&!h.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},h.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e=h(379),t=h.n(e),r=h(795),n=h.n(r),o=h(569),a=h.n(o),i=h(565),c=h.n(i),s=h(216),u=h.n(s),p=h(589),l=h.n(p),d=h(426),(f={}).styleTagTransform=l(),f.setAttributes=c(),f.insert=a().bind(null,"head"),f.domAPI=n(),f.insertStyleElement=u(),t()(d.Z,f),d.Z&&d.Z.locals&&d.Z.locals})();
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leaderboard",
3 | "version": "1.0.0",
4 | "description": "",
5 | "private": true,
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "webpack serve --open",
9 | "build": "webpack"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/AnicetFantomas/Leaderboard.git"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "ISC",
18 | "bugs": {
19 | "url": "https://github.com/AnicetFantomas/Leaderboard/issues"
20 | },
21 | "homepage": "https://github.com/AnicetFantomas/Leaderboard#readme",
22 | "devDependencies": {
23 | "babel-eslint": "^10.1.0",
24 | "css-loader": "^6.7.1",
25 | "eslint": "^7.32.0",
26 | "eslint-config-airbnb-base": "^14.2.1",
27 | "eslint-plugin-import": "^2.26.0",
28 | "hint": "^6.1.11",
29 | "html-webpack-plugin": "^5.5.0",
30 | "style-loader": "^3.3.1",
31 | "stylelint": "^13.13.1",
32 | "stylelint-config-standard": "^21.0.0",
33 | "stylelint-csstree-validator": "^1.9.0",
34 | "stylelint-scss": "^3.21.0",
35 | "webpack": "^5.72.0",
36 | "webpack-cli": "^4.9.2",
37 | "webpack-dev-server": "^4.8.1"
38 | },
39 | "dependencies": {
40 | "axios": "^0.26.1",
41 | "lodash": "^4.17.21"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Anicet's Leaderboard
7 |
8 |
9 | Leaderboard
10 |
11 |
12 |
18 |
19 |
Add your score
20 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import './style.css';
2 | import { loadData, loadScores, getScores } from './modules/scoreUI';
3 |
4 | const url = 'https://us-central1-js-capstone-backend.cloudfunctions.net/api/games/AkKe5vSRElL4hhJr8Kvj/scores';
5 | const refreshBtn = document.getElementById('refresh-btn');
6 | const getForm = document.querySelector('.myForm');
7 |
8 | window.addEventListener('load', () => loadScores(url))
9 | refreshBtn.addEventListener('click',() => loadScores(url));
10 | getForm.addEventListener('submit', async (e) => {
11 | e.preventDefault();
12 | await getScores(url, loadData());
13 | loadScores(url);
14 | getForm.reset();
15 | })
16 |
--------------------------------------------------------------------------------
/src/modules/scoreUI.js:
--------------------------------------------------------------------------------
1 | const userInput = document.getElementById('name');
2 | const scoreInput = document.getElementById('score');
3 | const listBoard = document.getElementById('my-list');
4 |
5 | const getAll = async (url) => (await fetch(url)).json();
6 | const getScores = async(url, score) => (await fetch(url, score)).json();
7 |
8 | const loadData = () => {
9 | const scoresData = {
10 | method : 'POST',
11 | headers: { 'Content-Type': 'application/json' },
12 | body : JSON.stringify({
13 | user: userInput.value.trim(),
14 | score: scoreInput.value.trim()
15 | })
16 | };
17 | return scoresData;
18 | };
19 |
20 | loadData();
21 |
22 | const loadScores = async (url) => {
23 | const {result} = await getAll(url);
24 | listBoard.innerHTML = result.sort((a,b) => b.score - a.score).map((score) => `${score.user}: ${score.score}`).join('')
25 | }
26 | export {loadData, loadScores, getScores};
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | padding: 0;
3 | margin: 0;
4 | box-sizing: border-box;
5 | }
6 |
7 | body {
8 | background-color: rgb(235, 242, 245);
9 | width: 100%;
10 | display: flex;
11 | justify-content: center;
12 | flex-direction: column;
13 | padding-bottom: 17%;
14 | padding-top: 30px;
15 | }
16 |
17 | h1 {
18 | text-align: center;
19 | color: red;
20 | font-size: 50px;
21 | margin-bottom: 30px;
22 | }
23 |
24 | .wrapper {
25 | background-color: #fff;
26 | width: 90%;
27 | border: 2px solid #000;
28 | padding: 8px;
29 | align-self: center;
30 | border-radius: 10px;
31 | }
32 |
33 | .scores-container {
34 | width: 90%;
35 | display: flex;
36 | margin: 30px;
37 | }
38 |
39 | .scores {
40 | width: 50%;
41 | }
42 |
43 | .scores-header {
44 | display: flex;
45 | margin-bottom: 10px;
46 | }
47 |
48 | .scores-header h2 {
49 | color: red;
50 | flex-grow: 1;
51 | font-size: 30px;
52 | }
53 |
54 | form button {
55 | padding: 5px;
56 | width: 30%;
57 | margin-left: 68%;
58 | font-weight: bold;
59 | font-size: 18px;
60 | }
61 |
62 | .scores-header button {
63 | padding: 5px;
64 | width: 30%;
65 | font-weight: bold;
66 | font-size: 18px;
67 | }
68 |
69 | .scores-form {
70 | width: 50%;
71 | margin-left: 20px;
72 | }
73 |
74 | .scores-form h2 {
75 | color: red;
76 | margin-bottom: 10px;
77 | font-size: 30px;
78 | }
79 |
80 | form {
81 | background-color: #fff;
82 | border: 2px solid #000;
83 | padding: 6px;
84 | }
85 |
86 | .scores-form form {
87 | display: flex;
88 | flex-direction: column;
89 | }
90 |
91 | form input {
92 | margin: 10px;
93 | padding: 5px;
94 | border: 2px solid black;
95 | }
96 |
97 | button:focus {
98 | transform: scale(0.7);
99 | }
100 |
101 | button:hover {
102 | background-color: rgb(145, 115, 115);
103 | }
104 |
105 | ul {
106 | background-color: #fff;
107 | border: 2px solid black;
108 | list-style: none;
109 | padding: 8px;
110 | font-size: 20px;
111 | font-weight: bold;
112 | }
113 |
114 | li:hover {
115 | transform: scaleY(0.9);
116 | font-size: 20px;
117 | }
118 |
119 | li:nth-child(odd) {
120 | background-color: rgb(207, 202, 202);
121 | }
122 |
123 | input::placeholder {
124 | color: rgb(37, 36, 36);
125 | font-size: 18px;
126 | }
127 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 |
4 | module.exports = {
5 | mode: 'development',
6 | entry: './src/index.js',
7 | devServer: {
8 | static: './dist',
9 | },
10 | plugins: [
11 | new HtmlWebpackPlugin({
12 | template: './src/index.html',
13 | }),
14 | ],
15 | output: {
16 | filename: 'main.js',
17 | path: path.resolve(__dirname, 'dist'),
18 | },
19 | module: {
20 | rules: [
21 | {
22 | test: /\.css$/i,
23 | use: ['style-loader', 'css-loader'],
24 | },
25 | ],
26 | },
27 | };
--------------------------------------------------------------------------------