diff --git a/src/api/index.ts b/src/api/index.ts index 22e1808268ec1c7b349141ce999d2e4b668cb45c..952a64c78269b8003d0635a30820fdd15932e689 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -12,6 +12,7 @@ export type { ExceptionResponse } from './models/ExceptionResponse'; export type { LeaderboardDTO } from './models/LeaderboardDTO'; export type { LeaderboardEntryDTO } from './models/LeaderboardEntryDTO'; export type { LoginRequest } from './models/LoginRequest'; +export type { PasswordResetDTO } from './models/PasswordResetDTO'; export type { ProfileDTO } from './models/ProfileDTO'; export type { SignUpRequest } from './models/SignUpRequest'; export type { UserDTO } from './models/UserDTO'; @@ -19,4 +20,4 @@ export type { UserUpdateDTO } from './models/UserUpdateDTO'; export { AuthenticationService } from './services/AuthenticationService'; export { LeaderboardService } from './services/LeaderboardService'; -export { UserControllerService } from './services/UserControllerService'; +export { UserService } from './services/UserService'; diff --git a/src/api/services/UserControllerService.ts b/src/api/services/UserService.ts similarity index 53% rename from src/api/services/UserControllerService.ts rename to src/api/services/UserService.ts index 5a1ea3560cb016455a4d2c77c2e34e9b0754a684..d4bec02c95c05faf669d7d0663da47d34ff67771 100644 --- a/src/api/services/UserControllerService.ts +++ b/src/api/services/UserService.ts @@ -2,15 +2,55 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { PasswordResetDTO } from '../models/PasswordResetDTO'; import type { ProfileDTO } from '../models/ProfileDTO'; import type { UserDTO } from '../models/UserDTO'; import type { UserUpdateDTO } from '../models/UserUpdateDTO'; import type { CancelablePromise } from '../core/CancelablePromise'; import { OpenAPI } from '../core/OpenAPI'; import { request as __request } from '../core/request'; -export class UserControllerService { +export class UserService { /** - * Update profile + * Initiate a password reset + * Send a password reset mail to the user with the specified email + * @returns any Successfully initiated a password reset + * @throws ApiError + */ + public static resetPassword({ + requestBody, + }: { + requestBody: string, + }): CancelablePromise<any> { + return __request(OpenAPI, { + method: 'POST', + url: '/api/users/reset-password', + body: requestBody, + mediaType: 'text/plain', + }); + } + /** + * Confirm a password reset + * Confirms a password reset using a token and a new password + * @returns void + * @throws ApiError + */ + public static confirmPasswordReset({ + requestBody, + }: { + requestBody: PasswordResetDTO, + }): CancelablePromise<void> { + return __request(OpenAPI, { + method: 'POST', + url: '/api/users/confirm-password', + body: requestBody, + mediaType: 'application/json', + errors: { + 403: `Invalid token`, + }, + }); + } + /** + * Update a profile * Update the profile of the authenticated user * @returns UserDTO Successfully updated profile * @throws ApiError @@ -28,8 +68,8 @@ export class UserControllerService { }); } /** - * Get profile - * Get user profile + * Get a profile + * Get the profile of a user * @returns ProfileDTO Successfully got profile * @throws ApiError */ @@ -47,8 +87,8 @@ export class UserControllerService { }); } /** - * Get user - * Get user information + * Get the authenticated user + * Get all user information for the authenticated user * @returns UserDTO Successfully got user * @throws ApiError */ diff --git a/src/assets/items/pigcoin.png b/src/assets/items/pigcoin.png new file mode 100644 index 0000000000000000000000000000000000000000..302e7e190ca30d4868094fb733fc1b8f2a438a55 Binary files /dev/null and b/src/assets/items/pigcoin.png differ diff --git a/src/components/Buttons/ShopButton.vue b/src/components/Buttons/ShopButton.vue index 4e28fcdb6e68eb87602d970bd0d612a46270668d..4ed5e02632d891b47f6752b8e870987b3e178edc 100644 --- a/src/components/Buttons/ShopButton.vue +++ b/src/components/Buttons/ShopButton.vue @@ -1,5 +1,5 @@ <template> - <button type="button" class="btn btn-primary" id="buttonStyle"><img src="@/assets/items/v-buck.png" style="width: 2rem"> +{{ buttonText }}</button> + <button type="button" class="btn btn-primary" id="buttonStyle"><img src="@/assets/items/pigcoin.png" style="width: 2rem"> +{{ buttonText }}</button> </template> <script> diff --git a/src/components/LeaderboardComponents/Leaderboard.vue b/src/components/LeaderboardComponents/Leaderboard.vue index 0a1b9a5b472971d79cac4aa62380e891caa72ec7..9003a25a4c37050a30eafc00cb1551c4885b0dc7 100644 --- a/src/components/LeaderboardComponents/Leaderboard.vue +++ b/src/components/LeaderboardComponents/Leaderboard.vue @@ -1,161 +1,197 @@ <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> + <div id="leaderboard"> + <div class="ribbon"></div> + <table> + <tbody> + <tr v-for="(entry, index) in leaderboard" :key="entry.user.id" :class="{ 'is-user-5': entry.user.firstName === 'User' }"> + <td class="number">{{ entry.rank }}</td> <td class="name" @click="navigateToUserProfile(entry.user.id)">{{ entry.user.firstName }}</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 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> + </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; - height: 4rem; - } - - 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; + </tbody> + <tbody id="line">`</tbody> + <tbody v-if="!userInLeaderboard"> + <tr></tr> + <tr v-for="(entry, index) in leaderboardExtra" :key="entry.user.id" :class="{ 'is-user-5': entry.user.firstName === 'User' }"> + <td class="number">{{ entry.rank }}</td> + <td class="name" @click="navigateToUserProfile(entry.user.id)">{{ entry.user.firstName }}</td> + <td class="points">{{ entry.score }}</td> + </tr> + </tbody> + <tbody v-else></tbody> + </table> + </div> +</template> + + +<script setup lang="ts"> +import { computed } from 'vue'; +import { useRouter } from 'vue-router'; +import { useUserInfoStore } from '@/stores/UserStore'; + +const router = useRouter(); +const userStore = useUserInfoStore(); + +const props = defineProps({ + leaderboard: { + type: Array, + required: true + }, + leaderboardExtra: { + type: Array, + required: true + } +}); + +console.log(props.leaderboardExtra); + +const userInLeaderboard = computed(() => props.leaderboard.some(entry => entry.user.email === userStore.email)); + +const navigateToUserProfile = () => { + router.push({ name: 'user-profile' }); +}; +</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; + height: 4rem; +} + +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 { - 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; + padding: 0.2rem 0.5rem; } +} - @media (max-width: 1000px) { - .number .name .points { - font-size: 0.5rem; - } - td { - padding: 0.2rem 0.5rem; - } - } +.points:first-child { + width: 10rem; +} - - .points:first-child { - width: 10rem; - } - - .gold-medal { - height: 3rem; - margin-left: 1.5rem; - } - - .ribbon { - width: 106%; - 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 +.gold-medal { + height: 3rem; + margin-left: 1.5rem; +} + +.ribbon { + width: 106%; + 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; +} + +#line { + width: 100%; + height: 0.01rem; + border-top: 8px solid #0A58CA; +} + +tr.is-user-5 { + background-color: #419c5c !important; + color: #fff !important; +} +</style> \ No newline at end of file diff --git a/src/components/Login/Login.vue b/src/components/Login/Login.vue index 25469a19d2cbd3c352545218fe480114ae8c6181..834e1b7e7e3c7c62076dbfdb0c3897abc3f1daaa 100644 --- a/src/components/Login/Login.vue +++ b/src/components/Login/Login.vue @@ -3,11 +3,29 @@ import LoginForm from '@/components/Login/LoginForm.vue' </script> <template> - <div class="container-fluid"> - <LoginForm/> + <div class="containers"> + <div class="box"> + <LoginForm/> + </div> </div> </template> -<style> +<style scoped> + .containers { + background-color: #A2CC99; + height: 100vh; + display: flex; + justify-content: center; + align-items: center; + } + .box { + background-color: white; + border-radius: 3rem; + max-width: 450px; + padding: 1rem 4rem; + box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; + } + + </style> \ No newline at end of file diff --git a/src/components/Login/LoginForm.vue b/src/components/Login/LoginForm.vue index 6091ab82d28b5c081b081370cd01996d78d327d2..3401af96d2977c1f508c15d2dc47e3e9748a2c6d 100644 --- a/src/components/Login/LoginForm.vue +++ b/src/components/Login/LoginForm.vue @@ -93,6 +93,7 @@ const handleSubmit = async () => { valid-message="Valid password" invalid-message="Password must be between 4 and 16 characters and contain one capital letter, small letter and a number" /> + <p>Forgotten password? <RouterLink to="/forgotten-password">Reset password</RouterLink></p> <p class="text-danger">{{ errorMsg }}</p> <button1 id="confirmButton" type="submit" @click="handleSubmit" button-text="Login"></button1> diff --git a/src/components/SignUp/SignUp.vue b/src/components/SignUp/SignUp.vue index 4dff39dcd4bbe2a47e1471473d7a1632eb540827..eb272c6f1966c45c9d61045a1375fc8dcfb14981 100644 --- a/src/components/SignUp/SignUp.vue +++ b/src/components/SignUp/SignUp.vue @@ -4,9 +4,27 @@ import SignUpForm from '@/components/SignUp/SignUpForm.vue' </script> <template> - <SignUpForm/> + <div class="containers"> + <div class="box"> + <SignUpForm /> + </div> + </div> </template> <style scoped> +.containers { + background-color: #A2CC99; + height: 100vh; + display: flex; + justify-content: center; +} +.box { + width: 450px; + margin: 2rem; + background-color: white; + border-radius: 3rem; + padding: 1rem 4rem; + box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px; +} </style> \ No newline at end of file diff --git a/src/components/SignUp/SignUpForm.vue b/src/components/SignUp/SignUpForm.vue index dad760e62b52dab00c22e31e5c63dba26ad0c0bb..b0b04ad2309aeaa1c82d1d86e651838a98decc68 100644 --- a/src/components/SignUp/SignUpForm.vue +++ b/src/components/SignUp/SignUpForm.vue @@ -68,6 +68,7 @@ const handleSubmit = async () => { <template> <div class="container"> + <img src="@/assets/Sparesti-logo.png" style="width: 120px"> <form ref="formRef" id="signUpForm" @submit.prevent="handleSubmit" novalidate> <BaseInput :model-value=firstNameRef @input-change-event="handleFirstNameInputEvent" @@ -124,12 +125,17 @@ const handleSubmit = async () => { .container { max-width: 450px; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; } #signUpForm { display: flex; flex-direction: column; justify-items: center; + width: 100%; } #firstNameInput, #surnameInput, #emailInput, #passwordInput, #confirmButton, #confirmPasswordInput { diff --git a/src/components/UpdateUserComponents/UpdateUserLayout.vue b/src/components/UpdateUserComponents/UpdateUserLayout.vue index 0f9dd3fd163e45d56aebd0a6beb58ebf406847d5..e90c5cd6f503c23c640bafa9a11b90d60e4ad582 100644 --- a/src/components/UpdateUserComponents/UpdateUserLayout.vue +++ b/src/components/UpdateUserComponents/UpdateUserLayout.vue @@ -1,7 +1,7 @@ <script setup lang="ts"> import BaseInput from "@/components/InputFields/BaseInput.vue"; import {onMounted, ref} from "vue"; -import {AuthenticationService, LeaderboardService, UserControllerService, type UserUpdateDTO} from "@/api"; +import {AuthenticationService, LeaderboardService, UserService, type UserUpdateDTO} from "@/api"; import {useUserInfoStore} from "@/stores/UserStore"; @@ -18,7 +18,7 @@ let samePasswords = ref(true) async function setupForm() { try { - let response = await UserControllerService.getUser(); + let response = await UserService.getUser(); console.log(response.firstName) firstNameRef.value = response.firstName; diff --git a/src/components/UserProfile/UserProfileLayout.vue b/src/components/UserProfile/UserProfileLayout.vue index ac27b9b22f02fbc1e66824f6108ff54bcb47c399..db2c4bca1ccc4af534c3a5aacd5dd162657ffaec 100644 --- a/src/components/UserProfile/UserProfileLayout.vue +++ b/src/components/UserProfile/UserProfileLayout.vue @@ -2,8 +2,8 @@ import Menu from "@/components/BaseComponents/Menu.vue"; import Footer from "@/components/BaseComponents/Footer.vue"; -import {useRouter} from "vue-router"; -import {useUserInfoStore} from "../../stores/UserStore"; +import { useRouter } from "vue-router"; +import { useUserInfoStore } from "../../stores/UserStore"; let numberOfHistory = 6; @@ -13,113 +13,119 @@ let points = 0; let streak = 0; let route = useRouter() -function toRoadmap(){ +function toRoadmap() { route.push('/roadmap') } -function toUpdateUserSettings(){ +function toUpdateUserSettings() { route.push('/update-user') } </script> <template> - <div class="container text-center"> - <div class="row"> - <div class="col"> - <img src="/src/assets/userprofile.png" class="img-fluid"> - <p class="h2">{{useUserInfoStore().getFirstName}}</p> - <p><a class="link-dark" @click="toUpdateUserSettings" href="#">Edit profile</a></p> - </div> - </div> - <div class="row"> - <div class="col"> - <img src="/src/assets/icons/fire.png" class="img-fluid" style="width: 30px; height: 30px" alt="dollar"> - <p>Streak: 10</p> - </div> - </div> - <div class="row"> - <div class="col-12"> - <img src="/src/assets/icons/dollar.png" class="img-fluid" style="width: 30px; height: 30px" alt="dollar"> - <p class="">Points: 2000 </p> - </div> - </div> - - <div class="row"> - <div class="col"> - total points earned - </div> - <div class="col"> - total badges earned - </div> - </div> - <div class="row"> - <div class="col"> - <!-- Here is the badges of the user --> - <div class="container-fluid"> - <h1 class="mt-5 text-start badges-text">Badges</h1> - <div class="scrolling-wrapper-badges row flex-row flex-nowrap mt-4 pb-4 pt-2"> - - <div class="col-5"> - <div class="card badges-block card-1"></div> - </div> - <div class="col-5"> - <div class="card badges-block card-2"></div> + <div class="container py-5 h-100"> + <div class="row d-flex justify-content-center align-items-center h-100"> + <div class="col 12"> + <div class="card"> + <div class="rounded-top text-white d-flex flex-row bg-primary" style="height:200px;"> + <div class="ms-4 mt-5 d-flex flex-column" style="width: 150px;"> + <img src="https://bootdey.com/img/Content/avatar/avatar3.png" alt="Generic placeholder image" + class="img-fluid img-thumbnail mt-4 mb-2" style="width: 150px; z-index: 1"> + <button type="button" data-mdb-button-init data-mdb-ripple-init class="btn btn-outline-primary" + data-mdb-ripple-color="dark" style="z-index: 1;" @click="toUpdateUserSettings"> + Edit profile + </button> </div> - <div class="col-5"> - <div class="card badges-block card-3"></div> + <div class="ms-3" style="margin-top: 130px;"> + <h1>Andy Horwitz</h1> </div> - <div class="col-5"> - <div class="card badges-block card-4"></div> - </div> - <div class="col-5"> - <div class="card badges-block card-5"></div> - </div> - <div class="col-5"> - <div class="card badges-block card-6"></div> - </div> - <div class="col-5"> - <div class="card badges-block card-7"></div> - </div> - <div class="col-5"> - <div class="card badges-block card-8"></div> - </div> - <div class="col-5"> - <div class="card badges-block card-9"></div> - </div> - <div class="col-5"> - <div class="card badges-block card-10"></div> + </div> + <div class="p-4 text-black" style="background-color: #f8f9fa;"> + <div class="d-flex justify-content-end text-center py-1"> + <div> + <p class="mb-1 h2">253 <img src="@/assets/items/pigcoin.png" style="width: 4rem"></p> + <p class="small text-muted mb-0">Points</p> + </div> + <div class="px-3"> + <p class="mb-1 h2">1026 <img src="@/assets/icons/fire.png" style="width: 4rem"></p> + <p class="small text-muted mb-0">Streak</p> + </div> </div> </div> - </div> - </div> - </div> - <div class="row"> - <div class="col"> - <!-- Here is the history of saving target --> - <div class="container-fluid mb-5"> - <h1 class="mt-5 text-start history-text">History</h1> - <div class="row scrolling-wrapper-history"> - <div v-for="index in numberOfHistory" :key="index" class="col-md-4 col-sm-4 col-lg-4 col-xs-4 col-xl-4 control-label"> - <div class="card history-block" > - <div class="card mb-3" style="max-width: 540px;"> - <div class="row g-0"> - <div class="col-md-4"> - <img src="/src/assets/icons/piggybank.svg" class="img-fluid rounded-start h-40 mx-auto d-none d-md-block" alt="..."> + <div class="card-body p-1 text-black"> + <div class="row"> + <div class="col"> + <div class="container-fluid"> + <h1 class="mt-5 text-start badges-text">Badges</h1> + <div class="scrolling-wrapper-badges row flex-row flex-nowrap mt-4 pb-4 pt-2"> + + <div class="col-5"> + <div class="card badges-block card-1"></div> + </div> + <div class="col-5"> + <div class="card badges-block card-2"></div> + </div> + <div class="col-5"> + <div class="card badges-block card-3"></div> + </div> + <div class="col-5"> + <div class="card badges-block card-4"></div> + </div> + <div class="col-5"> + <div class="card badges-block card-5"></div> + </div> + <div class="col-5"> + <div class="card badges-block card-6"></div> </div> - <div class="col-md-8"> - <div class="card-body"> - <h5 class="card-title">{{cardTitles[index-1]}}</h5> - <p class="card-text">Money saved: 200 <br/>You are one challenge: 21</p> - <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p> - <a href="#" class="btn stretched-link" @click="toRoadmap"></a> + <div class="col-5"> + <div class="card badges-block card-7"></div> + </div> + <div class="col-5"> + <div class="card badges-block card-8"></div> + </div> + <div class="col-5"> + <div class="card badges-block card-9"></div> + </div> + <div class="col-5"> + <div class="card badges-block card-10"></div> + </div> + </div> + </div> + </div> + </div> + <div class="row"> + <div class="col"> + <!-- Here is the history of saving target --> + <div class="container-fluid mb-5"> + <h1 class="mt-5 text-start history-text">History</h1> + <div class="row scrolling-wrapper-history"> + <div v-for="index in numberOfHistory" :key="index" + class="col-md-4 col-sm-4 col-lg-4 col-xs-4 col-xl-4 control-label"> + <div class="card history-block"> + <div class="card mb-3" style="max-width: 540px;"> + <div class="row g-0"> + <div class="col-md-4"> + <img src="/src/assets/icons/piggybank.svg" + class="img-fluid rounded-start h-40 mx-auto d-none d-md-block" alt="..."> + </div> + <div class="col-md-8"> + <div class="card-body"> + <h5 class="card-title">{{ cardTitles[index - 1] }}</h5> + <p class="card-text">Money saved: 200 <br />You are one challenge: 21</p> + <p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p> + <a href="#" class="btn stretched-link" @click="toRoadmap"></a> + </div> + </div> + </div> + </div> </div> </div> </div> + </div> </div> </div> </div> - </div> </div> </div> @@ -127,28 +133,28 @@ function toUpdateUserSettings(){ </template> <style scoped> -.scrolling-wrapper-badges{ +.scrolling-wrapper-badges { overflow-x: auto; } -.scrolling-wrapper-history{ +.scrolling-wrapper-history { max-height: 300px; overflow: auto; } -.badges-text{ +.badges-text { font-weight: 500; font-size: 2.0em; } -.history-text{ +.history-text { font-weight: 500; font-size: 2.0em; } -.badges-block{ +.badges-block { height: 200px; background-color: #fff; border: none; @@ -156,14 +162,15 @@ function toUpdateUserSettings(){ background-size: cover; transition: all 0.2s ease-in-out !important; border-radius: 24px; - &:hover{ + + &:hover { transform: translateY(-5px); box-shadow: none; opacity: 0.9; } } -.history-block{ +.history-block { height: 200px; background-color: #fff; @@ -173,61 +180,68 @@ function toUpdateUserSettings(){ transition: all 0.2s ease-in-out !important; border-radius: 24px; margin: 20px; - &:hover{ + + &:hover { transform: translateY(-5px); box-shadow: none; opacity: 0.9; } } -.card-1{ +.card-1 { background-color: #4158D0; background-image: linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%); } -.card-2{ +.card-2 { background-color: #0093E9; background-image: linear-gradient(160deg, #0093E9 0%, #80D0C7 100%); } -.card-3{ +.card-3 { background-color: #00DBDE; background-image: linear-gradient(90deg, #00DBDE 0%, #FC00FF 100%); } -.card-4{ +.card-4 { background-color: #FBAB7E; background-image: linear-gradient(62deg, #FBAB7E 0%, #F7CE68 100%); } -.card-5{ +.card-5 { background-color: #85FFBD; background-image: linear-gradient(45deg, #85FFBD 0%, #FFFB7D 100%); } -.card-6{ +.card-6 { background-color: #FA8BFF; background-image: linear-gradient(45deg, #FA8BFF 0%, #2BD2FF 52%, #2BFF88 90%); } -.card-7{ +.card-7 { background-color: #FA8BFF; background-image: linear-gradient(45deg, #FA8BFF 0%, #2BD2FF 52%, #2BFF88 90%); } -.card-8{ +.card-8 { background-color: #FBDA61; background-image: linear-gradient(45deg, #FBDA61 0%, #FF5ACD 100%); } -.card-9{ +.card-9 { background-color: #4158D0; background-image: linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%); } -.card-10{ +.card-10 { background-color: #FF3CAC; background-image: linear-gradient(225deg, #FF3CAC 0%, #784BA0 50%, #2B86C5 100%); } + + +/*-------*/ +.rounded-top { + background-color: #00DBDE; +} </style> \ No newline at end of file diff --git a/src/views/Authentication/ChangePasswordView.vue b/src/views/Authentication/ChangePasswordView.vue index ad52ab1b300380fa1c665e882c88b83f217424ac..36e2e75e365f0f9bb01d81155533be0fa48e2ec0 100644 --- a/src/views/Authentication/ChangePasswordView.vue +++ b/src/views/Authentication/ChangePasswordView.vue @@ -1,61 +1,86 @@ <template> - <div class="container"> - <div class="row justify-content-center"> - <div class="col-lg-5"> - <div class="card shadow-lg border-0 rounded-lg mt-5"> - <div class="card-header"><h3 class="text-center font-weight-light my-4">Password Recovery</h3></div> - <div class="card-body"> - <div class="small mb-3 text-muted">Enter the new password for your account</div> - <form @submit.prevent="submitForm"> - <div class="form-floating mb-3"> - <input v-model="newPassword" class="form-control" id="newPassword" type="password" placeholder="New Password" required> - <label for="newPassword">Enter your new password</label> + <div class="containers"> + <div class="row justify-content-center"> + <div class="col-lg-5"> + <div class="card shadow-lg border-0 rounded-lg mt-5"> + <div class="card-header"> + <h3 class="text-center font-weight-light my-4">Password Recovery</h3> + </div> + <div class="card-body"> + <div class="small mb-3 text-muted">Enter the new password for your account</div> + <form @submit.prevent="submitForm"> + <div class="form-floating mb-3"> + <input v-model="newPassword" class="form-control" id="newPassword" type="password" + placeholder="New Password" required> + <label for="newPassword">Enter your new password</label> + </div> + <div class="form-floating mb-3"> + <input v-model="confirmPassword" class="form-control" id="confirmPassword" + type="password" placeholder="Confirm Password" required> + <label for="confirmPassword">Confirm your new password</label> + </div> + <div class="errorMsg">{{ errormsg }}</div> + <div class="d-flex align-items-center justify-content-between mt-4 mb-0"> + <router-link to="/login" class="small">Return to login</router-link> + <button class="btn btn-primary" type="submit">Confirm Password</button> + </div> + </form> + </div> + <div class="card-footer text-center py-3"> + <div class="small"><router-link to="/sign-up">Need an account? Sign up!</router-link></div> + </div> </div> - <div class="form-floating mb-3"> - <input v-model="confirmPassword" class="form-control" id="confirmPassword" type="password" placeholder="Confirm Password" required> - <label for="confirmPassword">Confirm your new password</label> - </div> - <div class="d-flex align-items-center justify-content-between mt-4 mb-0"> - <router-link to="/login" class="small">Return to login</router-link> - <button class="btn btn-primary" type="submit">Confirm Password</button> - </div> - </form> </div> - <div class="card-footer text-center py-3"> - <div class="small"><router-link to="/sign-up">Need an account? Sign up!</router-link></div> - </div> - </div> </div> - </div> </div> - </template> - - <script setup lang="ts"> - import { ref } from 'vue'; - import { useRouter } from 'vue-router'; - import axios from 'axios'; - - const router = useRouter(); - - const newPassword = ref(''); - const confirmPassword = ref(''); - - const submitForm = async () => { +</template> + +<script setup lang="ts"> +import { ref } from 'vue'; +import { useRouter, useRoute } from 'vue-router'; +import axios from 'axios'; +import { UserService } from '@/api'; + +const router = useRouter(); +const route = useRoute(); + +const token = route.params.token; + +const newPassword = ref(''); +const confirmPassword = ref(''); + +let errormsg = ref(''); + +const submitForm = async () => { if (newPassword.value !== confirmPassword.value) { - alert('Passwords do not match!'); - return; + errormsg.value = 'The passwords do not match'; + return; } + errormsg.value = ''; try { - const response = await axios.post('/api/reset-password', { - password: newPassword.value, - confirmPassword: confirmPassword.value - }); - console.log('Success:', response.data); + const resetPassword = { + password: newPassword.value, + token: token + }; + const response = await UserService.confirmPasswordReset({ requestBody: resetPassword }); + console.log(response); router.push('/login'); } catch (error) { - console.error('Error:', error); + console.error('Error:', error); + } +}; + +</script> + +<style scoped> + .containers { + width: 100%; + background-color: #A2CC99; + height: 100vh; + } + + .row { + margin-right: 0px; + margin-left: 0px; } - }; - - </script> - \ No newline at end of file +</style> \ No newline at end of file diff --git a/src/views/Authentication/ForgottenPasswordView.vue b/src/views/Authentication/ForgottenPasswordView.vue index 521c2aa54b52169ae720d94d9862f8464cb10ebf..07501b6f6593a120c171fc99bc72a7bbf0bfe893 100644 --- a/src/views/Authentication/ForgottenPasswordView.vue +++ b/src/views/Authentication/ForgottenPasswordView.vue @@ -1,52 +1,72 @@ <template> - <div class="container"> - <div class="row justify-content-center"> - <div class="col-lg-5"> - <div class="card shadow-lg border-0 rounded-lg mt-5"> - <div class="card-header"><h3 class="text-center font-weight-light my-4">Password Recovery</h3></div> - <div class="card-body"> - <div class="small mb-3 text-muted">Enter your email address and we will send you a link to reset your password.</div> - <form @submit.prevent="submitForm"> - <div class="form-floating mb-3"> - <input v-model="email" class="form-control" id="inputEmail" type="email" placeholder="name@example.com" required> - <label for="inputEmail">Enter email address</label> + <div class="containers"> + <div class="row justify-content-center"> + <div class="col-lg-5"> + <div class="card shadow-lg border-0 rounded-lg mt-5"> + <div class="card-header"> + <h3 class="text-center font-weight-light my-4">Password Recovery</h3> + </div> + <div class="card-body"> + <div class="small mb-3 text-muted">Enter your email address and we will send you a link to reset + your password.</div> + <form @submit.prevent="submitForm"> + <div class="form-floating mb-3"> + <input v-model="email" class="form-control" id="inputEmail" type="email" + placeholder="name@example.com" required> + <label for="inputEmail">Enter email address</label> + </div> + <div class="d-flex align-items-center justify-content-between mt-4 mb-0"> + <router-link to="/login" class="small">Return to login</router-link> + <button class="btn btn-primary" type="submit">Reset Password</button> + </div> + <div class="text-success"> + {{ confirmationMessage }} + </div> + </form> + </div> + <div class="card-footer text-center py-3"> + <div class="small"><router-link to="/sign-up">Need an account? Sign up!</router-link></div> + </div> </div> - <div class="d-flex align-items-center justify-content-between mt-4 mb-0"> - <router-link to="/login" class="small">Return to login</router-link> - <button class="btn btn-primary" type="submit">Reset Password</button> - </div> - <div class="text-success"> - {{ confirmationMessage }} - </div> - </form> - </div> - <div class="card-footer text-center py-3"> - <div class="small"><router-link to="/sign-up">Need an account? Sign up!</router-link></div> </div> - </div> </div> - </div> </div> - </template> - - <script setup lang="ts"> - import { ref } from 'vue'; - import { useRouter } from 'vue-router'; - import axios from 'axios'; - - const router = useRouter(); - const email = ref(''); +</template> + +<script setup lang="ts"> +import { ref } from 'vue'; +import { useRouter, useRoute } from 'vue-router'; +import axios from 'axios'; +import { UserService } from '@/api'; + +const router = useRouter(); + +const email = ref(''); +let confirmationMessage = ref(''); - let confirmationMessage = ref(''); - - const submitForm = async () => { +const submitForm = async () => { try { - const response = await axios.post('/api/password-reset', { email: email.value }); - console.log('Success:', response.data); - confirmationMessage.value = 'An email has been sent to your email address with a link to reset your password.'; + const response = await UserService.resetPassword({ + requestBody: email.value + }); + console.log('Success:', response.data); + confirmationMessage.value = 'An email has been sent to your email address with a link to reset your password.'; } catch (error) { - console.error('Error:', error); + console.error('Error:', error); + } +}; + +</script> + +<style scoped> + .containers { + width: 100%; + background-color: #A2CC99; + height: 100vh; + } + + .row { + margin-right: 0px; + margin-left: 0px; } - }; - </script> - \ No newline at end of file +</style> \ No newline at end of file diff --git a/src/views/LeaderboardView.vue b/src/views/LeaderboardView.vue index 769c67973bae2d8b15612e38a1141279d845ed3e..16b4ae404a28b1fa484d981980f072ce8fa8824d 100644 --- a/src/views/LeaderboardView.vue +++ b/src/views/LeaderboardView.vue @@ -17,16 +17,16 @@ </div> <main> <div id="leaderboard"> - <h1><img src="@/assets/items/v-buck.png" style="width: 2rem"> Total points</h1> - <Leaderboard :leaderboard="pointsLeaderboardData" @navigateToUserProfile="navigateToUserProfile" /> + <h1><img src="@/assets/items/pigcoin.png" style="width: 3rem"> Total points</h1> + <Leaderboard :leaderboard="pointsLeaderboardData" :leaderboardExtra="pointsLeaderboardDataExtra" @navigateToUserProfile="navigateToUserProfile" /> </div> <div id="leaderboard"> <h1><img src="@/assets/icons/fire.png" style="width: 2rem"> Current streak</h1> - <Leaderboard :leaderboard="currentLeaderboardData" @navigateToUserProfile="navigateToUserProfile" /> + <Leaderboard :leaderboard="currentLeaderboardData" :leaderboardExtra="currentLeaderboardDataExtra" @navigateToUserProfile="navigateToUserProfile" /> </div> <div id="leaderboard"> <h1><img src="@/assets/icons/fire.png" style="width: 2rem"> Highest streak</h1> - <Leaderboard :leaderboard="streakLeaderboardData" @navigateToUserProfile="navigateToUserProfile" /> + <Leaderboard :leaderboard="streakLeaderboardData" :leaderboardExtra="streakLeaderboardDataExtra" @navigateToUserProfile="navigateToUserProfile" /> </div> </main> </div> @@ -41,12 +41,16 @@ import { onMounted, ref } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import Leaderboard from '@/components/LeaderboardComponents/Leaderboard.vue'; import { on } from 'events'; -import { LeaderboardService } from '@/api'; +import { LeaderboardService, UserControllerService } from '@/api'; let streakLeaderboardData = ref([]); let currentLeaderboardData = ref([]); let pointsLeaderboardData = ref([]); +let streakLeaderboardDataExtra = ref([]); +let currentLeaderboardDataExtra = ref([]); +let pointsLeaderboardDataExtra = ref([]); + const router = useRouter(); async function fetchQuizData() { @@ -73,23 +77,26 @@ async function global() { let globalPointsYou = await LeaderboardService.getSurrounding({ type: "TOTAL_POINTS", filter: "GLOBAL", + entryCount: 2, }); let globalStreakYou = await LeaderboardService.getSurrounding({ type: "TOP_STREAK", filter: "GLOBAL", + entryCount: 2, }); let globalCurrentStreakYou = await LeaderboardService.getSurrounding({ type: "CURRENT_STREAK", filter: "GLOBAL", + entryCount: 2, }); - - console.log(globalPointsYou); - console.log(globalStreakYou); - console.log(globalCurrentStreakYou); - + pointsLeaderboardData.value = globalPoints.entries; currentLeaderboardData.value = globalCurrentStreak.entries; streakLeaderboardData.value = globalStreak.entries; + + pointsLeaderboardDataExtra.value = globalPointsYou.entries; + currentLeaderboardDataExtra.value = globalCurrentStreakYou.entries; + streakLeaderboardDataExtra.value = globalStreakYou.entries; } async function friends() { @@ -105,9 +112,30 @@ async function friends() { type: "CURRENT_STREAK", filter: "FRIENDS", }); + let friendsPointsYou = await LeaderboardService.getSurrounding({ + type: "TOTAL_POINTS", + filter: "FRIENDS", + entryCount: 2, + }); + let friendsStreakYou = await LeaderboardService.getSurrounding({ + type: "TOP_STREAK", + filter: "FRIENDS", + entryCount: 2, + }); + let friendsCurrentStreakYou = await LeaderboardService.getSurrounding({ + type: "CURRENT_STREAK", + filter: "FRIENDS", + entryCount: 2, + }); + + pointsLeaderboardData.value = friendsPoints.entries; currentLeaderboardData.value = friendsCurrentStreak.entries; streakLeaderboardData.value = friendsStreak.entries; + + pointsLeaderboardDataExtra.value = friendsPointsYou.entries; + currentLeaderboardDataExtra.value = friendsStreakYou.entries; + streakLeaderboardDataExtra.value = friendsCurrentStreakYou.entries; } const navigateToUserProfile = (userId: number) => { @@ -121,7 +149,7 @@ main { width: 80%; display: flex; justify-content: space-around; - align-items: center; + align-items: start; flex-wrap: wrap; flex-direction: row; } @@ -138,7 +166,6 @@ main { #content { display: flex; flex-direction: row; - justify-content: center; flex-wrap: wrap; } @@ -156,7 +183,6 @@ h1 { display: flex; justify-content: center; margin-bottom: 2rem; - } #radioContainer { diff --git a/src/views/ShopView.vue b/src/views/ShopView.vue index 2bf2eafe93dff64150ce1365535defa33e28fa01..86c22ee13bdac2ab935a2bb0dce6c1712aeab55d 100644 --- a/src/views/ShopView.vue +++ b/src/views/ShopView.vue @@ -73,14 +73,14 @@ <img src="@/assets/items/adfree.png" class="card-img-top" alt="..."> <div class="card-body"> <h5 class="card-title">Adfree</h5> - <ShopButton button-text="35kr"></ShopButton> + <button type="button" class="btn btn-primary" id="buttonStyle"> +35kr</button> </div> </div> <div class="card text-center" style="width: 16rem; border: none"> <img src="@/assets/items/piggybank.webp" class="card-img-top" alt="..."> <div class="card-body"> <h5 class="card-title">Premium</h5> - <ShopButton button-text="50kr"></ShopButton> + <button type="button" class="btn btn-primary" id="buttonStyle">+50kr</button> </div> </div> </div>