Bladeren bron

can patch node from editablecontent fields

bach 2 maanden geleden
bovenliggende
commit
8c90f54b3c

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

@@ -23,6 +23,7 @@ fragment ConcernementFields on Concernement {
   }
   created
   changed
+  can_update
   lieu {
     name
   }

+ 37 - 0
src/assets/main.scss

@@ -1026,3 +1026,40 @@ body{
 
   }
 }
+
+*[contenteditable="true"] {
+  background: #eee;
+  border: #eee 2px solid;
+  border-radius: 5px;
+  $p:0.3em;
+  padding:$p;
+  margin:0 0 0 -$p;
+  box-sizing: border-box;
+  transition: all 0.2s ease-in-out;
+  &:focus{
+    outline: none;
+    border: #01ffe2 2px solid;
+  }
+}
+
+button.edit-btn,
+button.save-btn{
+  background-color: #444;
+  color: #fff;
+  border: none;
+  border-radius: 6px;
+  $size: 25px;
+  width: $size; height:$size;
+  
+  display: inline;
+
+  >svg{
+    width:100%;
+    height:100%;
+  }
+  transition: all 0.2s ease-in-out;
+  &:hover{
+    cursor:pointer;
+    opacity: 0.7;
+  }
+}

+ 2 - 2
src/components/ConcernementMapItem.vue

@@ -1985,7 +1985,7 @@ export default {
       }.bind(this);
 
       this.paper_main_object.onMouseMove = function(event){
-        console.log(`onmousemove ${this.id}`, this.cartouch_is_opened);
+        // console.log(`onmousemove ${this.id}`, this.cartouch_is_opened);
 
         // prevent hover map item mouse event if cartouch is opened
         if (this.cartouch_is_opened && this.map_mode !== "superposition") return;
@@ -2068,7 +2068,7 @@ export default {
           }
 
           let result = paper_group_tohit ? paper_group_tohit.hitTest(event.point) : null;
-          console.log('move result', result);
+          // console.log('move result', result);
           if (result && result.item.item_id && (result.item.name != "label_click_zone" || this.detailsZoomValue > 2)) {
             console.log('move has result', result);
             let new_hover_elmt = {

+ 37 - 5
src/components/contents/TerrainDeVie.vue

@@ -21,6 +21,8 @@ import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiChevronRight } from '@mdi/js';
 import { mdiChevronDown } from '@mdi/js';
 
+import ContentEditable from '@components/misc/ContentEditable.vue';
+
 export default {
   props: ['cid', 'eid'],
   data(){
@@ -206,13 +208,20 @@ export default {
       } else {
         this.headerreduced = false;
       }
+    },
+    onContentEditableFocusOut(e){
+      console.log('onContentEditableFocusOut', e);
+      let new_field_content = e.target.innerText;
+      console.log('onContentEditableFocusOut', new_field_content);
+      console.log('onContentEditableFocusOut this.concernement.title', this.concernement.title);
     }
   },
   components: {
     CartoucheLayout,
     Entite,
     VueSlider,
-    SvgIcon
+    SvgIcon,
+    ContentEditable
   }
 }
 
@@ -233,13 +242,36 @@ export default {
       <template v-if="!entite">
         <section v-if="concernement.description" class="description">
             <label v-if="ct_concernement">{{ ct_concernement.field_description.description }}</label>
-            <p v-html="concernement.description"/>
-            <!-- <p>{{ concernement.description }}</p> -->
+            
+            <ContentEditable 
+              tag="p"
+              :value="concernement.description"
+              :html="true" 
+              :class="{ ellipsed: headerreduced }" 
+              :contenteditable="concernement.can_update"
+              :data="{
+                entitytype: 'node',
+                bundle: 'concernement',
+                nid: this.concernement.id,
+                field: 'field_description'
+              }" />
+
         </section>
         <section v-if="concernement.caillou" class="caillou">
             <label v-if="ct_concernement">{{ ct_concernement.field_caillou.description }}</label>
-            <!-- <p v-html="concernement.caillou"/> -->
-            <p>{{ concernement.caillou }}</p>
+            
+            <ContentEditable 
+              tag="p"
+              :value="concernement.caillou" 
+              :class="{ ellipsed: headerreduced }" 
+              :contenteditable="concernement.can_update"
+              :data="{
+                entitytype: 'node',
+                bundle: 'concernement',
+                nid: this.concernement.id,
+                field: 'field_caillou'
+              }" />
+              
         </section>
       </template>
 

