TerrainDeVie.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <script>
  2. import { mapActions, mapState } from 'pinia'
  3. import { ConcernementsStore } from '@stores/concernements'
  4. import { UserStore } from '@/stores/user'
  5. // import { CommonStore } from '@/stores/common'
  6. import REST from '@api/rest-axios'
  7. import { print } from 'graphql/language/printer'
  8. import gql from 'graphql-tag'
  9. import GQL from '@api/graphql-axios'
  10. import EntiteFields from '@api/gql/entite.fragment.gql'
  11. import CartoucheLayout from '@components/layout/CartoucheLayout.vue';
  12. import Entite from '@components/contents/Entite.vue';
  13. import VueSlider from 'vue-slider-component'
  14. import 'vue-slider-component/theme/default.css'
  15. import SvgIcon from '@jamescoyle/vue-icon';
  16. import { mdiChevronRight } from '@mdi/js';
  17. import { mdiChevronDown } from '@mdi/js';
  18. import { mdiStickerPlusOutline } from '@mdi/js';
  19. import ContentEditable from '@components/editable/ContentEditable.vue';
  20. import CheckboxEditable from '@components/editable/CheckboxEditable.vue';
  21. import SelectEditable from '@components/editable/SelectEditable.vue';
  22. export default {
  23. props: ['cid', 'eid'],
  24. data(){
  25. return {
  26. concernement: null,
  27. entite: null,
  28. history_value: 0,
  29. history_slider_ops: null,
  30. details_value: 1,
  31. details_slider_ops: null,
  32. infos_opened: false,
  33. chevronright_path: mdiChevronRight,
  34. chevrondown_path: mdiChevronDown,
  35. headerreduced: false,
  36. mdiStickerPlusOutline_path: mdiStickerPlusOutline,
  37. reloading_concernements: false
  38. }
  39. },
  40. computed: {
  41. ...mapState(ConcernementsStore,['map_mode',
  42. 'opened_concernement',
  43. 'concernementsByID',
  44. 'ct_concernement',
  45. 'ct_entite',
  46. 'detailsZoomValue',
  47. // 'concernements_are_loading'
  48. ]),
  49. ...mapState(UserStore,['name','csrf_token']),
  50. created(){
  51. let d = new Date(this.concernement.created);
  52. console.log('d', d);
  53. return d.toLocaleDateString("fr-FR");//.toISOString().split('T')[0];
  54. },
  55. changed(){
  56. let d = new Date(this.concernement.changed);
  57. console.log('d', d);
  58. return d.toLocaleDateString("fr-FR");//.toISOString().split('T')[0];
  59. },
  60. display_concernement(){
  61. return this.ct_concernement && !this.entite && this.map_mode !== 'puissancedagir' && this.map_mode !== 'doleancer';
  62. },
  63. entity_title_label(){
  64. let menacemaintien_str;
  65. if (this.concernement.entites_byid[this.eid].menacemaintien > 0) {
  66. menacemaintien_str = 'maintenu';
  67. } else {
  68. menacemaintien_str = 'menacé';
  69. }
  70. let actuelfuture_str;
  71. if (this.concernement.entites_byid[this.eid].actuelfuture > 0) {
  72. actuelfuture_str = 'sera';
  73. } else {
  74. actuelfuture_str = 'est';
  75. }
  76. return `Pouvez-vous nommer par qui ou par quoi cet élément ${actuelfuture_str} ${menacemaintien_str} ?`;
  77. }
  78. },
  79. created () {
  80. this.concernement = this.concernementsByID[this.cid];
  81. console.log(`terraindevie created, cid: ${this.cid}, eid: ${this.eid}, this.concernement:`, this.concernement);
  82. //entite
  83. if (this.eid) {
  84. this.loadEntite()
  85. }
  86. // revisions
  87. let data=[];
  88. if (this.concernement && this.concernement.revisions) {
  89. this.concernement.revisions.forEach(rev => {
  90. if (rev.entites.length > 3) {
  91. let d = new Date(rev.changed);
  92. data.push({
  93. 'id': rev.revision_id,
  94. 'changed': d.toLocaleDateString("fr-FR")
  95. })
  96. this.history_value = Math.max(this.history_value, parseInt(rev.revision_id));
  97. }
  98. });
  99. }
  100. if (data.length > 1) {
  101. this.history_slider_ops = {
  102. dotSize:10,
  103. data: data,
  104. 'data-value': 'id',
  105. 'data-label': 'changed',
  106. adsorb: true,
  107. 'hide-label': true
  108. }
  109. } else {
  110. this.history_slider_ops = null;
  111. }
  112. // details
  113. this.details_slider_ops = {
  114. min: 1,
  115. max: 4,
  116. interval: 0.05,
  117. 'hide-label': true,
  118. tooltip: 'none',
  119. dotSize:10,
  120. }
  121. },
  122. // mounted(){
  123. // console.log('terrain de vie mounted', this);
  124. // // this.$refs.cartouche_main.addEventListener('scroll', (event) => {
  125. // // console.log('main is scrolling', event);
  126. // // })
  127. // },
  128. watch: {
  129. history_value: {
  130. handler (n, o) {
  131. // console.log(`TerrainDeVie watch history_value o:${o}, n:${n}`);
  132. this.setActiveRevision(this.cid, n);
  133. },
  134. deep: true
  135. },
  136. details_value: {
  137. handler (n, o) {
  138. // console.log(`TerrainDeVie watch history_value o:${o}, n:${n}`);
  139. this.setDetailsZoomValue(n);
  140. },
  141. deep: true
  142. },
  143. detailsZoomValue: {
  144. handler (n, o) {
  145. // console.log(`TerrainDeVie watch history_value o:${o}, n:${n}`);
  146. this.details_value = n;
  147. },
  148. deep: true
  149. },
  150. cid: {
  151. handler (n,o) {
  152. console.log(`TerrainDeVie watch cid o:${o}, n:${n}`);
  153. if (n) {
  154. this.concernement = this.concernementsByID[n];
  155. }
  156. },
  157. deep: true
  158. },
  159. eid: {
  160. handler (n, o) {
  161. console.log(`TerrainDeVie watch eid o:${o}, n:${n}`);
  162. if (n) {
  163. this.loadEntite()
  164. }else{
  165. this.entite = null;
  166. }
  167. },
  168. deep: true
  169. }
  170. },
  171. methods: {
  172. ...mapActions(ConcernementsStore, ['setActiveRevision',
  173. 'setDetailsZoomValue',
  174. 'loadConcernements',
  175. 'reloadConcernements',
  176. 'reloadConcernementEntites']),
  177. // 'loadConcernementsRevisions'
  178. onClickInfos(){
  179. this.infos_opened = !this.infos_opened;
  180. },
  181. loadEntite(){
  182. const ast = gql`{
  183. entite (id: ${this.eid}) {
  184. ...EntiteFields
  185. }
  186. }
  187. ${EntiteFields}
  188. `
  189. console.log('ast', ast);
  190. GQL.post('', { query: print(ast) })
  191. .then(({data: { data: { entite }}}) => {
  192. console.log('load entite loaded', entite)
  193. this.entite = entite
  194. })
  195. .catch(error => {
  196. console.warn('Issue with load entite', error)
  197. })
  198. },
  199. onMainScrolled(scrolled){
  200. // console.log('this.$refs', this.$refs);
  201. let cartouche_main = this.$refs.cartouche_layout.$refs.cartouche_main;
  202. // console.log('cartouche_main', cartouche_main);
  203. if(scrolled && cartouche_main.scrollHeight > 600){
  204. this.headerreduced = true;
  205. } else {
  206. this.headerreduced = false;
  207. }
  208. },
  209. onContentEditableFocusOut(e){
  210. console.log('onContentEditableFocusOut', e);
  211. let new_field_content = e.target.innerText;
  212. console.log('onContentEditableFocusOut', new_field_content);
  213. console.log('onContentEditableFocusOut this.concernement.title', this.concernement.title);
  214. },
  215. addEntite(e){
  216. console.log('add entite');
  217. this.reloading_concernements = true;
  218. // 1 create entite node
  219. this.createEntiteNode()
  220. .then((entite) => {
  221. console.log('createEntiteNode then node', entite);
  222. // 2 create entite paragraph with entite in it
  223. this.createEntiteParag(entite)
  224. .then((parag) => {
  225. console.log('createEntiteParag then parag', parag);
  226. // 3 record on concernement field_entites
  227. this.recordConcernementEntiteField(parag)
  228. .then((concernement) => {
  229. console.log('concernement', concernement);
  230. // TODO reload the map item
  231. this.reloadConcernementEntites(concernement.nid[0].value)
  232. .then(() => {
  233. this.reloading_concernements = false;
  234. });
  235. // this.reloadConcernements()
  236. // .then(() => {
  237. // this.reloading_concernements = false;
  238. // });
  239. })
  240. })
  241. })
  242. },
  243. createEntiteNode(){
  244. return new Promise((resolve, reject) => {
  245. // 1 create entite node
  246. const params_node_entite = {
  247. type: 'entite',
  248. title: [{value:'Titre à personaliser'}],
  249. field_confidentialite: [{value:'confidentialite_public'}]
  250. };
  251. const configs = {
  252. headers: {'X-CSRF-Token': this.csrf_token}
  253. };
  254. REST.post(`/node?_format=json`, params_node_entite, configs)
  255. .then(({ data }) => {
  256. console.log('REST post new node entite', data);
  257. resolve(data)
  258. })
  259. .catch(error => {
  260. console.warn(`Issue with post new paragraph source`, error)
  261. reject(error)
  262. })
  263. })
  264. },
  265. createEntiteParag(entite){
  266. return new Promise((resolve, reject) => {
  267. // 2 create paragraphe
  268. const params_parag_entite = {
  269. type: [{target_id: 'entite_concernement'}],
  270. parent_type:{value: 'node'},
  271. parent_id:{value: this.cid},
  272. parent_field_name:{value: 'field_entite'}, // entity reference revision
  273. 'field_entite':[{target_id: entite.nid[0].value}] // entity reference
  274. };
  275. const configs = {
  276. headers: {'X-CSRF-Token': this.csrf_token}
  277. };
  278. REST.post(`/entity/paragraph?_format=json`, params_parag_entite, configs)
  279. .then(({ data }) => {
  280. console.log('REST post new source parag', data);
  281. resolve(data)
  282. })
  283. .catch(error => {
  284. console.warn(`Issue with post new paragraph source`, error)
  285. reject(error)
  286. })
  287. })
  288. },
  289. recordConcernementEntiteField(parag){
  290. return new Promise((resolve, reject) => {
  291. // 3 record concernement field_entite
  292. // get all the field_entite values, we don't want to ersae everything
  293. let entites = [];
  294. this.concernement.entites.forEach((entite) =>{
  295. entites.push({
  296. target_id: entite.id,
  297. target_revision_id: entite.revision_id
  298. })
  299. })
  300. // add the new field value
  301. entites.push({
  302. target_id: parag.id[0].value,
  303. target_revision_id: parag.revision_id[0].value
  304. })
  305. console.log('entites', entites);
  306. const params_node = {
  307. type: 'concernement',
  308. nid: [{value: this.cid}],
  309. 'field_entite': entites
  310. };
  311. const configs = {
  312. headers: {'X-CSRF-Token': this.csrf_token}
  313. };
  314. REST.patch(`/node/${this.cid}?_format=json`, params_node, configs)
  315. .then(({ data }) => {
  316. console.log('REST patch entite new field_sources', data)
  317. resolve(data)
  318. })
  319. .catch(error => {
  320. console.warn(`Issue with patch node entite field_sources`, error)
  321. reject(error)
  322. })
  323. // resolve('test')
  324. })
  325. }
  326. },
  327. components: {
  328. CartoucheLayout,
  329. Entite,
  330. VueSlider,
  331. SvgIcon,
  332. ContentEditable,
  333. CheckboxEditable,
  334. SelectEditable
  335. }
  336. }
  337. </script>
  338. <template>
  339. <CartoucheLayout ref="cartouche_layout" :cid="cid" @main_scrolled="onMainScrolled">
  340. <template v-slot:header>
  341. <div class="entite">
  342. <!-- TODO update entite with revisions -->
  343. <label v-if="entite" class="menacemaintient" :class="{ hidden: headerreduced}">{{ entity_title_label }}</label>
  344. <!-- <h3 v-if="entite" class="entite-title">{{ entite.title }}</h3> -->
  345. <ContentEditable
  346. v-if="entite"
  347. tag="h3"
  348. :value="entite.title"
  349. class="entite-title"
  350. :contenteditable="entite.can_update"
  351. :data="{
  352. entitytype: 'node',
  353. bundle: 'entite',
  354. id: this.entite.id,
  355. field: {field_name: 'title', value:'value'}
  356. }" />
  357. <CheckboxEditable
  358. v-if="entite && entite.can_update"
  359. :checked="entite.agissante"
  360. label="Entité action"
  361. :data="{
  362. entitytype: 'node',
  363. bundle: 'entite',
  364. nid: this.entite.id,
  365. field: 'field_entite_agissante'
  366. }" />
  367. <SelectEditable
  368. v-if="entite && entite.can_update"
  369. label="Confidentialité"
  370. :value="entite.confidentialite"
  371. :options="{
  372. 'confidentialite_prive': 'privé',
  373. 'confidentialite_interne': 'interne',
  374. 'confidentialite_public': 'public'
  375. }"
  376. :data="{
  377. entitytype: 'node',
  378. bundle: 'entite',
  379. nid: this.entite.id,
  380. field: 'field_confidentialite'
  381. }" />
  382. </div>
  383. </template>
  384. <template v-slot:main>
  385. <!-- concernement -->
  386. <template v-if="!entite">
  387. <section v-if="concernement.description || concernement.can_update" class="description">
  388. <label v-if="ct_concernement">{{ ct_concernement.field_description.description }}</label>
  389. <ContentEditable
  390. tag="p"
  391. :value="concernement.description"
  392. :html="true"
  393. :class="{ ellipsed: headerreduced }"
  394. :contenteditable="concernement.can_update"
  395. :data="{
  396. entitytype: 'node',
  397. bundle: 'concernement',
  398. id: this.concernement.id,
  399. field: {field_name: 'field_description', value:'value'}
  400. }" />
  401. </section>
  402. <section v-if="concernement.caillou || concernement.can_update" class="caillou">
  403. <label v-if="ct_concernement">{{ ct_concernement.field_caillou.description }}</label>
  404. <ContentEditable
  405. tag="p"
  406. :value="concernement.caillou"
  407. :class="{ ellipsed: headerreduced }"
  408. :contenteditable="concernement.can_update"
  409. :data="{
  410. entitytype: 'node',
  411. bundle: 'concernement',
  412. id: this.concernement.id,
  413. field: {field_name: 'field_caillou', value:'value'}
  414. }" />
  415. </section>
  416. <template v-if="concernement.can_update">
  417. <div v-if="!reloading_concernements" @click="addEntite" class="add-entite-btn">
  418. <span>Ajouter une entité</span>
  419. <svg-icon type="mdi" :path="mdiStickerPlusOutline_path"/>
  420. </div>
  421. <div v-else class="add-entite-btn">
  422. <div class="loading">Chargement</div>
  423. </div>
  424. </template>
  425. </template>
  426. <!-- entite -->
  427. <Entite v-if="entite" :concernement="concernement" :entite="entite" :eid="eid" v-on:reloadEntite="loadEntite"/>
  428. </template>
  429. <template v-slot:footer>
  430. <section class="infos">
  431. <svg-icon
  432. type="mdi"
  433. :path="!infos_opened ? chevronright_path : chevrondown_path"
  434. class="open-btn"
  435. @click="onClickInfos"
  436. ></svg-icon>
  437. <div
  438. class="author info"
  439. @click="onClickInfos">
  440. <span>une enquête de</span> {{ concernement.author.username }}<br/>
  441. </div>
  442. <div class="wrapper" :class="{ 'opened': infos_opened }">
  443. <div class="info structure" v-if="concernement.author.structure.length"><span>avec</span> {{ concernement.author.structure[0].name }}<br/></div>
  444. <div class="info lieu" v-if="concernement.lieu.length"><span>à</span> {{ concernement.lieu[0].name }}<br/></div>
  445. <div class="info created"><span>démarrée le</span> {{ created }}<br/></div>
  446. <div class="info changed"><span>mise à jour le</span> {{ changed }}</div>
  447. <div class="info recit-colophon" v-if="concernement.recit_colophon" v-html="concernement.recit_colophon"/>
  448. </div>
  449. </section>
  450. <section class="sliders" v-if="history_slider_ops || details_slider_ops">
  451. <section class="historique" v-if="history_slider_ops">
  452. <label>Historique</label>
  453. <!-- <h3>{{ history_value }}</h3> -->
  454. <vue-slider
  455. ref="slider"
  456. v-model="history_value"
  457. v-bind="history_slider_ops"
  458. ></vue-slider>
  459. </section>
  460. <section class="details" v-if="details_slider_ops && map_mode === 'terraindevie'">
  461. <label>Détails</label>
  462. <!-- <h3>{{ details_value }}</h3> -->
  463. <vue-slider
  464. ref="details_slider"
  465. v-model="details_value"
  466. v-bind="details_slider_ops"
  467. ></vue-slider>
  468. </section>
  469. </section>
  470. </template>
  471. </CartoucheLayout>
  472. </template>
  473. <style lang="css">
  474. span.entite-point{
  475. color: #01ffe2;
  476. }
  477. </style>