diff --git a/src/components/LayersEditor.vue b/src/components/LayersEditor.vue index 3d4d3ee6821c1037c9e03a2a4dc26697d497e026..c3b86daa8d24674dafd054bd8fd48c49950e5be4 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 44afa98208fb4d1b7ecd9ff64c61f00c33fcb952..8c9093325dc76ac32690a1a85a1e6498712d7252 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 0000000000000000000000000000000000000000..f5e52b64caf1d90649d74de5a17d3770d80d62cf --- /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 7151ed2780f8568f5230a81c61952edc62b7089b..ddf95544b790aec1898724dd3abcbe87d86f4195 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 0792ffa14302734c1b0328c90c9a4050c69d9296..0e97d71d61db92392316d137351afac962c9353f 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 77a63abeed1e4db788f5cfa52612d6ca92c57020..8a780a32c24e863a45a3a5ed8fca293fed33f85e 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 925ec119fb30f3dbe3c425a9f9ab05b81e895634..dd88602c61d38ad968de53bb7dd1a4881d198d01 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 609fdb4691da7d3d4c0fa74d64907c75c73f91ba..6a2b47da2ba6241e0dc21f2975f200827db67c50 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 440874f39b7813f8ad7c40d80fe5d4e45818af87..17e0c51e448282799d9e0ad77c966d7a4380b7b7 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 51c35027a6083eeb48ad03ecac697d1cf9912e2d..46f7b29606042debd091c7c42fe85bf4728d544b 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 69f30ac20d554e2cc0d2b3bbaa830f6b0d23800f..d088c265c0990f1e798826cda221bf8821e6af29 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 0000000000000000000000000000000000000000..65f1cf6197b7b759f3538c93a60bd33e53f291a8 --- /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>