+ 21 - 3
src/components/layout/CartoucheLayout.vue

@@ -6,6 +6,8 @@ import { ConcernementsStore } from '@stores/concernements'
 import SvgIcon from '@jamescoyle/vue-icon';
 import { mdiHeadphones } from '@mdi/js';
 
+import ContentEditable from '@components/misc/ContentEditable.vue';
+
 export default {
   props: ['cid'],
   emits: ['main_scrolled'],
@@ -19,6 +21,7 @@ export default {
   created () {
     console.log('Cartouch layout created', this.cid);
     this.concernement = this.concernementsByID[this.cid];
+    console.log('can_update', this.concernement.can_update);
   },
   mounted () {
     // console.log('cartouche layout mounted', this);
@@ -44,16 +47,18 @@ export default {
         console.log(`TerrainDeVie watch cid o:${o}, n:${n}`);
         if (n) {
           this.concernement = this.concernementsByID[n];
+          console.log('can_update', this.concernement.can_update);
         }
       },
       deep: true
     }
   },
   methods: {
-    ...mapActions(ConcernementsStore,['setMapMode'])
+    ...mapActions(ConcernementsStore,['setMapMode']),
   },
   components: {
-    SvgIcon
+    SvgIcon,
+    ContentEditable
   }
 }
 </script>
@@ -62,7 +67,20 @@ export default {
   <header ref="cartouche_header">
     <div class="concernement-cartouche-icons">
       <label :class="{ hidden: headerreduced }">{{ ct_concernement.title.description }}</label>
-      <h2 :class="{ ellipsed: headerreduced }">{{ concernement.title }}</h2>
+      
+      <ContentEditable 
+        tag="h2"
+        :value="concernement.title" 
+        :class="{ ellipsed: headerreduced }" 
+        :contenteditable="concernement.can_update"
+        :data="{
+          entitytype: 'node',
+          bundle: 'concernement',
+          nid: this.concernement.id,
+          field: 'title'
+        }" />
+
+
       <!-- <nav class="icons">
         <ul>
           <li v-if="concernement.has_recit" >

+ 95 - 0
src/components/misc/ContentEditable.vue

