From ce1ea0b5b749d4ca4842093a22620a3c5b3db6d7 Mon Sep 17 00:00:00 2001
From: "thomas.blanc.2@etu.univ-amu.fr" <gltron3000@gmail.com>
Date: Tue, 12 May 2020 14:50:26 +0200
Subject: [PATCH] Added account edit & model add/edit improvement

---
 src/components/LayersEditor.vue |   4 +-
 src/components/LoginForm.vue    |   7 +-
 src/components/ModelUpload.vue  | 166 ++++++++++++++++++++++++++++++++
 src/components/RegisterForm.vue |  52 ++++++++--
 src/router/index.js             |   5 +
 src/views/About.vue             |   2 +-
 src/views/Account.vue           |  22 ++++-
 src/views/Docs.vue              |   2 +-
 src/views/Model.vue             |   9 +-
 src/views/ModelAdd.vue          |  31 ++++--
 src/views/ModelEdit.vue         |  26 +++--
 src/views/UserEdit.vue          |  40 ++++++++
 12 files changed, 335 insertions(+), 31 deletions(-)
 create mode 100644 src/components/ModelUpload.vue
 create mode 100644 src/views/UserEdit.vue

diff --git a/src/components/LayersEditor.vue b/src/components/LayersEditor.vue
index 3d4d3ee..c3b86da 100644
--- a/src/components/LayersEditor.vue
+++ b/src/components/LayersEditor.vue
@@ -15,13 +15,13 @@
       <b-input :placeholder="'Layer ' + index + ' name'" v-model="layer.name"/>
 
       <p class="control">
-        <b-upload v-model="file" accept=".py">
+        <b-upload v-model="layer.file" accept=".py">
           <a class="button is-primary">
             <b-icon icon="upload"></b-icon>
             <span>Upload layer file</span>
           </a>
         </b-upload>
-        <span class="file-name" v-if="file">{{ file.name }}</span>
+        <span class="file-name" v-if="layer.file">{{ layer.file.name }}</span>
       </p>
 
     </b-field>
diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue
index 44afa98..8c90933 100644
--- a/src/components/LoginForm.vue
+++ b/src/components/LoginForm.vue
@@ -5,6 +5,8 @@
         <p class="modal-card-title">Login</p>
       </header>
       <section class="modal-card-body">
+        <h2 class="subtitle has-text-danger">{{errorMessage}}</h2>
+
         <b-field label="Email">
           <b-input type="email" v-model="email" placeholder="email" required/>
         </b-field>
