diff --git a/src/App.vue b/src/App.vue index 1a140ee58a2d942ddacb457b5fbd6f5b5ad23e0b..12ad03a6b286a47ed6a392e7338562c82b213f64 100644 --- a/src/App.vue +++ b/src/App.vue @@ -14,5 +14,7 @@ import ErrorBoundaryCatcher from '@/components/Exceptions/ErrorBoundaryCatcher.v <style> main { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + font-weight: 600; + } </style> \ No newline at end of file diff --git a/src/assets/Sparesti-logo.png b/src/assets/Sparesti-logo.png index 22c480c6044969c4c3352cd33f20a3a0334441b9..edbd70f919853607975310bd02e62da2bbc7d53d 100644 Binary files a/src/assets/Sparesti-logo.png and b/src/assets/Sparesti-logo.png differ diff --git a/src/assets/icons/feedback.svg b/src/assets/icons/feedback.svg new file mode 100644 index 0000000000000000000000000000000000000000..796e4346c08bed42140a3d053a93e4a5dee90093 --- /dev/null +++ b/src/assets/icons/feedback.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M240-399h313v-60H240v60Zm0-130h480v-60H240v60Zm0-130h480v-60H240v60ZM80-80v-740q0-24 18-42t42-18h680q24 0 42 18t18 42v520q0 24-18 42t-42 18H240L80-80Zm134-220h606v-520H140v600l74-80Zm-74 0v-520 520Z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/assets/icons/leaderboard.svg b/src/assets/icons/leaderboard.svg new file mode 100644 index 0000000000000000000000000000000000000000..a5859255eb4c0914af29429319172e7d19eef324 --- /dev/null +++ b/src/assets/icons/leaderboard.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M140-180h187v-360H140v360Zm247 0h186v-600H387v600Zm246 0h187v-280H633v280ZM80-120v-480h247v-240h306v320h247v400H80Z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/assets/icons/logout.svg b/src/assets/icons/logout.svg new file mode 100644 index 0000000000000000000000000000000000000000..1c5e27e1a1886b2c5acc18c7aca5de957f4569f2 --- /dev/null +++ b/src/assets/icons/logout.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M180-120q-24 0-42-18t-18-42v-600q0-24 18-42t42-18h299v60H180v600h299v60H180Zm486-185-43-43 102-102H360v-60h363L621-612l43-43 176 176-174 174Z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/assets/icons/newsletter.svg b/src/assets/icons/newsletter.svg new file mode 100644 index 0000000000000000000000000000000000000000..e9b493b41c32538c4a52fc2224d8670aa4288e5d --- /dev/null +++ b/src/assets/icons/newsletter.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M140-120q-24 0-42-18t-18-42v-600q0-24 18-42t42-18h680q24 0 42 18t18 42v600q0 24-18 42t-42 18H140Zm0-60h680v-600H140v600Zm109-106h462v-60H249v60Zm0-166h155v-222H249v222Zm259 0h203v-60H508v60Zm0-162h203v-60H508v60ZM140-180v-600 600Z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/assets/icons/person.svg b/src/assets/icons/person.svg new file mode 100644 index 0000000000000000000000000000000000000000..ef3c9d45ac4c8345e1444ae4361c56641ad4b0c0 --- /dev/null +++ b/src/assets/icons/person.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M480-481q-66 0-108-42t-42-108q0-66 42-108t108-42q66 0 108 42t42 108q0 66-42 108t-108 42ZM160-160v-94q0-38 19-65t49-41q67-30 128.5-45T480-420q62 0 123 15.5t127.921 44.694q31.301 14.126 50.19 40.966Q800-292 800-254v94H160Zm60-60h520v-34q0-16-9.5-30.5T707-306q-64-31-117-42.5T480-360q-57 0-111 11.5T252-306q-14 7-23 21.5t-9 30.5v34Zm260-321q39 0 64.5-25.5T570-631q0-39-25.5-64.5T480-721q-39 0-64.5 25.5T390-631q0 39 25.5 64.5T480-541Zm0-90Zm0 411Z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/assets/icons/saving.svg b/src/assets/icons/saving.svg new file mode 100644 index 0000000000000000000000000000000000000000..d90502d4010c6e9c6a379c96621e3db8dfdae386 --- /dev/null +++ b/src/assets/icons/saving.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M640-520q17 0 28.5-11.5T680-560q0-17-11.5-28.5T640-600q-17 0-28.5 11.5T600-560q0 17 11.5 28.5T640-520ZM320-620h200v-60H320v60ZM180-120q-34-114-67-227.5T80-580q0-92 64-156t156-64h200q29-38 70.5-59t89.5-21q25 0 42.5 17.5T720-820q0 6-1.5 12t-3.5 11q-4 11-7.5 22.5T702-751l91 91h87v279l-113 37-67 224H480v-80h-80v80H180Zm45-60h115v-80h200v80h115l63-210 102-35v-175h-52L640-728q1-25 6.5-48.5T658-824q-38 10-72 29.5T534-740H300q-66.286 0-113.143 46.857T140-580q0 103.158 29 201.579T225-180Zm255-322Z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/assets/icons/settings.svg b/src/assets/icons/settings.svg new file mode 100644 index 0000000000000000000000000000000000000000..542fa092683df8d0777dd62eedfe9260983b14e1 --- /dev/null +++ b/src/assets/icons/settings.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="m388-80-20-126q-19-7-40-19t-37-25l-118 54-93-164 108-79q-2-9-2.5-20.5T185-480q0-9 .5-20.5T188-521L80-600l93-164 118 54q16-13 37-25t40-18l20-127h184l20 126q19 7 40.5 18.5T669-710l118-54 93 164-108 77q2 10 2.5 21.5t.5 21.5q0 10-.5 21t-2.5 21l108 78-93 164-118-54q-16 13-36.5 25.5T592-206L572-80H388Zm48-60h88l14-112q33-8 62.5-25t53.5-41l106 46 40-72-94-69q4-17 6.5-33.5T715-480q0-17-2-33.5t-7-33.5l94-69-40-72-106 46q-23-26-52-43.5T538-708l-14-112h-88l-14 112q-34 7-63.5 24T306-642l-106-46-40 72 94 69q-4 17-6.5 33.5T245-480q0 17 2.5 33.5T254-413l-94 69 40 72 106-46q24 24 53.5 41t62.5 25l14 112Zm44-210q54 0 92-38t38-92q0-54-38-92t-92-38q-54 0-92 38t-38 92q0 54 38 92t92 38Zm0-130Z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/assets/icons/storefront.svg b/src/assets/icons/storefront.svg new file mode 100644 index 0000000000000000000000000000000000000000..e259d836feaf2d2600d3311bcd713d8b0555a9bc --- /dev/null +++ b/src/assets/icons/storefront.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M840-519v339q0 24-18 42t-42 18H179q-24 0-42-18t-18-42v-339q-28-24-37-59t2-70l43-135q8-27 28-42t46-15h553q28 0 49 15.5t29 41.5l44 135q11 35 1.5 70T840-519Zm-270-31q29 0 49-19t16-46l-25-165H510v165q0 26 17 45.5t43 19.5Zm-187 0q28 0 47.5-19t19.5-46v-165H350l-25 165q-4 26 14 45.5t44 19.5Zm-182 0q24 0 41.5-16.5T263-607l26-173H189l-46 146q-10 31 8 57.5t50 26.5Zm557 0q32 0 50.5-26t8.5-58l-46-146H671l26 173q3 24 20.5 40.5T758-550ZM179-180h601v-311q1 1-6.5 1H758q-25 0-47.5-10.5T666-533q-16 20-40 31.5T573-490q-30 0-51.5-8.5T480-527q-15 18-38 27.5t-52 9.5q-31 0-55-11t-41-32q-24 21-47 32t-46 11h-13.5q-6.5 0-8.5-1v311Zm601 0H179h601Z" fill="#ffffff"/></svg> \ No newline at end of file diff --git a/src/components/BaseComponents/Footer.vue b/src/components/BaseComponents/Footer.vue index 4df3ae59a8d6290c679910dcd5cfb4ad0d273c18..7fddbaae0fe94207787bd1d31cc1c2798eeabaa9 100644 --- a/src/components/BaseComponents/Footer.vue +++ b/src/components/BaseComponents/Footer.vue @@ -1,9 +1,11 @@ <template> <div> - <footer class="text-center text-white bg-danger-subtle" style="width: 100%"> + <footer id = "footer" class="text-center text-white" style="width: 100%"> <div class="text-center p-3"> © 2024 Copyright: Anders Høvik, Andreas Svendsrud, Henrik Dybdal, Henrik Sandok, Jens Aanestad, Victor Kaste, Viktor Grevskott </div> </footer> </div> </template> + +<style scoped> #footer {background-color: #0A58CA;}</style> diff --git a/src/components/BaseComponents/Menu.vue b/src/components/BaseComponents/Menu.vue index 1b5a85fab29f79d7defebcf13d54a8cd5a509eb5..a46ebcc60c793084f563604563e53e3dad8cd7cb 100644 --- a/src/components/BaseComponents/Menu.vue +++ b/src/components/BaseComponents/Menu.vue @@ -1,8 +1,9 @@ <template> - <nav class="navbar navbar-expand-lg bg-success"> - <div class="container-fluid" > + <nav id="navBar" class="navbar navbar-expand-xl"> + <div class="container-fluid"> <a class="navbar-brand" href="/" @click="toHome"> - <img src="/src/assets/Sparesti-logo.png" alt="Sparesti-logo" width="60"> + <img id="logoImg" src="/src/assets/Sparesti-logo.png" alt="Sparesti-logo" width="60"> + <span id="logo" class="text-white">Sparesti</span> </a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" @@ -12,95 +13,158 @@ <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav ms-auto mb-2 mb-lg-0 ui-menu"> <li class="nav-item"> - <a class="nav-link text-white" href="/news" @click="toSavingGoals">Saving goals</a> + <a class="nav-link text-white" href="#" @click="toSavingGoals"><img + src="@/assets/icons/saving.svg">Saving goals</a> </li> <li class="nav-item"> - <a class="nav-link text-white" href="/news" @click="toLeaderboard">Leaderboard</a> + <a class="nav-link text-white" href="#" @click="toLeaderboard"><img + src="@/assets/icons/leaderboard.svg">Leaderboard</a> + </li> + <li class="nav-item"> + <a class="nav-link text-white" href="#" @click="toNews"><img + src="@/assets/icons/newsletter.svg">News</a> + </li> + <li class="nav-item"> + <a class="nav-link text-white" href="#" @click="toStore"><img + src="@/assets/icons/storefront.svg">Store</a> + </li> + <li class="nav-item dropdown"> + <a class="nav-link dropdown-toggle username-text text-white " href="#" role="button" + data-bs-toggle="dropdown" aria-expanded="false"> + <img src="@/assets/icons/person.svg">Username + </a> + <ul class="dropdown-menu dropdown-username-content"> + <li><a class="dropdown-item text-white dropdown-username-link" href="#" + @click="toUserProfile"><img src="@/assets/icons/person.svg">User Profile</a></li> + <li><a class="dropdown-item text-white dropdown-username-link" href="#" + @click="toSetting"><img src="@/assets/icons/settings.svg">Setting</a></li> + <li><a class="dropdown-item text-white dropdown-username-link" href="#" + @click="toFeedback"><img src="@/assets/icons/feedback.svg">Feedback</a></li> + <li><a class="dropdown-item text-white dropdown-username-link" href="#" + @click="toFeedback"><img src="@/assets/icons/logout.svg">Log out</a></li> + </ul> </li> - <li class="nav-item"> - <a class="nav-link text-white" href="/news" @click="toNews">News</a> - </li> - <li class="nav-item"> - <a class="nav-link text-white" href="/news" @click="toStore">Store</a> - </li> - </ul> - <nav class="navbar bg-success"> - <div class="container-fluid"> - <a class="nav-link dropdown-toggle username-text text-white " href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> - <img src="/src/assets/userprofile.png" alt="Userprofile" width="30" height="30" class="d-inline-block align-text-top"> - Username - </a> - <ul class="dropdown-menu bg-success dropdown-username-content"> - <li><a class="dropdown-item text-white dropdown-username-link" href="/news" @click="toUserProfile">User Profile</a></li> - <li><a class="dropdown-item text-white dropdown-username-link" href="/news" @click="toSetting">Setting</a></li> - <li><a class="dropdown-item text-white dropdown-username-link" href="/news" @click="toFeedback">Feedback</a></li> </ul> - </div> - </nav> + </div> </div> </nav> </template> <script setup lang="ts"> - - -/** - * May need to change from a-links to routerlinks to avoid complications with href. - */ - -import {useRouter} from "vue-router"; +import { useRouter } from "vue-router"; const router = useRouter(); -function toHome(){ - router.push('/') +function toHome() { + router.push('/') } -function toSavingGoals(){ - router.push('/news') +function toSavingGoals() { + router.push('/news') } -function toLeaderboard(){ - router.push('/news') +function toLeaderboard() { + router.push('/news') } -function toNews(){ - router.push('/news') +function toNews() { + router.push('/news') } -function toStore(){ - router.push('/news') +function toStore() { + router.push('/news') } -function toSetting(){ - router.push('/news') +function toSetting() { + router.push('/news') } -function toFeedback(){ - router.push('/news') +function toFeedback() { + router.push('/news') } -function toUserProfile(){ - router.push('/news') +function toUserProfile() { + router.push('/news') } </script> <style scoped> -.ui-menu{ - font-size: 150%; +.navbar-brand { + display: flex; + align-items: center; +} + +.navbar-toggler-icon { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3E%3Cpath stroke='rgba(255, 255, 255)' stroke-width='2' stroke-linecap='round' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); +} + +.nav-item { + padding: 0.3rem 0.6rem; + font-size: 1.7rem; +} + +.nav-item:hover { + background-color: #2b6ac7; } -.username-text{ - font-size: 150%; +.nav-item .dropdown { + display: flex; + justify-content: center; +} + +.nav-link { + display: flex; + align-items: center; + justify-content: center; +} + +.dropdown-item { + width: 100%; + display: flex; + justify-content: center; +} + +.dropdown-menu { + background-color: #0A58CA; +} + +.dropdown-username-link { + font-size: 1.7rem; + display: flex; + justify-self: center; +} + +.dropdown-username-link:hover { + background-color: #2b6ac7; +} + +#navBar { + background-color: #0A58CA; +} + +.navbar { + display: flex; + align-items: center; +} + +.container-fluid { + font-size: 1.7rem; +} +#logo { + font-size: 2.5rem; + height: 100%; } -.dropdown-username-content{ - font-size: 150%; +.nav-link img { + margin-right: 5px; } -.dropdown-username-link:hover{ - background-color: #538d53 +#logoImg { + margin-right: 0.3rem; + width: 75px; + height: auto; + aspect-ratio: 1.3/1; } </style> \ No newline at end of file diff --git a/src/components/LeaderboardComponents/Leaderboard.vue b/src/components/LeaderboardComponents/Leaderboard.vue new file mode 100644 index 0000000000000000000000000000000000000000..48317805da23489922b3e7bd0cffbbca7fe30da7 --- /dev/null +++ b/src/components/LeaderboardComponents/Leaderboard.vue @@ -0,0 +1,161 @@ +<template> + <div id="leaderboard"> + <div class="ribbon"></div> + <table> + <tr v-for="(entry, index) in leaderboard" :key="entry.user.id"> + <td class="number">{{ index + 1 }}</td> + <td class="name" @click="navigateToUserProfile(entry.user.id)">{{ entry.user.username }}</td> + <td class="points" v-if="index === 0"> + {{ entry.score }} + <div class = "medal"> + <img class="gold-medal" src="https://github.com/malunaridev/Challenges-iCodeThis/blob/master/4-leaderboard/assets/gold-medal.png?raw=true" alt="gold medal" /> + </div> + </td> + <td v-else class="points">{{ entry.score }}</td> + </tr> + </table> + </div> + </template> + + <script setup lang="ts"> + import { ref } from 'vue'; + import { useRouter } from 'vue-router'; + + const router = useRouter(); + + const props = defineProps({ + leaderboard: { + type: Array, + required: true + } + }); + + const navigateToUserProfile = () => { + router.push({ name: 'news' }); + }; + </script> + + <style scoped> + #leaderboard { + width: 100%; + position: relative; + } + + table { + width: 100%; + border-collapse: collapse; + table-layout: fixed; + color: #141a39; + cursor: default; + } + + tr { + transition: all 0.2s ease-in-out; + border-radius: 0.2rem; + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + } + + tr:not(:first-child):hover { + background-color: #fff; + transform: scale(1.1); + -webkit-box-shadow: 0px 5px 15px 8px #e4e7fb; + box-shadow: 0px 5px 15px 8px #e4e7fb; + } + + tr:nth-child(even) { + background-color: #f9f9f9; + } + + tr:nth-child(1) { + color: #fff; + } + + td { + height: 2rem; + font-family: "Rubik", sans-serif; + font-size: 1.4rem; + padding: 1rem 2rem; + position: relative; + } + + .number { + width: 1rem; + font-size: 2.2rem; + font-weight: bold; + text-align: left; + display: flex; + align-items: center; + } + + .name { + font-size: 1.3rem; + cursor: pointer; + display: flex; + align-items: center; + } + + .points { + font-weight: bold; + font-size: 1.3rem; + display: flex; + justify-content: flex-end; + align-items: center; + } + + @media (max-width: 1000px) { + .number .name .points { + font-size: 0.5rem; + } + + td { + padding: 0.2rem 0.5rem; + } + } + + + .points:first-child { + width: 10rem; + } + + .gold-medal { + height: 3rem; + margin-left: 1.5rem; + } + + .ribbon { + width: 100%; + height: 4.5rem; + top: -0.5rem; + background-color: #0A58CA; + position: absolute; + left: -1rem; + box-shadow: 0px 15px 11px -6px #7a7a7d; + } + + .ribbon::before { + content: ""; + height: 1.5rem; + width: 1.5rem; + bottom: -0.8rem; + left: 0.35rem; + transform: rotate(45deg); + background-color: #0A58CA; + position: absolute; + z-index: -1; + } + + .ribbon::after { + content: ""; + height: 1.5rem; + width: 1.5rem; + bottom: -0.8rem; + right: 0.35rem; + transform: rotate(45deg); + background-color: #0A58CA; + position: absolute; + z-index: -1; + } + </style> \ No newline at end of file diff --git a/src/views/LeaderboardView.vue b/src/views/LeaderboardView.vue new file mode 100644 index 0000000000000000000000000000000000000000..7f58d1768c051c4c1b780a40ba89e02f88e6964d --- /dev/null +++ b/src/views/LeaderboardView.vue @@ -0,0 +1,48 @@ +<template> + <main> + <div id="leaderboard"> + <h1>Ranking</h1> + <Leaderboard :leaderboard="leaderboardData" @navigateToUserProfile="navigateToUserProfile" /> + </div> + </main> +</template> + +<script setup lang="ts"> +import { ref } from 'vue'; +import { useRoute, useRouter } from 'vue-router'; +import Leaderboard from '@/components/LeaderboardComponents/Leaderboard.vue'; + +let leaderboardData = ref([]); + +const router = useRouter(); + +async function fetchQuizData() { + /*leaderboard(quizId).then((response) => { + leaderboardData.value = response.data.slice(0, 10); + }).catch((error) => { + console.error("Failed to fetch leaderboard data:", error); + });*/ +} + +const navigateToUserProfile = (userId: number) => { + router.push({ name: 'user', params: { id: userId } }); +}; +</script> + +<style scoped> +main { + width: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +#leaderboard { + width: 60%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-bottom: 3rem; +} +</style> \ No newline at end of file