@@ -0,0 +1,95 @@
+<script>
+
+import REST from '@api/rest-axios'
+// import JSONAPI from '@api/json-axios'
+// import qs from 'querystring-es3'
+
+// import { print } from 'graphql/language/printer'
+// import gql from 'graphql-tag'
+// import GQL from '@api/graphql-axios'
+
+import { mapActions, mapState } from 'pinia'
+import { UserStore } from '@stores/user'
+
+
+export default {
+  props: {
+    tag: String, 
+    value: String, 
+    contenteditable : {
+      type : [Boolean, String],
+      default : true,
+    },
+    html : {
+      type : [Boolean, String],
+      default : false,
+    }, 
+    data: Object
+  },
+  // emits: ['returned'],
+  data(){
+    return {
+      
+    }
+  },
+  computed: {
+    ...mapState(UserStore,['csrf_token']),
+  },
+  created () {
+    console.log('ContentEditable created');
+  },
+  mounted () {
+
+  },
+  methods: {
+    onContentEditableFocusOut(e){
+      console.log('onContentEditableFocusOut', e);
+      // console.log('onContentEditableFocusOut data', this.data);
+      let new_field_content = this.html ? e.target.innerHTML : e.target.innerText;
+      // console.log('onContentEditableFocusOut', new_field_content);
+      this.save(new_field_content)
+    },
+    save(content){
+      console.log('save csrf_token', this.csrf_token);
+      const params = {
+        type: this.data.bundle,
+        nid: [{"value":this.data.nid}],
+        [this.data.field]: {value: content}
+      };
+      const configs = {
+        headers: {
+          'X-CSRF-Token': this.csrf_token,
+          // Authorization: `Bearer ${this.csrf_token}`,
+          // 'customheader': "bobobo"
+        }
+      };
+      console.log('REST', REST);
+      REST.patch(`/node/${this.data.nid}?_format=json`, params, configs)
+        .then(({ data }) => {
+          console.log('user REST post node data', data)
+          // TODO if success update the data in pinia 
+        })
+        .catch(error => {
+          console.warn('Issue with post node', error)
+          Promise.reject(error)
+        })
+    }
+  },
+}
+</script>
+
+<template>
+  <component
+    v-if="!html" 
+    :is="tag"
+    :contenteditable="contenteditable"
+    @focusout="onContentEditableFocusOut"
+  >{{ value }}</component>
+  <component
+    v-else
+    :is="tag"
+    v-html="value"
+    :contenteditable="contenteditable"
+    @focusout="onContentEditableFocusOut"
+  />
+</template>

+ 23 - 18
src/stores/user.js

@@ -38,27 +38,32 @@ export const UserStore = defineStore({
             this.name = data.attributes.name
             this.isloggedin = true //data.attributes.status
             console.log('user store checkuser isloggedin', this.isloggedin);
-
-            this.userGetRoles()
-              .then(({ data : { data  : { user } } }) => {
-                console.log('graphql user loaded', user)
-                this.roles = user.roles
-                this.checkIsAdmin()
-              })
-              .catch(error => {
-                console.warn('Issue with graphql user loading', error)
-                Promise.reject(error)
-              })
+            
+            this.getUser(); // necessery to get the csrf-token
+            
+            // this.userGetRoles()
+            //   .then(({ data : { data  : { user } } }) => {
+            //     console.log('graphql user loaded', user)
+            //     this.roles = user.roles
+            //     this.checkIsAdmin()
+            //   })
+            //   .catch(error => {
+            //     console.warn('Issue with graphql user loading', error)
+            //     Promise.reject(error)
+            //   })
           })
         }
       })
+    },
+    getSessionToken(){
+
     },
     userLogin (credentials) {
       console.log('user store userLogin', credentials);
       return new Promise((resolve, reject) => {
-        this.getToken(credentials)
+        this.postCredentials(credentials)
           .then((response) => {
-            console.log('userLogin getToken response', response)
+            console.log('userLogin postCredentials response', response)
 
             if (response.status === 200) {
               this.uid = response.data.current_user.uid
@@ -71,24 +76,24 @@ export const UserStore = defineStore({
                 console.log('User Loggedin')
                 // todo reload concernements
                 // concrnmtStore().reloadConcernements(); // INFO would be good but to much complicated for now, just reload the page
-                window.location.reload(); 
+                // window.location.reload(); 
                 resolve()
               })
             } else {
               this.loginMessage = response.data.message
-              console.warn('Issue with getToken', response)
+              console.warn('Issue with postCredentials', response)
               console.log('user loggein failed', this.loginMessage)
               Promise.reject(new Error('user loggin failed'))
             }
           })
           .catch(error => {
-            console.warn('Issue with Dispatch getToken', error)
+            console.warn('Issue with Dispatch postCredentials', error)
             Promise.reject(error)
           })
       })
     },
-    getToken (credentials) {
-      console.log('userStore getToken', credentials)
+    postCredentials (credentials) {
+      console.log('userStore postCredentials', credentials)
       return REST.post('/user/login?_format=json',
         credentials,
         {