summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSanto Cariotti <santo@dcariotti.me>2022-09-24 15:20:44 +0200
committerSanto Cariotti <santo@dcariotti.me>2022-09-24 15:20:44 +0200
commit7442612a9b6899a52575eac4a27c1a0090f81273 (patch)
tree6309517bf5f5c0252ed89cdbb30bccb1fb037cb6
parentdc2d283d04a1ee3418d5851d93b10d79d6175cbc (diff)
Manage file of a model
-rw-r--r--components/ModelForm.vue333
1 files changed, 241 insertions, 92 deletions
diff --git a/components/ModelForm.vue b/components/ModelForm.vue
index 12821df..a2658fc 100644
--- a/components/ModelForm.vue
+++ b/components/ModelForm.vue
@@ -1,97 +1,179 @@
<template lang="pug">
form.mt-3
+ .relative.z-10(aria-labelledby="modal-title", role="dialog", aria-modal="true" v-if="boxDeleteModelUpload > 0")
+ .fixed.inset-0.bg-gray-900.bg-opacity-90.transition-opacity
+ .fixed.inset-0.z-10.overflow-y-auto
+ .flex.min-h-full.items-end.justify-center.p-4.text-center(class="sm:items-center sm:p-0")
+ .relative.transform.overflow-hidden.rounded-lg.bg-white.text-left.shadow-xl.transition-all(class="sm:my-8 sm:w-full sm:max-w-lg")
+ .bg-white.px-4.pt-5.pb-4(class="sm:p-6 sm:pb-4")
+ div(class="sm:flex sm:items-start")
+ .mx-auto.flex.h-12.w-12.flex-shrink-0.items-center.justify-center.rounded-full.bg-red-100(class="sm:mx-0 sm:h-10 sm:w-10")
+ svg.h-6.w-6.text-red-600(xmlns="http://www.w3.org/2000/svg", fill="none", viewbox="0 0 24 24", stroke-width="1.5", stroke="currentColor", aria-hidden="true")
+ path(stroke-linecap="round", stroke-linejoin="round", d="M12 10.5v3.75m-9.303 3.376C1.83 19.126 2.914 21 4.645 21h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 4.88c-.866-1.501-3.032-1.501-3.898 0L2.697 17.626zM12 17.25h.007v.008H12v-.008z")
+ .mt-3.text-center(class="sm:mt-0 sm:ml-4 sm:text-left")
+ h3#modal-title.text-lg.font-medium.leading-6.text-gray-900 Are you sure?
+ .mt-2
+ p.text-sm.text-gray-500
+ | This file will be deleted from '{{ form.name }}' and our servers. This action cannot be undone.
+ .bg-gray-50.px-4.py-3(class="sm:flex sm:flex-row-reverse sm:px-6")
+ button.inline-flex.w-full.justify-center.rounded-md.border.border-transparent.bg-red-600.px-4.py-2.text-base.font-medium.text-white.shadow-sm(
+ type="button"
+ :class="{'hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm': true, 'opacity-25 cursor-default': isLoading}"
+ :disabled="isLoading"
+ :readonly="isLoading"
+ @click="deleteFile"
+ )
+ <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" v-if="isLoading">
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
+ </svg>
+ | Yes, confirm
+ button.mt-3.inline-flex.w-full.justify-center.rounded-md.border.border-gray-300.bg-white.px-4.py-2.text-base.font-medium.text-gray-700.shadow-sm(
+ type="button"
+ class="hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
+ @click="boxDeleteModelUpload = 0"
+ ) Cancel
.shadow(class="sm:overflow-hidden sm:rounded-md")
- .space-y-6.bg-white.px-4.py-5(class="sm:p-6")
- div
- label.block.text-sm.font-medium.text-gray-700(for="name") Name
- .mt-1
- input#name.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
- name="name"
- class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
- placeholder="My super project"
- required
- v-model="form.name"
- )
- div
- label.block.text-sm.font-medium.text-gray-700(for="description") Description
- span.text-gray-300 (Optional)
- .mt-1
- textarea#description.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
- name="description" rows="3"
- class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
- placeholder="Now this is the story all about how, My life got flipped-turned upside down... "
- v-model="form.description"
- )
- div
- label.block.text-sm.font-medium.text-gray-700(for="duration") Duration
- .mt-1
- input#duration.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
- name="duration"
- type="number"
- step="0.01"
- class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
- required
- v-model="form.duration"
- )
- p.text-sm.text-gray-500 Print duration in minutes
- div
- label.block.text-sm.font-medium.text-gray-700(for="height") Height
- .mt-1
- input#height.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
- name="height"
- type="number"
- step="0.01"
- class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
- required
- v-model="form.height"
- )
- p.text-sm.text-gray-500 Height in mm
- div
- label.block.text-sm.font-medium.text-gray-700(for="weight") Weight
- .mt-1
- input#weight.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
- name="weight"
- type="number"
- step="0.01"
- class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
- required
- v-model="form.weight"
- )
- p.text-sm.text-gray-500 Weight in g
- div
- label.block.text-sm.font-medium.text-gray-700(for="printer") Printer
- span.text-gray-300 (Optional)
- .mt-1
- input#printer.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
- name="printer"
- class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
- placeholder="Formlabs Form 3BL"
- v-model="form.printer"
- )
- div
- label.block.text-sm.font-medium.text-gray-700(for="material") Material
- span.text-gray-300 (Optional)
- .mt-1
- input#material.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
- name="material"
- class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
- placeholder="PLA"
- v-model="form.material"
- )
- .py-3.text-right(class="sm:px-6")
- button.inline-flex.justify-center.rounded-md.border.border-transparent.bg-green-600.py-2.px-4.text-sm.font-medium.text-white.shadow-sm(
- type="submit"
- :class="{'hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2': true, 'opacity-25 cursor-default': isLoading}"
- :disabled="isLoading"
- :readonly="isLoading"
- @click="save"
- )
- <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" v-if="isLoading">
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
- </svg>
- | Save
- p.text-sm.text-gray-700.mt-2(v-if="!form.id") You will add assets like images and STL/OBJ later.
+ .grid.grid-cols-2(v-if="form.id")
+ button.p-2.duration-100(
+ type="button"
+ :class="{'hover:bg-white': true, 'bg-white border-green-600 border-b-2': tab == 'info'}"
+ @click="tab = 'info'"
+ ) Info
+ button.p-2.duration-100(
+ type="button"
+ :class="{'hover:bg-white': true, 'bg-white border-green-600 border-b-2': tab == 'files'}"
+ @click="tab = 'files'"
+ ) Files
+ div(v-if="tab == 'info'")
+ .space-y-6.bg-white.px-4.py-5(class="sm:p-6")
+ div
+ label.block.text-sm.font-medium.text-gray-700(for="name") Name
+ .mt-1
+ input#name.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
+ name="name"
+ class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
+ placeholder="My super project"
+ required
+ v-model="form.name"
+ )
+ div
+ label.block.text-sm.font-medium.text-gray-700(for="description") Description
+ span.text-gray-300 (Optional)
+ .mt-1
+ textarea#description.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
+ name="description" rows="3"
+ class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
+ placeholder="Now this is the story all about how, My life got flipped-turned upside down... "
+ v-model="form.description"
+ )
+ div
+ label.block.text-sm.font-medium.text-gray-700(for="duration") Duration
+ .mt-1
+ input#duration.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
+ name="duration"
+ type="number"
+ step="0.01"
+ class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
+ required
+ v-model="form.duration"
+ )
+ p.text-sm.text-gray-500 Print duration in minutes
+ div
+ label.block.text-sm.font-medium.text-gray-700(for="height") Height
+ .mt-1
+ input#height.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
+ name="height"
+ type="number"
+ step="0.01"
+ class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
+ required
+ v-model="form.height"
+ )
+ p.text-sm.text-gray-500 Height in mm
+ div
+ label.block.text-sm.font-medium.text-gray-700(for="weight") Weight
+ .mt-1
+ input#weight.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
+ name="weight"
+ type="number"
+ step="0.01"
+ class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
+ required
+ v-model="form.weight"
+ )
+ p.text-sm.text-gray-500 Weight in g
+ div
+ label.block.text-sm.font-medium.text-gray-700(for="printer") Printer
+ span.text-gray-300 (Optional)
+ .mt-1
+ input#printer.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
+ name="printer"
+ class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
+ placeholder="Formlabs Form 3BL"
+ v-model="form.printer"
+ )
+ div
+ label.block.text-sm.font-medium.text-gray-700(for="material") Material
+ span.text-gray-300 (Optional)
+ .mt-1
+ input#material.mt-1.block.w-full.rounded-md.border-gray-300.border-1.px-2.py-1(
+ name="material"
+ class="focus:border-green-500 focus:ring-green-500 sm:text-sm"
+ placeholder="PLA"
+ v-model="form.material"
+ )
+ .py-3.text-right(class="sm:px-6")
+ button.inline-flex.justify-center.rounded-md.border.border-transparent.bg-green-600.py-2.px-4.text-sm.font-medium.text-white.shadow-sm(
+ type="submit"
+ :class="{'hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2': true, 'opacity-25 cursor-default': isLoading}"
+ :disabled="isLoading"
+ :readonly="isLoading"
+ @click="save"
+ )
+ <svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" v-if="isLoading">
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
+ </svg>
+ | Save
+ p.text-sm.text-gray-700.mt-2(v-if="!form.id") You will add assets like images and STL/OBJ later.
+ div(v-else)
+ .space-y-6.bg-white.px-4.py-5(class="sm:p-6")
+ h3 Manage '{{ form.name }}' already uploaded files
+ ul.divide-y.divide-gray-200.rounded-b-md.border.border-gray-200(role="list")
+ li.flex.items-center.justify-between.py-3.pl-3.pr-4.text-sm(v-for="upload in form.uploads" :key="upload.id")
+ .flex.w-0.flex-1.items-center
+ svg.h-5.w-5.flex-shrink-0.text-gray-400(xmlns="http://www.w3.org/2000/svg", viewbox="0 0 20 20", fill="currentColor", aria-hidden="true")
+ path(fill-rule="evenodd", d="M15.621 4.379a3 3 0 00-4.242 0l-7 7a3 3 0 004.241 4.243h.001l.497-.5a.75.75 0 011.064 1.057l-.498.501-.002.002a4.5 4.5 0 01-6.364-6.364l7-7a4.5 4.5 0 016.368 6.36l-3.455 3.553A2.625 2.625 0 119.52 9.52l3.45-3.451a.75.75 0 111.061 1.06l-3.45 3.451a1.125 1.125 0 001.587 1.595l3.454-3.553a3 3 0 000-4.242z", clip-rule="evenodd")
+ span.ml-2.w-0.flex-1.truncate {{ getFileName(upload.filepath) }}
+ .ml-4
+ a.font-medium.text-black-700.cursor-pointer.mr-2(class="hover:underline") Preview
+ a.font-medium.text-red-700.cursor-pointer(class="hover:underline" @click="boxDeleteModelUpload = upload.id") Delete
+ hr
+ h3 Add new uploads
+ label.inline-flex.leading-6.justify-center.rounded-md.border.border-transparent.bg-green-600.py-2.px-4.text-sm.font-medium.text-white.shadow-sm.cursor-pointer(
+ class="hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2"
+ for="upload"
+ )
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="mr-1 w-6 h-6">
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 10.5v6m3-3H9m4.06-7.19l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" />
+ </svg>
+ | Select file(s)
+ input.hidden(
+ id="upload"
+ type="file"
+ multiple
+ @change="manageUploads"
+ )
+ ul
+ li.flex.items-center.justify-between.py-3.pl-3.pr-4.text-sm(v-for="file in filesToUpload" :key="file.lastModified+''+file.name")
+ .flex.w-0.flex-1.items-center
+ svg.h-5.w-5.flex-shrink-0.text-gray-400(xmlns="http://www.w3.org/2000/svg", viewbox="0 0 20 20", fill="currentColor", aria-hidden="true")
+ path(fill-rule="evenodd", d="M15.621 4.379a3 3 0 00-4.242 0l-7 7a3 3 0 004.241 4.243h.001l.497-.5a.75.75 0 011.064 1.057l-.498.501-.002.002a4.5 4.5 0 01-6.364-6.364l7-7a4.5 4.5 0 016.368 6.36l-3.455 3.553A2.625 2.625 0 119.52 9.52l3.45-3.451a.75.75 0 111.061 1.06l-3.45 3.451a1.125 1.125 0 001.587 1.595l3.454-3.553a3 3 0 000-4.242z", clip-rule="evenodd")
+ span.ml-2.w-0.flex-1.truncate(v-if="checkExt(file.name)") {{ file.name }} {{ file.uploaded }}
+ span.ml-2.w-0.flex-1.truncate.text-gray-500(v-else)
+ s.text-red-500.mr-2 {{ file.name }}
+ | (this file type is not supported, so it'll not be uploaded)
</template>
<script>
@@ -103,6 +185,9 @@ export default {
data() {
return {
form: {},
+ tab: "info",
+ boxDeleteModelUpload: 0,
+ filesToUpload: [],
};
},
computed: {
@@ -121,7 +206,7 @@ export default {
if (response.status == 201 || response.status == 200) {
this.$toast.success("Model has been saved");
setTimeout(() => {
- window.location.href = "/models/" + response.data.id;
+ window.location.href = "/models/" + response.data.id + "/edit";
}, 1000);
} else {
this.$toast.error(response.data.error);
@@ -130,6 +215,70 @@ export default {
}
event.preventDefault();
},
+ getFileName(path) {
+ return path.split("/").at(-1);
+ },
+ deleteFile() {
+ this.$store
+ .dispatch("models/deleteUpload", {
+ id: this.form.id,
+ uid: this.boxDeleteModelUpload,
+ })
+ .then((response) => {
+ if (response.status == 204) {
+ this.$toast.success("File deleted successfully");
+ for (const i in this.form.uploads) {
+ if (this.form.uploads[i].id == this.boxDeleteModelUpload) {
+ this.form.uploads.splice(i, 1);
+ break;
+ }
+ }
+ this.boxDeleteModelUpload = 0;
+ } else {
+ this.$toast.error("Error: " + response.data.error);
+ }
+ });
+ },
+ manageUploads(event) {
+ for (const file of event.target.files) {
+ if (this.checkExt(file.name)) {
+ this.$store
+ .dispatch("models/upload", { id: this.form.id, file })
+ .then((response) => {
+ if (response == 200) {
+ this.$toast.success(`'${file.name}' has been uploaded`);
+ this.$store
+ .dispatch("models/findModel", this.form.id)
+ .then((response) => {
+ this.form.uploads = response.data.uploads;
+ });
+ file.uploaded = true;
+ } else {
+ this.$toast.error(`Error uploading '${file.name}'`);
+ file.uploaded = false;
+ }
+ });
+ }
+ this.filesToUpload.push(file);
+ }
+ },
+ checkExt(name) {
+ const exts = [
+ ".stl",
+ ".obj",
+ ".png",
+ ".jpg",
+ ".jpeg",
+ ".gif",
+ ".webp",
+ ".blend",
+ ];
+ for (const ext of exts) {
+ if (name.toLowerCase().indexOf(ext) > 0) return true;
+ }
+
+ return false;
+ },
},
};
</script>