|
@@ -0,0 +1,301 @@
|
|
|
|
+
|
|
|
|
+let eventBus = new Vue(
|
|
|
|
+);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Vue.component('product', {
|
|
|
|
+ props: {
|
|
|
|
+ premium: {
|
|
|
|
+ type: Boolean,
|
|
|
|
+ required: true
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ template: `
|
|
|
|
+ <div class="product">
|
|
|
|
+ <div class="product-image">
|
|
|
|
+ <img :src="image" :alt="altText"/>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ <div class="product-info">
|
|
|
|
+ <h1>{{ title }}</h1>
|
|
|
|
+
|
|
|
|
+ <p v-if="inStock">In stock</p>
|
|
|
|
+ <p v-else>Out of Stock</p>
|
|
|
|
+
|
|
|
|
+ <p>Shipping: {{ shipping }}</p>
|
|
|
|
+
|
|
|
|
+ <product-details></product-details>
|
|
|
|
+
|
|
|
|
+ <div
|
|
|
|
+ class="color-box"
|
|
|
|
+ v-for="(variant, index) in variants"
|
|
|
|
+ :key="variant.variantId"
|
|
|
|
+ :style="{ backgroundColor:variant.variantColor }"
|
|
|
|
+ @mouseover="updateProduct(index)"
|
|
|
|
+ ></div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ <button
|
|
|
|
+ @click="addToCart"
|
|
|
|
+ :disabled="!inStock"
|
|
|
|
+ :class="{ disabledButton: !inStock }"
|
|
|
|
+ >
|
|
|
|
+ Add to cart
|
|
|
|
+ </button>
|
|
|
|
+
|
|
|
|
+ <button
|
|
|
|
+ @click="removeToCart"
|
|
|
|
+ :disabled="!inStock"
|
|
|
|
+ :class="{ disabledButton: !inStock }"
|
|
|
|
+ >
|
|
|
|
+ remove to cart
|
|
|
|
+ </button>
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+<product-tabs :reviews="reviews"></product-tabs>
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ </div>
|
|
|
|
+ `,
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ product: "Socks",
|
|
|
|
+ reviews: [],
|
|
|
|
+ brand: 'Vue Mastery',
|
|
|
|
+ selectedVariant: 0,
|
|
|
|
+ altText: "A pair of socks",
|
|
|
|
+ details: ['80% cotton', '20% polyester', 'Gender-neutral'],
|
|
|
|
+ variants: [
|
|
|
|
+ {
|
|
|
|
+ variantId: 2234,
|
|
|
|
+ variantColor: 'green',
|
|
|
|
+ variantImage: "./assets/vmSocks-green-onWhite.jpg",
|
|
|
|
+ variantQuantity: 10
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ variantId: 2235,
|
|
|
|
+ variantColor: 'blue',
|
|
|
|
+ variantImage: "./assets/vmSocks-blue-onWhite.jpg",
|
|
|
|
+ variantQuantity: 0
|
|
|
|
+ }
|
|
|
|
+ ],
|
|
|
|
+ cart: 0
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ mounted() {
|
|
|
|
+ eventBus.$on('review-submitted', productReview => {
|
|
|
|
+ this.reviews.push(productReview)
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ methods: {
|
|
|
|
+ addToCart() {
|
|
|
|
+ this.$emit('add-to-cart', this.variants[this.selectedVariant].variantId);
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ removeToCart() {
|
|
|
|
+ this.$emit('remove-to-cart', this.variants[this.selectedVariant].variantId);
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ updateProduct(index) {
|
|
|
|
+ this.selectedVariant = index;
|
|
|
|
+ console.log(index);
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ computed: {
|
|
|
|
+ title() {
|
|
|
|
+ return this.brand + ' ' + this.product;
|
|
|
|
+ },
|
|
|
|
+ image() {
|
|
|
|
+ return this.variants[this.selectedVariant].variantImage;
|
|
|
|
+ },
|
|
|
|
+ inStock() {
|
|
|
|
+ return this.variants[this.selectedVariant].variantQuantity
|
|
|
|
+ },
|
|
|
|
+ shipping() {
|
|
|
|
+ if (this.premium) {
|
|
|
|
+ return "Free";
|
|
|
|
+ } else {
|
|
|
|
+ return 2.99
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+Vue.component('product-details', {
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ details: ['80% cotton', '20% polyester', 'Gender-neutral'],
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ },
|
|
|
|
+ template: `
|
|
|
|
+ <ul>
|
|
|
|
+ <li v-for="detail in details">{{ detail }}</li>
|
|
|
|
+ </ul>
|
|
|
|
+ `
|
|
|
|
+ ,
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Vue.component('product-review', {
|
|
|
|
+ template: `
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ <form class="review-form" @submit.prevent="onSubmit">
|
|
|
|
+
|
|
|
|
+ <p v-if="errors.length">
|
|
|
|
+ <b>Please correct the following error(s):</b>
|
|
|
|
+ <ul>
|
|
|
|
+ <li v-for="error in errors">{{ error }}</li>
|
|
|
|
+ </ul>
|
|
|
|
+</p>
|
|
|
|
+
|
|
|
|
+ <p>
|
|
|
|
+ <label for="name">Name:</label>
|
|
|
|
+ <input id="name" v-model="name" placeholder="name">
|
|
|
|
+ </p>
|
|
|
|
+ <p>
|
|
|
|
+ <label for="name">Would you recommend this product?:</label>
|
|
|
|
+ <input id="name" v-model="radio" type="radio" value="yes" placeholder="no">
|
|
|
|
+ <input id="name" v-model="radio" type="radio" value="no" placeholder="yes">
|
|
|
|
+ </p>
|
|
|
|
+
|
|
|
|
+ <p>
|
|
|
|
+ <label for="review">Review:</label>
|
|
|
|
+ <textarea id="review" v-model="review"></textarea>
|
|
|
|
+ </p>
|
|
|
|
+
|
|
|
|
+ <p>
|
|
|
|
+ <label for="rating">Rating:</label>
|
|
|
|
+ <select id="rating" v-model.number="rating">
|
|
|
|
+ <option>5</option>
|
|
|
|
+ <option>4</option>
|
|
|
|
+ <option>3</option>
|
|
|
|
+ <option>2</option>
|
|
|
|
+ <option>1</option>
|
|
|
|
+ </select>
|
|
|
|
+ </p>
|
|
|
|
+
|
|
|
|
+ <p>
|
|
|
|
+ <input type="submit" value="Submit">
|
|
|
|
+ </p>
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ </form>
|
|
|
|
+
|
|
|
|
+ `,
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ name: null,
|
|
|
|
+ review: null,
|
|
|
|
+ rating: null,
|
|
|
|
+ radio: null,
|
|
|
|
+ errors: []
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+ methods: {
|
|
|
|
+ onSubmit() {
|
|
|
|
+ if(this.name && this.review && this.rating&& this.radio) {
|
|
|
|
+ let productReview = {
|
|
|
|
+ name: this.name,
|
|
|
|
+ review: this.review,
|
|
|
|
+ rating: this.rating,
|
|
|
|
+ radio: this.radio,
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ eventBus.$emit('review-submitted', productReview)
|
|
|
|
+
|
|
|
|
+ this.name = null
|
|
|
|
+ this.review = null
|
|
|
|
+ this.rating = null
|
|
|
|
+ this.radio = null
|
|
|
|
+ } else {
|
|
|
|
+ if(!this.name) this.errors.push("Name required.")
|
|
|
|
+ if(!this.review) this.errors.push("Review required.")
|
|
|
|
+ if(!this.rating) this.errors.push("Rating required.")
|
|
|
|
+ if(!this.radio) this.errors.push("Radio required.")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+Vue.component('product-tabs', {
|
|
|
|
+ props: {
|
|
|
|
+ reviews: {
|
|
|
|
+ type: Array,
|
|
|
|
+ required: false
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ template: `
|
|
|
|
+ <div>
|
|
|
|
+ <ul>
|
|
|
|
+ <span class="tab"
|
|
|
|
+ :class="{ activeTab: selectedTab === tab }"
|
|
|
|
+ v-for="(tab, index) in tabs"
|
|
|
|
+ @click="selectedTab = tab"
|
|
|
|
+ >{{ tab }}</span>
|
|
|
|
+ </ul>
|
|
|
|
+ <div v-show="selectedTab === 'Reviews'">
|
|
|
|
+ <p v-if="!reviews.length">There are no reviews yet.</p>
|
|
|
|
+ <ul>
|
|
|
|
+ <li v-for="review in reviews">
|
|
|
|
+ <p>{{ review.name }}</p>
|
|
|
|
+ <p>Rating: {{ review.rating }}</p>
|
|
|
|
+ <p>{{ review.review }}</p>
|
|
|
|
+ </li>
|
|
|
|
+ </ul>
|
|
|
|
+ </div>
|
|
|
|
+ <div v-show="selectedTab === 'Make a Review'">
|
|
|
|
+ <product-review></product-review>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+
|
|
|
|
+ `,
|
|
|
|
+ data() {
|
|
|
|
+ return {
|
|
|
|
+ tabs: ['Reviews', 'Make a Review'],
|
|
|
|
+ selectedTab: 'Reviews' // устанавливается с помощью @click
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+let app = new Vue({
|
|
|
|
+ el: '#app',
|
|
|
|
+ data: {
|
|
|
|
+ premium: true,
|
|
|
|
+ cart: []
|
|
|
|
+ },
|
|
|
|
+ methods: {
|
|
|
|
+ updateCart(id) {
|
|
|
|
+ this.cart.push(id);
|
|
|
|
+ },
|
|
|
|
+ removeCart(id) {
|
|
|
|
+ let i = this.cart.indexOf(id);
|
|
|
|
+ this.cart.splice(i, 1);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|