Browse Source

source documents editable-file ok

bach 4 weeks ago
parent
commit
24b41ed00d

+ 1 - 0
src/api/gql/entite.fragment.gql

@@ -28,6 +28,7 @@ fragment EntiteFields on Entite {
     documents {
       description
       file {
+        fid
         filemime
         filename
         url

+ 37 - 1
src/assets/main.scss

@@ -1070,11 +1070,13 @@ body{
 
 div.editable-image,
 div.editable-video,
-div.editable-audios{
+div.editable-audios,
+ul.editable-files{
   background: #eee;
   max-width: 100%;
   div.file-btn{
     border: #eee 2px solid;
+    background-color: #eee;
     border-radius: 5px;
     margin: 1em 0 0;
     font-size: 1em;
@@ -1148,4 +1150,38 @@ li.link-editable{
   div.delete-btn{
     flex: 0 0 auto;
   }
+}
+ul.editable-files{
+  background-color: transparent;
+  li.file-editable{
+    // background-color: #eee;
+    // border: #eee 2px solid;
+    display: flex;
+    flex-direction: row;
+    vertical-align: middle;
+    margin: 0.5em 0!important;
+    gap: 0.5em;
+    >input{
+      background-color: #eee;
+      border: #eee 2px solid;
+      overflow: hidden;
+      text-wrap:nowrap;
+      width: 40%;
+      flex: 0 1 auto;
+      font-size: 0.756em;
+      &:focus{
+        outline: none;
+        border: #01ffe2 2px solid;
+      }
+    }
+    >div.filename{
+      width:40%;
+      flex: 1 0 auto;
+      font-size: 0.756em;
+    }
+    >div.delete-btn{
+      flex: 0 0 auto;
+    }
+  }
+
 }

+ 22 - 6
src/components/contents/Source.vue

@@ -11,6 +11,7 @@ import ImageEditable from '@components/editable/ImageEditable.vue';
 import VideoEditable from '@components/editable/VideoEditable.vue';
 import AudioEditable from '@components/editable/AudioEditable.vue';
 import LinkEditable from '@components/editable/LinkEditable.vue';
+import FileEditable from '@components/editable/FileEditable.vue';
 
 export default {
   props: ['concernement', 'entite', "eid", 'source'],
@@ -45,7 +46,8 @@ export default {
     ImageEditable,
     VideoEditable,
     AudioEditable,
-    LinkEditable
+    LinkEditable,
+    FileEditable
   }
 }
 </script>
@@ -141,20 +143,34 @@ export default {
             field_name: 'field_liens'
           }
         }"
+        label="Liens"
         v-on:updated="reloadEntite "/>
     </section>
 
 
-    <section
-      v-if="source.documents.length"
-      class="documents multiple">
-        <a
+    <!-- v-if="source.documents.length" -->
+    <section class="documents multiple">
+        <!-- <a
           v-for="(doc,d) in source.documents"
           :key="d"
           :href="doc.file.url">
           <template v-if="doc.description">{{ doc.description }}</template>
           <template v-else>{{ doc.file.url }}</template>
-        </a>
+        </a> -->
+        <FileEditable
+          :can_update="entite.can_update"
+          :files="source.documents"
+          :data="{
+            entitytype: 'paragraph',
+            bundle: 'source',
+            id: this.source.id,
+            uuid: this.source.uuid,
+            field: {
+              field_name: 'field_documents'
+            }
+          }"
+          label="Documents"
+          v-on:updated="reloadEntite "/>
     </section>
       
   </section>

+ 1 - 1
src/components/editable/AudioEditable.vue

@@ -163,7 +163,7 @@ export default {
         <svg-icon type="mdi" :path="mdiTrashCanOutline_path" />
       </div>
     </template>
-    <!-- with out img -->
+    <!-- with out audio -->
     <template v-else-if="can_update">
       <div @click="addAudio" class="file-btn">
         <svg-icon type="mdi" :path="mdiMusicNotePlus_path"/>

+ 192 - 0
src/components/editable/FileEditable.vue

@@ -0,0 +1,192 @@
+<script>
+
+import JSONAPI from '@api/json-axios'
+import REST from '@api/rest-axios'
+
+import { mapActions, mapState } from 'pinia'
+import { ConcernementsStore } from '@stores/concernements'
+import { UserStore } from '@stores/user'
+
+import ContentEditable from '@components/editable/ContentEditable.vue';
+
+import SvgIcon from '@jamescoyle/vue-icon';
+import { mdiTrashCanOutline } from '@mdi/js';
+import { mdiFilePlusOutline } from '@mdi/js';
+
+import { nextTick } from 'vue'
+
+export default {
+  props: {
+    can_update: Boolean,
+    files: Object,
+    data: Object,
+    label: String,
+  },
+  emits: ['updated'],
+  data(){
+    return {
+      mdiTrashCanOutline_path: mdiTrashCanOutline,
+      mdiFilePlusOutline_path: mdiFilePlusOutline,
+    }
+  },
+  computed: {
+    ...mapState(UserStore,['csrf_token']),
+  },
+  created () {
+    console.log('ImageEditable created')
+  },
+  mounted () {
+
+  },
+  methods: {
+    ...mapActions(ConcernementsStore, ['reloadConcernements']),
+    addFile(e){
+      console.log('addFile', this.$refs);
+      this.$refs.file_input.click();
+    },
+    onInputNewfile(e){
+      console.log('onFileInput', e);
+      let file = e.target.files[0];
+      console.log('onFileInput file', file);
+      this.saveNewFile(file);
+    },
+    saveNewFile(file){
+      const configs = {
+        headers: {
+          'X-CSRF-Token': this.csrf_token,
+          'Content-Type': 'application/octet-stream',
+          'Content-Disposition': `file; filename="${file.name}"`,
+        },
+      };
+
+      JSONAPI.post(`/${this.data.entitytype}/${this.data.bundle}/${this.data.uuid}/${this.data.field.field_name}`, file, configs)
+        .then(({ data : { data } }) => {
+          console.log('jsonapi post file', data)
+          this.$emit('updated');
+        })
+        .catch(error => {
+          console.warn('Issue with jsonapi post file', error)
+          Promise.reject(error)
+        })
+    },
+    onFocusOut(e){
+      console.log('onfocusOut', e, this.$refs.file_list);
+      this.buildFileListThenSaveList();
+    },
+    buildFileListThenSaveList(force_Save){
+      console.log('buildLinkListThenSave files', this.files);
+      // build links list
+      let list = [];
+      let tobesaved = false;
+      for (let i = 0; i < this.$refs.file_list.children.length; i++) {
+        const $li = this.$refs.file_list.children[i];
+        console.log('$li', $li);
+        if ($li.querySelector('input.description')) {
+          let description = $li.querySelector('input.description').value;
+          list.push({
+            description: description,
+            target_id: this.files[i].file.fid
+          });
+          tobesaved = (description !== this.files[i].description);
+        }
+      }
+      if (!tobesaved) {
+        tobesaved = this.files.length !== list.length;
+      }
+
+      if (tobesaved || force_Save) {
+        this.saveList(list);
+      }
+    },
+    saveList(list){
+      console.log('saveList list', list);
+      const params = {
+        type: this.data.bundle,
+        [this.data.field.field_name]: list
+      };
+      if (this.data.entitytype === 'node') {
+        params.nid = [{"value":this.data.id}];
+      } else {
+        params.id = [{"value":this.data.id}];
+      }
+      // // we need additional values for image alt for example
+      // // console.log('additional_values', this.data.field.additional_values);
+      // if (this.data.field.additional_values) {
+      //   for (const key in this.data.field.additional_values) {
+      //     if (Object.hasOwnProperty.call(this.data.field.additional_values, key)) {
+      //       params[this.data.field.field_name][0][key] = this.data.field.additional_values[key]
+      //     }
+      //   }
+      // }
+      
+      const configs = {
+        headers: {'X-CSRF-Token': this.csrf_token}
+      };
+
+      // url is not the same between nodes and others entities
+      let url_base = `/${this.data.entitytype === 'node' ? '' : 'entity/'}${this.data.entitytype}`;
+      // call the api
+      REST.patch(`${url_base}/${this.data.id}?_format=json`, params, configs)
+        .then(({ data }) => {
+          console.log('user REST post node data', data)
+          this.$emit('updated');
+        })
+        .catch(error => {
+          console.warn(`Issue with patch ${this.data.entitytype} ${this.data.bundle}`, error)
+          Promise.reject(error)
+        })
+    },
+    async onDeleteFile(i){
+      console.log(`onDeleteLink ${i}`, this.$refs.file_list.children[i]);
+      // this.links_nb = this.$refs.links_list.children.length;
+      // this.$refs.links_list.children[i].remove();
+      this.files.splice(i,1);
+      await nextTick();
+      this.buildFileListThenSaveList(true);
+    },
+    onUpdated(){
+      this.$emit('updated');
+    },
+    fileMime(mime){
+      return mime.replace('application/', '');
+    },
+    docLabel(doc){
+      return doc.description ? doc.description : doc.file.filename;
+    }
+  },
+  components: {
+    SvgIcon,
+    ContentEditable
+  }
+}
+</script>
+
+<template>
+  <label>{{ label }}</label>
+  <ul ref="file_list" class="editable-files" :class="{'can-update': can_update}">
+    <li v-for="(doc,d) in files" :key="d" class="file-editable">
+      <template v-if="!can_update">
+        <a
+          :href="doc.file.url"
+          target="_blank"
+        >
+          {{ docLabel(doc) }} [{{ fileMime(doc.file.filemime) }}]
+        </a>  
+      </template>
+      <template v-else>
+        <input type="text" class="description" :value="doc.description" placeholder="Description du fichier" @focusout="onFocusOut" />
+        <!-- <input type="text" class="url" :value="link.url" @focusout="onFocusOut" placeholder="Url du lien" /> -->
+        <div class="filename">{{ doc.file.filename }}</div>
+        <div @click="onDeleteFile(d)" class="delete-btn">
+          <svg-icon type="mdi" :path="mdiTrashCanOutline_path" />
+        </div>
+      </template>
+    </li>
+    <li v-if="can_update">
+      <div @click="addFile" class="file-btn">
+        <svg-icon type="mdi" :path="mdiFilePlusOutline_path"/>
+      </div>
+      <input ref="file_input" type="file" accept="application/txt, application/pdf, application/doc, application/docx, application/obs, application/xls"   @input="onInputNewfile"/>
+    </li>
+  </ul>
+</template>

+ 12 - 10
src/components/editable/LinkEditable.vue

@@ -20,7 +20,8 @@ export default {
   props: {
     can_update: Boolean,
     links: Array,
-    data: Object
+    data: Object,
+    label: String
   },
   emits: ['updated'],
   data(){
@@ -121,15 +122,15 @@ export default {
           console.warn(`Issue with patch ${this.data.entitytype} ${this.data.bundle}`, error)
           Promise.reject(error)
         })
-    },
-    async onDeleteLink(i){
-      console.log(`onDeleteLink ${i}`, this.$refs.links_list.children[i]);
-      // this.links_nb = this.$refs.links_list.children.length;
-      // this.$refs.links_list.children[i].remove();
-      this.links.splice(i,1);
-      await nextTick();
-      this.buildLinkListThenSave(true);
-    }
+      },
+      async onDeleteLink(i){
+        console.log(`onDeleteLink ${i}`, this.$refs.links_list.children[i]);
+        // this.links_nb = this.$refs.links_list.children.length;
+        // this.$refs.links_list.children[i].remove();
+        this.links.splice(i,1);
+        await nextTick();
+        this.buildLinkListThenSave(true);
+      }
   },
   components: {
     SvgIcon,
@@ -140,6 +141,7 @@ export default {
 </script>
 
 <template>
+  <label>{{ label }}</label>
   <ul ref="links_list">
     <li v-for="(link, l) in links" :key="l" :class="{'link-editable':can_update}">
       <!-- link exists can't update -->