diff --git a/src/components/AddNewItem.vue b/src/components/AddNewItem.vue new file mode 100644 index 0000000000000000000000000000000000000000..cfe5ea645bcbfd2cff19a9a68d46499547f45e4c --- /dev/null +++ b/src/components/AddNewItem.vue @@ -0,0 +1,269 @@ +<template> + <div class="m-6"> + <!-- Component heading --> + <div class="flex justify-center"> + <p class="text-4xl mb-6 mt-6">Utleie</p> + </div> + + <!-- Title --> + <div class="mb-6" :class="{ error: v$.item.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="bg-gray-200 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + v-model="v$.item.title.$model" + required + /> + + <!-- error message for title--> + <div + class="text-red" + v-for="(error, index) of v$.item.title.$errors" + :key="index" + > + <div class="text-red-600 text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Select 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 + v-model="v$.item.select.$model" + id="categories" + class="bg-gray-200 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + > + <option class="text-gray-400" value="" disabled selected> + Select a category + </option> + <option + v-for="category in categories" + :key="category" + class="text-gray-900 text-sm" + > + {{ category }} + </option> + </select> + + <!-- error message for select box --> + <div + class="text-red" + v-for="(error, index) of v$.item.select.$errors" + :key="index" + > + <div class="text-red-600 text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- price --> + <div class="mb-6" :class="{ error: v$.item.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$.item.price.$model" + id="price" + class="bg-gray-200 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + required + /> + + <!-- error message for price --> + <div + class="text-red" + v-for="(error, index) of v$.item.price.$errors" + :key="index" + > + <div class="text-red-600 text-sm"> + {{ error.$message }} + </div> + </div> + </div> + + <!-- Description --> + <div class="mb-6" :class="{ error: v$.item.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$.item.description.$model" + class="block p-2.5 w-full text-sm text-gray-900 bg-gray-200 rounded-lg border border-gray-300 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" + required + ></textarea> + + <!-- error message for description --> + <div + class="text-red" + v-for="(error, index) of v$.item.description.$errors" + :key="index" + > + <div class="text-red-600 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" + > + Bilder + </label> + + <input + type="file" + ref="file" + style="display: none" + @change="addImage" + multiple + accept="image/png, image/jpeg" + /> + + <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" + > + Velg bilde + </button> + + <div v-for="image in item.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="flex justify-center"> + <button + @click="saveClicked" + class="content-center text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm sm:w-auto px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" + id="saveButton" + > + Lagre + </button> + </div> + </div> +</template> + +<script> +import useVuelidate from "@vuelidate/core"; +import { + required, + helpers, + maxLength, + between, + minLength, +} from "@vuelidate/validators"; + +export default { + name: "AddNewItem", + + setup() { + return { v$: useVuelidate() }; + }, + + validations() { + return { + item: { + title: { + required, + max: helpers.withMessage( + () => `Tittelen kan inneholde max 50 tegn`, + maxLength(50) + ), + }, + description: { + 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) + ), + }, + select: { + required: helpers.withMessage(() => `Velg en kategori`, required), + }, + }, + }; + }, + + data() { + return { + item: { + title: "", + description: "", + price: "", + category: "", + select: null, + type: "", + images: [], + }, + //Kategorier skal legges inn ved api/hente fra db, her må det endres etterhvert + categories: ["Hage", "Kjøkken", "Musikk", "Annet"], + }; + }, + methods: { + checkValidation: function () { + console.log("sjekker validering"); + + this.v$.item.$touch(); + if (this.v$.item.$invalid) { + 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..."); + console.log("Tittel: " + this.item.title); + console.log("Kategori: " + this.item.select); + console.log("Beskrivelse: " + this.item.description); + console.log("Pris: " + this.item.price); + console.log("bilder: " + this.item.images); + } + }, + + addImage: function (event) { + console.log(event.target.files); + this.item.images.push(URL.createObjectURL(event.target.files[0])); + console.log("antall bilder: " + this.item.images.length); + }, + }, +}; +</script> diff --git a/src/router/index.js b/src/router/index.js index cb3eca7e5476af3a0d4f8889ac6e31f4d5f5eb5a..44952f13e32ea6a00b992f9b4465bc80a84569b0 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -43,7 +43,6 @@ const routes = [ component: NewPasswordView, }, { - path: "/searchItemList", name: "searchItemList", component: () => import("../views/SearchItemListView.vue"), @@ -52,7 +51,11 @@ const routes = [ path: "/createNewGroup", name: "createNewGroup", component: () => import("../views/CreateNewGroupView.vue"), - + }, + { + path: "/addNewItem", + name: "addNewItem", + component: () => import("../views/AddNewItemView.vue"), }, ]; diff --git a/src/views/AddNewItemView.vue b/src/views/AddNewItemView.vue new file mode 100644 index 0000000000000000000000000000000000000000..2724eed8bff21b81d628b0b64715a22026e23568 --- /dev/null +++ b/src/views/AddNewItemView.vue @@ -0,0 +1,16 @@ +<template> + <AddNewItem></AddNewItem> +</template> + +<script> +import AddNewItem from "@/components/AddNewItem"; + +export default { + name: "AddNewItemView.vue", + components: { + AddNewItem, + }, +}; +</script> + +<style scoped></style> diff --git a/tests/unit/add-new-item.spec.js b/tests/unit/add-new-item.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..930eceba9362b86b5cc0b664dad1b82af987a467 --- /dev/null +++ b/tests/unit/add-new-item.spec.js @@ -0,0 +1,36 @@ +import { shallowMount } from "@vue/test-utils"; +import AddNewItem from "@/components/AddNewItem.vue"; + +describe("addNewItem elements rendering", () => { + it("renders all labels", () => { + const wrapper = shallowMount(AddNewItem); + + expect(wrapper.find("#titleLabel").text()).toMatch("Tittel"); + expect(wrapper.find("#selectCategoryLabel").text()).toMatch("Kategori"); + expect(wrapper.find("#priceLabel").text()).toMatch("Pris"); + expect(wrapper.find("#descriptionLabel").text()).toMatch("Beskrivelse"); + expect(wrapper.find("#imageLabel").text()).toMatch("Bilde"); + }); + + it("Tests setting values of input field", async () => { + const wrapper = shallowMount(AddNewItem); + + const titleInput = wrapper.find("#title"); + await titleInput.setValue("Dyson"); + expect(titleInput.element.value).toBe("Dyson"); + + const selectedCategory = wrapper.find("#categories"); + await selectedCategory.setValue("Hage"); + expect(selectedCategory.element.value).toBe("Hage"); + + const priceInput = wrapper.find("#price"); + await priceInput.setValue(500); + expect(priceInput.element.value).toBe("500"); + + const descriptionInput = wrapper.find("#description"); + await descriptionInput.setValue("Dette er en støvsuer fra Dyson"); + expect(descriptionInput.element.value).toBe( + "Dette er en støvsuer fra Dyson" + ); + }); +});