From 9b77f9ec2c00b48c551f65b2e9d7a087004de4c0 Mon Sep 17 00:00:00 2001
From: "Adam T. Carpenter" <atc@53hor.net>
Date: Thu, 12 Nov 2020 16:27:13 -0500
Subject: Noice. Product creation and updating is totally functional.

---
 .../src/components/admin/ProductEditCard.vue       | 229 +++++++++++++--------
 1 file changed, 143 insertions(+), 86 deletions(-)

(limited to 'iridescence/src/components/admin/ProductEditCard.vue')

diff --git a/iridescence/src/components/admin/ProductEditCard.vue b/iridescence/src/components/admin/ProductEditCard.vue
index a8117ce..aced39c 100644
--- a/iridescence/src/components/admin/ProductEditCard.vue
+++ b/iridescence/src/components/admin/ProductEditCard.vue
@@ -2,17 +2,38 @@
   <div id="productEditCard">
     <div class="card">
       <div class="card-header">
-        <p class="card-header-title" v-if="currentProduct.id">
-          {{ currentProduct.id }}: {{ currentProduct.name }}
+        <p class="card-header-title" v-if="old.id">
+          {{ old.id }}: {{ old.name }}
         </p>
       </div>
       <div class="card-content">
+        <div class="field">
+          <div class="file has-name is-boxed is-fullwidth">
+            <label class="file-label">
+              <input
+                class="file-input"
+                type="file"
+                name="image"
+                accept=".jpg,.jpeg,.JPG,.JPEG"
+                @change="changePhotoSet"
+              />
+              <span class="file-cta">
+                <span class="file-label has-text-centered">
+                  Upload a Photo
+                </span>
+              </span>
+              <span class="file-name">
+                {{ filename }}
+              </span>
+            </label>
+          </div>
+        </div>
         <div class="field">
           <input
             class="input"
             type="text"
             placeholder="(product name)"
-            v-model="newProduct.name"
+            v-model="name"
           />
         </div>
         <div class="field">
@@ -20,7 +41,7 @@
             class="input"
             type="text"
             placeholder="(product category)"
-            v-model="newProduct.category_path"
+            v-model="category"
           />
         </div>
         <div class="field has-addons">
@@ -34,7 +55,7 @@
               class="input"
               type="text"
               placeholder="inventory (quantity)"
-              v-model="newProductQuantity"
+              v-model="quantity"
             />
           </p>
           <div class="control">
@@ -62,148 +83,184 @@
               class="input"
               type="text"
               placeholder="price"
-              v-model="newProductPrice"
+              v-model="price"
             />
           </p>
         </div>
-        <div class="field">
-          <div class="file has-name is-boxed is-fullwidth">
-            <label class="file-label">
-              <input
-                class="file-input"
-                type="file"
-                name="image"
-                accept=".jpg,.jpeg,.JPG,.JPEG"
-                @change="changePhotoSet"
-              />
-              <span class="file-cta">
-                <span class="file-label has-text-centered">
-                  ↑ Choose a picture…
-                </span>
-              </span>
-              <span class="file-name">
-                {{ filename }}
-              </span>
-            </label>
-          </div>
-        </div>
         <div class="field">
           <textarea
             class="textarea"
             type="text"
             placeholder="(product description)"
-            v-model="newProduct.description"
+            v-model="description"
           >
           </textarea>
         </div>
         <div class="field">
           <label class="checkbox">
-            <input type="checkbox" v-model="newProduct.featured" />
+            <input type="checkbox" v-model="featured" />
             Featured?
           </label>
         </div>
       </div>
-      <transition
-        enter-active-class="animate__animated animate__fadeIn"
-        leave-active-class="animate__animated animate__fadeOut"
-      >
-        <div
-          class="card-footer"
-          v-if="
-            newProduct.isValidPost() || newProduct.isValidPatch(currentProduct)
-          "
-        >
-          <div class="card-footer-item">
-            <button class="button is-primary is-fullwidth" @click="saveProduct">
-              Save
-            </button>
-          </div>
+      <div class="card-footer" v-if="isValidPost || isValidPatch">
+        <div class="card-footer-item">
+          <button class="button is-primary is-fullwidth" @click="saveProduct">
+            Save
+          </button>
         </div>
-      </transition>
+      </div>
     </div>
   </div>
 </template>
 
 <script>
