diff --git a/cypress/e2e/LeaderboardView.cy.ts b/cypress/e2e/LeaderboardView.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..cf4eb93ae22801101d7beded40ef13f1b5531cf8 --- /dev/null +++ b/cypress/e2e/LeaderboardView.cy.ts @@ -0,0 +1,57 @@ +describe("Leaderboard Test", () => { + beforeEach(() => { + cy.visit('/login'); + cy.get('#emailInput input').type('user@example.com'); + cy.get('#passwordInput input').type('John1'); + cy.get('form').submit(); + cy.wait(1000); + cy.visit('/leaderboard') + }); + + it('loads global leaderboards', () => { + cy.wait(5000) + cy.get('[data-cy="total-points-board"]') + .find('[data-cy="top-leaderboard-tablerow"]') + .should('have.length', 10); + + cy.get('[data-cy="total-points-board"]') + .find('[data-cy="surrounding-user-leaderboard-tablerow"]') + .should('have.length', 4); + + cy.get('[data-cy="current-points-board"]') + .find('[data-cy="top-leaderboard-tablerow"]') + .should('have.length', 10); + + cy.get('[data-cy="streak-board"]') + .find('[data-cy="top-leaderboard-tablerow"]') + .should('have.length', 10); + }) + + it('loads friends leaderboards', () => { + cy.get('[data-cy="friends-leaderboard-btn"]').click() + + cy.get('[data-cy="total-points-board"]') + .find('[data-cy="top-leaderboard-tablerow"]') + .should('have.length', 3); + + cy.get('[data-cy="current-points-board"]') + .find('[data-cy="top-leaderboard-tablerow"]') + .should('have.length', 3); + + cy.get('[data-cy="streak-board"]') + .find('[data-cy="top-leaderboard-tablerow"]') + .should('have.length', 3); + }) + + it('redirects to profile-page when clicking user', () => { + cy.get('[data-cy="total-points-board"]') + .find('[data-cy="top-leaderboard-tablerow"]') + .first() + .within(() => { + cy.get('td:nth-child(2)').click(); + }) + cy.wait(1000); + cy.url().should('include', '/profile/15'); + }) + +}) \ No newline at end of file diff --git a/src/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue b/src/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue index 97a2423dd702cd11fc796e02a625c345b735859c..226d395bdcc6d796fd2b579872b7c99a07f4e7f8 100644 --- a/src/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue +++ b/src/components/Configuration/ConfigurationSteps/FirstSavingGoal.vue @@ -3,6 +3,7 @@ import BaseInput from '@/components/InputFields/BaseInput.vue' import { ref } from 'vue' import Button1 from '@/components/Buttons/Button1.vue' import { useRouter } from 'vue-router' +import {type CreateGoalDTO, GoalService} from "@/api"; const router = useRouter(); const emit = defineEmits(['changeRouterEvent']) @@ -13,8 +14,9 @@ const titleRef = ref<string>() let descriptionRef = ref<string>() const sumRef = ref<number>() const dateRef = ref<string>() +const errorMessage = ref("") -const handleSubmit = () => { +const handleSubmit = async () => { formRef.value.classList.add("was-validated") const form = formRef.value if (!form.checkValidity()) { @@ -22,7 +24,20 @@ const handleSubmit = () => { } // TODO integrate user creation and goal creation with backend. - + const createGoalPayload: CreateGoalDTO = { + name: titleRef.value, + description: descriptionRef.value, + targetAmount: sumRef.value, + targetDate: dateRef.value + " 00:00:00.000000000", + }; + + try { + await GoalService.createGoal({ requestBody: createGoalPayload }); + await router.push("/") + } catch (error: any) { + console.log(error.message); + errorMessage.value = error.message; + } } const getTodayDate = () => { @@ -98,6 +113,9 @@ const handleSumInputEvent = (newSum: number) => { <div class="confirm-button-container"> <button1 id="confirmButton" @click="handleSubmit" button-text="Continue"></button1> </div> + <div style="color: red"> + {{ errorMessage }} + </div> </div> </template> diff --git a/src/components/Configuration/ConfigurationSteps/SuitableChallenges.vue b/src/components/Configuration/ConfigurationSteps/SuitableChallenges.vue index 439d65333cfacdd03d81e0a281b4d31298b931af..44c9c455d7f5751ce9fabfabd0b53897567bfe3c 100644 --- a/src/components/Configuration/ConfigurationSteps/SuitableChallenges.vue +++ b/src/components/Configuration/ConfigurationSteps/SuitableChallenges.vue @@ -76,7 +76,7 @@ const signUpUser = async () => { role: response.role, }); useUserInfoStore().resetPassword() - await router.push({ name: 'home' }); + await router.push("/first-saving-goal") } catch (error) { errorMsg.value = handleUnknownError(error); @@ -89,7 +89,11 @@ const handleSubmit = () => { } useConfigurationStore().setChallenges(chosenChallenges.value) console.log(useConfigurationStore().getChallenges) - router.push("/first-saving-goal") + try { + signUpUser() + } catch (e) { + console.log(e) + } } </script> diff --git a/src/components/LeaderboardComponents/Leaderboard.vue b/src/components/LeaderboardComponents/Leaderboard.vue index cbed60338d34c4b03e64ec83c8fc7d52761c440f..c2e6c4f88ba9cfb29986d5bb931172c9176b514f 100644 --- a/src/components/LeaderboardComponents/Leaderboard.vue +++ b/src/components/LeaderboardComponents/Leaderboard.vue @@ -2,8 +2,10 @@ <div id="leaderboard"> <table> - <tbody> - <tr v-for="(entry, index) in leaderboard" :key="entry.user?.id" :class="{ 'is-user-5': entry.user?.id === userStore.id }"> + <tbody data-cy="top-leaderboard-table"> + <tr data-cy="top-leaderboard-tablerow" v-for="(entry, index) in leaderboard" + :key="entry.user?.id" :class="{ 'is-user-5': + entry.user?.id === userStore.id }"> <td class="number">{{ entry.rank }}</td> <td class="name" @click="navigateToUserProfile(entry.user?.id ?? 0)">{{ entry.user?.firstName }}</td> <td class="points" v-if="index === 0"> @@ -13,8 +15,16 @@ </tr> </tbody> <tbody id="line">`</tbody> +<<<<<<< HEAD <tbody v-if="!userInLeaderboard"> <tr v-for="(entry, index) in leaderboardExtra" :key="entry.user?.id" :class="{ 'is-user-5': entry.user?.id === userStore.id }"> +======= + <tbody data-cy="surrounding-user-leaderboard-table" v-if="!userInLeaderboard"> + <tr></tr> + <tr data-cy="surrounding-user-leaderboard-tablerow" v-for="(entry, index) in + leaderboardExtra" :key="entry.user?.id" :class="{ + 'is-user-5': entry.user?.id === userStore.id }"> +>>>>>>> e50d5c050dfc8dc2ff7931c3a05f22c024a70e31 <td class="number">{{ entry.rank }}</td> <td class="name" @click="navigateToUserProfile(entry.user?.id ?? 0)">{{ entry.user?.firstName }}</td> <td class="points">{{ entry.score }}</td> diff --git a/src/components/SavingGoalComponents/SavingGoalCreate.vue b/src/components/SavingGoalComponents/SavingGoalCreate.vue index e839cd594e127679b9ea649ee87c7a56487b9240..e5b1d5a99201c6b085e6c8e97cd3783da1dd5120 100644 --- a/src/components/SavingGoalComponents/SavingGoalCreate.vue +++ b/src/components/SavingGoalComponents/SavingGoalCreate.vue @@ -24,7 +24,6 @@ const createGoalClick = async () => { targetDate: date.value + " 00:00:00.000000000", }; - console.log(createGoalPayload) try { let response = await GoalService.createGoal({ requestBody: createGoalPayload }); @@ -85,5 +84,7 @@ const createGoalClick = async () => { .col-lg-8 { width: 63%; margin-top: 50px; + padding-right: 56px; + padding-bottom: 28px; } </style> \ No newline at end of file diff --git a/src/components/SavingGoalComponents/SavingGoalRoadmap.vue b/src/components/SavingGoalComponents/SavingGoalRoadmap.vue index 0f31c099235859f68a00f65281020f6375b73276..65bbfb8876c1ed6f2b076ee56cbed0621f3a187f 100644 --- a/src/components/SavingGoalComponents/SavingGoalRoadmap.vue +++ b/src/components/SavingGoalComponents/SavingGoalRoadmap.vue @@ -28,12 +28,12 @@ export default { roadmapSelected: true as boolean, statsSelected: false as boolean, chartData: { - labels: [1, 2, 3, 4, 5, 6, '7'], + labels: ["start"], datasets: [ { label: this.selectedGoal.name, backgroundColor: '#003A58', - data: [11, 24, 30, 47, 53, 62, 79] + data: [0] } ] }, @@ -60,6 +60,7 @@ export default { this.calculateSavedSoFar() this.onLoadDisableChecks(this.selectedGoal) this.onLoadAddDataToGraph(this.selectedGoal) + console.log() }, 500); }, computed: { @@ -242,6 +243,36 @@ export default { } } }) + }, + + formatDate(date: string) { + const date1 = new Date(date); + return date1.toISOString().split('T')[0] + }, + + calculateSavedSoFarPerChallengeInPercent(challenge: ChallengeDTO) { + let savedSoFarOnChallenge = 0 + let targetAmount = 1 + challenge.progressList?.forEach(progress => { + if(progress.amount) { + savedSoFarOnChallenge += progress.amount + } + }) + if(challenge.amount) { + targetAmount = challenge.amount + } + + return (savedSoFarOnChallenge / targetAmount) * 100 + }, + + calculateSavedSoFarPerChallenge(challenge: ChallengeDTO) { + let savedSoFar = 0 + challenge.progressList?.forEach(progress => { + if(progress.amount) { + savedSoFar += progress.amount + } + }) + return savedSoFar } }, }; @@ -292,7 +323,7 @@ export default { </div> <br> <div class="progress"> - <div class="progress-bar" role="progressbar" :style="{ width: challenge.percentFinished + '%' }" :aria-valuenow="challenge.percentFinished" aria-valuemin="0" aria-valuemax="100"></div> + <div class="progress-bar" role="progressbar" :style="{ width: calculateSavedSoFarPerChallengeInPercent(challenge) + '%' }" :aria-valuenow="calculateSavedSoFarPerChallengeInPercent(challenge)" aria-valuemin="0" aria-valuemax="100">{{ calculateSavedSoFarPerChallenge(challenge)}} Kr</div> </div> <br> <div class="checkbox-row"> @@ -308,9 +339,33 @@ export default { </ul> </div> <div v-else> - <h3 style="font-weight: 600;">Du har så langt spart {{ savedSoFar }} kr</h3> - <h3 style="font-weight: 600;">Ditt penge mål er: {{selectedGoal.targetAmount}} kr</h3> - <h3 style="font-weight: 600;">Utfordringene i denne sparestien vil spare deg {{ calculateTotalAmountFromChallenges() }} kr</h3> + + <div class="row"> + <div class="col-sm-3"> + <div class="card-box tilebox-one"><i class="icon-layers float-right text-muted"></i> + <h6 class="text-muted text-uppercase mt-0">Du ønsker å spare</h6> + <h2 class="" data-plugin="counterup">Kr {{ selectedGoal.targetAmount }}</h2></div> + </div> + + <div class="col-sm-3"> + <div class="card-box tilebox-one"><i class="icon-rocket float-right text-muted"></i> + <h6 class="text-muted text-uppercase mt-0">Du kan spare</h6> + <h2 class="" data-plugin="counterup">Kr {{ calculateTotalAmountFromChallenges() }}</h2></div> + </div> + + <div class="col-sm-3"> + <div class="card-box tilebox-one"><i class="icon-paypal float-right text-muted"></i> + <h6 class="text-muted text-uppercase mt-0">Du har spart</h6> + <h2 class="">Kr <span data-plugin="counterup">{{ savedSoFar }}</span></h2></div> + </div> + + <div class="col-sm-3"> + <div class="card-box tilebox-one"><i class="icon-rocket float-right text-muted"></i> + <h6 class="text-muted text-uppercase mt-0">Sparemålet ender </h6> + <h2 class="" data-plugin="counterup">{{ formatDate(selectedGoal.targetDate) }}</h2></div> + </div> + </div> + <br> <Line ref="chart" :data="chartData" :options="chartOptions" /> </div> </div> @@ -648,4 +703,5 @@ export default { flex: 0 0 calc(25% - 10px); /* 20% width for each checkbox, adjust margin accordingly */ margin-right: 10px; /* Adjust as needed */ } + </style> \ No newline at end of file diff --git a/src/components/SignUp/SignUpForm.vue b/src/components/SignUp/SignUpForm.vue index 660317944e50aa68c7da4050405b8a35570fdaf8..83a5da5494869633326e780f9cc2917e5c954621 100644 --- a/src/components/SignUp/SignUpForm.vue +++ b/src/components/SignUp/SignUpForm.vue @@ -83,17 +83,19 @@ const handleSubmit = async () => { id="firstNameInput" input-id="first-name" type="text" + pattern="^[^\d]+$" label="Fornavn" placeholder="Skriv inn ditt fornavn" - invalid-message="Ugyldig fornavn"/> + invalid-message="Ugyldig fornavn, husk ingen tall"/> <BaseInput :model-value="surnameRef" @input-change-event="handleSurnameInputEvent" id="surnameInput" input-id="surname" type="text" + pattern="^[^\d]+$" label="Etternavn" placeholder="Skriv inn ditt etternavn" - invalid-message="Ugyldig etternavn"/> + invalid-message="Ugyldig etternavn, husk ingen tall"/> <BaseInput :model-value="emailRef" @input-change-event="handleEmailInputEvent" id="emailInput" diff --git a/src/views/LeaderboardView.vue b/src/views/LeaderboardView.vue index 68b4f25358fd99611482efe4170095cee23b3692..c0324c51cdfe209e31036b36a33f56a7954cc5a4 100644 --- a/src/views/LeaderboardView.vue +++ b/src/views/LeaderboardView.vue @@ -11,12 +11,15 @@ <input type="radio" class="btn-check" name="vbtn-radio" id="vbtn-radio1" autocomplete="off" checked> <label class="btn btn-outline-primary" for="vbtn-radio1" @click="global"><img src="@/assets/globe.png" style="width: 60px" alt="globus"> Global</label> <input type="radio" class="btn-check" name="vbtn-radio" id="vbtn-radio2" autocomplete="off"> - <label class="btn btn-outline-primary" for="vbtn-radio2" @click="friends"><img src="@/assets/friends.png" style="width: 60px" alt="venner"> Venner</label> + <label data-cy="friends-leaderboard-btn" class="btn btn-outline-primary" + for="vbtn-radio2" + @click="friends"><img src="@/assets/friends.png" style="width: 60px" alt="venner"> Venner</label> </div> </div> </div> <main> <div id="leaderboard"> +<<<<<<< HEAD <h1><img src="@/assets/items/pigcoin.png" style="width: 2rem" alt="pig coin"> Poeng</h1> <Leaderboard :leaderboard="pointsLeaderboardData" :leaderboardExtra="pointsLeaderboardDataExtra" @navigateToUserProfile="navigateToUserProfile" /> </div> @@ -27,6 +30,21 @@ <div id="leaderboard"> <h1><img src="@/assets/icons/fire.png" style="width: 2rem" alt="ild"> Streak</h1> <Leaderboard :leaderboard="streakLeaderboardData" :leaderboardExtra="streakLeaderboardDataExtra" @navigateToUserProfile="navigateToUserProfile" /> +======= + <h1><img src="@/assets/items/pigcoin.png" style="width: 2rem" alt="pig coin"> Totale poeng</h1> + <Leaderboard data-cy="total-points-board" :leaderboard="pointsLeaderboardData" + :leaderboardExtra="pointsLeaderboardDataExtra" @navigateToUserProfile="navigateToUserProfile" /> + </div> + <div id="leaderboard"> + <h1><img src="@/assets/icons/fire.png" style="width: 2rem" alt="ild"> Nåværende rekke</h1> + <Leaderboard data-cy="current-points-board" :leaderboard="currentLeaderboardData" + :leaderboardExtra="currentLeaderboardDataExtra" @navigateToUserProfile="navigateToUserProfile" /> + </div> + <div id="leaderboard"> + <h1><img src="@/assets/icons/fire.png" style="width: 2rem" alt="ild"> Høyeste rekke</h1> + <Leaderboard data-cy="streak-board" :leaderboard="streakLeaderboardData" + :leaderboardExtra="streakLeaderboardDataExtra" @navigateToUserProfile="navigateToUserProfile" /> +>>>>>>> e50d5c050dfc8dc2ff7931c3a05f22c024a70e31 </div> </main> </div>