@@ -29,7 +31,8 @@ export default {
   data () {
     return {
       email: '',
-      password: ''
+      password: '',
+      errorMessage: null
     }
   },
   methods: {
@@ -40,7 +43,7 @@ export default {
         this.$parent.close()
         this.$router.go()
       } else {
-        this.$buefy.toast.open('Email ou mot de passe incorect')
+        this.errorMessage = 'Email or password incorect'
       }
     },
     async login (email, password) {
diff --git a/src/components/ModelUpload.vue b/src/components/ModelUpload.vue
new file mode 100644
index 0000000..f5e52b6
--- /dev/null
+++ b/src/components/ModelUpload.vue
@@ -0,0 +1,166 @@
+<template>
+  <div class="modal-card">
+    <b-progress :value="progress" size="is-large" show-value :type="status">
+      {{message}}
+    </b-progress>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'ModelUpload',
+  props: {
+    model: {
+      type: Object,
+      default: function () {
+        return {}
+      }
+    }
+  },
+  data () {
+    return {
+      message: '',
+      progress: 0,
+      status: 'is-primary',
+      stausMessage: '',
+      modelId: null
+    }
+  },
+  mounted () {
+    this.saveModel()
+  },
+  methods: {
+    setProgress (step) {
+      switch (step) {
+        case 0:
+          this.message = 'Sending description'
+          this.progress = 25
+          break
+        case 1:
+          this.message = 'Sending model file'
+          this.progress = 50
+          break
+        case 2:
+          this.message = 'Sending custom layers model'
+          this.progress = 75
+          break
+        case 3:
+          this.message = 'Upload completed !'
+          this.progress = 100
+          this.status = 'is-success'
+          break
+      }
+    },
+    setError (message) {
+      this.message = message
+      this.status = 'is-danger'
+    },
+    async saveModel () {
+      this.setProgress(0)
+      var response = await this.uploadModel()
+      if (response.error) {
+        this.setError(response.message)
+        return
+      } else {
+        this.modelId = response.id
+      }
+
+      if (this.model.file !== undefined) {
+        console.log('Upload model ' + this.model.file)
+        this.setProgress(1)
+        if (!await this.uploadModelFile()) {
+          this.setError('Model upload error')
+          return
+        }
+      }
+
+      if (this.model.customLayers !== undefined) {
+        console.log('Upload layers ' + this.model.customLayers)
+        this.setProgress(2)
+        if (!await this.uploadLayers()) {
+          this.setError('Custom layer upload error')
+          return
+        }
+      }
+
+      this.setProgress(3)
+    },
+    async uploadModel () {
+      const url = this.$serverurl + 'models'
+      const token = await localStorage.getItem('token')
+      try {
+        const response = await fetch(url, {
+          method: 'POST',
+          headers: {
+            Accept: 'application/json',
+            'Content-Type': 'application/json',
+            Authorization: 'Bearer ' + token
+          },
+          body: JSON.stringify({
+            name: this.model.name,
+            shortDescription: this.model.shortDescription,
+            longDescription: this.model.longDescription,
+            performance: this.model.performance,
+            tags: this.model.tags,
+            customLayers: this.model.customLayers
+          })
+        })
+        return await response.json()
+      } catch (error) {
+        return null
+      }
+    },
+    async uploadModelFile () {
+      const data = new FormData()
+      const url = this.$serverurl + 'models?id=' + this.modelId + '/upload'
+      const token = await localStorage.getItem('token')
+
+      data.append('model', this.model.file)
+
+      try {
+        const response = await fetch(url, {
+          method: 'POST',
+          headers: {
+            Authorization: 'Bearer ' + token
+          },
+          body: data
+        })
+        return response !== 200
+      } catch (error) {
+        return null
+      }
+    },
+    async uploadLayers () {
+      const token = await localStorage.getItem('token')
+      for (var i = 0; i < this.model.customLayers.length; i++) {
+        const layer = this.model.customLayers[i]
+        const data = new FormData()
+        const url = this.$serverurl + 'models?id=' + this.modelId + '/layer?id=' + i
+
+        console.log('Upload layers ' + i + ' | ' + layer.file)
+        if (layer.file === undefined) continue
+
+        data.append('layer', layer.file)
+
+        try {
+          const response = await fetch(url, {
+            method: 'POST',
+            headers: {
+              Authorization: 'Bearer ' + token
+            },
+            body: data
+          })
+          if (response !== 200) return false
+        } catch (error) {
+          return false
+        }
+      }
+      return true
+    }
+  }
+}
+</script>
+
+<style>
+
+</style>
diff --git a/src/components/RegisterForm.vue b/src/components/RegisterForm.vue
index 7151ed2..ddf9554 100644
--- a/src/components/RegisterForm.vue
+++ b/src/components/RegisterForm.vue
@@ -5,25 +5,26 @@
         <p class="modal-card-title">Sign up</p>
       </header>
       <section class="modal-card-body">
+        <h2 class="subtitle has-text-danger">{{errorMessage}}</h2>
         <b-field label="Email">
-          <b-input type="email" v-model="email" placeholder="email" required/>
+          <b-input type="email" v-model="email" placeholder="email" required maxlength="50"/>
         </b-field>
 
         <b-field label="Username">
-          <b-input v-model="username" placeholder="username" required/>
+          <b-input v-model="username" placeholder="username" required maxlength="30"/>
         </b-field>
 
         <b-field label="Password">
-          <b-input type="password" v-model="password" password-reveal placeholder="password" required/>
+          <b-input type="password" v-model="password" password-reveal placeholder="password" required maxlength="50"/>
         </b-field>
 
         <b-field label="Confirm password">
-          <b-input type="password" v-model="passwordConfirm" password-reveal placeholder="password" required/>
+          <b-input type="password" v-model="passwordConfirm" password-reveal placeholder="password" required maxlength="50"/>
         </b-field>
 
       </section>
       <footer class="modal-card-foot">
-        <button class="button is-primary">Make account</button>
+        <button class="button is-primary" @click="onRegister">Make account</button>
       </footer>
     </div>
   </form>
@@ -37,7 +38,46 @@ export default {
       email: '',
       username: '',
       password: '',
-      passwordConfirm: ''
+      passwordConfirm: '',
+      errorMessage: null
+    }
+  },
+  methods: {
+    async onRegister () {
+      if (this.password !== this.passwordConfirm) {
+        this.errorMessage = 'Passwords are different'
+        return
+      }
+      const response = await this.register(this.username, this.email, this.password)
+      if (response.error) {
+        this.errorMessage = response.message
+      } else {
+        await localStorage.setItem('token', response.token)
+        this.$parent.close()
+        this.$router.go()
+      }
+    },
+    async register (username, email, password) {
+      const url = this.$serverurl + 'user'
+      try {
+        const response = await fetch(
+          url, {
+            method: 'POST',
+            headers: {
+              Accept: 'application/json',
+              'Content-Type': 'application/json'
+            },
+            body: JSON.stringify({
+              email: email,
+              username: username,
+              password: password
+            })
+          }
+        )
+        return await response.json()
+      } catch (error) {
+        return null
+      }
     }
   }
 }