-import Product from "@/models/product";
-import ProductDiff from "@/models/product_diff";
-
 const dollarRe = /^\$?(\d+)\.(\d{2})/gm;
 
 export default {
   name: "ProductEditCard",
   data: function() {
     return {
-      currentProduct: new Product(this.parentProduct),
-      newProduct: new ProductDiff(this.currentProduct),
-      filename: ""
+      filename: "",
+      diff: {}
     };
   },
   props: {
-    parentProduct: {
-      type: Product,
+    index: {
+      type: Number,
       required: true
     }
   },
+  created() {
+    this.diff = {
+      id: (this.old && this.old.id) || null,
+      name: null,
+      description: null,
+      cents: null,
+      quantity: (this.old && this.old.quantity) || 0,
+      category_path: null,
+      featured:
+        this.old && typeof this.old.featured === "boolean"
+          ? this.old.featured
+          : false,
+      photo_set: null
+    };
+  },
   computed: {
-    newProductQuantity: {
+    old: function() {
+      return this.$store.getters.products[this.index] || {};
+    },
+    id: function() {
+      return this.diff.id || this.old.id;
+    },
+    name: {
       get: function() {
-        return this.newProduct.quantity;
+        return this.diff.name || this.old.name;
       },
-      set: function(val) {
-        if (!isNaN(val)) {
-          this.newProduct.quantity = 1 * val;
-        }
+      set: function(name) {
+        this.diff.name = name;
       }
     },
-    newProductPrice: {
+    description: {
       get: function() {
-        return (this.newProduct.cents / 100).toFixed(2);
+        return this.diff.description || this.old.description;
       },
-      set: function(val) {
-        const groups = dollarRe.exec(val);
+      set: function(description) {
+        this.diff.description = description;
+      }
+    },
+    category: {
+      get: function() {
+        return this.diff.category_path || this.old.category;
+      },
+      set: function(category) {
+        this.diff.category_path = category;
+      }
+    },
+    featured: {
+      get: function() {
+        return this.diff.featured;
+      },
+      set: function(featured) {
+        this.diff.featured = featured;
+      }
+    },
+    price: {
+      get: function() {
+        const cents = this.diff.cents || this.old.cents || 0;
+        return (cents / 100).toFixed(2);
+      },
+      set: function(price) {
+        const groups = dollarRe.exec(price);
         if (groups && groups[1] && groups[2]) {
-          this.newProduct.cents = 100 * groups[1] + 1 * groups[2];
+          this.diff.cents = 100 * groups[1] + 1 * groups[2];
         }
       }
+    },
+    quantity: {
+      get: function() {
+        return this.diff.quantity || this.old.quantity || 0;
+      },
+      set: function(quantity) {
+        if (Number.isFinite(quantity)) {
+          this.diff.quantity = quantity;
+        }
+      }
+    },
+    isValidPost: function() {
+      return (
+        !this.diff.id &&
+        this.diff.name &&
+        this.diff.description &&
+        Number.isFinite(this.diff.cents) &&
+        Number.isFinite(this.diff.quantity) &&
+        this.diff.photo_set &&
+        this.diff.category_path &&
+        typeof this.featured === "boolean"
+      );
+    },
+    isValidPatch: function() {
+      return (
+        this.diff.id &&
+        (this.diff.photo_set ||
+          (this.diff.name && this.diff.name != this.old.name) ||
+          (Number.isFinite(this.diff.cents) &&
+            this.diff.cents != this.old.cents) ||
+          (this.diff.category_path &&
+            this.diff.category_path != this.old.category) ||
+          (Number.isFinite(this.diff.quantity) &&
+            this.diff.quantity != this.old.quantity) ||
+          (this.diff.description &&
+            this.diff.description != this.old.description) ||
+          this.diff.featured != this.old.featured)
+      );
     }
   },
   methods: {
+    incrementQuantity(by) {
+      if (this.quantity + by >= 0) {
+        this.diff.quantity += by;
+      }
+    },
     saveProduct() {
-      if (this.newProduct.id) {
+      if (this.id) {
         // update existing
-        const updatedProduct = this.$store.dispatch(
-          "updateProduct",
-          this.newProduct
-        );
-        if (updatedProduct) {
-          this.currentProduct = updatedProduct;
-        }
+        this.$store.dispatch("updateProduct", this.diff);
       } else {
         // new product
-        const newProduct = this.$store.dispatch(
-          "createProduct",
-          this.newProduct
-        );
-        if (newProduct) {
-          this.currentProduct = newProduct;
-        }
-      }
-    },
-    incrementQuantity(amount) {
-      if (this.newProduct.quantity + amount >= 0) {
-        this.newProduct.quantity += amount;
+        this.$store.dispatch("createProduct", this.diff);
       }
+
+      this.diff.photo_set = null;
     },
     changePhotoSet(event) {
       const file = event.target.files[0];
       if (!file) {
         return;
       }
-
       this.$store.dispatch("createPhotoSet", file).then(r => {
         this.filename = file.name;
-        this.newProduct.photo_set = r[0].id;
+        this.diff.photo_set = r[0].id;
       });
     }
   }
-- 
cgit v1.2.3

