diff --git a/src/components/BaseComponents/InputField.vue b/src/components/BaseComponents/InputField.vue deleted file mode 100644 index a6cbb32e98ed5e1f94ec9b784ea492b9b138048e..0000000000000000000000000000000000000000 --- a/src/components/BaseComponents/InputField.vue +++ /dev/null @@ -1,11 +0,0 @@ -<template> - <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" - /> -</template> - -<script> -export default { - name: "InputField", -}; -</script> diff --git a/src/components/BaseComponents/NavBar.vue b/src/components/BaseComponents/NavBar.vue index 3b6937f7b737afc98c8f871b46104ee44aebd6e0..1fb35bdf7951e829200ab59cc23f9a0cf0e0a406 100644 --- a/src/components/BaseComponents/NavBar.vue +++ b/src/components/BaseComponents/NavBar.vue @@ -11,21 +11,29 @@ /> </div> <ul class="flex justify-between"> - <li class="cursor-pointer" @click="$router.push('/newItem')"> + <li + class="cursor-pointer" + v-if="this.$store.state.user.token !== null" + @click="$router.push('/newItem')" + > <PlusIcon class="m-6 md:mr-2 h-7 text-primary-medium float-left" alt="Legg til" /> <a class="hidden md:block mt-7 text-sm float-right">Legg til</a> </li> - <li> + <li + class="cursor-pointer" + v-if="this.$store.state.user.token !== null" + @click="loadMessages" + > <div class="notification-container"> <ChatAlt2Icon - class="m-6 cursor-pointer h-7" + class="m-6 md:mr-2 h-7 text-primary-medium float-left" alt="Meldinger" - @click="loadMessages()" /> - <p @click="loadMessages()" class="notification" v-if="newMessages > 0">{{notifications}}</p> + <p class="notification" v-if="newMessages > 0">{{ notifications }}</p> + <a class="hidden md:block mt-7 text-sm float-right">Meldinger</a> </div> </li> <li class="cursor-pointer" @click="loadProfile"> @@ -33,7 +41,14 @@ class="m-6 md:mr-2 h-7 text-primary-medium float-left" alt="Profil" /> - <a class="hidden md:block mr-4 mt-7 text-sm float-right">Profil</a> + <a + v-if="this.$store.state.user.token !== null" + class="hidden md:block mr-4 mt-7 text-sm float-right" + >Profil</a + > + <a v-else class="hidden md:block mr-4 mt-7 text-sm float-right" + >Logg inn</a + > </li> </ul> </nav> @@ -42,24 +57,24 @@ <script> import { parseUserFromToken } from "@/utils/token-utils"; import { PlusIcon, ChatAlt2Icon, UserCircleIcon } from "@heroicons/vue/outline"; -import ws from '@/services/ws'; +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' + return "+99"; } else { - return this.newMessages + return this.newMessages; } - } + }, }, components: { PlusIcon, @@ -79,15 +94,19 @@ export default { }, loadMessages() { this.newMessages = 0; - this.$router.push('/messages'); - } + this.$router.push("/messages"); + }, }, created() { - ws.on('NEW_MESSAGE', () => { - if(this.$router.currentRoute.value.name == 'messages') return; - this.newMessages += 1; - }, "header"); - } + ws.on( + "NEW_MESSAGE", + () => { + if (this.$router.currentRoute.value.name == "messages") return; + this.newMessages += 1; + }, + "header" + ); + }, }; </script> @@ -96,19 +115,19 @@ export default { 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; + 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/NotificationsForm.vue b/src/components/BaseComponents/NotificationsForm.vue deleted file mode 100644 index b142fb2a8e36ec57b52256194cc35de433f71d63..0000000000000000000000000000000000000000 --- a/src/components/BaseComponents/NotificationsForm.vue +++ /dev/null @@ -1,11 +0,0 @@ -<template> - <div>This is a notification page</div> -</template> - -<script> -export default { - name: "NotificationsForm", -}; -</script> - -<style scoped></style> diff --git a/src/components/BaseComponents/PaginationTemplate.vue b/src/components/BaseComponents/PaginationTemplate.vue index b6907f178de39c1dace54149174e972e8e764b4b..939d7350598667c7e2710346f3ba80bb83f96748 100644 --- a/src/components/BaseComponents/PaginationTemplate.vue +++ b/src/components/BaseComponents/PaginationTemplate.vue @@ -7,7 +7,9 @@ > Forrige </span> - <label class="mx-2">{{ currentPage + 1 }} av {{ totalPages() }}</label> + <label class="mx-2 text-primary-light" + >{{ currentPage + 1 }} av {{ totalPages() }}</label + > <span v-if="showNextLink()" 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" diff --git a/src/components/ChatComponents/ChatComponent.vue b/src/components/ChatComponents/ChatComponent.vue index ff6f25d4ae56e5d7175eb1dfcf8ffed2bf287fed..6b2252b6a241430f9cc7e2dd91718b9817713a9f 100644 --- a/src/components/ChatComponents/ChatComponent.vue +++ b/src/components/ChatComponents/ChatComponent.vue @@ -190,9 +190,13 @@ export default { await this.getRecipient(); await this.reloadRents(); - ws.on("NEW_MESSAGE", () => { - this.reloadMessages(); - }, "chat"); + ws.on( + "NEW_MESSAGE", + () => { + this.reloadMessages(); + }, + "chat" + ); }, updated() { if (this.canScroll) this.scroll(); @@ -201,7 +205,7 @@ export default { }, unmounted() { ws.end("NEW_MESSAGE", "chat"); - } + }, }; </script> diff --git a/src/components/ChatComponents/ChatProfile.vue b/src/components/ChatComponents/ChatProfile.vue index bfed6d6281e7ae81a7c2851aeb0f60fdf16bb425..db4c47cad8cc00e943159362ca45f4dc1ee7df65 100644 --- a/src/components/ChatComponents/ChatProfile.vue +++ b/src/components/ChatComponents/ChatProfile.vue @@ -82,6 +82,6 @@ export default { selectUser() { this.$emit("recipient", this.conversation?.recipient.userId); }, - } + }, }; </script> diff --git a/src/components/ChatComponents/ChatsComponent.vue b/src/components/ChatComponents/ChatsComponent.vue index b5ee4f6cf257ba47619d1ddcb68ff9a911e988a7..f2cc2b00b788c5154aa5e137ae3ff1f9cc1482ab 100644 --- a/src/components/ChatComponents/ChatsComponent.vue +++ b/src/components/ChatComponents/ChatsComponent.vue @@ -1,7 +1,7 @@ <template> <div class="chat"> <div class="conversations"> - <h1>Samtaler:</h1> + <h1>Meldinger</h1> <hr /> <ChatProfile v-for="(conversation, i) in conversations" @@ -90,9 +90,13 @@ export default { }, async created() { await this.fetchChats(); - ws.on("NEW_MESSAGE",async () => { - await this.fetchChats() - }, "chats"); + ws.on( + "NEW_MESSAGE", + async () => { + await this.fetchChats(); + }, + "chats" + ); this.recipientID = this.$route.query?.userID || null; if (!this.recipientID) this.hambugerDisplay = "block"; }, diff --git a/src/components/ChatComponents/RentalMessage.vue b/src/components/ChatComponents/RentalMessage.vue index e57640b14711ff8908c210e5cf9faefc9f8fd004..8dab267ac5d9db895be02f6b04bcd3102b160c28 100644 --- a/src/components/ChatComponents/RentalMessage.vue +++ b/src/components/ChatComponents/RentalMessage.vue @@ -18,15 +18,15 @@ </p> </div> </div> - <div class="buttons" v-if = "(!rent.isAccepted && !rent.deleted)"> + <div class="buttons" v-if="!rent.isAccepted && !rent.deleted"> <button class="button green" @click="accept">Godta</button> <button class="button red" @click="reject">Avslå</button> </div> - <div class="" v-if = rent.isAccepted> - <h1 class="green">Godtatt</h1> + <div class="" v-if="rent.isAccepted"> + <h1 class="green">Godtatt</h1> </div> - <div class="" v-if = rent.deleted> - <h1 class="red">Avslått</h1> + <div class="" v-if="rent.deleted"> + <h1 class="red">Avslått</h1> </div> </div> </template> @@ -38,9 +38,9 @@ export default { props: { rent: { type: Object, - required: true - }, + required: true, }, + }, computed: { img() { return "https://images.unsplash.com/photo-1453728013993-6d66e9c9123a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8dmlld3xlbnwwfHwwfHw%3D&w=1000&q=80"; //this.rent.listing.imageUrl; @@ -67,19 +67,18 @@ export default { }, methods: { async accept() { - await axios.put( - process.env.VUE_APP_BASEURL + - `renting/${this.rent.rentId}/accept` ,null, + await axios.put( + process.env.VUE_APP_BASEURL + `renting/${this.rent.rentId}/accept`, + null, { headers: tokenHeader() } ); }, async reject() { - await axios.delete( - process.env.VUE_APP_BASEURL + - `renting/${this.rent.rentId}/delete`,null, + await axios.delete( + process.env.VUE_APP_BASEURL + `renting/${this.rent.rentId}/delete`, + null, { headers: tokenHeader() } ); - }, }, }; diff --git a/src/components/CommunityComponents/CommunityHamburger.vue b/src/components/CommunityComponents/CommunityHamburger.vue index 7754ce6eb97658c51124f5bd2b476e6b14a32c0e..d788a57adb429e7aeb3af5cbd9f1c10f35ff6a8d 100644 --- a/src/components/CommunityComponents/CommunityHamburger.vue +++ b/src/components/CommunityComponents/CommunityHamburger.vue @@ -39,7 +39,6 @@ <script> import { LeaveCommunity } from "@/utils/apiutil"; -import CommunityAdminService from "@/services/community-admin.service"; export default { name: "CommunityHamburger", @@ -57,13 +56,12 @@ export default { this.$router.push("/"); }, }, - async mounted() { - this.admin = await CommunityAdminService.isUserAdmin( - this.$route.params.communityID - ); - }, created() { this.communityID = this.$route.params.communityID; + if (!Array.isArray(this.$store.state.user.adminList)) return; + this.admin = this.$store.state.user.adminList.includes( + parseInt(this.communityID) + ); }, }; </script> diff --git a/src/components/CommunityComponents/CommunityHeader.vue b/src/components/CommunityComponents/CommunityHeader.vue index eba31718a765b9adfc08bd6b449dbff4c3de28a1..201d9fb337b3666965d0584ee17ecc91d576d7e7 100644 --- a/src/components/CommunityComponents/CommunityHeader.vue +++ b/src/components/CommunityComponents/CommunityHeader.vue @@ -126,13 +126,15 @@ export default { this.community = await CommunityService.getCommunity( this.$route.params.communityID ); - const members = await CommunityService.getCommunityMembers( - this.$route.params.communityID - ); - for (let i = 0; i < members.length; i++) { - if (members[i].userId == this.userid) { - this.member = true; - return; + if (this.$store.state.user.token !== null) { + const members = await CommunityService.getCommunityMembers( + this.$route.params.communityID + ); + for (let i = 0; i < members.length; i++) { + if (members[i].userId == this.userid) { + this.member = true; + return; + } } } }, diff --git a/src/components/CommunityComponents/CommunityHome.vue b/src/components/CommunityComponents/CommunityHome.vue index 5ba88fc1c7d94bcb9c69fbe4f17c322cc50a13e9..49103a9a86db563b3d2efd6dc733b4ef7efc7f40 100644 --- a/src/components/CommunityComponents/CommunityHome.vue +++ b/src/components/CommunityComponents/CommunityHome.vue @@ -33,7 +33,7 @@ /> </div> - <div class="absolute inset-x-0 px-5 py-3"> + <div class="absolute inset-x-0"> <!-- ItemCards --> <div class="flex items-center justify-center w-screen"> <!-- Shows items based on pagination --> diff --git a/src/components/CommunityComponents/CommunityListItem.vue b/src/components/CommunityComponents/CommunityListItem.vue index 4116a893a97845095d11aa60b7735de685aac623..40d90645e430cd49f104c91c7def3e125b9721fb 100644 --- a/src/components/CommunityComponents/CommunityListItem.vue +++ b/src/components/CommunityComponents/CommunityListItem.vue @@ -54,7 +54,10 @@ 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" /> + <UserGroupIcon + alt="Felleskapets bilde" + class="h-10 w-10 text-primary-dark" + /> </div> <div v-else @@ -67,7 +70,7 @@ /> </div> <div class="flex-1 pl-1 overflow-hidden"> - <div class="font-medium dark:text-white truncate"> + <div class="font-medium text-gray-800 dark:text-white truncate"> {{ community.name }} </div> </div> diff --git a/src/components/CommunityComponents/CommunityRequestForm.vue b/src/components/CommunityComponents/CommunityRequestForm.vue index 7c5f430106fee65e8a424de790f41ec1df7c4fc5..8508efd202f46350266f1c198b5f7419aca8aac3 100644 --- a/src/components/CommunityComponents/CommunityRequestForm.vue +++ b/src/components/CommunityComponents/CommunityRequestForm.vue @@ -41,6 +41,14 @@ <div class="flex justify-center mt-10 float-right"> <Button @click="saveClicked" id="saveButton" :text="'Send'"> </Button> </div> + + <notification-modal + @click="routeToHome" + :visible="sendRequestClicked" + :title="'Vellykket'" + :message="'Forespørsel sendt!'" + > + </notification-modal> </div> </template> @@ -51,12 +59,14 @@ import { required, helpers, maxLength } from "@vuelidate/validators"; import Button from "@/components/BaseComponents/ColoredButton"; import { tokenHeader } from "@/utils/token-utils"; import { GetCommunity } from "@/utils/apiutil"; +import NotificationModal from "@/components/BaseComponents/NotificationModal"; export default { name: "CommunityRequestForm.vue", components: { Button, + NotificationModal, }, setup() { return { v$: useVuelidate() }; @@ -81,10 +91,14 @@ export default { message: "", communityId: null, community: {}, + sendRequestClicked: false, }; }, computed: {}, methods: { + routeToHome() { + this.$router.push("/"); + }, //TODO fix so that community id is set (not null) async saveClicked() { this.communityID = await this.$router.currentRoute.value.params @@ -96,6 +110,8 @@ export default { { message: this.message }, { headers: tokenHeader() } ); + + this.sendRequestClicked = true; }, getCommunityFromAPI: async function () { this.communityID = await this.$router.currentRoute.value.params diff --git a/src/components/CommunityComponents/NewCommunityForm.vue b/src/components/CommunityComponents/NewCommunityForm.vue index 200db5b1a33be506325ffbb4fe7a15d2ec2a472b..7a62e90f302416559f695459aafcfddfd6f43244 100644 --- a/src/components/CommunityComponents/NewCommunityForm.vue +++ b/src/components/CommunityComponents/NewCommunityForm.vue @@ -136,8 +136,8 @@ <!-- Images --> <div class="mt-6"> <label - class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" - id="imageLabel" + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" + id="imageLabel" > Bilde (bildet må være .png) </label> diff --git a/src/components/ItemComponents/EditItemForm.vue b/src/components/ItemComponents/EditItemForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..5ec681920f74b6a1a2a2adc304b7f1a7156c96f4 --- /dev/null +++ b/src/components/ItemComponents/EditItemForm.vue @@ -0,0 +1,461 @@ +<template> + <div + 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-primary-light mt-4 mb-8"> + Rediger gjenstand + </h3> + + <!-- Title --> + <div class="mb-6" :class="{ error: v$.updatedItem.title.$errors.length }"> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" + id="titleLabel" + >Tittel</label + > + <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" + v-model="v$.updatedItem.title.$model" + required + /> + + <!-- error message for title--> + <div + class="text-error-medium" + v-for="(error, index) of v$.updatedItem.title.$errors" + :key="index" + > + <div class="text-error-medium text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Category --> + <div class="mb-6"> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" + id="selectCategoryLabel" + >Kategori</label + > + <select + @change="onChangeCategory($event)" + v-model="v$.updatedItem.selectedCategory.$model" + id="categories" + 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" + > + <option class="text-gray-400" value="" disabled> + Velg en kategori + </option> + <option + :value="category" + :selected="category == updatedItem.selectedCategory" + v-for="category in categories" + :key="category" + class="text-gray-900 text-sm" + > + {{ category }} + </option> + </select> + + <!-- error message for select box --> + <div + class="text-error-medium" + v-for="(error, index) of v$.updatedItem.selectedCategory.$errors" + :key="index" + > + <div class="text-error-medium text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Grupper --> + <div class="mb-6"> + <label class="block text-sm font-medium text-gray-900 dark:text-gray-400" + >Grupper</label + > + <div + class="overflow-auto w-full max-h-32 mt-2 text-base list-none bg-white rounded divide-y divide-gray-100 dark:bg-gray-700" + > + <ul class="py-1" aria-labelledby="dropdownDefault"> + <li> + <div + class="form-check" + v-for="community in communities" + :key="community" + > + <input + class="form-check-input appearance-none h-4 w-4 border border-gray-300 rounded-sm bg-white checked:bg-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="checkbox" + :checked="isInSelectedCommunity(community.communityId)" + :value="community.communityId" + @change="onChangeCommunity($event)" + /> + <label class="form-check-label inline-block text-gray-800"> + {{ community.name }} + </label> + </div> + </li> + </ul> + </div> + <label class="text-error-medium text-sm block">{{ + communityErrorMessage + }}</label> + </div> + + <!-- price --> + <div + class="mb-6 mt-4" + :class="{ error: v$.updatedItem.price.$errors.length }" + > + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" + id="priceLabel" + >Pris</label + > + <input + type="number" + v-model="v$.updatedItem.price.$model" + id="price" + 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 + /> + + <!-- error message for price --> + <div + class="text-error" + v-for="(error, index) of v$.updatedItem.price.$errors" + :key="index" + > + <div class="text-error-medium text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Description --> + <div + class="mb-6" + :class="{ error: v$.updatedItem.description.$errors.length }" + > + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" + id="descriptionLabel" + >Beskrivelse</label + > + <textarea + id="description" + rows="4" + v-model="v$.updatedItem.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" + required + ></textarea> + + <!-- error message for description --> + <div + class="text-error" + v-for="(error, index) of v$.updatedItem.description.$errors" + :key="index" + > + <div class="text-error-medium text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Address --> + <div class="mb-6" :class="{ error: v$.updatedItem.address.$errors.length }"> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300" + id="addressLabel" + >Adresse</label + > + <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" + v-model="v$.updatedItem.address.$model" + id="adress" + required + /> + + <!-- error message for address--> + <div + class="text-error" + v-for="(error, index) of v$.updatedItem.address.$errors" + :key="index" + > + <div class="text-error-medium text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Images --> + <!-- + <div> + <label + class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-400" + id="imageLabel" + > + Legg til flere bilder (bildene må være .png) + </label> + + <input + type="file" + ref="file" + style="display: none" + @change="addImage" + multiple + accept="image/png" + /> + + <Button :text="'Velg bilde'" @click="$refs.file.click()" /> + + <div v-for="image in updatedItem.images" :key="image" class="m-2"> + <img :src="image" class="w-2/5 inline" alt="Bilde av gjenstanden" /> + </div> + </div> + --> + + <!-- Save item button --> + <div class="float-right"> + <Button :text="'Lagre'" @click="saveClicked" id="saveButton" /> + </div> + </div> +</template> + +<script> +import useVuelidate from "@vuelidate/core"; +import Button from "@/components/BaseComponents/ColoredButton"; +import ListingService from "@/services/listing.service"; +import CommunityService from "@/services/community.service"; +import { parseCurrentUser } from "@/utils/token-utils"; + +import { + required, + helpers, + maxLength, + between, + minLength, +} from "@vuelidate/validators"; + +export default { + name: "EditNewItem", + + components: { + Button, + }, + + setup() { + return { v$: useVuelidate() }; + }, + + validations() { + return { + updatedItem: { + title: { + required: helpers.withMessage( + () => "Tittelen kan ikke være tom", + required + ), + max: helpers.withMessage( + () => `Tittelen kan inneholde max 50 tegn`, + maxLength(50) + ), + }, + description: { + required: helpers.withMessage( + () => "Beskrivelsen kan ikke være tom", + required + ), + max: helpers.withMessage( + () => `Beskrivelsen kan inneholde max 200 tegn`, + maxLength(200) + ), + min: helpers.withMessage( + () => `Beskrivelsen kan ikke være tom`, + minLength(0) + ), + }, + price: { + required, + between: helpers.withMessage( + () => `Leieprisen kan ikke være større enn 25000`, + between(0, 25000) + ), + }, + selectedCategory: { + required: helpers.withMessage(() => `Velg en kategori`, required), + }, + address: { + required: helpers.withMessage( + () => "Addressen kan ikke være tom", + required + ), + max: helpers.withMessage( + () => `Addressen kan inneholde max 50 tegn`, + maxLength(50) + ), + }, + }, + }; + }, + + data() { + return { + updatedItem: { + title: "", + description: "", + address: "", + price: "", + category: "", + selectedCategory: "", + selectedCategories: [], + images: [], + userId: -1, + selectedCommunityId: -1, + selectedCommunities: [], + }, + categories: [ + "Antikviteter og kunst", + "Dyr og utstyr", + "Elektronikk og hvitevarer", + "Foreldre og barn", + "Fritid, hobby og underholdning", + "Hage, oppussing og hus", + "Klær, kosmetikk og tilbehør", + "Møbler og interiør", + "Næringsvirksomhet", + "Sport og friluftsliv", + "Utstyr til bil, båt og MC", + ], + initialItem: {}, + communities: [], + communityErrorMessage: "", + images: [], + }; + }, + + methods: { + checkValidation() { + this.v$.updatedItem.$touch(); + if ( + this.v$.updatedItem.$invalid || + this.updatedItem.selectedCommunities.length === 0 + ) { + if (this.updatedItem.selectedCommunities.length === 0) { + this.communityErrorMessage = "Velg gruppe/grupper"; + } + return false; + } + return true; + }, + + async saveClicked() { + if (this.checkValidation()) { + let itemInfo = { + listingID: parseInt(this.initialItem.listingID), + title: this.updatedItem.title, + description: this.updatedItem.description, + pricePerDay: this.updatedItem.price, + address: this.updatedItem.address, + userID: this.updatedItem.userId, + categoryNames: this.updatedItem.selectedCategories, + communityIDs: this.updatedItem.selectedCommunities, + }; + await ListingService.putItem(itemInfo); + this.$router.push("/itempage/" + this.initialItem.listingID); + } + }, + + addImage(event) { + this.updatedItem.images.push(URL.createObjectURL(event.target.files[0])); + }, + + onChangeCommunity(e) { + this.updatedItem.selectedCommunityId = e.target.value; + let alreadyInGroupList = false; + + for (let i = 0; i <= this.updatedItem.selectedCommunities.length; i++) { + if ( + this.updatedItem.selectedCommunityId == + this.updatedItem.selectedCommunities[i] + ) { + const index = this.updatedItem.selectedCommunities.indexOf( + this.updatedItem.selectedCommunityId + ); + if (index > -1) { + this.updatedItem.selectedCommunities.splice(index, 1); + } + alreadyInGroupList = true; + } + } + + if (!alreadyInGroupList) { + this.updatedItem.selectedCommunities.push( + this.updatedItem.selectedCommunityId + ); + this.communityErrorMessage = ""; + } + }, + + onChangeCategory(e) { + this.updatedItem.selectedCategory = e.target.value; + this.updatedItem.selectedCategories = [e.target.value]; + }, + + isInSelectedCommunity(id) { + for (let i in this.updatedItem.selectedCommunities) { + if (this.updatedItem.selectedCommunities[i] == id) { + return true; + } + } + return false; + }, + }, + + async beforeMount() { + let itemID = await this.$router.currentRoute.value.params.id; + let item = await ListingService.getItem(itemID); + + // Check if user is the owner of the item + let userID = await parseCurrentUser().userId; + if (item.userID == userID) { + this.$router.push(this.$router.options.history.state.back); + } + + this.initialItem = item; + this.communities = await CommunityService.getUserCommunities(); + this.images = await ListingService.getItemPictures(itemID); + + let initialCategories = []; + for (let i in this.initialItem.categoryNames) { + initialCategories.push(this.initialItem.categoryNames[i]); + } + let selectedCategory = + initialCategories.length > 0 ? initialCategories[0] : ""; + + let initialCommunities = []; + for (let i in this.initialItem.communityIDs) { + initialCommunities.push(this.initialItem.communityIDs[i]); + } + + this.updatedItem = { + title: this.initialItem.title, + description: this.initialItem.description, + address: this.initialItem.address, + price: this.initialItem.pricePerDay, + selectedCategories: initialCategories, + selectedCategory: selectedCategory, + images: this.images, + userId: this.initialItem.userID, + selectedCommunityId: 0, + selectedCommunities: initialCommunities, + }; + }, +}; +</script> diff --git a/src/components/ItemComponents/ItemCard.vue b/src/components/ItemComponents/ItemCard.vue index 0d81988d6ae5dca8c620b69c0eadeb7a6fb7cc7f..b8125f4f70bcdfd289c1c5437ee9a31764911793 100644 --- a/src/components/ItemComponents/ItemCard.vue +++ b/src/components/ItemComponents/ItemCard.vue @@ -1,21 +1,23 @@ <template> - <div class="mt-5"> + <div class="mt-5 px-5"> <div - class="w-4/5 rounded bg-gray-200 h-full overflow-hidden display:inline-block correct-size" + class="w-full h-full rounded bg-gray-200 overflow-hidden display:inline-block correct-size" > - <img - class="h-3/4" - :src="item.img || require('../../assets/default-product.png')" - alt="Item image" - /> + <div class="relative h-0 pb-[66.7%]"> + <img + class="w-full h-full absolute inset-0" + :src="item.img || require('../../assets/default-product.png')" + alt="Item image" + /> + </div> <div class="p-1 m-1 bottom-0"> - <p class="text-gray-700 text-xs font-bold" id="adress"> - {{ item.address }} - </p> <p class="font-bold text-sm" id="title">{{ item.title }}</p> <p class="text-gray-700 text-xs" id="price"> {{ item.pricePerDay }} kr </p> + <p class="text-gray-700 text-xs font-bold" id="adress"> + {{ item.address }} + </p> </div> </div> </div> diff --git a/src/components/ItemComponents/NewItemForm.vue b/src/components/ItemComponents/NewItemForm.vue index 244d24434ca753ffd3ecd2f3e76ebd0929fb3dcd..2fd16fe3b736db08e26b37a8a8880ae8e89ae407 100644 --- a/src/components/ItemComponents/NewItemForm.vue +++ b/src/components/ItemComponents/NewItemForm.vue @@ -76,7 +76,7 @@ >Grupper</label > <div - class="overflow-auto w-full h-32 mt-2 text-base list-none bg-white rounded divide-y divide-gray-100 dark:bg-gray-700" + class="overflow-auto w-full max-h-32 mt-2 text-base list-none bg-white rounded divide-y divide-gray-100 dark:bg-gray-700" > <ul class="py-1" aria-labelledby="dropdownDefault"> <li> @@ -203,6 +203,7 @@ <div v-for="image in item.images" :key="image" class="m-2"> <img :src="image" class="w-2/5 inline" alt="Bilde av gjenstanden" /> + <!-- @click="removeImage(image)" --> </div> </div> @@ -311,7 +312,19 @@ export default { imagesToSend: [], }, //Kategorier skal legges inn ved api/hente fra db, her må det endres etterhvert - categories: ["Hage", "Kjøkken", "Musikk", "Annet"], + categories: [ + "Antikviteter og kunst", + "Dyr og utstyr", + "Elektronikk og hvitevarer", + "Foreldre og barn", + "Fritid, hobby og underholdning", + "Hage, oppussing og hus", + "Klær, kosmetikk og tilbehør", + "Møbler og interiør", + "Næringsvirksomhet", + "Sport og friluftsliv", + "Utstyr til bil, båt og MC", + ], groups: [], groupErrorMessage: "", }; @@ -338,7 +351,7 @@ export default { pricePerDay: this.item.price, address: this.item.address, userID: this.item.userId, - categoryNames: [], + categoryNames: [this.item.select], communityIDs: this.item.selectedGroups, }; await postNewItem(itemInfo); @@ -355,8 +368,6 @@ export default { }, addImage: async function (event) { - this.item.images.push(URL.createObjectURL(event.target.files[0])); - var that = this; let image = event.target.files[0]; let fileReader = new FileReader(); @@ -366,6 +377,7 @@ export default { const API_URL = process.env.VUE_APP_BASEURL; that.item.imagesToSend.push(API_URL + "images/" + id); + that.item.images.push(API_URL + "images/" + id); }; fileReader.readAsArrayBuffer(image); }, @@ -393,6 +405,16 @@ export default { this.groupErrorMessage = ""; } }, + + removeImage(image) { + let newImages = []; + for (let i in this.item.images) { + if (this.item.images[i] != image) { + newImages.push(this.item.images[i]); + } + } + this.item.images = newImages; + }, }, beforeMount() { this.getGroups(); diff --git a/src/components/RentingComponents/ImageCarousel.vue b/src/components/RentingComponents/ImageCarousel.vue index aef0e652e6609273c2505025287c152ece3d10d4..4e369460a97d6f256c6757f3017942b34ed4d274 100644 --- a/src/components/RentingComponents/ImageCarousel.vue +++ b/src/components/RentingComponents/ImageCarousel.vue @@ -5,6 +5,7 @@ data-bs-ride="carousel" > <div + v-if="setButtons" class="carousel-indicators absolute right-0 bottom-0 left-0 flex justify-center p-0 mb-4" > <button @@ -27,30 +28,34 @@ <img :src="image.src" class="block w-full" :alt="image.alt" /> </div> </div> - <button - class="carousel-control-prev absolute top-0 bottom-0 flex items-center justify-center p-0 text-center border-0 hover:outline-none hover:no-underline focus:outline-none focus:no-underline left-0" - type="button" - data-bs-target="#carouselIndicators" - data-bs-slide="prev" - > - <span - class="carousel-control-prev-icon inline-block bg-no-repeat" - aria-hidden="true" - ></span> - <span class="visually-hidden">Previous</span> - </button> - <button - class="carousel-control-next absolute top-0 bottom-0 flex items-center justify-center p-0 text-center border-0 hover:outline-none hover:no-underline focus:outline-none focus:no-underline right-0" - type="button" - data-bs-target="#carouselIndicators" - data-bs-slide="next" - > - <span - class="carousel-control-next-icon inline-block bg-no-repeat" - aria-hidden="true" - ></span> - <span class="visually-hidden">Next</span> - </button> + <div v-if="setButtons"> + <button + class="carousel-control-prev absolute top-0 bottom-0 flex items-center justify-center p-0 text-center border-0 hover:outline-none hover:no-underline focus:outline-none focus:no-underline left-0" + type="button" + data-bs-target="#carouselIndicators" + data-bs-slide="prev" + > + <span + class="carousel-control-prev-icon inline-block bg-no-repeat" + aria-hidden="true" + ></span> + <span class="visually-hidden">Previous</span> + </button> + </div> + <div v-if="setButtons"> + <button + class="carousel-control-next absolute top-0 bottom-0 flex items-center justify-center p-0 text-center border-0 hover:outline-none hover:no-underline focus:outline-none focus:no-underline right-0" + type="button" + data-bs-target="#carouselIndicators" + data-bs-slide="next" + > + <span + class="carousel-control-next-icon inline-block bg-no-repeat" + aria-hidden="true" + ></span> + <span class="visually-hidden">Next</span> + </button> + </div> </div> </template> @@ -72,6 +77,11 @@ export default { data() { return {}; }, + computed: { + setButtons() { + return this.images.length > 1; + }, + }, }; </script> diff --git a/src/components/RentingComponents/ItemInfo.vue b/src/components/RentingComponents/ItemInfo.vue index 18f8a558678bf4a038a8d05bcac2a39400a81326..33e0d6595225af5e34e503feac89000bd408d2ca 100644 --- a/src/components/RentingComponents/ItemInfo.vue +++ b/src/components/RentingComponents/ItemInfo.vue @@ -4,12 +4,13 @@ </div> <div v-if="!confirm"> <div> - <div - v-bind:class="{ - 'grid grid-flow-row-dense grid-cols-2 md:grid-cols-4 h-[600px] w-auto lg:grid-cols-5 place-items-center': - noPicture, - }" - > + <div v-if="noPicture" class="md:grid md:place-items-center md:h-screen"> + <img + :src="require('@/assets/default-product.png')" + alt="No image found" + /> + </div> + <div v-else> <ImageCarousel :images="pictures"></ImageCarousel> </div> </div> @@ -64,6 +65,7 @@ <DatepickerRange @value="setDate" :messageOnDisplay="dateMessage" + :blockedDaysRange="nonAvailableTimes" ></DatepickerRange> </p> </div> @@ -90,7 +92,11 @@ <script> import NewRent from "@/components/RentingComponents/NewRent.vue"; -import { getItem, getItemPictures } from "@/utils/apiutil"; +import { + getItem, + getItemPictures, + getAvailableTimesForListing, +} 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"; @@ -133,6 +139,7 @@ export default { totPrice: 0, dateMessage: "Venligst velg dato for leieperioden", allowForRent: false, + nonAvailableTimes: [], }; }, components: { @@ -165,13 +172,7 @@ export default { let id = this.$router.currentRoute.value.params.id; this.images = await getItemPictures(id); - if (this.images.length < 1) { - let noImage = { - src: require("@/assets/default-product.png"), - alt: "No image found", - }; - this.pictures.push(noImage); - } else { + if (this.images.length > 0) { this.noPicture = false; for (let i = 0; i < this.images.length; i++) { let oneImage = { @@ -187,6 +188,20 @@ export default { async getUser(userId) { this.userForId = await UserService.getUserFromId(userId); }, + async getAvailableTimesForListing() { + let datesTakenInMilliseconds = await getAvailableTimesForListing( + this.item.listingID + ); + for (var i = 0; i < datesTakenInMilliseconds.length; i++) { + let oneArray = datesTakenInMilliseconds[i]; + let bigArray = []; + let startDate = new Date(oneArray[0]); + let endDate = new Date(oneArray[1]); + bigArray.push(startDate); + bigArray.push(endDate); + this.nonAvailableTimes.push(bigArray); + } + }, setDate(dateOfsomthing) { if (dateOfsomthing.startDate == null || dateOfsomthing.endDate == null) { this.totPrice = this.item.pricePerDay; @@ -208,6 +223,7 @@ export default { await this.getItemPictures(); await this.getItem(); await this.getUser(this.item.userID); + await this.getAvailableTimesForListing(); }, }; </script> diff --git a/src/components/TimepickerComponents/DatepickerRange/CalendarComponent.vue b/src/components/TimepickerComponents/DatepickerRange/CalendarComponent.vue index 3dc900ba723e3ef0f6c600a186d37810540a0f07..9f3f69394a515cc21c516ee741e216f9dfc0a474 100644 --- a/src/components/TimepickerComponents/DatepickerRange/CalendarComponent.vue +++ b/src/components/TimepickerComponents/DatepickerRange/CalendarComponent.vue @@ -98,8 +98,11 @@ export default { start.getMonth() <= this.monthDate.getMonth() && end.getMonth() >= this.monthDate.getMonth() ) { - if(start.getMonth() === this.monthDate.getMonth() && end.getMonth() === this.monthDate.getMonth()) { - for(let i = start.getDate(); i <= end.getDate(); i++) { + if ( + start.getMonth() === this.monthDate.getMonth() && + end.getMonth() === this.monthDate.getMonth() + ) { + for (let i = start.getDate(); i <= end.getDate(); i++) { blockedDays.push(i); } } else if ( diff --git a/src/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue b/src/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue index 9fe015ba1722a593be7a5b281332d77ac5495918..49fd65dc1249dc57add5e7c73d69a60bca2a1736 100644 --- a/src/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue +++ b/src/components/TimepickerComponents/DatepickerRange/DatepickerRange.vue @@ -54,12 +54,7 @@ export default { }, blockedDaysRange: { type: Array, - default: () => [ - [ - new Date(), - new Date(new Date().setDate(new Date().getDate() + 3)), - ] - ], + default: () => [], }, messageOnDisplay: String, }, diff --git a/src/components/UserAuthComponents/DeleteUserModal.vue b/src/components/UserAuthComponents/DeleteUserModal.vue new file mode 100644 index 0000000000000000000000000000000000000000..fd72ea52de88494162c320db0386e2777ef65ae3 --- /dev/null +++ b/src/components/UserAuthComponents/DeleteUserModal.vue @@ -0,0 +1,59 @@ +<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 items-start justify-between p-4 border-b rounded-t dark:border-gray-600" + > + <h3 class="text-xl font-semibold text-gray-900 dark:text-white"> + Er du helt sikker? + </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"> + <div @click="deleteUser" class="flex justify-center text-xl text-error-dark cursor-pointer">Slett bruker</div> + </div> + </div> + </div> + </div> +</template> + +<script> +export default { + name: "DeleteUserModal", + props: { + visible: Boolean, + }, + methods: { + close() { + this.$emit("close"); + }, + deleteUser(){ + this.$emit("deleteUser"); + }, + }, +}; +</script> diff --git a/src/components/FormComponents/LoginForm.vue b/src/components/UserAuthComponents/LoginForm.vue similarity index 96% rename from src/components/FormComponents/LoginForm.vue rename to src/components/UserAuthComponents/LoginForm.vue index d6fdcad5ebe8158d746a32288ebf12bc60c710ad..add14c8052ca57b452893b4397a02ff5c5fe6066 100644 --- a/src/components/FormComponents/LoginForm.vue +++ b/src/components/UserAuthComponents/LoginForm.vue @@ -94,6 +94,7 @@ import useVuelidate from "@vuelidate/core"; import { required, email, helpers } from "@vuelidate/validators"; import { doLogin } from "@/utils/apiutil"; import Button from "@/components/BaseComponents/ColoredButton"; +import UserService from "@/services/user.service"; export default { name: "LoginForm.vue", @@ -151,6 +152,8 @@ export default { this.message = "Feil e-post/passord"; } else if (loginResponse.isLoggedIn === true) { this.$store.commit("saveToken", loginResponse.token); + const adminList = await UserService.getAdminList(); + this.$store.commit("addAdminList", adminList); await this.$router.push("/"); } }, diff --git a/src/components/FormComponents/NewPasswordForm.vue b/src/components/UserAuthComponents/NewPasswordForm.vue similarity index 100% rename from src/components/FormComponents/NewPasswordForm.vue rename to src/components/UserAuthComponents/NewPasswordForm.vue diff --git a/src/components/FormComponents/RegisterForm.vue b/src/components/UserAuthComponents/RegisterForm.vue similarity index 98% rename from src/components/FormComponents/RegisterForm.vue rename to src/components/UserAuthComponents/RegisterForm.vue index 17cd3cf736afe4e9a3c53d30af6527e7a3d8602a..da8011bdce8f479730d0e8e16b082a09842243ba 100644 --- a/src/components/FormComponents/RegisterForm.vue +++ b/src/components/UserAuthComponents/RegisterForm.vue @@ -175,6 +175,7 @@ import { helpers, } from "@vuelidate/validators"; import Button from "@/components/BaseComponents/ColoredButton"; +import UserService from "@/services/user.service"; // const isEmailTaken = (value) => // fetch(`/api/unique/${value}`).then((r) => r.json()); // check the email in the server @@ -263,8 +264,9 @@ export default { await this.$router.push("/login"); return; } - this.$store.commit("saveToken", loginResponse.token); + const adminList = await UserService.getAdminList(); + this.$store.commit("addAdminList", adminList); await this.$router.push("/"); }, async sendRegisterRequest() { diff --git a/src/components/FormComponents/ResetPasswordForm.vue b/src/components/UserAuthComponents/ResetPasswordForm.vue similarity index 100% rename from src/components/FormComponents/ResetPasswordForm.vue rename to src/components/UserAuthComponents/ResetPasswordForm.vue diff --git a/src/components/UserProfileComponents/UserItems.vue b/src/components/UserProfileComponents/UserItems.vue index 88bd3ce45ce066f67be1739aba37e39c219ca129..418a4f22fa9e684959fe6cfa2d5033c7ce5671c3 100644 --- a/src/components/UserProfileComponents/UserItems.vue +++ b/src/components/UserProfileComponents/UserItems.vue @@ -39,7 +39,11 @@ v-for="item in visibleItems" :key="item" > - <ItemCard class="ItemCard w-fit h-fit" :item="item" /> + <ItemCard + id="ItemCardPage" + class="ItemCard w-fit h-fit" + :item="item" + /> <TripleDotButton class="DotButton" @click="openDotMenu(item)"> </TripleDotButton> @@ -54,7 +58,9 @@ > <li> <button - to="/user/userItems" + @click=" + this.$router.push('/item/' + item.listingID + '/edit') + " 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" > Rediger gjenstand @@ -103,7 +109,7 @@ v-if="showSearchedItems" > <div class="cardContainer" v-for="item in searchedItems" :key="item"> - <ItemCard class="ItemCard" :item="item" /> + <ItemCard id="ItemCardSearch" class="ItemCard" :item="item" /> <TripleDotButton class="DotButton" @click="openDotMenu(item)"> </TripleDotButton> @@ -117,7 +123,9 @@ > <li> <button - to="/user/userItems" + @click=" + this.$router.push('/item/' + item.listingID + '/edit') + " 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" > Rediger gjenstand @@ -268,12 +276,10 @@ export default { } }, goToDeleteItem(item) { - console.log("Halllllo: " + item); this.chosenItem = item; this.readyToDelete = true; }, async deleteItem() { - console.log("HEI " + this.chosenItem); await UserService.setListingToDeleted(this.chosenItem); this.$router.go(0); }, diff --git a/src/components/UserProfileComponents/UserProfile.vue b/src/components/UserProfileComponents/UserProfile.vue index 142f3e612d45c5b27fb4c9172b6513e357cdfb66..b14c8062b29d5dfef4eab56812b539184731f14e 100644 --- a/src/components/UserProfileComponents/UserProfile.vue +++ b/src/components/UserProfileComponents/UserProfile.vue @@ -2,6 +2,8 @@ <div class="w-full max-w-xl m-auto md:ring-1 ring-gray-300 overflow-hidden rounded-xl p-4" > + <DeleteUserModal :visible="show" @close="this.show = false" + @deleteUser="deleteUser"/> <div v-show="isCurrentUser" class="float-right px-4 pt-4"> <button id="dropdownDefault" @@ -68,11 +70,11 @@ > </li> <li> - <router-link - to="" - 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 - > + <div class="block py-2 px-4 text-sm text-error-dark cursor-pointer" + @click="toggleModal" + > + Slett bruker + </div> </li> </ul> </div> @@ -108,6 +110,7 @@ import RatingComponent from "@/components/UserProfileComponents/RatingComponents import { parseCurrentUser } from "@/utils/token-utils"; import { getUser } from "@/utils/apiutil"; import UserService from "@/services/user.service"; +import DeleteUserModal from "@/components/UserAuthComponents/DeleteUserModal"; export default { name: "LargeProfileCard", @@ -123,10 +126,12 @@ export default { profileImage: { src: require("../../assets/defaultUserProfileImage.jpg"), }, + show: false, }; }, components: { RatingComponent, + DeleteUserModal, }, computed: { getProfilePicture() { @@ -135,6 +140,9 @@ export default { } return this.profileImage.src; }, + adminList() { + return this.$store.state.user.adminList; + }, }, methods: { async getUser() { @@ -162,6 +170,13 @@ export default { this.$store.commit("logout"); this.$router.push("/"); }, + toggleModal() { + this.show = !this.show; + }, + async deleteUser(){ + await UserService.deleteUser(); + this.logout() + }, }, beforeMount() { this.getUser(); diff --git a/src/router/index.js b/src/router/index.js index b0a701a8e3e72570a55bb60873802b88f200adfd..9f7228cc993ef163a3a40f9ea84fe3ce57f3bf2d 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -15,6 +15,12 @@ function guardRoute(to, from, next) { } } +function isAdmin(to, from, next) { + if (store.state.user.adminList.includes(parseInt(from.params.communityID))) + next(); + else next("/"); +} + const routes = [ { path: "/", @@ -47,7 +53,7 @@ const routes = [ { path: "/register", name: "register", - component: () => import("../views/FormViews/RegisterView.vue"), + component: () => import("../views/UserAuthViews/RegisterView.vue"), }, { path: "/messages", @@ -58,12 +64,12 @@ const routes = [ { path: "/login", name: "login", - component: () => import("../views/FormViews/LoginView.vue"), + component: () => import("../views/UserAuthViews/LoginView.vue"), }, { path: "/newPassword", name: "newPassword", - component: () => import("../views/FormViews/NewPasswordView"), + component: () => import("../views/UserAuthViews/NewPasswordView"), beforeEnter: guardRoute, }, { @@ -74,7 +80,7 @@ const routes = [ { path: "/resetPassword", name: "resetPassword", - component: () => import("../views/FormViews/ResetPasswordView.vue"), + component: () => import("../views/UserAuthViews/ResetPasswordView.vue"), }, { path: "/newCommunity", @@ -94,13 +100,6 @@ const routes = [ component: () => import("../views/ItemViews/NewItemView.vue"), beforeEnter: guardRoute, }, - { - path: "/notifications", - name: "notifications", - component: () => - import("../components/BaseComponents/NotificationsForm.vue"), - beforeEnter: guardRoute, - }, { path: "/community/:communityID", name: "communityHome", @@ -110,9 +109,9 @@ const routes = [ path: "/community/:communityID/private/join", name: "communityRequest", component: () => import("../views/CommunityViews/CommunityRequestView.vue"), + beforeEnter: guardRoute, }, { - beforeEnter: guardRoute, path: "/test", name: "test", component: () => import("../views/TestView.vue"), @@ -121,7 +120,7 @@ const routes = [ path: "/community/:communityID/admin", name: "CommunityAdminView", component: () => import("@/views/CommunityViews/AdminView.vue"), - beforeEnter: guardRoute, + beforeEnter: isAdmin, }, { path: "/itempage/:id", @@ -129,10 +128,17 @@ const routes = [ component: () => import("../views/RentingViews/ItemInfoPageView.vue"), beforeEnter: guardRoute, }, + { + path: "/item/:id/edit", + name: "editItem", + component: () => import("../views/ItemViews/EditItemView.vue"), + beforeEnter: guardRoute, + }, { path: "/user/userItems", name: "UserItems", component: () => import("../views/UserProfileViews/UserItemsView.vue"), + beforeEnter: guardRoute, }, // Make sure it's your last route definition { path: "/:pathMatch(.*)*", name: "not-found", component: NotFound }, diff --git a/src/services/listing.service.js b/src/services/listing.service.js new file mode 100644 index 0000000000000000000000000000000000000000..d0bfb6ca24e2494ef85e524a3f9bd830a07bc57c --- /dev/null +++ b/src/services/listing.service.js @@ -0,0 +1,45 @@ +import { tokenHeader } from "@/utils/token-utils"; +import axios from "axios"; + +const API_URL = process.env.VUE_APP_BASEURL; + +class ListingService { + async putItem(itemInfo) { + return await axios + .put(API_URL + "listing/change", itemInfo, { + headers: tokenHeader(), + }) + .then((res) => { + return res.data; + }) + .catch((err) => console.error(err)); + } + + async getItem(itemid) { + return await axios + .get(API_URL + "listing/" + itemid, { + headers: tokenHeader(), + }) + .then((response) => { + return response.data; + }) + .catch((error) => { + console.error(error); + }); + } + + async getItemPictures(itemid) { + return await axios + .get(API_URL + "listing/" + itemid + "/pictures", { + headers: tokenHeader(), + }) + .then((response) => { + return response.data; + }) + .catch((error) => { + console.error(error); + }); + } +} + +export default new ListingService(); diff --git a/src/services/user.service.js b/src/services/user.service.js index e2bf48bd4b19f07b3b5f2de07544d1d05a0fc020..b6d908ba08d23bbfc32c6229ff1c093229c57a39 100644 --- a/src/services/user.service.js +++ b/src/services/user.service.js @@ -1,4 +1,3 @@ -// import { tokenHeader } from "@/utils/token-utils"; import { tokenHeader } from "@/utils/token-utils"; import axios from "axios"; @@ -16,6 +15,19 @@ class UserService { .catch((err) => console.error(err)); } + async getAdminList() { + return await axios + .get(API_URL + "communities/admin", { + headers: tokenHeader(), + }) + .then((res) => { + return res.data; + }) + .catch((err) => { + console.error(err); + }); + } + async getUserRatingAverage(userId) { return await axios .get(API_URL + "rating/" + userId + "/average", { @@ -29,13 +41,15 @@ class UserService { async setListingToDeleted(listingId) { return await axios - .put(API_URL + "listing/" + listingId, { + .delete(API_URL + "listing/" + listingId, { headers: tokenHeader(), }) .then((res) => { return res.data; }) - .catch((err) => console.error(err)); + .catch((err) => { + console.error(err); + }); } async getRenterHistory() { @@ -99,5 +113,16 @@ class UserService { }) .catch((err) => console.error(err)); } + + async deleteUser() { + return await axios + .delete(API_URL + "user/delete", { + headers: tokenHeader(), + }) + .then((res) => { + return res.data; + }) + .catch((err) => console.log(err)); + } } export default new UserService(); diff --git a/src/services/ws.js b/src/services/ws.js index 4a8cbbd485ee497068d73d5828a8d2e55cd039d9..233ed883af296c9da8417e1884ca35cba91106ae 100644 --- a/src/services/ws.js +++ b/src/services/ws.js @@ -12,9 +12,8 @@ const ws = (function () { const fire = function (event, data) { if (handlers[event]) { - // for each object in object fire event - for(const key in handlers[event]) { + for (const key in handlers[event]) { handlers[event][key](data); } } @@ -45,9 +44,9 @@ const ws = (function () { return { on: function (event, callback, id = "none") { // Generate random id - if(!handlers[event]) { - handlers[event] = {} - }; + if (!handlers[event]) { + handlers[event] = {}; + } handlers[event][id] = callback; }, fire: fire, diff --git a/src/store/modules/user.js b/src/store/modules/user.js index c4ce2e515e6d57453551ba3c6d80e6376441ab6b..8c3744e58a0c4e387ddb83cfcef1a99efebbff7d 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -1,5 +1,6 @@ const state = { token: null, + adminList: [], }; const mutations = { @@ -9,6 +10,14 @@ const mutations = { saveToken(state, token) { state.token = token; }, + addAdminList(state, communityIDArray) { + if (!Array.isArray(communityIDArray)) return; + if (communityIDArray.length === 0) return; + for (let i = 0; i < communityIDArray.length; i++) { + if (isNaN(communityIDArray[i])) continue; + state.adminList.push(communityIDArray[i]); + } + }, }; export default { diff --git a/src/utils/apiutil.js b/src/utils/apiutil.js index 86b911d547006c86e8c1fece8cc0b7ba0b3be32a..7b42b8219865c3b58a02716b8326923f1946e09d 100644 --- a/src/utils/apiutil.js +++ b/src/utils/apiutil.js @@ -353,3 +353,16 @@ export function PostImagesArrayToListing(imagesArray) { return error; }); } + +export function getAvailableTimesForListing(listingId) { + return axios + .get(API_URL + "listing/" + listingId + "/availability", { + headers: tokenHeader(), + }) + .then((response) => { + return response.data; + }) + .catch((error) => { + console.error(error); + }); +} diff --git a/src/views/CommunityViews/CommunityView.vue b/src/views/CommunityViews/CommunityView.vue index b15181fcaf9b8af58f74958ecbf6fc71c7b4ee38..329917065b0679060da6503891b05c932ebaf18a 100644 --- a/src/views/CommunityViews/CommunityView.vue +++ b/src/views/CommunityViews/CommunityView.vue @@ -8,7 +8,7 @@ <div v-if="loggedIn"> <div class="flex flex-row p-4 relative"> <div - class="text-xl md:text-2xl text-primary-light font-medium w-full" + class="text-xl md:text-2xl text-primary-medium font-medium w-full" > Mine grupper </div> @@ -33,7 +33,7 @@ </div> <!-- Public communities, with search and pagination --> - <p class="text-xl md:text-2xl text-primary-light font-medium w-full p-4"> + <p class="text-xl md:text-2xl text-primary-medium font-medium w-full p-4"> Offentlige grupper </p> <!-- Search field --> diff --git a/src/views/HelpView.vue b/src/views/HelpView.vue index 585749da03358203863f9f07e23a64c86fec264d..343f1f47c4d432352db1c3c670c293ef7c4a1fdf 100644 --- a/src/views/HelpView.vue +++ b/src/views/HelpView.vue @@ -1,16 +1,20 @@ <template> - <div class="mt-6 bg-white justify-center w-screen"> + <div class="mt-10 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"> + <h2 + class="flex justify-center text-primary-dark mb-6 uppercase font-bold text-2xl mt-4" + > Kontakt oss </h2> - <p class="text-gray-500 leading-relaxed mb-9"> + <p + class="flex justify-center mx-auto text-gray-500 leading-relaxed mb-12 max-w-md mt-8" + > {{ contact.description }} </p> </div> - <div class="flex mb-8 ml-2 w-full"> + <div class="flex justify-center mb-12 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" @@ -18,17 +22,17 @@ </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.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="flex justify-center mb-10 w-full pr-11"> <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> + <h4 class="font-bold text-gray-800 text-xl mb-1">E-postadresse</h4> <p>{{ contact.email }}</p> </div> </div> @@ -36,9 +40,9 @@ </div> <div id="faq"> <div - class="mx-auto text-center px-4 text-2xl text-primary-dark font-semibold" + class="mx-auto text-center px-4 mt-8 text-2xl text-primary-dark font-semibold" > - Frequently Asked Questions + Ofte stilte spørsmål </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" @@ -49,7 +53,7 @@ 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"> + <dt id="question" @click="toggle(faqItem)"> <div class="flex justify-between text-gray-800"> <div class="font-bold"> {{ faqItem.question }} @@ -93,7 +97,7 @@ export default { 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", + email: "kontakt@boco.no", address: "O. S. Bragstads Plass 2G", city: "Trondheim", country: "Norge", diff --git a/src/views/ItemViews/EditItemView.vue b/src/views/ItemViews/EditItemView.vue new file mode 100644 index 0000000000000000000000000000000000000000..763ecdafeb6145a16e7518c4630cf1ebdb79e5d0 --- /dev/null +++ b/src/views/ItemViews/EditItemView.vue @@ -0,0 +1,16 @@ +<template> + <div class="h-screen grid md:mt-8"> + <edit-item-form :initialItem="initialItem" :communities="communities" /> + </div> +</template> + +<script> +import EditItemForm from "@/components/ItemComponents/EditItemForm.vue"; + +export default { + name: "EditItemView", + components: { + EditItemForm, + }, +}; +</script> diff --git a/src/views/FormViews/LoginView.vue b/src/views/UserAuthViews/LoginView.vue similarity index 74% rename from src/views/FormViews/LoginView.vue rename to src/views/UserAuthViews/LoginView.vue index ec6fa68a7c9b217e2d199d2bd8b9d8781556343a..6ab7e4ca537206fceb2a2b1628d374b6aee0e8fb 100644 --- a/src/views/FormViews/LoginView.vue +++ b/src/views/UserAuthViews/LoginView.vue @@ -5,7 +5,7 @@ </template> <script> -import LoginForm from "@/components/FormComponents/LoginForm"; +import LoginForm from "@/components/UserAuthComponents/LoginForm"; export default { name: "LoginView.vue", components: { diff --git a/src/views/FormViews/NewPasswordView.vue b/src/views/UserAuthViews/NewPasswordView.vue similarity index 73% rename from src/views/FormViews/NewPasswordView.vue rename to src/views/UserAuthViews/NewPasswordView.vue index 1c08586c58f04a984fa22a5af77219c79907c145..890a8f524b4490c03ba510c85c2dfc429b228143 100644 --- a/src/views/FormViews/NewPasswordView.vue +++ b/src/views/UserAuthViews/NewPasswordView.vue @@ -5,7 +5,7 @@ </template> <script> -import NewPasswordForm from "@/components/FormComponents/NewPasswordForm"; +import NewPasswordForm from "@/components/UserAuthComponents/NewPasswordForm"; export default { name: "NewPasswordView.vue", components: { diff --git a/src/views/FormViews/RegisterView.vue b/src/views/UserAuthViews/RegisterView.vue similarity index 67% rename from src/views/FormViews/RegisterView.vue rename to src/views/UserAuthViews/RegisterView.vue index 93013987e5e31384c4391768e5468a6bc77df35d..0bf15f1143c38c4bf7fd336ca82afb2a302b9073 100644 --- a/src/views/FormViews/RegisterView.vue +++ b/src/views/UserAuthViews/RegisterView.vue @@ -5,7 +5,7 @@ </template> <script> -import RegisterFormComponent from "../../components/FormComponents/RegisterForm.vue"; +import RegisterFormComponent from "../../components/UserAuthComponents/RegisterForm.vue"; export default { components: { diff --git a/src/views/FormViews/ResetPasswordView.vue b/src/views/UserAuthViews/ResetPasswordView.vue similarity index 73% rename from src/views/FormViews/ResetPasswordView.vue rename to src/views/UserAuthViews/ResetPasswordView.vue index 284c4fe3582728f817ad5fb7b6fbb98373dd38dc..3bb12c1738c08a4578fb2d381dd52cb86a7032a3 100644 --- a/src/views/FormViews/ResetPasswordView.vue +++ b/src/views/UserAuthViews/ResetPasswordView.vue @@ -5,7 +5,7 @@ </template> <script> -import ResetPassword from "@/components/FormComponents/ResetPasswordForm"; +import ResetPassword from "@/components/UserAuthComponents/ResetPasswordForm"; export default { name: "ResetPasswordView.vue", components: { diff --git a/tailwind.config.js b/tailwind.config.js index 65380e429009105b923a55266f6b301464b97a88..d8ef3f55c175f5952769a6b41dec929ef3533f4d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -24,7 +24,7 @@ module.exports = { primary: { light: "#306EC1", medium: "#004aad", - dark: "#003884", + dark: "#002B66", }, secondary: { light: "#653273", diff --git a/tests/unit/api-mock-tests/apiutil-image-mock.spec.js b/tests/unit/api-mock-tests/apiutil-image-mock.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..7645767b988db46f412650f5cf20a2ebdfa57ae2 --- /dev/null +++ b/tests/unit/api-mock-tests/apiutil-image-mock.spec.js @@ -0,0 +1,40 @@ +import { + postNewImageCommunity, + PostImagesArrayToListing, +} from "@/utils/apiutil"; +import axios from "axios"; + +jest.mock("axios"); + +describe("testing mocking of apiutil.js image api calls", () => { + it("check that image gets posted", async () => { + let expectedResponse = 1; + + axios.post.mockImplementation(() => + Promise.resolve({ data: expectedResponse }) + ); + + const imageResponse = await postNewImageCommunity("image"); + expect(imageResponse).toBe(expectedResponse); + }); + + it("check that image array gets posted to listing", async () => { + let expectedResponse = "OK"; + + const imageArray = { + image1: { + image: "image1", + }, + image2: { + image2: "image2", + }, + }; + + axios.post.mockImplementation(() => + Promise.resolve({ data: expectedResponse }) + ); + + const imageResponse = await PostImagesArrayToListing(imageArray); + expect(imageResponse.data).toBe(expectedResponse); + }); +}); diff --git a/tests/unit/component-tests/base-component-tests/nav-bar.spec.js b/tests/unit/component-tests/base-component-tests/nav-bar.spec.js index 556dd737972108fccdd326ccea4133af1ddbe377..c728013aea767ae263d383c9f0cf92597874512b 100644 --- a/tests/unit/component-tests/base-component-tests/nav-bar.spec.js +++ b/tests/unit/component-tests/base-component-tests/nav-bar.spec.js @@ -1,11 +1,19 @@ -import { mount } from "@vue/test-utils"; +import { shallowMount } from "@vue/test-utils"; import NavBar from "@/components/BaseComponents/NavBar.vue"; +import { store, $store } from "../../mock-store"; describe("NavBar component", () => { let wrapper; beforeEach(() => { - wrapper = mount(NavBar); + wrapper = shallowMount(NavBar, { + global: { + mocks: { + store, + $store, + }, + }, + }); }); it("is instantiated", () => { 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 6ea43db8f525841f2ff49a8c9a1d45066552348b..4edde89c2c743d9c2411aa4a1a5c1403b7077c70 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 @@ -22,7 +22,7 @@ exports[`CommunityListItem component renders correctly 1`] = ` class="flex-1 pl-1 overflow-hidden" > <div - class="font-medium dark:text-white truncate" + class="font-medium text-gray-800 dark:text-white truncate" > string </div> diff --git a/tests/unit/component-tests/community-component-tests/__snapshots__/community-list.spec.js.snap b/tests/unit/component-tests/community-component-tests/__snapshots__/community-list.spec.js.snap index 52d4de639d8683ee0b575f40d2290c6ad383d554..a385829acb32b11671e1d9788b5b401b95c4d892 100644 --- a/tests/unit/component-tests/community-component-tests/__snapshots__/community-list.spec.js.snap +++ b/tests/unit/component-tests/community-component-tests/__snapshots__/community-list.spec.js.snap @@ -27,7 +27,7 @@ exports[`CommunityList component renders correctly 1`] = ` class="flex-1 pl-1 overflow-hidden" > <div - class="font-medium dark:text-white truncate" + class="font-medium text-gray-800 dark:text-white truncate" > string </div> @@ -78,7 +78,7 @@ exports[`CommunityList component renders correctly 1`] = ` class="flex-1 pl-1 overflow-hidden" > <div - class="font-medium dark:text-white truncate" + class="font-medium text-gray-800 dark:text-white truncate" > string </div> diff --git a/tests/unit/component-tests/community-component-tests/__snapshots__/item-card.spec.js.snap b/tests/unit/component-tests/community-component-tests/__snapshots__/item-card.spec.js.snap index 356fa6b63aa9ece26edbdd78e5d07728036f38b7..9167369f515802cca42dd7be7026189a2fa8c4be 100644 --- a/tests/unit/component-tests/community-component-tests/__snapshots__/item-card.spec.js.snap +++ b/tests/unit/component-tests/community-component-tests/__snapshots__/item-card.spec.js.snap @@ -2,25 +2,23 @@ exports[`ItemCard component renders correctly 1`] = ` <div - class="mt-5" + class="mt-5 px-5" > <div - class="w-4/5 rounded bg-gray-200 h-full overflow-hidden display:inline-block correct-size" + class="w-full h-full rounded bg-gray-200 overflow-hidden display:inline-block correct-size" > - <img - alt="Item image" - class="h-3/4" - src="String" - /> + <div + class="relative h-0 pb-[66.7%]" + > + <img + alt="Item image" + class="w-full h-full absolute inset-0" + src="String" + /> + </div> <div class="p-1 m-1 bottom-0" > - <p - class="text-gray-700 text-xs font-bold" - id="adress" - > - String - </p> <p class="font-bold text-sm" id="title" @@ -33,6 +31,12 @@ exports[`ItemCard component renders correctly 1`] = ` > 0 kr </p> + <p + class="text-gray-700 text-xs font-bold" + id="adress" + > + String + </p> </div> </div> </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 c0152222de3fa62b177a362dde7b4b755e039b5b..0fb93e452e415b69e0155700117307baef0361ef 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 @@ -55,22 +55,57 @@ exports[`NewItemForm component renders correctly 1`] = ` <option class="text-gray-900 text-sm" > - Hage + Antikviteter og kunst </option> <option class="text-gray-900 text-sm" > - Kjøkken + Dyr og utstyr </option> <option class="text-gray-900 text-sm" > - Musikk + Elektronikk og hvitevarer </option> <option class="text-gray-900 text-sm" > - Annet + Foreldre og barn + </option> + <option + class="text-gray-900 text-sm" + > + Fritid, hobby og underholdning + </option> + <option + class="text-gray-900 text-sm" + > + Hage, oppussing og hus + </option> + <option + class="text-gray-900 text-sm" + > + Klær, kosmetikk og tilbehør + </option> + <option + class="text-gray-900 text-sm" + > + Møbler og interiør + </option> + <option + class="text-gray-900 text-sm" + > + Næringsvirksomhet + </option> + <option + class="text-gray-900 text-sm" + > + Sport og friluftsliv + </option> + <option + class="text-gray-900 text-sm" + > + Utstyr til bil, båt og MC </option> </select> @@ -88,7 +123,7 @@ exports[`NewItemForm component renders correctly 1`] = ` Grupper </label> <div - class="overflow-auto w-full h-32 mt-2 text-base list-none bg-white rounded divide-y divide-gray-100 dark:bg-gray-700" + class="overflow-auto w-full max-h-32 mt-2 text-base list-none bg-white rounded divide-y divide-gray-100 dark:bg-gray-700" > <ul aria-labelledby="dropdownDefault" diff --git a/tests/unit/component-tests/time-picker-components-tests/date-range-picker-tests/CalendarComponent.spec.js b/tests/unit/component-tests/time-picker-components-tests/date-range-picker-tests/CalendarComponent.spec.js index 9ed6d65483a684d332ba961c31262b387ec9b1db..e244058b3b8821425bf1a3f582f55bbbf490d4c7 100644 --- a/tests/unit/component-tests/time-picker-components-tests/date-range-picker-tests/CalendarComponent.spec.js +++ b/tests/unit/component-tests/time-picker-components-tests/date-range-picker-tests/CalendarComponent.spec.js @@ -2,44 +2,43 @@ import { shallowMount } from "@vue/test-utils"; import CalendarComponent from "@/components/TimepickerComponents/DatepickerRange/CalendarComponent.vue"; describe("CalendarComponent tests", () => { - let wrapper; - beforeEach(() => { - wrapper = shallowMount(CalendarComponent, { - propsData: { - month: new Date(1651739228545),// May 2022 - blockedDaysRange: [ - [new Date(1651739228545)], // 5 May - [ - new Date(1652733228545), // 16 May - new Date(1652833228545) // 18 May - ]] - } - }); + let wrapper; + beforeEach(() => { + wrapper = shallowMount(CalendarComponent, { + propsData: { + month: new Date(1651739228545), // May 2022 + blockedDaysRange: [ + [new Date(1651739228545)], // 5 May + [ + new Date(1652733228545), // 16 May + new Date(1652833228545), // 18 May + ], + ], + }, }); - - it("Is instansiated", () => { - expect(wrapper.exists()).toBeTruthy(); - }); - - it("Check that all week days are rendered", () => { - expect(wrapper.findAll(".months").length).toBe(7); - }) - - it("Check that the correct amount of days are rendered", () => { - // 31 days in May, 6 for start of week from last month and 5 for end of month - expect(wrapper.find(".daysList").findAll("div").length).toBe(42); - }) - - it("Check select day works", () => { - wrapper.find(".daysList").findAll("div")[7].find("button").trigger("click"); - expect(wrapper.emitted()).toHaveProperty('selectDate') - }) - - it("Test that selecting day outside of month does not work", () => { - // Click on the first day, which is not in the month - wrapper.find(".daysList").findAll("div")[0].find("button").trigger("click"); - expect(wrapper.emitted()).not.toHaveProperty('selectDate') - }) - - -}); \ No newline at end of file + }); + + it("Is instansiated", () => { + expect(wrapper.exists()).toBeTruthy(); + }); + + it("Check that all week days are rendered", () => { + expect(wrapper.findAll(".months").length).toBe(7); + }); + + it("Check that the correct amount of days are rendered", () => { + // 31 days in May, 6 for start of week from last month and 5 for end of month + expect(wrapper.find(".daysList").findAll("div").length).toBe(42); + }); + + it("Check select day works", () => { + wrapper.find(".daysList").findAll("div")[7].find("button").trigger("click"); + expect(wrapper.emitted()).toHaveProperty("selectDate"); + }); + + it("Test that selecting day outside of month does not work", () => { + // Click on the first day, which is not in the month + wrapper.find(".daysList").findAll("div")[0].find("button").trigger("click"); + expect(wrapper.emitted()).not.toHaveProperty("selectDate"); + }); +}); diff --git a/tests/unit/component-tests/time-picker-components-tests/date-range-picker-tests/month-selector-test.spec.js b/tests/unit/component-tests/time-picker-components-tests/date-range-picker-tests/month-selector-test.spec.js index bba2c88846de4db57ea3535c56ff329502b6c366..f5e88fff4fbdeb8ecef45fe39b43e955b572f5a8 100644 --- a/tests/unit/component-tests/time-picker-components-tests/date-range-picker-tests/month-selector-test.spec.js +++ b/tests/unit/component-tests/time-picker-components-tests/date-range-picker-tests/month-selector-test.spec.js @@ -2,45 +2,44 @@ import { shallowMount } from "@vue/test-utils"; import monthSelector from "@/components/TimepickerComponents/DatepickerRange/MonthSelector.vue"; describe("MonthSelector tests", () => { - let wrapper; - beforeEach(() => { - wrapper = shallowMount(monthSelector, { - propsData: { - month: new Date(1651739228545), // 05 May 2022 UTC - type: "type" - } - }); + let wrapper; + beforeEach(() => { + wrapper = shallowMount(monthSelector, { + propsData: { + month: new Date(0), // 01 JAN 1970 UTC + type: "type", + }, }); - - it("Is instansiated", () => { - expect(wrapper.exists()).toBeTruthy(); - }); - - it("Check if fields show correct informations", () => { - // Check if container exists - expect(wrapper.find(".container-c .text")) - const children = wrapper.find(".container-c .text").findAll("div"); - - // Check if there are two children - expect(children.length).toBe(2); - - // Check children content - expect(children[0].text()).toBe("MAY"); - expect(children[1].text()).toBe("2022"); - }); - - it("Check that changing are emitted", async () => { - // Check that there are two buttons - expect(wrapper.findAll(".button").length).toBe(2); - - const buttons = wrapper.findAll(".button"); - console.log(buttons[0].html()); - // Check that the first button goes a month back - await buttons[0].trigger("click"); - expect(wrapper.emitted()).toHaveProperty('back') - - // Check that the second button goes a month forward - await buttons[1].trigger("click"); - expect(wrapper.emitted()).toHaveProperty('forward') - }) -}); \ No newline at end of file + }); + + it("Is instansiated", () => { + expect(wrapper.exists()).toBeTruthy(); + }); + + it("Check if fields show correct informations", () => { + // Check if container exists + expect(wrapper.find(".container-c .text")); + const children = wrapper.find(".container-c .text").findAll("div"); + + // Check if there are two children + expect(children.length).toBe(2); + + // Check children content + expect(children[0].text()).toBe("JAN"); + expect(children[1].text()).toBe("1970"); + }); + + it("Check that changing are emitted", async () => { + // Check that there are two buttons + expect(wrapper.findAll(".button").length).toBe(2); + + const buttons = wrapper.findAll(".button"); + // Check that the first button goes a month back + await buttons[0].trigger("click"); + expect(wrapper.emitted()).toHaveProperty("back"); + + // Check that the second button goes a month forward + await buttons[1].trigger("click"); + expect(wrapper.emitted()).toHaveProperty("forward"); + }); +}); diff --git a/tests/unit/component-tests/user-component-tests/__snapshots__/user-items.spec.js.snap b/tests/unit/component-tests/user-component-tests/__snapshots__/user-items.spec.js.snap new file mode 100644 index 0000000000000000000000000000000000000000..db5d7bf26553750dd6c9f3789f9541ffc2d2f071 --- /dev/null +++ b/tests/unit/component-tests/user-component-tests/__snapshots__/user-items.spec.js.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UserItems component renders correctly 1`] = ` +<div + data-v-app="" +> + + <div + class="text-xl md:text-2xl text-primary-light font-medium" + id="headline" + > + Mine gjenstander + </div> + <!-- Search field --> + <div + class="relative" + id="searchComponent" + > + <span + class="absolute inset-y-0 left-0 flex items-center pl-3" + > + <svg + class="w-5 h-5 text-gray-400" + fill="none" + viewBox="0 0 24 24" + > + <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-linecap="round" + stroke-linejoin="round" + stroke-width="2" + /> + </svg> + </span> + <input + class="w-full py-3 pl-10 pr-4 text-gray-700 bg-white border rounded-md dark:bg-gray-800 dark:text-gray-300 dark:border-gray-600 focus:border-primary-medium dark:focus:border-primary-medium focus:outline-none focus:ring" + id="searchInput" + placeholder="Search" + type="text" + /> + </div> + <div + class="absolute inset-x-0 px-5 py-3" + > + <!-- ItemCards --> + <div + class="flex items-center justify-center w-screen" + > + <!-- Shows items based on pagination --> + <div + class="grid grid-flow-row-dense grid-cols-2 md:grid-cols-4 lg:grid-cols-5 w-full" + > + + + + <!-- Main modal --> + <!--v-if--> + + </div> + <!-- Shows items based on search field input --> + <!--v-if--> + </div> + <!-- pagination --> + <div + class="flex justify-center" + > + <div + class="mt-10" + > + <!--v-if--> + <label + class="mx-2 text-primary-light" + > + 1 av 1 + </label> + <!--v-if--> + </div> + </div> + </div> + +</div> +`; diff --git a/tests/unit/component-tests/user-component-tests/login-form.spec.js b/tests/unit/component-tests/user-component-tests/login-form.spec.js index 40fa711393e969435a609474c0f550f179513135..bb0285db4c868fa9acc66080b46d0fa184cceb7e 100644 --- a/tests/unit/component-tests/user-component-tests/login-form.spec.js +++ b/tests/unit/component-tests/user-component-tests/login-form.spec.js @@ -1,5 +1,5 @@ import { mount } from "@vue/test-utils"; -import LoginForm from "@/components/FormComponents/LoginForm.vue"; +import LoginForm from "@/components/UserAuthComponents/LoginForm.vue"; describe("LoginForm component", () => { let wrapper; diff --git a/tests/unit/component-tests/user-component-tests/new-password-form.spec.js b/tests/unit/component-tests/user-component-tests/new-password-form.spec.js index 638e597bd9b130a6b2a1c69a0b724b3414436648..3d52388c9b35a953ceca35a27a87c578f603135e 100644 --- a/tests/unit/component-tests/user-component-tests/new-password-form.spec.js +++ b/tests/unit/component-tests/user-component-tests/new-password-form.spec.js @@ -1,5 +1,5 @@ import { mount } from "@vue/test-utils"; -import NewPasswordForm from "@/components/FormComponents/NewPasswordForm.vue"; +import NewPasswordForm from "@/components/UserAuthComponents/NewPasswordForm.vue"; describe("NewPasswordForm component", () => { let wrapper; diff --git a/tests/unit/component-tests/user-component-tests/register-user-component.spec.js b/tests/unit/component-tests/user-component-tests/register-user-component.spec.js index 9accd167543e4b2291397821c4aeaedf42804313..4c2ca3b84153c1a1f12b75765948b59a59dc6262 100644 --- a/tests/unit/component-tests/user-component-tests/register-user-component.spec.js +++ b/tests/unit/component-tests/user-component-tests/register-user-component.spec.js @@ -1,5 +1,5 @@ import { mount } from "@vue/test-utils"; -import RegisterFormComponent from "@/components/FormComponents/RegisterForm"; +import RegisterFormComponent from "@/components/UserAuthComponents/RegisterForm"; describe("RegisterFormComponent", () => { let wrapper; diff --git a/tests/unit/component-tests/user-component-tests/reset-password-form.spec.js b/tests/unit/component-tests/user-component-tests/reset-password-form.spec.js index 1dc9f0a4f379129d888436a42d40bf354ae56f66..45f67cef373d59a5d51850d424378a3af218490c 100644 --- a/tests/unit/component-tests/user-component-tests/reset-password-form.spec.js +++ b/tests/unit/component-tests/user-component-tests/reset-password-form.spec.js @@ -1,5 +1,5 @@ import { mount } from "@vue/test-utils"; -import ResetPasswordForm from "@/components/FormComponents/ResetPasswordForm.vue"; +import ResetPasswordForm from "@/components/UserAuthComponents/ResetPasswordForm.vue"; describe("ResetPasswordForm component", () => { let wrapper; diff --git a/tests/unit/component-tests/user-component-tests/user-items.spec.js b/tests/unit/component-tests/user-component-tests/user-items.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..7640696dee98524a786e6f059aa1520e6934a429 --- /dev/null +++ b/tests/unit/component-tests/user-component-tests/user-items.spec.js @@ -0,0 +1,37 @@ +import { mount } from "@vue/test-utils"; +import UserItems from "@/components/UserProfileComponents/UserItems.vue"; + +describe("UserItems component", () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(UserItems, { + data() { + return { + items: [ + { + listingID: 1, + img: "", + address: "Veien", + title: "Matboks", + pricePerDay: 50, + toggle: false, + }, + ], + }; + }, + }); + }); + + it("renders correctly", () => { + expect(wrapper.element).toMatchSnapshot(); + }); + + it("is instantiated", () => { + expect(wrapper.exists()).toBeTruthy(); + }); + + it("Check headline", () => { + expect(wrapper.find("#headline").text()).toMatch("Mine gjenstander"); + }); +});