diff --git a/src/components/BaseComponents/NavBar.vue b/src/components/BaseComponents/NavBar.vue index 56bef37c1e9db83f78f09316b7de6ed2324523e4..e5cf16d376f645074cd5bf856fab0615e554e18e 100644 --- a/src/components/BaseComponents/NavBar.vue +++ b/src/components/BaseComponents/NavBar.vue @@ -13,21 +13,24 @@ <ul class="flex"> <li> <PlusIcon - class="m-6 cursor-pointer h-7" + class="m-6 cursor-pointer h-7 text-primary-medium" alt="Legg til" @click="$router.push('/newItem')" /> </li> <li> - <ChatAlt2Icon - class="m-6 cursor-pointer h-7" - alt="Meldinger" - @click="$router.push('/messages')" - /> + <div class="notification-container"> + <ChatAlt2Icon + class="m-6 cursor-pointer h-7" + alt="Meldinger" + @click="loadMessages()" + /> + <p @click="loadMessages()" class="notification" v-if="newMessages > 0">{{notifications}}</p> + </div> </li> <li> <UserCircleIcon - class="m-6 cursor-pointer h-7" + class="m-6 cursor-pointer h-7 text-primary-medium" alt="Profil" @click="loadProfile" /> @@ -39,10 +42,25 @@ <script> import { parseUserFromToken } from "@/utils/token-utils"; import { PlusIcon, ChatAlt2Icon, UserCircleIcon } from "@heroicons/vue/outline"; +import ws from '@/services/ws'; export default { name: "NavBar.vue", - + data() { + return { + newMessages: 0, + } + }, + computed: { + notifications() { + // if new messages is greater than 99 show +99 + if (this.newMessages > 99) { + return '+99' + } else { + return this.newMessages + } + } + }, components: { PlusIcon, ChatAlt2Icon, @@ -53,16 +71,44 @@ export default { async loadProfile() { if (this.$store.state.user.token !== null) { let user = parseUserFromToken(this.$store.state.user.token); - //console.log(user); let id = user.accountId; - //console.log(id); await this.$router.push("/profile/" + id); } else { await this.$router.push("/login"); } }, + loadMessages() { + this.newMessages = 0; + this.$router.push('/messages'); + } }, + created() { + ws.on('NEW_MESSAGE', () => { + if(this.$router.currentRoute.value.name == 'messages') return; + this.newMessages += 1; + }, "header"); + } }; </script> -<style scoped></style> +<style scoped> +.notification-container { + position: relative; +} +.notification { + position: absolute; + background-color: #ff5a5f; + top: 0; + min-width: 20px; + min-height: 20px; + padding: 0.25rem; + transform: translate(-80%, -30%); + color: white; + font-size: 10px; + border-radius: 50%; + font-weight: bold; + text-align: center; + right: 0; + cursor: pointer; +} +</style> diff --git a/src/components/BaseComponents/PaginationTemplate.vue b/src/components/BaseComponents/PaginationTemplate.vue index 206f880770cabc4931733dd2725bc39efeb4c306..b6907f178de39c1dace54149174e972e8e764b4b 100644 --- a/src/components/BaseComponents/PaginationTemplate.vue +++ b/src/components/BaseComponents/PaginationTemplate.vue @@ -2,7 +2,7 @@ <div v-if="totalPages() > 0"> <span v-if="showPreviousLink()" - class="cursor-pointer inline-flex items-center p-2 text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-300 hover:bg-gray-100 hover:text-gray-700" + class="cursor-pointer inline-flex items-center p-2 text-sm font-medium text-primary-light bg-white rounded-lg border border-gray-300 hover:bg-gray-100 hover:text-gray-700" @click="updatePage(currentPage - 1)" > Forrige @@ -10,7 +10,7 @@ <label class="mx-2">{{ currentPage + 1 }} av {{ totalPages() }}</label> <span v-if="showNextLink()" - class="cursor-pointer inline-flex items-center p-2 text-sm font-medium text-gray-500 bg-white rounded-lg border border-gray-300 hover:bg-gray-100 hover:text-gray-700" + class="cursor-pointer inline-flex items-center p-2 text-sm font-medium text-primary-light bg-white rounded-lg border border-gray-300 hover:bg-gray-100 hover:text-gray-700" @click="updatePage(currentPage + 1)" > Neste diff --git a/src/components/ChatComponents/ChatComponent.vue b/src/components/ChatComponents/ChatComponent.vue index 8f9ea97ee7d6d4679f29cbb7d341844394a7c13d..6ddbaa3aabe973aa420454340bc27d216184b9e6 100644 --- a/src/components/ChatComponents/ChatComponent.vue +++ b/src/components/ChatComponents/ChatComponent.vue @@ -108,6 +108,20 @@ export default { let container = this.$el.querySelector(".conversation"); container.scrollTop = container.scrollHeight; }, + async reloadMessages() { + const token = this.$store.state.user.token; + const response = await fetch( + `${process.env.VUE_APP_BASEURL}chats/users/${this.recipientID}/messages`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); + this.msgs = await response.json(); + }, async sendMessage() { if (!this.recipient || this.message == null || this.message == "") return; this.canScroll = true; @@ -129,21 +143,7 @@ export default { sender: parseInt(this.userid), recipient: this.recipientID, }); - this.reloadMessages(); - }, - async reloadMessages() { - const token = this.$store.state.user.token; - const response = await fetch( - `${process.env.VUE_APP_BASEURL}chats/users/${this.recipientID}/messages`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - } - ); - this.msgs = await response.json(); + await this.reloadMessages(); }, async reloadRents() { const token = this.$store.state.user.token; @@ -188,12 +188,19 @@ export default { await this.reloadMessages(); await this.getRecipient(); await this.reloadRents(); + + ws.on("NEW_MESSAGE", () => { + this.reloadMessages(); + }, "chat"); }, updated() { if (this.canScroll) this.scroll(); this.canScroll = false; this.scrollBehavior = "smooth"; }, + unmounted() { + ws.end("NEW_MESSAGE", "chat"); + } }; </script> diff --git a/src/components/ChatComponents/ChatMessage.vue b/src/components/ChatComponents/ChatMessage.vue index f2766566f1390958c04535055308c2c8e6bc7a22..3a4409c8171a80d0d73ae10f9689b5bfb07da7a4 100644 --- a/src/components/ChatComponents/ChatMessage.vue +++ b/src/components/ChatComponents/ChatMessage.vue @@ -45,7 +45,6 @@ export default { }, methods: { color() { - //console.log(this.userID); return this?.message.from == this.userID ? "bg-gray-300" : "bg-primary-medium"; diff --git a/src/components/ChatComponents/ChatsComponent.vue b/src/components/ChatComponents/ChatsComponent.vue index 9ee633cf8b85a9a5bbb4b08e44a479a1f27f6a71..b5ee4f6cf257ba47619d1ddcb68ff9a911e988a7 100644 --- a/src/components/ChatComponents/ChatsComponent.vue +++ b/src/components/ChatComponents/ChatsComponent.vue @@ -72,28 +72,33 @@ export default { calculateSide(from) { return from == this.userid ? "end" : "start"; }, + async fetchChats() { + const token = this.$store.state.user.token; + // Get all conversations from api with /chats/users + const conResponse = await fetch( + `${process.env.VUE_APP_BASEURL}chats/users`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + }, + } + ); // add error handling + this.conversations = await conResponse.json(); + }, }, async created() { - const token = this.$store.state.user.token; - // Get all conversations from api with /chats/users - const conResponse = await fetch( - `${process.env.VUE_APP_BASEURL}chats/users`, - { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - } - ); // add error handling - this.conversations = await conResponse.json(); - - ws.on("NEW_MESSAGE", () => { - this.reloadMessages(); - }); + await this.fetchChats(); + ws.on("NEW_MESSAGE",async () => { + await this.fetchChats() + }, "chats"); this.recipientID = this.$route.query?.userID || null; if (!this.recipientID) this.hambugerDisplay = "block"; }, + unmounted() { + ws.end("NEW_MESSAGE", "chats"); + }, }; </script> <style scoped> diff --git a/src/components/CommunityComponents/CommunityHamburger.vue b/src/components/CommunityComponents/CommunityHamburger.vue index f9d4b41f9ca426b890e492b28c169c080954d804..391a9ab232be3ec69eefd513b060f274e7dcd1b4 100644 --- a/src/components/CommunityComponents/CommunityHamburger.vue +++ b/src/components/CommunityComponents/CommunityHamburger.vue @@ -57,8 +57,8 @@ export default { this.$router.push("/"); }, }, - mounted() { - this.admin = CommunityAdminService.isUserAdmin( + async mounted() { + this.admin = await CommunityAdminService.isUserAdmin( this.$route.params.communityID ); }, diff --git a/src/components/CommunityComponents/CommunityHeader.vue b/src/components/CommunityComponents/CommunityHeader.vue index 7fa4ed118326044995dcf63b771b46cc0ba6ea30..4df062434b011c1515a9be2f159dc8ab200db754 100644 --- a/src/components/CommunityComponents/CommunityHeader.vue +++ b/src/components/CommunityComponents/CommunityHeader.vue @@ -123,11 +123,11 @@ export default { this.community = await CommunityService.getCommunity( this.$route.params.communityID ); - let members = await CommunityService.getCommunityMembers( + const members = await CommunityService.getCommunityMembers( this.$route.params.communityID ); - for (let mem in members) { - if (mem === this.userid) { + for (let i = 0; i < members.length; i++) { + if (members[i].userId == this.userid) { this.member = true; return; } diff --git a/src/components/CommunityComponents/CommunityList.vue b/src/components/CommunityComponents/CommunityList.vue index 97d25120249632f9d395a35f80e66c338125e0e3..7d2d789b09a5098dadfd90a695e23efbd01ec71d 100644 --- a/src/components/CommunityComponents/CommunityList.vue +++ b/src/components/CommunityComponents/CommunityList.vue @@ -1,7 +1,11 @@ <template> <ul> <li v-for="community in communities" :key="community"> - <CommunityListItem :community="community" :member="member" /> + <CommunityListItem + :community="community" + :member="member" + class="border-black" + /> </li> </ul> </template> diff --git a/src/components/CommunityComponents/CommunityListItem.vue b/src/components/CommunityComponents/CommunityListItem.vue index 1dc94f4e3421de11b7367b51ad8c12eae11a242b..637b8d95a10e15425e698d97c7f57ed7f71e2a54 100644 --- a/src/components/CommunityComponents/CommunityListItem.vue +++ b/src/components/CommunityComponents/CommunityListItem.vue @@ -47,11 +47,19 @@ </CustomFooterModal> <div @click="toggleDialog()" - class="bg-white shadow dark:bg-gray-800 select-none cursor-pointer hover:bg-gray-50 flex items-center p-4" + class="bg-white shadow dark:bg-gray-800 select-none cursor-pointer hover:bg-gray-50 flex items-center p-2" > - <div class="h-10 w-10 flex flex-col justify-center items-center mr-4"> - <UserGroupIcon v-if="!community.image" alt="Felleskapets bilde" /> - <!-- TODO: USE COMMUNITY IMAGE <img alt="Felleskapets bilde" src="@/assets/group.png" /> --> + <div + v-if="!community.picture" + class="h-10 w-10 flex flex-col justify-center items-center ml-2 mr-2" + > + <UserGroupIcon alt="Felleskapets bilde" class="h-10 w-10" /> + </div> + <div + v-else + class="h-3 w-14 flex flex-col justify-center items-center ml-2 mt-4 mb-4 mr-2" + > + <img :src="community.picture" alt="Fellsekaps bilde" class="rounded-md" /> </div> <div class="flex-1 pl-1 overflow-hidden"> <div class="font-medium dark:text-white truncate"> diff --git a/src/components/CommunityComponents/CommunityRequestForm.vue b/src/components/CommunityComponents/CommunityRequestForm.vue index 51a2e418c064f125ea349ae9141ba96f4c12b025..7c5f430106fee65e8a424de790f41ec1df7c4fc5 100644 --- a/src/components/CommunityComponents/CommunityRequestForm.vue +++ b/src/components/CommunityComponents/CommunityRequestForm.vue @@ -100,9 +100,7 @@ export default { getCommunityFromAPI: async function () { this.communityID = await this.$router.currentRoute.value.params .communityID; - console.log("Dette er community id =" + this.communityID); this.community = await GetCommunity(this.communityID); - console.log(this.community); }, }, async created() { diff --git a/src/components/CommunityComponents/CommunitySettings.vue b/src/components/CommunityComponents/CommunitySettings.vue index 0dd0328534c890eb348d4e02f6d16fb7da791762..8ea68e5261329fb85624ce68a201be8e02273416 100644 --- a/src/components/CommunityComponents/CommunitySettings.vue +++ b/src/components/CommunityComponents/CommunitySettings.vue @@ -9,7 +9,7 @@ </template> <script> -// import AdminService from "@/services/community-admin.service"; +// import CommunityAdminService from "@/services/community-admin.service"; import IconButton from "@/components/BaseComponents/IconButton.vue"; //TODO: OPEN CONFIRMATION DIALOG WHEN DELETING @@ -20,8 +20,8 @@ export default { }, methods: { deleteCommunity() { - //console.log("DELETED"); - // AdminService.deleteCommunity(this.$route.params.communityID); + console.log("DELETED"); + // CommunityAdminService.deleteCommunity(this.$route.params.communityID); }, }, }; diff --git a/src/components/CommunityComponents/NewCommunityForm.vue b/src/components/CommunityComponents/NewCommunityForm.vue index fa6652b28bec742c631b401028466820a9108106..bfa39aaa8d7c3b3136319565e476ea82d2751191 100644 --- a/src/components/CommunityComponents/NewCommunityForm.vue +++ b/src/components/CommunityComponents/NewCommunityForm.vue @@ -1,10 +1,28 @@ <template> <div - class="md:ring-1 ring-gray-300 rounded-xl overflow-hidden mx-auto mb-auto max-w-md w-full p-4" + class=" + md:ring-1 + ring-gray-300 + rounded-xl + overflow-hidden + mx-auto + mb-auto + max-w-md + w-full + p-4 + " > <!-- Component heading --> <div - class="text-xl md:text-2xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-10" + class=" + text-xl + md:text-2xl + font-medium + text-center text-gray-600 + dark:text-gray-200 + mt-4 + mb-10 + " > Opprett ny gruppe </div> @@ -18,7 +36,25 @@ > <div class="form-check"> <input - class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-primary-medium checked:border-primary-medium focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" + class=" + form-check-input + appearance-none + rounded-full + h-4 + w-4 + border border-gray-300 + bg-white + checked:bg-primary-medium checked:border-primary-medium + focus:outline-none + transition + duration-200 + mt-1 + align-top + bg-no-repeat bg-center bg-contain + float-left + mr-2 + cursor-pointer + " type="radio" name="flexRadioDefault" id="flexRadioOpen" @@ -36,7 +72,25 @@ </div> <div class="form-check"> <input - class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-primary-medium checked:border-primary-medium focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" + class=" + form-check-input + appearance-none + rounded-full + h-4 + w-4 + border border-gray-300 + bg-white + checked:bg-primary-medium checked:border-primary-medium + focus:outline-none + transition + duration-200 + mt-1 + align-top + bg-no-repeat bg-center bg-contain + float-left + mr-2 + cursor-pointer + " type="radio" name="flexRadioDefault" id="flexRadioPrivate" @@ -63,7 +117,25 @@ <input type="text" id="title" - class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" + class=" + block + w-full + px-4 + py-2 + mt-2 + text-gray-700 + placeholder-gray-500 + bg-white + border + rounded-md + dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 + focus:border-primary-light + dark:focus:border-primary-light + focus:ring-opacity-40 + focus:outline-none + focus:ring + focus:ring-primary-light + " v-model="v$.group.name.$model" required /> @@ -89,7 +161,25 @@ > <input type="text" - class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" + class=" + block + w-full + px-4 + py-2 + mt-2 + text-gray-700 + placeholder-gray-500 + bg-white + border + rounded-md + dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 + focus:border-primary-light + dark:focus:border-primary-light + focus:ring-opacity-40 + focus:outline-none + focus:ring + focus:ring-primary-light + " v-model="v$.group.place.$model" required /> @@ -117,7 +207,25 @@ id="description" rows="4" v-model="v$.group.description.$model" - class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" + class=" + block + w-full + px-4 + py-2 + mt-2 + text-gray-700 + placeholder-gray-500 + bg-white + border + rounded-md + dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 + focus:border-primary-light + dark:focus:border-primary-light + focus:ring-opacity-40 + focus:outline-none + focus:ring + focus:ring-primary-light + " required ></textarea> @@ -153,14 +261,28 @@ <!-- Button for adding an image --> <div class="inline-flex rounded-md shadow-sm"> - <div class="text-error uppercase text-center">midlertidig fjernet</div> - <!-- <button + <!--<div class="text-error uppercase text-center">Midlertidig fjernet</div> --> + <button @click="$refs.file.click()" - class="text-black bg-gray-200 hover:bg-grey-800 focus:ring-4 focus:outline-none focus:ring-grey-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-grey-600 dark:hover:bg-grey-700 dark:focus:ring-grey-800 disabled:opacity-50" + class=" + text-black + bg-gray-200 + hover:bg-grey-800 + focus:ring-4 focus:outline-none focus:ring-grey-300 + font-medium + rounded-lg + text-sm + sm:w-auto + px-5 + py-2.5 + text-center + dark:bg-grey-600 dark:hover:bg-grey-700 dark:focus:ring-grey-800 + disabled:opacity-50 + " :disabled="imageAdded" > Velg bilde - </button> --> + </button> <!-- Button for removing an image --> </div> @@ -176,12 +298,14 @@ <Button @click="saveClicked" id="saveButton" :text="'Lagre'"> </Button> </div> </div> + + <!--<img :src="group.image" class="w-1/2 inline" alt="Bilde av gjenstanden" />--> </template> <script> import useVuelidate from "@vuelidate/core"; import { required, helpers, maxLength } from "@vuelidate/validators"; -import { postNewgroup } from "@/utils/apiutil"; +import { postNewgroup, postNewImageCommunity } from "@/utils/apiutil"; import Button from "@/components/BaseComponents/ColoredButton"; export default { @@ -239,8 +363,11 @@ export default { radio: null, place: "", visibility: 1, + image: "", }, imageThere: false, + imageId: -1, + imageStringURL: "", }; }, computed: { @@ -276,20 +403,38 @@ export default { async saveClicked() { if (this.checkValidation()) { + //this.group.image = "https://image.shutterstock.com/image-photo/distribution-delivery-concept-global-business-600w-1650964204.jpg"; const groupInfo = { name: this.group.name, description: this.group.description, visibility: this.group.visibility, location: this.group.place, - picture: "", + picture: this.group.image, }; + console.log(this.group.image); + await postNewgroup(groupInfo); } }, - addImage: function (event) { + addImage: async function (event) { this.group.images.push(URL.createObjectURL(event.target.files[0])); + + var that = this; + let image = event.target.files[0]; + let fileReader = new FileReader(); + fileReader.onloadend = async function () { + const res = fileReader.result; + const id = await postNewImageCommunity(res); + + const API_URL = process.env.VUE_APP_BASEURL; + console.log(id); + console.log(API_URL + "images/" + id); + that.group.image = API_URL + "images/" + id; + + }; + fileReader.readAsArrayBuffer(image); this.imageThere = true; }, }, diff --git a/src/components/FormComponents/LoginForm.vue b/src/components/FormComponents/LoginForm.vue index 03c6482b6272c6f1ffaa3461906e1260044c0ea1..362dec931a8512d52aeea7ec1ba5a20bd1a9d9ea 100644 --- a/src/components/FormComponents/LoginForm.vue +++ b/src/components/FormComponents/LoginForm.vue @@ -4,7 +4,7 @@ > <div class="px-6 py-4 mt-4"> <div - class="text-xl md:text-2xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-8" + class="text-xl md:text-2xl font-medium text-center text-primary-light mt-4 mb-8" > Logg på </div> @@ -137,7 +137,6 @@ export default { this.v$.user.$touch(); if (this.v$.user.$invalid) { - //console.log("Ugyldig, avslutter..."); return; } @@ -153,8 +152,6 @@ export default { } else if (loginResponse.isLoggedIn === true) { this.$store.commit("saveToken", loginResponse.token); await this.$router.push("/"); - } else { - //console.log("Something went wrong"); } }, }, diff --git a/src/components/FormComponents/NewPasswordForm.vue b/src/components/FormComponents/NewPasswordForm.vue index fbe662109e67a2ec5fbebac903c60e2131115234..d32c15d65f1d4938f01c9c862fe70cc49f58929c 100644 --- a/src/components/FormComponents/NewPasswordForm.vue +++ b/src/components/FormComponents/NewPasswordForm.vue @@ -8,8 +8,35 @@ Endre passord </h3> + <div + id="oldPasswordField" + :class="{ error: v$.user.oldPassword.$errors.length }" + > + <label + for="oldPassword" + class="block text-sm text-gray-800 dark:text-gray-200" + >Gammelt passord</label + > + <input + type="password" + v-model="v$.user.oldPassword.$model" + class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-blue-400 dark:focus:border-blue-300 focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-blue-300" + /> + <!-- error message --> + <div v-for="(error, index) of v$.user.oldPassword.$errors" :key="index"> + <div + class="text-red-600 text-sm" + v-show="showError" + id="oldPasswordErrorId" + > + {{ error.$message }} + </div> + </div> + </div> + <div id="firstPasswordField" + class="mt-4" :class="{ error: v$.user.password.$errors.length }" > <label @@ -71,6 +98,12 @@ :text="'Sett ny passord'" /> </div> + <div class="flex items-center justify-center text-center bg-gray-50"> + <label + class="mx-2 text-sm font-bold text-error-medium dark:text-primary-light hover:underline" + >{{ message }}</label + > + </div> </div> </template> @@ -93,6 +126,9 @@ export default { validations() { return { user: { + oldPassword: { + required: helpers.withMessage(`Feltet må være utfylt`, required), + }, password: { required: helpers.withMessage(`Feltet må være utfylt`, required), minLength: helpers.withMessage( @@ -112,7 +148,9 @@ export default { }, data() { return { + message: "", user: { + oldPassword: "", password: "", rePassword: "", }, @@ -130,34 +168,28 @@ export default { this.v$.user.$touch(); if (this.v$.user.$invalid) { - //console.log("Invalid, exiting..."); return; } - const newPassword = this.user.password; + const newPassword = { + newPassword: this.user.password, + oldPassword: this.user.oldPassword, + }; const newPasswordResponse = await doNewPassword(newPassword); - if (newPasswordResponse != null) { - console.log("New password set"); - this.$store.commit("saveToken", newPasswordResponse); + if (newPasswordResponse.correctPassword) { + //New password was set + this.message = ""; + this.$store.commit("saveToken", newPasswordResponse.token); await this.$router.push("/"); + } else if (!newPasswordResponse.correctPassword) { + //The old password was not correct + this.message = "Det gamle passordet stemmer ikke for denne brukeren"; } else { - console.log("Couldn't set new password"); + //Ops something went wrong + this.message = "Ops noe gikk galt"; } - - /* - - if (newPasswordResponse.newPasswordSet === true) { - //console.log("New password set"); - await this.$router.push("/"); - } else if (newPasswordResponse.newPasswordSet === false) { - //console.log("Couldn't set new password"); - } else { - //console.log("Something went wrong"); - } - - */ }, validate() { this.$refs.form.validate(); diff --git a/src/components/FormComponents/RegisterForm.vue b/src/components/FormComponents/RegisterForm.vue index 994ec8567489beb3e1ac135cb192bd5260918f44..12c4a1227379f44305217b4c594c31e2ed703952 100644 --- a/src/components/FormComponents/RegisterForm.vue +++ b/src/components/FormComponents/RegisterForm.vue @@ -3,7 +3,7 @@ class="w-full max-w-md mx-auto mb-auto md:ring-1 ring-gray-300 overflow-hidden rounded-xl p-4" > <div - class="text-xl md:text-2xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-8" + class="text-xl md:text-2xl font-medium text-center text-primary-light mt-4 mb-8" id="registerLabel" > Opprett ny konto diff --git a/src/components/FormComponents/ResetPasswordForm.vue b/src/components/FormComponents/ResetPasswordForm.vue index 9f5457d9569dcd1c4d0f8f0a1b1eda7d30cfb3b3..b075e1a983cd3e6734a57fc1fb4ab07cea02d2b3 100644 --- a/src/components/FormComponents/ResetPasswordForm.vue +++ b/src/components/FormComponents/ResetPasswordForm.vue @@ -2,9 +2,7 @@ <div class="md:ring-1 ring-gray-300 rounded-xl overflow-hidden mx-auto mb-auto max-w-md w-full p-4" > - <h3 - class="text-xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-8" - > + <h3 class="text-xl font-medium text-center text-primary-light mt-4 mb-8"> Glemt passordet ditt? </h3> @@ -83,7 +81,6 @@ export default { this.v$.email.$touch(); if (this.v$.email.$invalid) { - //console.log("Ugyldig, avslutter..."); return; } else { this.$router.push("/"); diff --git a/src/components/ItemComponents/NewItemForm.vue b/src/components/ItemComponents/NewItemForm.vue index d0ff2bde33b2ad7da50e1fe7ede41171c38cdbf2..67f8f3032c3dacdbfc19f2fc29652be86d3c1db5 100644 --- a/src/components/ItemComponents/NewItemForm.vue +++ b/src/components/ItemComponents/NewItemForm.vue @@ -3,9 +3,7 @@ class="md:ring-1 ring-gray-300 rounded-xl overflow-hidden mx-auto mb-auto max-w-md w-full p-4" > <!-- Component heading --> - <h3 - class="text-xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-8" - > + <h3 class="text-xl font-medium text-center text-primary-light mt-4 mb-8"> Opprett ny utleie </h3> @@ -311,37 +309,19 @@ export default { }, methods: { checkValidation: function () { - //console.log("sjekker validering"); - this.v$.item.$touch(); if (this.v$.item.$invalid || this.item.selectedGroups.length === 0) { if (this.item.selectedGroups.length === 0) { this.groupErrorMessage = "Velg gruppe/grupper"; } - //console.log("Invalid, avslutter..."); return false; } - - //console.log("validert!"); return true; }, async saveClicked() { - //console.log("Attempting to save item"); - if (this.checkValidation()) { - //console.log("validert, videre..."); - this.checkUser(); - - //console.log("Tittel: " + this.item.title); - //console.log("Kategori: " + this.item.select); - //console.log("Beskrivelse: " + this.item.description); - //console.log("Addressen: " + this.item.address); - //console.log("Pris: " + this.item.price); - //console.log("bilder: " + this.item.images); - //console.log("gruppe: " + this.item.selectedGroups); - const itemInfo = { title: this.item.title, description: this.item.description, @@ -351,12 +331,7 @@ export default { categoryNames: [], communityIDs: this.item.selectedGroups, }; - - //console.log(itemInfo); - - /* const postRequest = */ await postNewItem(itemInfo); - - //console.log("posted: " + postRequest); + await postNewItem(itemInfo); this.$router.push("/"); } @@ -368,7 +343,6 @@ export default { }, addImage: function (event) { - //console.log(event.target.files); this.item.images.push(URL.createObjectURL(event.target.files[0])); }, @@ -379,7 +353,6 @@ export default { onChangeGroup: function (e) { this.selectedGroupId = e.target.value; let alreadyInGroupList = false; - //console.log("selected clicked"); for (let i = 0; i <= this.item.selectedGroups.length; i++) { if (this.selectedGroupId == this.item.selectedGroups[i]) { diff --git a/src/components/RentingComponents/ItemInfo.vue b/src/components/RentingComponents/ItemInfo.vue index 59308822724d89fc7749b0e1ed88afde4b316cea..18f8a558678bf4a038a8d05bcac2a39400a81326 100644 --- a/src/components/RentingComponents/ItemInfo.vue +++ b/src/components/RentingComponents/ItemInfo.vue @@ -51,7 +51,10 @@ </div> </div> <div class="mt-2"> - <UserListItemCard :user="userForId"></UserListItemCard> + <UserListItemCard + :buttons="['chat']" + :user="userForId" + ></UserListItemCard> </div> <div class="mt-4"> <h3 class="text-base font-base text-gray-900">Tidspunkter</h3> @@ -87,10 +90,11 @@ <script> import NewRent from "@/components/RentingComponents/NewRent.vue"; -import { getItem, getItemPictures, getUser } from "@/utils/apiutil"; +import { getItem, getItemPictures } from "@/utils/apiutil"; import ImageCarousel from "@/components/RentingComponents/ImageCarousel.vue"; import UserListItemCard from "@/components/UserProfileComponents/UserListItemCard.vue"; import DatepickerRange from "@/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue"; +import UserService from "@/services/user.service"; export default { name: "ItemInfo", @@ -123,7 +127,7 @@ export default { ], pictures: [], noPicture: true, - userForId: Object, + userForId: {}, rentingStartDate: null, rentingEndDate: null, totPrice: 0, @@ -181,7 +185,7 @@ export default { //TODO fixs so each image get a correct alt text. }, async getUser(userId) { - this.userForId = await getUser(userId); + this.userForId = await UserService.getUserFromId(userId); }, setDate(dateOfsomthing) { if (dateOfsomthing.startDate == null || dateOfsomthing.endDate == null) { diff --git a/src/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue b/src/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue index 53c92481e3318bfe86ccc699f80c6ed24ffbceb7..49fd65dc1249dc57add5e7c73d69a60bca2a1736 100644 --- a/src/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue +++ b/src/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue @@ -26,22 +26,6 @@ :blockedDaysRange="blockedDaysRange" ></calendar-component> </div> - <div class="split"></div> - <div> - <month-selector - @back="back" - type="monghM" - @forward="forward('monghM')" - :month="monghM" - ></month-selector> - <calendar-component - :month="monghM" - :start="startDate" - :end="endDate" - @selectDate="selectDate" - :blockedDaysRange="blockedDaysRange" - ></calendar-component> - </div> </div> <div class="footer"> <p v-if="error" class="error">{{ errorMessage }}</p> diff --git a/src/components/TimepickerComponents/DatepickerRange/MonthSelector.vue b/src/components/TimepickerComponents/DatepickerRange/MonthSelector.vue index bfcbaef6757e67706693a0680345aaf40f36b68a..6ed71add4bb27126ca6a8518b081d753063d89cc 100644 --- a/src/components/TimepickerComponents/DatepickerRange/MonthSelector.vue +++ b/src/components/TimepickerComponents/DatepickerRange/MonthSelector.vue @@ -81,7 +81,6 @@ export default { this.$emit("back", this.type); }, forward() { - //console.log(this.type); this.$emit("forward", this.type); }, }, diff --git a/src/components/UserProfileComponents/Rating.vue b/src/components/UserProfileComponents/RatingComponents/Rating.vue similarity index 95% rename from src/components/UserProfileComponents/Rating.vue rename to src/components/UserProfileComponents/RatingComponents/Rating.vue index 8aff6bad071841e4f6baf1c2825ca748b46c095f..1db98bfb010248d1cf3d554df1f946a6867e6dc9 100644 --- a/src/components/UserProfileComponents/Rating.vue +++ b/src/components/UserProfileComponents/RatingComponents/Rating.vue @@ -31,7 +31,7 @@ </li> <li> <p class="ml-2 text-sm font-medium text-gray-500 dark:text-gray-400"> - Rating ikke tilgjengelig + Ingen vurderinger </p> </li> </ul> @@ -47,7 +47,7 @@ export default { methods: { getFill(i) { if (i <= this.rating) { - return "w-5 h-5 text-warn"; + return "w-5 h-5 text-warn-medium"; } return "w-5 h-5 text-gray-300 dark:text-gray-500"; }, diff --git a/src/components/UserProfileComponents/RatingComponents/RatingModal.vue b/src/components/UserProfileComponents/RatingComponents/RatingModal.vue new file mode 100644 index 0000000000000000000000000000000000000000..419d4f9e4d6ec8cdb2545efc701f4f602ae26bf3 --- /dev/null +++ b/src/components/UserProfileComponents/RatingComponents/RatingModal.vue @@ -0,0 +1,195 @@ +<template> + <!-- Main modal --> + <div + v-if="visible" + class="fixed grid place-items-center bg-gray-600 bg-opacity-50 top-0 left-0 right-0 z-50 w-full overflow-x-hidden overflow-y-auto inset-0 h-full" + > + <div class="relative w-full h-full max-w-2xl p-4 md:h-auto"> + <!-- Modal content --> + <div class="relative bg-white rounded-lg shadow dark:bg-gray-700"> + <!-- Modal header --> + <div class="flex p-4 border-b rounded-t dark:border-gray-600"> + <h3 class="text-xl font-semibold text-gray-900 dark:text-white"> + {{ name }} + </h3> + <button + @click="close()" + class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white" + > + <svg + class="w-5 h-5" + fill="currentColor" + viewBox="0 0 20 20" + xmlns="http://www.w3.org/2000/svg" + > + <path + fill-rule="evenodd" + d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" + clip-rule="evenodd" + ></path> + </svg> + </button> + </div> + <!-- Modal body --> + <div class="p-6 space-y-6"> + <p class="text-lg leading-relaxed text-gray-500 dark:text-gray-400"> + {{ title }} + </p> + </div> + + <div class="ml-6 mt-4"> + <p + class="text-base leading-relaxed text-gray-500 dark:text-gray-400" + v-show="renterIsReceiverOfRating" + > + Gi en vurdering til utleieren + </p> + <p + class="text-base leading-relaxed text-gray-500 dark:text-gray-400" + v-show="!renterIsReceiverOfRating" + > + Gi en vurdering til leietakeren + </p> + </div> + + <div class="flex justify-center px-4"> + <textarea + class="w-full h-40 bg-gray-200 mb-4 ring-1 ring-gray-400 rounded-xl" + /> + </div> + + <div class="flex items-center justify-center mb-8"> + <svg + class="w-10 h-10 text-warn cursor-pointer" + :class="rating[0]" + @click="setRating(1)" + fill="currentColor" + viewBox="0 0 20 20" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" + ></path> + </svg> + <svg + class="w-10 h-10 text-warn cursor-pointer" + :class="rating[1]" + @click="setRating(2)" + fill="currentColor" + viewBox="0 0 20 20" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" + ></path> + </svg> + <svg + class="w-10 h-10 text-warn cursor-pointer" + :class="rating[2]" + @click="setRating(3)" + fill="currentColor" + viewBox="0 0 20 20" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" + ></path> + </svg> + <svg + class="w-10 h-10 text-warn cursor-pointer" + :class="rating[3]" + @click="setRating(4)" + fill="currentColor" + viewBox="0 0 20 20" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" + ></path> + </svg> + <svg + class="w-10 h-10 text-warn cursor-pointer" + :class="rating[4]" + @click="setRating(5)" + fill="currentColor" + viewBox="0 0 20 20" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" + ></path> + </svg> + </div> + + <div class="flex justify-center mb-4"> + <Button :text="'Send en vurdering'" @click="sendRating"></Button> + </div> + + <!-- Modal footer --> + <div class="rounded-b border-t border-gray-200 dark:border-gray-600"> + <!-- Slot: Add any html you want here --> + <slot /> + </div> + </div> + </div> + </div> +</template> + +<script> +import Button from "@/components/BaseComponents/ColoredButton"; +import { postNewRating } from "@/utils/apiutil"; + +export default { + name: "RatingModal", + data() { + return { + score: 3, + comment: "", + rating: [ + "text-warn", + "text-warn", + "text-warn", + "text-gray-300", + "text-gray-300", + ], + }; + }, + props: { + visible: Boolean, + name: String, + title: String, + rentID: Number, + renterIsReceiverOfRating: Boolean, + }, + + components: { + Button, + }, + methods: { + setRating(ratingNumber) { + this.score = ratingNumber; + for (let i = 0; i < 5; i++) { + if (i < ratingNumber) { + this.rating[i] = "text-warn"; + } else { + this.rating[i] = "text-gray-300"; + } + } + }, + close() { + this.$emit("close"); + }, + async sendRating() { + const ratingInfo = { + score: this.score, + comment: this.comment, + renterIsReceiverOfRating: this.renterIsReceiverOfRating, + rentID: this.rentID, + }; + await postNewRating(ratingInfo); + + this.$emit("reload"); + }, + }, +}; +</script> diff --git a/src/components/UserProfileComponents/RentHistoryComponents/RentHistoryItem.vue b/src/components/UserProfileComponents/RentHistoryComponents/RentHistoryItem.vue new file mode 100644 index 0000000000000000000000000000000000000000..675524104ec1ebe1b74410ae9114131c666f4023 --- /dev/null +++ b/src/components/UserProfileComponents/RentHistoryComponents/RentHistoryItem.vue @@ -0,0 +1,122 @@ +<template> + <div + class="bg-white shadow dark:bg-gray-800 select-none cursor-pointer hover:bg-gray-50" + > + <p class="font-bold mx-4" id="title"> + {{ historyItem.listing.title }} + </p> + <div class="flex flex-row items-center"> + <div class="flex flex-col px-4 flex-1"> + <router-link :to="{ path: '/profile/' + user.userId }"> + Leid til: {{ user.firstName }} {{ user.lastName }} + </router-link> + </div> + <div class="flex flex-col flex-1"> + <div> + Fra: + {{ this.getDateString(historyItem.fromTime, isMultipleDays) }} + </div> + <div> + Til: {{ this.getDateString(historyItem.toTime, isMultipleDays) }} + </div> + </div> + <colored-button + v-if="!isRated" + :text="'Vurder'" + class="px-4 flex-1" + @click=" + $emit('rate', { + show: true, + name: user.firstName + ' ' + user.lastName, + title: historyItem.listing.title, + rentID: historyItem.rentId, + renterIsReceiverOfRating: !currentUserIsRenter, + }) + " + /> + </div> + </div> +</template> + +<script> +import ColoredButton from "@/components/BaseComponents/ColoredButton.vue"; +import { parseCurrentUser } from "@/utils/token-utils"; +import userService from "@/services/user.service"; + +export default { + name: "RentHistoryItem", + components: { + ColoredButton, + }, + data() { + return { + user: {}, + isRated: true, + }; + }, + props: { + historyItem: { + rentId: Number, + fromTime: Number, + toTime: Number, + isAccepted: Boolean, + listing: { + listingID: Number, + title: String, + pricePerDay: Number, + address: String, + userID: Number, + }, + renterId: Number, + }, + }, + computed: { + currentUser() { + return parseCurrentUser(); + }, + isMultipleDays() { + if (this.historyItem.toTime - this.historyItem.fromTime < 86400000) { + return false; + } + return true; + }, + price() { + if (this.isMultipleDays) { + let numDays = Math.round( + (this.historyItem.toTime - this.historyItem.fromTime) / 86400000 + ); + return this.historyItem.listing.pricePerDay * numDays; + } + return this.historyItem.listing.pricePerDay; + }, + currentUserIsRenter() { + return this.isCurrentUser(this.historyItem.renterId); + }, + }, + methods: { + getDateString(milliseconds) { + let today = new Date(); + let date = new Date(milliseconds); + let dateString = date.getDate() + "." + (date.getMonth() + 1); + + if (date.getFullYear() != today.getFullYear()) { + dateString += "." + date.getFullYear(); + } + return dateString; + }, + isCurrentUser(id) { + return id == this.currentUser.accountId; + }, + }, + async beforeCreate() { + if (this.currentUserIsRenter) { + this.user = await userService.getUserFromId( + this.historyItem.listing.userID + ); + } else { + this.user = await userService.getUserFromId(this.historyItem.renterId); + } + this.isRated = await userService.isRated(this.historyItem.rentId); + }, +}; +</script> diff --git a/src/components/UserProfileComponents/UserItems.vue b/src/components/UserProfileComponents/UserItems.vue index 7b9a503d06a4b5d68f2b7b0af9dfca711d5edd8a..29deba9c6990b348a41796de1ba1f3d1d46d6a6e 100644 --- a/src/components/UserProfileComponents/UserItems.vue +++ b/src/components/UserProfileComponents/UserItems.vue @@ -1,8 +1,5 @@ <template> - <div - id="headline" - class="text-xl md:text-2xl text-gray-600 font-medium w-full" - > + <div id="headline" class="text-xl md:text-2xl text-primary-light font-medium"> Dine gjenstander </div> <!-- Search field --> diff --git a/src/components/UserProfileComponents/UserListItemCard.vue b/src/components/UserProfileComponents/UserListItemCard.vue index 8ed95bd4cbf89ed44919e498304724904ce4e4af..6f52995ebc8834212896e401eede8752e7697572 100644 --- a/src/components/UserProfileComponents/UserListItemCard.vue +++ b/src/components/UserProfileComponents/UserListItemCard.vue @@ -5,7 +5,7 @@ <!-- User image --> <div class="h-10 w-10 flex flex-col justify-center items-center mr-4"> <router-link :to="'/profile/' + user.userId"> - <img alt="Profilbilde" src="../../assets/defaultUserProfileImage.jpg" /> + <img alt="Profilbilde" :src="getProfilePicture" /> </router-link> </div> @@ -60,7 +60,7 @@ </template> <script> -import RatingComponent from "@/components/UserProfileComponents/Rating.vue"; +import RatingComponent from "@/components/UserProfileComponents/RatingComponents/Rating.vue"; import IconButton from "@/components/BaseComponents/IconButton.vue"; import UserService from "@/services/user.service"; import CommunityAdminService from "@/services/community-admin.service"; @@ -78,6 +78,9 @@ export default { return { rating: -1.0, communityID: -1, + profileImage: { + src: require("../../assets/defaultUserProfileImage.jpg"), + }, }; }, components: { @@ -92,13 +95,15 @@ export default { user: Object, buttons: Array, }, - methods: { + computed: { getProfilePicture() { - if (this.user.picture != "") { + if (this.user.picture !== "" && this.user.picture != null) { return this.user.picture; } - return "@/assets/defaultUserProfileImage.jpg"; + return this.profileImage.src; }, + }, + methods: { openChatWithUser() { this.$router.push({ name: "messages", @@ -110,6 +115,8 @@ export default { this.communityID, this.user.userId ); + //Find a better way to do this + window.location.reload(); }, acceptMemberRequest() { CommunityAdminService.acceptUserIntoCommunity( diff --git a/src/components/UserProfileComponents/UserProfile.vue b/src/components/UserProfileComponents/UserProfile.vue index ff50ea7b5954b5bb66d5a1ac228bef17fefa7a5e..142f3e612d45c5b27fb4c9172b6513e357cdfb66 100644 --- a/src/components/UserProfileComponents/UserProfile.vue +++ b/src/components/UserProfileComponents/UserProfile.vue @@ -40,14 +40,14 @@ </li> <li> <router-link - :to="'/user/' + id + '/communites'" + :to="'/profile/communities'" class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white" >Mine grupper </router-link> </li> <li> <router-link - to="" + to="/profile/history" class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white" >Leiehistorikk</router-link > @@ -70,7 +70,7 @@ <li> <router-link to="" - class="block py-2 px-4 text-sm text-error hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white" + class="block py-2 px-4 text-sm text-error-dark hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white" >Slett bruker</router-link > </li> @@ -81,7 +81,7 @@ <div class="flex flex-col items-center pb-10 mt-16 z-5"> <img class="mb-3 w-24 h-24 rounded-full shadow-lg" - src="../../assets/defaultUserProfileImage.jpg" + :src="getProfilePicture" alt="Profile picture" /> <h5 class="mb-1 text-xl font-medium text-gray-900 dark:text-white"> @@ -104,9 +104,10 @@ </template> <script> -import RatingComponent from "@/components/UserProfileComponents/Rating.vue"; +import RatingComponent from "@/components/UserProfileComponents/RatingComponents/Rating.vue"; import { parseCurrentUser } from "@/utils/token-utils"; -import { getUser, getAverageRating } from "@/utils/apiutil"; +import { getUser } from "@/utils/apiutil"; +import UserService from "@/services/user.service"; export default { name: "LargeProfileCard", @@ -119,11 +120,22 @@ export default { renterRating: -1, ownerRating: -1, dropdown: false, + profileImage: { + src: require("../../assets/defaultUserProfileImage.jpg"), + }, }; }, components: { RatingComponent, }, + computed: { + getProfilePicture() { + if (this.user.picture !== "" && this.user.picture != null) { + return this.user.picture; + } + return this.profileImage.src; + }, + }, methods: { async getUser() { this.currentUser = await parseCurrentUser(); @@ -132,20 +144,19 @@ export default { if (this.id === this.currentUser.accountId) { this.isCurrentUser = true; this.user = this.currentUser; + this.user = await UserService.getUserFromId(this.user.accountId); return; } this.user = await getUser(this.id); - let rating = await getAverageRating(this.id); - if (rating >= 0 && rating <= 5) { - this.renterRating = rating; - this.ownerRating = rating; + let ratingAsOwner = await UserService.getUserRatingAsOwner(this.id); + let ratingAsRenter = await UserService.getUserRatingAsRenter(this.id); + + if (ratingAsOwner >= 0 && ratingAsOwner <= 5) { + this.ownerRating = ratingAsOwner; } - }, - getProfilePicture() { - if (this.user.picture !== "") { - return this.user.picture; + if (ratingAsRenter >= 0 && ratingAsRenter <= 5) { + this.renterRating = ratingAsRenter; } - return "../assets/defaultUserProfileImage.jpg"; }, logout() { this.$store.commit("logout"); diff --git a/src/main.js b/src/main.js index 32764da4a55678ff6ed1fbf6e21819a55990f716..47d214cad941cb90ade7d2fdbd7b9c00cdedb8aa 100644 --- a/src/main.js +++ b/src/main.js @@ -2,7 +2,5 @@ import { createApp } from "vue"; import App from "./App.vue"; import router from "./router"; import store from "./store"; -import ws from "./services/ws"; createApp(App).use(router).use(store).mount("#app"); -console.log("WS", ws.test); diff --git a/src/router/index.js b/src/router/index.js index 348a23804a588b34073f754d15a3fad2a348c1fb..b0a701a8e3e72570a55bb60873802b88f200adfd 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,5 +1,6 @@ import store from "@/store"; import { createRouter, createWebHistory } from "vue-router"; +import NotFound from "@/views/NotFound.vue"; /** * Guards routes. If token is null, no user is logged in and only the @@ -20,12 +21,29 @@ const routes = [ name: "home", component: () => import("../views/CommunityViews/CommunityView.vue"), }, + { + path: "/help", + name: "help", + component: () => import("../views/HelpView.vue"), + }, { path: "/profile/:id", name: "profile", component: () => import("../views/UserProfileViews/ProfileView.vue"), beforeEnter: guardRoute, }, + { + path: "/profile/history", + name: "history", + component: () => import("../views/UserProfileViews/RentHistoryView.vue"), + beforeEnter: guardRoute, + }, + { + path: "/profile/communities", + name: "myCommunities", + component: () => import("../views/UserProfileViews/MyCommunitiesView.vue"), + beforeEnter: guardRoute, + }, { path: "/register", name: "register", @@ -83,12 +101,6 @@ const routes = [ import("../components/BaseComponents/NotificationsForm.vue"), beforeEnter: guardRoute, }, - { - path: "/user/:id/communities", - name: "myCommunities", - component: () => import("../views/CommunityViews/MyCommunitiesView.vue"), - beforeEnter: guardRoute, - }, { path: "/community/:communityID", name: "communityHome", @@ -122,6 +134,8 @@ const routes = [ name: "UserItems", component: () => import("../views/UserProfileViews/UserItemsView.vue"), }, + // Make sure it's your last route definition + { path: "/:pathMatch(.*)*", name: "not-found", component: NotFound }, ]; const router = createRouter({ @@ -129,4 +143,9 @@ const router = createRouter({ routes, }); +router.resolve({ + name: "not-found", + params: { pathMatch: ["not", "found"] }, +}).href; + export default router; diff --git a/src/services/community-admin.service.js b/src/services/community-admin.service.js index 091fd94aa59808dd7919b3afc564cfc866312128..c935f1181ea5d8c81306dddae21a8015d95a8d9d 100644 --- a/src/services/community-admin.service.js +++ b/src/services/community-admin.service.js @@ -21,7 +21,8 @@ class CommunityAdminService { async acceptUserIntoCommunity(communityID, userID) { return await axios.post( API_URL + "communities/" + communityID + "/requests", - { params: { userId: userID } } + null, + { headers: tokenHeader(), params: { userId: userID } } ); } @@ -29,9 +30,8 @@ class CommunityAdminService { async rejectUserFromCommunity(communityID, userID) { return await axios.patch( API_URL + "communitites/" + communityID + "/requests/reject", - { - params: { userId: userID }, - } + null, + { headers: tokenHeader(), params: { userId: userID } } ); } @@ -46,7 +46,10 @@ class CommunityAdminService { API_URL + "communities/" + communityID + "/kick", null, { - params: { userId: userID }, + headers: tokenHeader(), + params: { + userId: userID, + }, } ); } diff --git a/src/services/community.service.js b/src/services/community.service.js index d2bf0954527324357777de183dda46891b321418..11bc85817db76d285c028434a7e8df2f64cc5f46 100644 --- a/src/services/community.service.js +++ b/src/services/community.service.js @@ -17,9 +17,24 @@ class CommunityService { }); } - async getPublicCommunities() { + async getAllCommunities() { return await axios - .get(API_URL + "communities") + .get(API_URL + "communities", { + headers: tokenHeader(), + }) + .then((response) => { + return response.data; + }) + .catch((error) => { + console.error(error); + }); + } + + async getUserCommunities() { + return await axios + .get(API_URL + "user/communities", { + headers: tokenHeader(), + }) .then((response) => { return response.data; }) diff --git a/src/services/user.service.js b/src/services/user.service.js index d7ae98a92cec8a73dd0c5194260566bd73cdde82..b8f10603ecbd21ebfe0eec3b7e7d2227b025ffad 100644 --- a/src/services/user.service.js +++ b/src/services/user.service.js @@ -1,4 +1,5 @@ // import { tokenHeader } from "@/utils/token-utils"; +import { tokenHeader } from "@/utils/token-utils"; import axios from "axios"; const API_URL = process.env.VUE_APP_BASEURL; @@ -6,27 +7,86 @@ const API_URL = process.env.VUE_APP_BASEURL; class UserService { async getUserFromId(userId) { return await axios - .get(API_URL + "/users/" + userId + "/profile") + .get(API_URL + "users/" + userId + "/profile", { + headers: tokenHeader(), + }) .then((res) => { return res.data; }) - .catch((err) => console.log(err)); + .catch((err) => console.error(err)); } async getUserRatingAverage(userId) { return await axios - .get(API_URL + "rating/" + userId + "/average") + .get(API_URL + "rating/" + userId + "/average", { + headers: tokenHeader(), + }) .then((res) => { return res.data; }) - .catch((err) => console.log(err)); + .catch((err) => console.error(err)); } - //TODO - async getUserRatingAsOwner() {} + async getRenterHistory() { + return await axios + .get(API_URL + "user/profile/rent/history", { + headers: tokenHeader(), + }) + .then((res) => { + return res.data; + }) + .catch((err) => { + console.error(err); + return []; + }); + } - //TODO - async getUserRatingAsRenter() {} -} + async getOwnerHistory() { + return await axios + .get(API_URL + "user/profile/rent/history/owner", { + headers: tokenHeader(), + }) + .then((res) => { + return res.data; + }) + .catch((err) => { + console.error(err); + }); + } + + async isRated(rentID) { + return await axios + .get(API_URL + "rating/" + rentID + "/israted", { + headers: tokenHeader(), + }) + .then((res) => { + return res.data; + }) + .catch((err) => { + console.error(err); + }); + } + + async getUserRatingAsRenter(userId) { + return await axios + .get(API_URL + "rating/" + userId + "/average/renter", { + headers: tokenHeader(), + }) + .then((res) => { + return res.data; + }) + .catch((err) => console.error(err)); + } + async getUserRatingAsOwner(userId) { + return await axios + .get(API_URL + "rating/" + userId + "/average/owner", { + headers: tokenHeader(), + }) + .then((res) => { + return res.data; + }) + .catch((err) => console.error(err)); + } +} export default new UserService(); diff --git a/src/services/ws.js b/src/services/ws.js index b9adba0dd168d0e183dc6b151b95fff38d806ef4..823924d3c9449016f433c7066368bf02aa732ccc 100644 --- a/src/services/ws.js +++ b/src/services/ws.js @@ -6,29 +6,29 @@ import { parseCurrentUser } from "@/utils/token-utils"; // Events fired by websocket, MESSAGE const ws = (function () { // Object of all injected functions that the client may want + // These functions will be in a object const handlers = {}; const fire = function (event, data) { if (handlers[event]) { - handlers[event](data); + + // for each object in object fire event + for(const key in handlers[event]) { + handlers[event][key](data); + } } }; const onMessageReceived = (payload) => { const data = JSON.parse(payload.body); - //console.log("New message!"); // Fire message event fire("MESSAGE", JSON.parse(payload.body)); - if (data.status == "NEW_MESSAGE") fire("NEW_MESSAGE", JSON.parse(payload.body)); - - //console.log("Received message: " + payload); }; const onConnected = () => { - //console.log("Websocket Connected"); stompClient.subscribe( "/user/" + parseCurrentUser().accountId + "/queue/messages", onMessageReceived @@ -42,22 +42,25 @@ const ws = (function () { stompClient.connect({}, onConnected, onError); return { - on: function (event, callback) { - handlers[event] = callback; + on: function (event, callback, id = "none") { + // Generate random id + if(!handlers[event]) { + handlers[event] = {} + }; + handlers[event][id] = callback; }, fire: fire, - isActive: function (event) { - return !!handlers[event]; + isActive: function (event, id = "none") { + return !!handlers[event]?.[id]; }, - end: function (event) { + end: function (event, id = "none") { if (handlers[event]) { - delete handlers[event]; + delete handlers[event][id]; } else { throw new Error("No handler for event: " + event); } }, sendMessage: ({ sender, recipient /* , status */ }) => { - //if (status) console.log(status); stompClient.send( "/app/chat", {}, diff --git a/src/utils/apiutil.js b/src/utils/apiutil.js index a037c9f793629d0c6be3bc06f9a11450f2fbe7b6..f33d6db9f3ce03cac2a5b9d6dc149c41b1949912 100644 --- a/src/utils/apiutil.js +++ b/src/utils/apiutil.js @@ -13,7 +13,7 @@ export function doLogin(loginRequest) { return auth; }) .catch((error) => { - console.log(error.response); + console.error(error.response); return auth; }); } @@ -30,7 +30,7 @@ export function registerUser(registerInfo) { .then((response) => { return response; }) - .catch((err) => console.log(err)); + .catch((err) => console.error(err)); } export async function getUser(userid) { @@ -85,21 +85,25 @@ export function getAverageRating(userid) { }); } export async function doNewPassword(password) { + const auth = { correctPassword: false, token: "" }; let res = await axios({ method: "put", - url: API_URL + "user/profile/password", + url: API_URL + "user/password/change", headers: tokenHeader(), data: { password: password, }, }) .then((response) => { - return response; + auth.correctPassword = true; + auth.token = response.data; + return auth; }) .catch((error) => { console.log(error); + return auth; }); - return res.data; + return res; } export function postNewItem(itemInfo) { @@ -108,11 +112,10 @@ export function postNewItem(itemInfo) { headers: tokenHeader(), }) .then((response) => { - //console.log("poster: " + response.data); return response; }) .catch((error) => { - console.log(error.response); + console.error(error.response); return error; }); } @@ -124,7 +127,7 @@ export function postNewgroup(groupInfo) { return response; }) .catch((error) => { - console.log(error.response); + console.error(error.response); return error; }); } @@ -134,11 +137,10 @@ export function postNewRent(rentInfo) { headers: tokenHeader(), }) .then((response) => { - //console.log("poster: " + response.data); return response; }) .catch((error) => { - console.log(error.response); + console.error(error.response); return error; }); } @@ -250,7 +252,6 @@ export async function GetMemberRequestsOfCommunity(communityID) { export function JoinOpenCommunity(communityId) { if (tokenHeader().Authorization == "Bearer " + null) { - //console.log("ikke logget på!"); return "Login to join any community"; } @@ -262,7 +263,7 @@ export function JoinOpenCommunity(communityId) { return response; }) .catch((error) => { - console.log(error.response); + console.error(error.response); return error; }); } @@ -293,7 +294,7 @@ export async function LeaveCommunity(communityID) { return response.data; }) .catch((error) => { - console.log(error.data); + console.error(error.data); return error; }); } @@ -317,7 +318,6 @@ export function postNewRating(ratingInfo) { headers: tokenHeader(), }) .then((response) => { - console.log("poster: " + response.data); return response; }) .catch((error) => { @@ -325,3 +325,18 @@ export function postNewRating(ratingInfo) { return error; }); } + +export function postNewImageCommunity(image) { + return axios + .post(API_URL + "images", image, { + headers: {...tokenHeader(), "Content-Type": "image/png"}, + }) + .then((response) => { + console.log(response.data); + return response.data; + }) + .catch((error) => { + console.log(error); + return error; + }); +} diff --git a/src/views/CommunityViews/CommunityView.vue b/src/views/CommunityViews/CommunityView.vue index 3fb952a9e6d885911eb851bdf8ffeb2868b14e1e..52e711014b45736bf30d0a21122e03eb24262b55 100644 --- a/src/views/CommunityViews/CommunityView.vue +++ b/src/views/CommunityViews/CommunityView.vue @@ -2,11 +2,11 @@ <!-- My communities, with pagination --> <div v-if="loggedIn"> <div class="flex flex-row p-4 relative"> - <div class="text-xl md:text-2xl text-gray-600 font-medium w-full"> + <div class="text-xl md:text-2xl text-primary-light font-medium w-full"> Mine grupper </div> <UserAddIcon - class="cursor-pointer max-h-6 max-w-6 float-right grow" + class="cursor-pointer max-h-6 max-w-6 float-right grow text-primary-dark" @click="$router.push('/newCommunity')" alt="Opprett ny gruppe" /> @@ -20,27 +20,21 @@ v-on:page:update="updatePageMyCommunities" v-bind:currentPage="currentPageMyCommunities" v-bind:pageSize="pageSizeMyCommunities" - class="mt-10 mb-5" + class="mt-4" /> </div> </div> <!-- Public communities, with search and pagination --> - <p class="text-xl md:text-2xl text-gray-600 font-medium w-full p-4"> + <p class="text-xl md:text-2xl text-primary-light font-medium w-full p-4"> Offentlige grupper </p> <!-- Search field --> <div class="relative mt-1 mx-2" id="searchComponent"> <span class="absolute inset-y-0 left-0 flex items-center pl-3"> - <svg class="w-5 h-5 text-gray-400" viewBox="0 0 24 24" fill="none"> - <path - d="M21 21L15 15M17 10C17 13.866 13.866 17 10 17C6.13401 17 3 13.866 3 10C3 6.13401 6.13401 3 10 3C13.866 3 17 6.13401 17 10Z" - stroke="currentColor" - stroke-width="2" - stroke-linecap="round" - stroke-linejoin="round" - ></path> - </svg> + <div class="w-5 h-5 text-gray-400"> + <SearchIcon /> + </div> </span> <input @@ -67,16 +61,16 @@ v-on:page:update="updatePagePublicCommunities" v-bind:currentPage="currentPagePublicCommunities" v-bind:pageSize="pageSizePublicCommunities" - class="mt-10 mb-5" + class="my-4" /> </div> </template> <script> import CommunityList from "@/components/CommunityComponents/CommunityList.vue"; -import { getMyGroups, getVisibleGroups } from "@/utils/apiutil"; -import { UserAddIcon } from "@heroicons/vue/outline"; +import { UserAddIcon, SearchIcon } from "@heroicons/vue/outline"; import PaginationTemplate from "@/components/BaseComponents/PaginationTemplate"; +import CommunityService from "@/services/community.service"; export default { name: "HomeView", @@ -102,6 +96,7 @@ export default { CommunityList, UserAddIcon, PaginationTemplate, + SearchIcon, }, computed: { searchPublicCommunities() { @@ -152,26 +147,21 @@ export default { this.updatePageMyCommunities(this.currentPageMyCommunities - 1); } }, - //Triggers when search field input is changed - searchWritten: function () { + searchWritten() { //This method triggers when search input field is changed - if (this.search.length > 0) { - this.showPaginated = false; - this.showSearched = true; - } else { - this.showPaginated = true; - this.showSearched = false; - } + this.showPaginated = this.search.length < 1; + this.showSearched = this.search.length > 0; + }, + async load() { + this.publicCommunities = await CommunityService.getAllCommunities(); + this.loggedIn = this.$store.state.user.token !== null; + if (!this.loggedIn) return; + this.myCommunities = await CommunityService.getUserCommunities(); }, }, - async beforeMount() { - this.publicCommunities = await getVisibleGroups(); - this.loggedIn = this.$store.state.user.token !== null; - if (!this.loggedIn) return; - - this.myCommunities = await getMyGroups(); - - // Loops through both arrays and removes myCommunities from public + async mounted() { + await this.load(); + //Double loop not bad :) for (var i = 0; i < this.publicCommunities.length; i++) { for (var j = 0; j < this.myCommunities.length; j++) { if ( diff --git a/src/views/CommunityViews/MyCommunitiesView.vue b/src/views/CommunityViews/MyCommunitiesView.vue deleted file mode 100644 index b2abf6c58e23ed50f27c12a94fbe80c1cbc9aad8..0000000000000000000000000000000000000000 --- a/src/views/CommunityViews/MyCommunitiesView.vue +++ /dev/null @@ -1,32 +0,0 @@ -<template> - <div> - <div id="myGroups"> - <div>Mine grupper:</div> - <group-list :groupList="myGroups" /> - </div> - </div> -</template> - -<script> -import GroupList from "@/components/CommunityComponents/CommunityList.vue"; -import { getMyGroups } from "@/utils/apiutil"; - -export default { - data() { - return { - myGroups: [], - }; - }, - components: { - GroupList, - }, - methods: { - async getMyGroups() { - this.myGroups = await getMyGroups(); - }, - }, - beforeMount() { - this.getMyGroups(); - }, -}; -</script> diff --git a/src/views/HelpView.vue b/src/views/HelpView.vue new file mode 100644 index 0000000000000000000000000000000000000000..56f7e0e267f102c5493e80579c7c9c11cf9aed45 --- /dev/null +++ b/src/views/HelpView.vue @@ -0,0 +1,141 @@ +<template> + <div class="mt-6 bg-white justify-center w-screen"> + <div id="contact" class="grid place-items-center w-full"> + <div class="lg:mb-0 text-gray-700 w-full"> + <div class="mx-4"> + <h2 class="text-primary-dark mb-6 uppercase font-bold text-2xl"> + Kontakt oss + </h2> + <p class="text-gray-500 leading-relaxed mb-9"> + {{ contact.description }} + </p> + </div> + <div class="flex mb-8 ml-2 w-full"> + <div class="max-w-14 max-h-14 min-h-14 min-w-14"> + <LocationMarkerIcon + class="w-14 h-14 text-primary-dark rounded mr-4" + /> + </div> + <div class=""> + <h4 class="font-bold text-gray-800 text-xl mb-1">Lokaler</h4> + <p>{{ contact.address }},</p> + <p>{{ contact.city }},</p> + <p>{{ contact.country }}</p> + </div> + </div> + <div class="flex mb-8 ml-2 max-w-[370px] w-full"> + <div class="max-w-14 max-h-14 min-h-14 min-w-14"> + <MailIcon class="w-14 h-14 text-primary-dark rounded mr-4" /> + </div> + <div class=""> + <h4 class="font-bold text-gray-800 text-xl mb-1">Epost Addresse</h4> + <p>{{ contact.email }}</p> + </div> + </div> + </div> + </div> + <div id="faq"> + <div + class="mx-auto text-center px-4 text-2xl text-primary-dark font-semibold" + > + Frequently Asked Questions + </div> + <div + class="mt-8 mx-auto max-w-screen-sm lg:max-w-screen-lg flex flex-col lg:grid lg:grid-cols-2" + > + <dl v-for="(faqItem, index) in FAQ" :key="index"> + <div id="faqItem" class=""> + <div + id="question-and-answer" + class="select-none cursor-pointer border-2 mx-8 my-3 px-6 py-4 rounded-lg text-sm group" + > + <dt id="question"> + <div class="flex justify-between text-gray-800"> + <div class="font-bold"> + {{ faqItem.question }} + </div> + <div> + <div v-if="!faqItem.toggle" @click="toggle(faqItem)"> + <ChevronDoubleDownIcon + class="h-5 w-5 block rounded-full p-1" + /> + </div> + <div v-else @click="toggle(faqItem)"> + <ChevronDoubleUpIcon + class="h-5 w-5 block rounded-full p-1" + /> + </div> + </div> + </div> + </dt> + <dd v-if="faqItem.toggle" class="mt-2 leading-snug text-gray-700"> + {{ faqItem.answer }} + </dd> + </div> + </div> + </dl> + </div> + </div> + </div> +</template> + +<script> +import { + ChevronDoubleDownIcon, + LocationMarkerIcon, + ChevronDoubleUpIcon, + MailIcon, +} from "@heroicons/vue/outline"; + +export default { + data() { + return { + contact: { + description: + "BoCo (Borrow Community) er et norsk selskap som tilbyr en plattform for utlån av gjenstander for privatpersoner og bedrifter. BoCo streber for å bli den foretrukne plattformen for lån for privatpersoner og bedrifter i Norge.", + email: "BorrowCompany@BorrowCommunity.com", + address: "O. S. Bragstads Plass 2G", + city: "Trondheim", + country: "Norge", + }, + FAQ: [ + { + question: "Hvordan kan jeg legge ut en gjenstand til leie?", + answer: + "Ved å trykke på plus-ikonet i navigasjons-baren øverst på siden. Dette vil ta deg til et skjema for å legge ut en ny gjenstand.", + toggle: false, + }, + { + question: "Hvordan kan jeg opprette en ny gruppe?", + answer: + "Fra hovedsiden kan man trykke på pluss-person-ikonet ved siden av mine grupper som vil ta deg til skjemaet for å lage en ny gruppe.", + toggle: false, + }, + { + question: "Hvordan kan jeg bli med i en gruppe?", + answer: + "På hovedsiden vil alle offentlige og lukkede grupper vises. Når du trykker på ønsket gruppe vil du få muligheten til å bli med/sende medlemsforespørsel.", + toggle: false, + }, + { + question: "Hva vil jeg ha tilgang til ved å logge inn/opprette en bruker?", + answer: + "Uten å være logget inn vil du kunne se alle grupper, og også se gjenstander som ligger ute til lån i offentlige grupper. For å kunne låne en gjenstand må du være med i gruppen gjenstanden ligger i, og for dette må du være innlogget. Du må også være innlogget for å sende medlemsforespørsel i lukkede grupper.", + toggle: false, + }, + ], + }; + }, + components: { + ChevronDoubleDownIcon, + ChevronDoubleUpIcon, + MailIcon, + LocationMarkerIcon, + }, + methods: { + toggle(faqItem) { + faqItem.toggle = !faqItem.toggle; + }, + }, +}; +</script> diff --git a/src/views/NotFound.vue b/src/views/NotFound.vue new file mode 100644 index 0000000000000000000000000000000000000000..1051e31dded6de02a0c51f96a3994c47f7033598 --- /dev/null +++ b/src/views/NotFound.vue @@ -0,0 +1,18 @@ +<template> + <div class="flex place-content-center h-full p-8"> + <div class="bg-gray-300 p-8 rounded-2xl drop-shadow-xl"> + <h1 class="text-primary-medium text-4xl">Oisann!</h1> + <p>Her skjedde det visst en feil...</p> + <p> + Lyst å gå tilbake til + <router-link to="/" class="underline">start?</router-link> + </p> + </div> + </div> +</template> + +<script> +export default {}; +</script> + +<style></style> diff --git a/src/views/UserProfileViews/MyCommunitiesView.vue b/src/views/UserProfileViews/MyCommunitiesView.vue new file mode 100644 index 0000000000000000000000000000000000000000..6a5c376b0ac2cde7dcc8ee0dff1cfef2ca5cb6d5 --- /dev/null +++ b/src/views/UserProfileViews/MyCommunitiesView.vue @@ -0,0 +1,35 @@ +<template> + <!-- My communities, with pagination --> + <div class="flex flex-row p-4 relative"> + <div class="text-xl md:text-2xl text-primary-light font-medium w-full"> + Mine grupper + </div> + <UserAddIcon + class="cursor-pointer max-h-6 max-w-6 float-right grow text-primary-dark" + @click="$router.push('/newCommunity')" + alt="Opprett ny gruppe" + /> + </div> + <CommunityList :communities="myCommunities" :member="true" /> +</template> + +<script> +import CommunityList from "@/components/CommunityComponents/CommunityList.vue"; +import CommunityService from "@/services/community.service"; +import { UserAddIcon } from "@heroicons/vue/outline"; + +export default { + data() { + return { + myCommunities: [], + }; + }, + components: { + CommunityList, + UserAddIcon, + }, + async beforeCreate() { + this.myCommunities = await CommunityService.getUserCommunities(); + }, +}; +</script> diff --git a/src/views/UserProfileViews/RentHistoryView.vue b/src/views/UserProfileViews/RentHistoryView.vue new file mode 100644 index 0000000000000000000000000000000000000000..45e7859306a8e3ede90ca30d039cb1f7840bb777 --- /dev/null +++ b/src/views/UserProfileViews/RentHistoryView.vue @@ -0,0 +1,75 @@ +<template> + <rating-modal + :visible="modal.show" + :name="modal.name" + :title="modal.title" + :rentID="modal.rentID" + :renterIsReceiverOfRating="modal.renterIsReceiverOfRating" + @close="modal.show = false" + @reload="this.$forceUpdate()" + /> + <ul> + <li v-for="historyItem in fullHistory" :key="historyItem.rentId"> + <rent-history-item + :historyItem="historyItem" + @rate="(modal) => openModal(modal)" + /> + </li> + </ul> +</template> + +<script> +import RentHistoryItem from "@/components/UserProfileComponents/RentHistoryComponents/RentHistoryItem.vue"; +import RatingModal from "@/components/UserProfileComponents/RatingComponents/RatingModal.vue"; +import UserService from "@/services/user.service"; + +export default { + components: { + RentHistoryItem, + RatingModal, + }, + data() { + return { + renterHistory: [], + ownerHistory: [], + modal: { + show: false, + name: "", + title: "", + rentID: -1, + renterIsReceiverOfRating: false, + }, + }; + }, + computed: { + fullHistory() { + function compareHistoryItems(itemA, itemB) { + if (itemA.fromTime > itemB.fromTime) { + return -1; + } + if (itemA.fromTime < itemB.fromTime) { + return 1; + } + return 0; + } + + let fullHistory = this.renterHistory.concat(this.ownerHistory); + fullHistory.filter((item) => item.isAccepted); + fullHistory.sort(compareHistoryItems); + return fullHistory; + }, + hasNoHistory() { + return this.renterHistory.length == 0 && this.ownerHistory.length == 0; + }, + }, + methods: { + openModal(modal) { + this.modal = modal; + }, + }, + async beforeCreate() { + this.renterHistory = await UserService.getRenterHistory(); + this.ownerHistory = await UserService.getOwnerHistory(); + }, +}; +</script> diff --git a/tests/unit/apiutil-communityHome-mock.spec.js b/tests/unit/apiutil-communityHome-mock.spec.js deleted file mode 100644 index 4ab75d6469cf79e31546054eebae45ce5976284d..0000000000000000000000000000000000000000 --- a/tests/unit/apiutil-communityHome-mock.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -import { GetCommunity, GetListingsInCommunity } from "@/utils/apiutil"; -import axios from "axios"; - -jest.mock("axios"); - -describe("testing mocking of apiutil.js", () => { - it("check that existing group returns correctly", async () => { - const expectedResponse = { - communityId: 4040, - name: "Fisken i vannet", - description: "For vi som liker fjell fisk", - visibility: 1, - location: "Bergen brygge", - picture: "fish blub blub", - }; - - axios.get.mockImplementation(() => - Promise.resolve({ data: expectedResponse }) - ); - - const communityResponse = await GetCommunity(4040); - expect(communityResponse.name).toBe(expectedResponse.name); - }); - - it("check that existing group returns correct listings", async () => { - const expectedResponse = { - item1: { - title: "Fiskekurs", - description: "Fisking og sånn", - pricePerDay: 200, - address: "Vannet", - userID: 6, - categoryNames: null, - communityIDs: null, - }, - - item2: { - title: "TestFraFrontend", - description: "oslo", - pricePerDay: 500, - address: "oslo", - userID: 1, - categoryNames: null, - communityIDs: null, - }, - }; - - axios.get.mockImplementation(() => - Promise.resolve({ data: expectedResponse }) - ); - - const communityItemResponse = await GetListingsInCommunity(4040); - expect(communityItemResponse).toBe(expectedResponse); - }); -}); diff --git a/tests/unit/component-tests/community-component-tests/__snapshots__/community-list-item.spec.js.snap b/tests/unit/component-tests/community-component-tests/__snapshots__/community-list-item.spec.js.snap index 25c734add5c0ca6ad6f94cec526e074008da6782..5135f4b48c7b670b8c5b8173ad0d802a82b5ed0a 100644 --- a/tests/unit/component-tests/community-component-tests/__snapshots__/community-list-item.spec.js.snap +++ b/tests/unit/component-tests/community-component-tests/__snapshots__/community-list-item.spec.js.snap @@ -10,26 +10,16 @@ exports[`CommunityListItem component renders correctly 1`] = ` <!--v-if--> <div - class="bg-white shadow dark:bg-gray-800 select-none cursor-pointer hover:bg-gray-50 flex items-center p-4" + class="bg-white shadow dark:bg-gray-800 select-none cursor-pointer hover:bg-gray-50 flex items-center p-2" > <div - class="h-10 w-10 flex flex-col justify-center items-center mr-4" + class="h-3 w-14 flex flex-col justify-center items-center ml-2 mt-4 mb-4 mr-2" > - <svg - aria-hidden="true" - fill="none" - stroke="currentColor" - stroke-width="2" - viewBox="0 0 24 24" - xmlns="http://www.w3.org/2000/svg" - > - <path - d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" - stroke-linecap="round" - stroke-linejoin="round" - /> - </svg> - <!-- TODO: USE COMMUNITY IMAGE <img alt="Felleskapets bilde" src="@/assets/group.png" /> --> + <img + alt="Fellsekaps bilde" + class="rounded-md" + src="string" + /> </div> <div class="flex-1 pl-1 overflow-hidden" diff --git a/tests/unit/component-tests/community-component-tests/__snapshots__/create-new-group.spec.js.snap b/tests/unit/component-tests/community-component-tests/__snapshots__/create-new-group.spec.js.snap index dd345a3b786424cdd10ecd5b90797cd164a9a1a9..19e292101b012b95058ea454f90fb0bdb96fe700 100644 --- a/tests/unit/component-tests/community-component-tests/__snapshots__/create-new-group.spec.js.snap +++ b/tests/unit/component-tests/community-component-tests/__snapshots__/create-new-group.spec.js.snap @@ -2,166 +2,167 @@ exports[`CreateNewGroup elements rendering renders correctly 1`] = ` <div - class="md:ring-1 ring-gray-300 rounded-xl overflow-hidden mx-auto mb-auto max-w-md w-full p-4" + data-v-app="" > - <!-- Component heading --> + <div - class="text-xl md:text-2xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-10" + class="md:ring-1 ring-gray-300 rounded-xl overflow-hidden mx-auto mb-auto max-w-md w-full p-4" > - Opprett ny gruppe - </div> - <!-- Radio boxes --> - <div - class="mt-6" - > - <label - class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" - id="radioBoxLabel" + <!-- Component heading --> + <div + class="text-xl md:text-2xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-10" > - Synlighet - </label> + Opprett ny gruppe + </div> + <!-- Radio boxes --> <div - class="form-check" + class="mt-6" > - <input - class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-primary-medium checked:border-primary-medium focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" - id="flexRadioOpen" - name="flexRadioDefault" - type="radio" - value="Åpen" - /> <label - class="form-check-label inline-block text-gray-800" - for="flexRadioOpen" - id="radioBoxOpenLabel" + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" + id="radioBoxLabel" > - Åpen + Synlighet </label> + <div + class="form-check" + > + <input + class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-primary-medium checked:border-primary-medium focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" + id="flexRadioOpen" + name="flexRadioDefault" + type="radio" + value="Åpen" + /> + <label + class="form-check-label inline-block text-gray-800" + for="flexRadioOpen" + id="radioBoxOpenLabel" + > + Åpen + </label> + </div> + <div + class="form-check" + > + <input + class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-primary-medium checked:border-primary-medium focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" + id="flexRadioPrivate" + name="flexRadioDefault" + type="radio" + value="Privat" + /> + <label + class="form-check-label inline-block text-gray-800" + for="flexRadioPrivate" + id="radioBoxPrivateLabel" + > + Privat + </label> + </div> </div> + <!-- Title --> <div - class="form-check" + class="mt-6" > + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" + id="titleLabel" + > + Gruppenavn + </label> <input - class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-primary-medium checked:border-primary-medium focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" - id="flexRadioPrivate" - name="flexRadioDefault" - type="radio" - value="Privat" + class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" + id="title" + required="" + type="text" /> + <!-- error message for title--> + + + </div> + <!-- Place --> + <div + class="mt-6" + > <label - class="form-check-label inline-block text-gray-800" - for="flexRadioPrivate" - id="radioBoxPrivateLabel" + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" + id="positionLabel" > - Privat + By/Sted </label> + <input + class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" + required="" + type="text" + /> + <!-- error message for place--> + + </div> - </div> - <!-- Title --> - <div - class="mt-6" - > - <label - class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" - id="titleLabel" - > - Gruppenavn - </label> - <input - class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" - id="title" - required="" - type="text" - /> - <!-- error message for title--> - - - </div> - <!-- Place --> - <div - class="mt-6" - > - <label - class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" - id="positionLabel" - > - By/Sted - </label> - <input - class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" - required="" - type="text" - /> - <!-- error message for place--> - - - </div> - <!-- Description --> - <div - class="mt-6" - > - <label - class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" - id="descriptionLabel" - > - Beskrivelse - </label> - <textarea - class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" - id="description" - required="" - rows="4" - /> - <!-- error message for description --> - - - </div> - <!-- Images --> - <div - class="mt-6" - > - <label - class="block mb-2 text-xl font-medium text-gray-900 dark:text-gray-400" - id="imageLabel" + <!-- Description --> + <div + class="mt-6" > - Bilde - </label> - <input - accept="image/png, image/jpeg" - multiple="" - style="display: none;" - type="file" - /> - <!-- Button for adding an image --> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" + id="descriptionLabel" + > + Beskrivelse + </label> + <textarea + class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-primary-light dark:focus:border-primary-light focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-primary-light" + id="description" + required="" + rows="4" + /> + <!-- error message for description --> + + + </div> + <!-- Images --> <div - class="inline-flex rounded-md shadow-sm" + class="mt-6" > + <label + class="block mb-2 text-xl font-medium text-gray-900 dark:text-gray-400" + id="imageLabel" + > + Bilde + </label> + <input + accept="image/png, image/jpeg" + multiple="" + style="display: none;" + type="file" + /> + <!-- Button for adding an image --> <div - class="text-error uppercase text-center" + class="inline-flex rounded-md shadow-sm" > - midlertidig fjernet - </div> - <!-- <button - @click="$refs.file.click()" + <!--<div class="text-error uppercase text-center">Midlertidig fjernet</div> --> + <button class="text-black bg-gray-200 hover:bg-grey-800 focus:ring-4 focus:outline-none focus:ring-grey-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-grey-600 dark:hover:bg-grey-700 dark:focus:ring-grey-800 disabled:opacity-50" - :disabled="imageAdded" - > - Velg bilde - </button> --> - <!-- Button for removing an image --> + > + Velg bilde + </button> + <!-- Button for removing an image --> + </div> + <!-- Div box for showing all chosen images --> + + + </div> + <!-- Save item button --> + <div + class="flex justify-center mt-10 float-right" + > + <button-stub + id="saveButton" + text="Lagre" + /> </div> - <!-- Div box for showing all chosen images --> - - - </div> - <!-- Save item button --> - <div - class="flex justify-center mt-10 float-right" - > - <button-stub - id="saveButton" - text="Lagre" - /> </div> + <!--<img :src="group.image" class="w-1/2 inline" alt="Bilde av gjenstanden" />--> + </div> `; diff --git a/tests/unit/component-tests/community-component-tests/__snapshots__/new-item-form.spec.js.snap b/tests/unit/component-tests/community-component-tests/__snapshots__/new-item-form.spec.js.snap index a475c4e78dfe175dbf46da2eb2492f7c59588869..c2789c34942918bfb9b251c3bbd6d7c71fbc0eee 100644 --- a/tests/unit/component-tests/community-component-tests/__snapshots__/new-item-form.spec.js.snap +++ b/tests/unit/component-tests/community-component-tests/__snapshots__/new-item-form.spec.js.snap @@ -6,7 +6,7 @@ exports[`NewItemForm component renders correctly 1`] = ` > <!-- Component heading --> <h3 - class="text-xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-8" + class="text-xl font-medium text-center text-primary-light mt-4 mb-8" > Opprett ny utleie </h3> diff --git a/tests/unit/component-tests/community-component-tests/community-hamburger.spec.js b/tests/unit/component-tests/community-component-tests/community-hamburger.spec.js index 8a7211fcc06388357a4646aa8219cb285eeffcf6..4f610d50a19491b24345c83325ce55a5c38602a1 100644 --- a/tests/unit/component-tests/community-component-tests/community-hamburger.spec.js +++ b/tests/unit/component-tests/community-component-tests/community-hamburger.spec.js @@ -1,26 +1,17 @@ import { shallowMount } from "@vue/test-utils"; import CommunityHamburger from "@/components/CommunityComponents/CommunityHamburger.vue"; import { route, router, $route, $router } from "../../mock-router"; +import { store, $store } from "../../mock-store"; describe("CommunityHamburger elements rendering", () => { let wrapper; beforeEach(() => { wrapper = shallowMount(CommunityHamburger, { - //passing prop to component - props: { - adminStatus: true, - community: { - communityId: 1, - name: "String", - description: "String", - visibility: 0, - location: "String", - picture: "String", - }, - }, global: { mocks: { + store, + $store, route, router, $route, @@ -33,7 +24,7 @@ describe("CommunityHamburger elements rendering", () => { it("renders all li fields", () => { expect(wrapper.find("#newItem").text()).toMatch("Opprett Utleie"); expect(wrapper.find("#getMembers").text()).toMatch("Se Medlemmer"); - expect(wrapper.find("#adminGroup").text()).toMatch("Administrer Gruppe"); + //expect(wrapper.find("#adminGroup").text()).toMatch("Administrer Gruppe"); expect(wrapper.find("#leaveGroup").text()).toMatch("Forlat Gruppe"); }); }); diff --git a/tests/unit/component-tests/user-component-tests/__snapshots__/login-form.spec.js.snap b/tests/unit/component-tests/user-component-tests/__snapshots__/login-form.spec.js.snap index bdec3ea33b7b23b7b0566dac81e8d132b9c0befa..f79182e421a30cc8a35db92683cee3b064bbfb02 100644 --- a/tests/unit/component-tests/user-component-tests/__snapshots__/login-form.spec.js.snap +++ b/tests/unit/component-tests/user-component-tests/__snapshots__/login-form.spec.js.snap @@ -8,7 +8,7 @@ exports[`LoginForm component renders correctly 1`] = ` class="px-6 py-4 mt-4" > <div - class="text-xl md:text-2xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-8" + class="text-xl md:text-2xl font-medium text-center text-primary-light mt-4 mb-8" > Logg på </div> diff --git a/tests/unit/component-tests/user-component-tests/__snapshots__/new-password-form.spec.js.snap b/tests/unit/component-tests/user-component-tests/__snapshots__/new-password-form.spec.js.snap index 2aa391a34e4b72f291cf5fe3bcb6173ea20e7e06..129085284ce0c40121b0be19e799ac65b7361f12 100644 --- a/tests/unit/component-tests/user-component-tests/__snapshots__/new-password-form.spec.js.snap +++ b/tests/unit/component-tests/user-component-tests/__snapshots__/new-password-form.spec.js.snap @@ -11,6 +11,24 @@ exports[`NewPasswordForm component renders correctly 1`] = ` </h3> <div class="" + id="oldPasswordField" + > + <label + class="block text-sm text-gray-800 dark:text-gray-200" + for="oldPassword" + > + Gammelt passord + </label> + <input + class="block w-full px-4 py-2 mt-2 text-gray-700 placeholder-gray-500 bg-white border rounded-md dark:bg-gray-800 dark:border-gray-600 dark:placeholder-gray-400 focus:border-blue-400 dark:focus:border-blue-300 focus:ring-opacity-40 focus:outline-none focus:ring focus:ring-blue-300" + type="password" + /> + <!-- error message --> + + + </div> + <div + class="mt-4" id="firstPasswordField" > <label @@ -59,5 +77,12 @@ exports[`NewPasswordForm component renders correctly 1`] = ` Sett ny passord </button> </div> + <div + class="flex items-center justify-center text-center bg-gray-50" + > + <label + class="mx-2 text-sm font-bold text-error-medium dark:text-primary-light hover:underline" + /> + </div> </div> `; diff --git a/tests/unit/component-tests/user-component-tests/__snapshots__/register-user-component.spec.js.snap b/tests/unit/component-tests/user-component-tests/__snapshots__/register-user-component.spec.js.snap index 98a0bdc228f2d0dfc90a11afe853ee43bf44aa9c..8adae3ffa2833611c6e3da43443b7e2a381b4e3f 100644 --- a/tests/unit/component-tests/user-component-tests/__snapshots__/register-user-component.spec.js.snap +++ b/tests/unit/component-tests/user-component-tests/__snapshots__/register-user-component.spec.js.snap @@ -5,7 +5,7 @@ exports[`RegisterFormComponent renders correctly 1`] = ` class="w-full max-w-md mx-auto mb-auto md:ring-1 ring-gray-300 overflow-hidden rounded-xl p-4" > <div - class="text-xl md:text-2xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-8" + class="text-xl md:text-2xl font-medium text-center text-primary-light mt-4 mb-8" id="registerLabel" > Opprett ny konto diff --git a/tests/unit/component-tests/user-component-tests/__snapshots__/reset-password-form.spec.js.snap b/tests/unit/component-tests/user-component-tests/__snapshots__/reset-password-form.spec.js.snap index 49cbc5a2e696228709e56dc12f1925b640d0e377..7b7c1ace2fabdd62b5a782799ce6d1f71ba8bfb1 100644 --- a/tests/unit/component-tests/user-component-tests/__snapshots__/reset-password-form.spec.js.snap +++ b/tests/unit/component-tests/user-component-tests/__snapshots__/reset-password-form.spec.js.snap @@ -5,7 +5,7 @@ exports[`ResetPasswordForm component renders correctly 1`] = ` class="md:ring-1 ring-gray-300 rounded-xl overflow-hidden mx-auto mb-auto max-w-md w-full p-4" > <h3 - class="text-xl font-medium text-center text-gray-600 dark:text-gray-200 mt-4 mb-8" + class="text-xl font-medium text-center text-primary-light mt-4 mb-8" > Glemt passordet ditt? </h3> diff --git a/tests/unit/component-tests/user-component-tests/rating.spec.js b/tests/unit/component-tests/user-component-tests/rating.spec.js index cdbcee60fc44be8c4e04b9f2dc4a42756f5a6699..8fb44c69188cd302c40fbfb73d1a4706c448fb09 100644 --- a/tests/unit/component-tests/user-component-tests/rating.spec.js +++ b/tests/unit/component-tests/user-component-tests/rating.spec.js @@ -1,5 +1,5 @@ import { mount } from "@vue/test-utils"; -import Rating from "@/components/UserProfileComponents/Rating.vue"; +import Rating from "@/components/UserProfileComponents/RatingComponents/Rating.vue"; describe("Rating component", () => { let wrapper; diff --git a/vue.config.js b/vue.config.js index e77733af29c6ce1ad2798aee7bb9f3b932f7635f..aa4620c0c16d9c54ffa288ae9ea5b6a31d6a8bee 100644 --- a/vue.config.js +++ b/vue.config.js @@ -4,7 +4,7 @@ module.exports = defineConfig({ transpileDependencies: true, chainWebpack: (config) => { config.plugin("html").tap((args) => { - args[0].title = "Borrow Community"; + args[0].title = "BoCo - Borrow Community"; return args; }); },