diff --git a/src/router/index.js b/src/router/index.js
index 0792ffa..0e97d71 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -25,6 +25,11 @@ const routes = [
     name: 'Search',
     component: () => import('../views/Search.vue')
   },
+  {
+    path: '/useredit',
+    name: 'UserEdit',
+    component: () => import('../views/UserEdit.vue')
+  },
   {
     path: '/model',
     name: 'Model',
diff --git a/src/views/About.vue b/src/views/About.vue
index 77a63ab..8a780a3 100644
--- a/src/views/About.vue
+++ b/src/views/About.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="about">
+  <div class="about container">
     <h1>About page</h1>
   </div>
 </template>
diff --git a/src/views/Account.vue b/src/views/Account.vue
index 925ec11..dd88602 100644
--- a/src/views/Account.vue
+++ b/src/views/Account.vue
@@ -3,8 +3,22 @@
     <div class="box">
       <h1 class="title">Welcome {{account.username}}</h1>
       <div class="buttons">
-        <b-button tag="router-link" :to="{ name: 'ModelAdd' }">Add new model</b-button>
-        <b-button>Change account details</b-button>
+        <b-button
+         icon-left="plus"
+         type="is-info"
+         tag="router-link"
+         :to="{ name: 'ModelAdd' }"
+        >
+        Add new model
+        </b-button>
+        <b-button
+         icon-left="pencil"
+         type="is-info"
+         tag="router-link"
+         :to="{ name: 'UserEdit' }"
+        >
+        Change account details
+        </b-button>
       </div>
     </div>
     <div class="box">
@@ -46,5 +60,7 @@ export default {
 </script>
 
 <style scoped>
-
+.account{
+  margin-top: 20px;
+}
 </style>
diff --git a/src/views/Docs.vue b/src/views/Docs.vue
index 609fdb4..6a2b47d 100644
--- a/src/views/Docs.vue
+++ b/src/views/Docs.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="docs">
+  <div class="docs container">
     <h1>Documentation page</h1>
   </div>
 </template>
diff --git a/src/views/Model.vue b/src/views/Model.vue
index 440874f..17e0c51 100644
--- a/src/views/Model.vue
+++ b/src/views/Model.vue
@@ -38,7 +38,14 @@
         </div>
       </div>
       <div class="box">
-        <div class="content" v-html="compiledLongDescription"></div>
+        <b-tabs>
+          <b-tab-item label="Read me">
+            <div class="content" v-html="compiledLongDescription"></div>
+          </b-tab-item>
+
+          <b-tab-item label="Layers">
+          </b-tab-item>
+        </b-tabs>
       </div>
       <div class="box">
         <Comments v-bind:comments="model.comments"/>
diff --git a/src/views/ModelAdd.vue b/src/views/ModelAdd.vue
index 51c3502..46f7b29 100644
--- a/src/views/ModelAdd.vue
+++ b/src/views/ModelAdd.vue
@@ -3,25 +3,25 @@
     <b-steps v-model="activeStep">
       <b-step-item step="1" label="Description" clickable>
         <b-field label="Model name">
-          <b-input maxlength="30" size="is-large"/>
+          <b-input maxlength="30" size="is-large" v-model="model.name"/>
         </b-field>
 
         <b-field label="Short description">
-          <b-input maxlength="200" type="textarea"/>
+          <b-input maxlength="200" type="textarea" v-model="model.shortDescription"/>
         </b-field>
 
         <b-field label="Long description">
-          <markdownEditor/>
+          <markdownEditor v-bind:input="model.longDescription"/>
         </b-field>
       </b-step-item>
 
       <b-step-item step="2" label="Tags" clickable>
-        <tagEditor/>
+        <tagEditor v-bind:tags="model.tags"/>
       </b-step-item>
 
       <b-step-item step="3" label="Files" clickable>
         <b-field label="Model">
-          <b-upload v-model="dropFiles" drag-drop expanded>
+          <b-upload v-model="model.file" drag-drop expanded>
             <section class="section">
               <div class="content has-text-centered">
                 <p>
@@ -35,14 +35,18 @@
       </b-step-item>
 
       <b-step-item step="4" label="Custom layers" clickable>
-        <layersEditor/>
+        <layersEditor v-bind:layers="model.customLayers"/>
       </b-step-item>
 
       <b-step-item step="5" label="Finish" clickable>
-        <b-button expanded size="is-large" type="is-success">Confirm and upload</b-button>
+        <b-button expanded size="is-large" type="is-success" @click="openUploadModal">Confirm and upload</b-button>
       </b-step-item>
 
     </b-steps>
+
+    <b-modal :active.sync="isUploadModalActive" has-modal-card trap-focus aria-role="dialog" aria-modal>
+      <ModelUpload v-bind:model="model"/>
+    </b-modal>
   </div>
 </template>
 
@@ -50,17 +54,26 @@
 import markdownEditor from '@/components/MarkdownEditor.vue'
 import tagEditor from '@/components/TagEditor.vue'
 import layersEditor from '@/components/LayersEditor.vue'
+import ModelUpload from '@/components/ModelUpload.vue'
 
 export default {
   name: 'ModelAdd',
   components: {
     markdownEditor,
     tagEditor,
-    layersEditor
+    layersEditor,
+    ModelUpload
   },
   data () {
     return {
-      activeStep: 0
+      model: {},
+      activeStep: 0,
+      isUploadModalActive: false
+    }
+  },
+  methods: {
+    openUploadModal () {
+      this.isUploadModalActive = true
     }
   }
 }
diff --git a/src/views/ModelEdit.vue b/src/views/ModelEdit.vue
index 69f30ac..d088c26 100644
--- a/src/views/ModelEdit.vue
+++ b/src/views/ModelEdit.vue
@@ -25,7 +25,7 @@
       <div class="columns">
         <div class="column">
           <b-field label="Model">
-            <b-upload v-model="dropFiles" drag-drop expanded>
+            <b-upload v-model="model.file" drag-drop expanded>
               <section class="section">
                 <div class="content has-text-centered">
                   <p>
@@ -45,9 +45,13 @@
 
     </div>
     <div class="box">
-      <b-button >Cancel</b-button>
-      <b-button type="is-success">Save</b-button>
+      <b-button tag="router-link" :to="{ name: 'Account' }">Cancel</b-button>
+      <b-button type="is-success" @click="openUploadModal">Save</b-button>
     </div>
+
+    <b-modal :active.sync="isUploadModalActive" has-modal-card trap-focus aria-role="dialog" aria-modal>
+      <ModelUpload v-bind:model="model"/>
+    </b-modal>
   </div>
 </template>
 
@@ -55,13 +59,15 @@
 import markdownEditor from '@/components/MarkdownEditor.vue'
 import tagEditor from '@/components/TagEditor.vue'
 import layersEditor from '@/components/LayersEditor.vue'
+import ModelUpload from '@/components/ModelUpload.vue'
 
 export default {
   name: 'ModelEdit',
   components: {
     markdownEditor,
     tagEditor,
-    layersEditor
+    layersEditor,
+    ModelUpload
   },
   props: {
     model: {
@@ -71,15 +77,23 @@ export default {
       }
     }
   },
+  data () {
+    return {
+      isUploadModalActive: false
+    }
+  },
   methods: {
-    async saveModel () {
-
+    openUploadModal () {
+      this.isUploadModalActive = true
     }
   }
 }
 </script>
 
 <style scoped>
+.modelEdit{
+  margin-top: 20px;
+}
 .rightColumn {
   border-left: 2px solid whitesmoke;
 }
diff --git a/src/views/UserEdit.vue b/src/views/UserEdit.vue
new file mode 100644
index 0000000..65f1cf6
--- /dev/null
+++ b/src/views/UserEdit.vue
@@ -0,0 +1,40 @@
+<template>
+  <div class="userEdit container">
+    <h1 class="title">Account details</h1>
+
+    <b-field label="Email">
+      <b-input v-model="user.email" type="email"  maxlength="50"/>
+    </b-field>
+
+    <b-field label="Username">
+      <b-input v-model="user.username" maxlength="30"/>
+    </b-field>
+
+    <b-field label="Password">
+      <b-input type="password" v-model="user.password" password-reveal maxlength="50"/>
+    </b-field>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'UserEdit',
+  props: {
+    user: {
+      type: Object,
+      default: function () {
+        return {}
+      }
+    }
+  },
+  methods: {
+    saveUser () {
+
+    }
+  }
+}
+</script>
+
+<style>
+
+</style>
-- 
GitLab