Skip to content
Snippets Groups Projects
EditItemForm.vue 14.8 KiB
Newer Older
Gilgard's avatar
Gilgard committed
<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
Gilgard's avatar
Gilgard committed
        @change="onChangeCategory($event)"
Gilgard's avatar
Gilgard committed
        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
Gilgard's avatar
Gilgard committed
          :value="category"
Gilgard's avatar
Gilgard committed
          :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
Gilgard's avatar
Gilgard committed
        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"
Gilgard's avatar
Gilgard committed
      >
        <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"
      >
Gilgard's avatar
Gilgard committed
        Legg til flere bilder (bildene må være .png)
Gilgard's avatar
Gilgard committed
      </label>

      <input
        type="file"
        ref="file"
        style="display: none"
        @change="addImage"
Gilgard's avatar
Gilgard committed
        accept="image/png"
Gilgard's avatar
Gilgard committed
      <ColoredButton :text="'Velg bilde'" @click="$refs.file.click()" />
Gilgard's avatar
Gilgard committed

Gilgard's avatar
Gilgard committed
      <div v-for="image in updatedItem.images" :key="image" class="m-2">
        <form-image-display :image="image" @remove="removeImage(image)" />
Gilgard's avatar
Gilgard committed
      </div>
    </div>

    <!-- Save item button -->
    <div class="float-right">
Gilgard's avatar
Gilgard committed
      <ColoredButton :text="'Lagre'" @click="saveClicked" id="saveButton" />
Gilgard's avatar
Gilgard committed
    </div>
  </div>
</template>

<script>
import useVuelidate from "@vuelidate/core";
Gilgard's avatar
Gilgard committed
import ColoredButton from "@/components/BaseComponents/ColoredButton";
Gilgard's avatar
Gilgard committed
import FormImageDisplay from "@/components/BaseComponents/FormImageDisplay.vue";
Gilgard's avatar
Gilgard committed
import ListingService from "@/services/listing.service";
import CommunityService from "@/services/community.service";
Gilgard's avatar
Gilgard committed
import ImageService from "@/services/image.service";
Gilgard's avatar
Gilgard committed
import { parseCurrentUser } from "@/utils/token-utils";

import {
  required,
  helpers,
  maxLength,
  between,
  minLength,
} from "@vuelidate/validators";

export default {
  name: "EditNewItem",

  components: {
Gilgard's avatar
Gilgard committed
    ColoredButton,
Gilgard's avatar
Gilgard committed
    FormImageDisplay,
Gilgard's avatar
Gilgard committed
  },

  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: [],
        userId: -1,
        selectedCommunityId: -1,
        selectedCommunities: [],
Gilgard's avatar
Gilgard committed
        images: [],
Gilgard's avatar
Gilgard committed
      },
      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);
Gilgard's avatar
Gilgard committed
        await ImageService.putListingImages(
          this.initialItem.listingID,
          this.updatedItem.images
        );
Gilgard's avatar
Gilgard committed
        this.$router.push("/itempage/" + this.initialItem.listingID);
      }
    },

Gilgard's avatar
Gilgard committed
    async addImage(event) {
      var that = this;
      let image = event.target.files[0];
      let fileReader = new FileReader();
      fileReader.onloadend = async function () {
        const res = fileReader.result;
        const id = await ImageService.postNewImage(res);

        const API_URL = process.env.VUE_APP_BASEURL;
Gilgard's avatar
Gilgard committed
        that.updatedItem.images.push(API_URL + "images/" + id);
Gilgard's avatar
Gilgard committed
      };
      fileReader.readAsArrayBuffer(image);
Gilgard's avatar
Gilgard committed
    },

    onChangeCommunity(e) {
Gilgard's avatar
Gilgard committed
      this.updatedItem.selectedCommunityId = e.target.value;
Gilgard's avatar
Gilgard committed
      let alreadyInGroupList = false;

      for (let i = 0; i <= this.updatedItem.selectedCommunities.length; i++) {
        if (
Gilgard's avatar
Gilgard committed
          this.updatedItem.selectedCommunityId ==
          this.updatedItem.selectedCommunities[i]
Gilgard's avatar
Gilgard committed
        ) {
          const index = this.updatedItem.selectedCommunities.indexOf(
Gilgard's avatar
Gilgard committed
            this.updatedItem.selectedCommunityId
Gilgard's avatar
Gilgard committed
          );
          if (index > -1) {
Gilgard's avatar
Gilgard committed
            this.updatedItem.selectedCommunities.splice(index, 1);
Gilgard's avatar
Gilgard committed
          }
          alreadyInGroupList = true;
        }
      }

      if (!alreadyInGroupList) {
Gilgard's avatar
Gilgard committed
        this.updatedItem.selectedCommunities.push(
          this.updatedItem.selectedCommunityId
        );
Gilgard's avatar
Gilgard committed
        this.communityErrorMessage = "";
      }
    },

Gilgard's avatar
Gilgard committed
    onChangeCategory(e) {
      this.updatedItem.selectedCategory = e.target.value;
      this.updatedItem.selectedCategories = [e.target.value];
    },

Gilgard's avatar
Gilgard committed
    isInSelectedCommunity(id) {
      for (let i in this.updatedItem.selectedCommunities) {
        if (this.updatedItem.selectedCommunities[i] == id) {
          return true;
        }
      }
      return false;
    },
Gilgard's avatar
Gilgard committed
    async removeImage(image) {
Gilgard's avatar
Gilgard committed
      let newImages = [];
Gilgard's avatar
Gilgard committed
      for (let i in this.updatedItem.images) {
        if (this.updatedItem.images[i] != image) {
Gilgard's avatar
Gilgard committed
          newImages.push(this.images[i]);
Gilgard's avatar
Gilgard committed
      this.updatedItem.images = newImages;
Gilgard's avatar
Gilgard committed
    },
Gilgard's avatar
Gilgard committed
  async beforeCreate() {
Gilgard's avatar
Gilgard committed
    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();
Gilgard's avatar
Gilgard committed

Gilgard's avatar
Gilgard committed
    this.images = await ListingService.getItemPictures(itemID);
Gilgard's avatar
Gilgard committed
    let imageURLS = [];
    for (let i in this.images) {
      imageURLS.push(this.images[i].picture);
    }
Gilgard's avatar
Gilgard committed

    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,
Gilgard's avatar
Gilgard committed
      images: imageURLS,
Gilgard's avatar
Gilgard committed
      userId: this.initialItem.userID,
      selectedCommunityId: 0,
      selectedCommunities: initialCommunities,
    };
  },
};
</script>