From b4fdbb9f208ba1715c107717c9656adb7fbd6f12 Mon Sep 17 00:00:00 2001
From: ManUtopiK <emmanuel.salomon@gmail.com>
Date: Fri, 17 Sep 2021 19:27:47 +0200
Subject: [PATCH] Improve lexique search terms and added synonyms

---
 components/global/Lexique.vue  | 65 ++++++++++++++++++++--------------
 components/page/PageBottom.vue |  2 +-
 content/lexique/blockchain.md  |  6 ++++
 content/lexique/junistes.md    |  2 +-
 content/lexique/noeud.md       |  7 ++--
 content/lexique/trm.md         | 19 +++++-----
 nuxt.config.js                 | 46 +++++++++++++++++++++++-
 pages/lexique/_slug.vue        | 29 +++++++--------
 static/admin/config.yml        |  1 +
 9 files changed, 121 insertions(+), 56 deletions(-)
 create mode 100644 content/lexique/blockchain.md

diff --git a/components/global/Lexique.vue b/components/global/Lexique.vue
index c34f43c3..bb88a3d7 100644
--- a/components/global/Lexique.vue
+++ b/components/global/Lexique.vue
@@ -1,27 +1,30 @@
 <template>
   <span
     class="tooltip-box relative"
-    :class="page && 'cursor-pointer'"
-    v-on="page ? { click: onClick } : {}"
+    :class="document && 'cursor-pointer'"
+    v-on="document ? { click: onClick } : {}"
   >
-    <abbr :aria-label="page ? page.title : title" :title="page ? '' : null">
+    <abbr
+      :aria-label="document ? document.title : title"
+      :title="document ? '' : null"
+    >
       <slot />
     </abbr>
 
     <client-only>
       <div
-        v-if="page"
-        class="tooltip absolute bg-gray-200 invisible opacity-0 left-1/2 px-4 py-2 rounded-xl shadow-lg text-gray-600 text-sm z-50"
+        v-if="document"
+        class="tooltip absolute bg-blue-100 border-blue-200 border invisible opacity-0 left-1/2 px-4 py-3 rounded-xl shadow-2xl text-gray-600 text-sm z-50"
       >
         <span class="triangle absolute"></span>
 
-        <span v-if="title" class="block mb-2 uppercase">
-          {{ title }}
+        <span class="block font-bold text-lg uppercase">
+          {{ document.title }} :
         </span>
 
-        <span>{{ page.description }}</span>
+        <span>{{ document.description }}</span>
 
-        <span class="block font-extralight mt-2 text-xs">
+        <span class="block font-light mt-3 text-xs text-purple-800">
           {{ $t('lexique.tooltipReadmore') }}
         </span>
       </div>
@@ -40,23 +43,24 @@ export default {
   },
   data() {
     return {
-      page: null,
+      document: null,
     }
   },
   async fetch() {
     if (this.computedTitle) {
-      try {
-        this.page = await this.$content(
-          'lexique',
-          this.computedTitle.toLowerCase()
-        )
-          .only(['title', 'description'])
-          .fetch()
-      } catch (e) {
-        // eslint-disable-next-line no-console
-        console.warn(
-          `lexique term '${this.computedTitle}' not found in /content/lexique/ from file ${this.$parent.fileUrl}`
-        )
+      const term = this.computedTitle.toLowerCase()
+      const results = await this.$content('lexique')
+        .where({
+          $or: [
+            { title: { $regex: [`^${term}$`, 'i'] } }, // case insensitive
+            { synonyms: { $contains: term } }, // search in synonyms
+          ],
+        })
+        .limit(1)
+        .fetch()
+
+      if (results.length) {
+        this.document = results[0]
       }
     }
   },
@@ -68,7 +72,7 @@ export default {
   },
   methods: {
     onClick() {
-      this.$router.push(`/lexique/${this.computedTitle.toLowerCase()}`)
+      this.$router.push(this.document.path)
     },
   },
 }
@@ -92,11 +96,20 @@ abbr {
   opacity: 1;
 }
 .triangle {
-  border-width: 0 6px 6px;
+  border-width: 0 10px 10px;
   border-color: transparent;
-  border-bottom-color: rgba(229, 231, 235, var(--tw-bg-opacity));
-  top: -6px;
+  border-bottom-color: #bfdbfe;
+  top: -10px;
   left: 50%;
   transform: translateX(-50%);
 }
+.triangle:before {
+  content: '';
+  position: absolute;
+  border-width: 0 10px 10px;
+  border-color: transparent;
+  border-bottom-color: #dbeafe;
+  top: 2px;
+  left: -10px;
+}
 </style>
diff --git a/components/page/PageBottom.vue b/components/page/PageBottom.vue
index 25f51118..39102ae9 100644
--- a/components/page/PageBottom.vue
+++ b/components/page/PageBottom.vue
@@ -6,7 +6,7 @@
     </span>
 
     <a
-      :href="`admin/#/collections${document.dir}/entries/${document.slug}`"
+      :href="`/admin/#/collections${document.dir}/entries/${document.slug}`"
       target="_blank"
       rel="noopener noreferrer"
       class="hover:underline"
diff --git a/content/lexique/blockchain.md b/content/lexique/blockchain.md
new file mode 100644
index 00000000..7088615d
--- /dev/null
+++ b/content/lexique/blockchain.md
@@ -0,0 +1,6 @@
+---
+title: Blockchain
+description: Mode de stockage et de transmission de données sous forme de blocs
+  liés les uns aux autres et protégés contre toute modification.
+---
+TODO: Expliquer le fonctionnement...
\ No newline at end of file
diff --git a/content/lexique/junistes.md b/content/lexique/junistes.md
index db67751f..92ec3822 100644
--- a/content/lexique/junistes.md
+++ b/content/lexique/junistes.md
@@ -1,7 +1,7 @@
 ---
 title: Junistes
 description: Les utilisateurs de la june
-synonym:
+synonyms:
   - juniste
   - jüniste
   - jünistes
diff --git a/content/lexique/noeud.md b/content/lexique/noeud.md
index dd3895bc..928f80db 100644
--- a/content/lexique/noeud.md
+++ b/content/lexique/noeud.md
@@ -1,9 +1,10 @@
 ---
-title: "NÅ“ud"
+title: NÅ“ud
 description: Un nœud est un ordinateur qui fait fonctionner la monnaie.
-synonym:
-  - nœud
+synonyms:
   - noeud
+  - nœuds
+  - noeuds
 ---
 Les nœuds sont les ordinateurs équipés du logiciel Duniter qui gère la <lexique>blockchain</lexique> de la Ğ1.
 Ces ordinateurs sont mis à disposition par des utilisateurs de la Ğ1.
diff --git a/content/lexique/trm.md b/content/lexique/trm.md
index 0ed907dc..475c9afe 100644
--- a/content/lexique/trm.md
+++ b/content/lexique/trm.md
@@ -1,16 +1,19 @@
 ---
-title: TRM
-description: Théorie Relative de la Monnaie
+title: Théorie Relative de La monnaie
+description: Théorie monétaire utilisé comme fondement de la monnaie libre
+synonyms:
+  - trm
 ---
 ## Une Théorie et un livre
 
-La Théorie est décrite par Stéphane Laborde dans son livre *"la théorie relative de la monnaie"*. 
+La théorie est décrite par Stéphane Laborde dans son livre *"la théorie relative de la monnaie"*.
 
 ### La théorie
 
-Basée sur 4 axiomes : les <lexique>4 libertés économiques</lexique>.   
-Avec un calcul mathématique, pour respecté ces 4 libertés la théorie décrit la création monétaire sous forme d'"un dividende universel tel que : **DU=c(M/N)**\
-Le Dividende Universel 'DU' est une portion 'c' de la masse monétaie par individu 'M/N'
+Basée sur 4 axiomes : les <lexique>4 libertés économiques</lexique>.\
+Avec un calcul mathématique, pour respecter ces 4 libertés la théorie décrit la création monétaire sous forme d'un dividende universel tel que : **DU=c(M/N)**.\
+Le Dividende Universel 'DU' est une portion 'c' de la masse monétaire par individu 'M/N'.
 
-### Le livre 
-Le livre est disponible en vente en ligne, mais également librement sur le site <http://trm.creationmonetaire.info/> Ou en pdf sur <https://www.econologie.com/fichiers/partager3/1319653067F3tO5k.pdf>
\ No newline at end of file
+### Le livre
+
+Le livre est disponible en vente en ligne, mais également librement sur le site <http://trm.creationmonetaire.info/> Ou en PDF sur <https://www.econologie.com/fichiers/partager3/1319653067F3tO5k.pdf>
\ No newline at end of file
diff --git a/nuxt.config.js b/nuxt.config.js
index da11f698..fc2c354b 100644
--- a/nuxt.config.js
+++ b/nuxt.config.js
@@ -210,9 +210,53 @@ export default {
         return
       file.data = JSON.stringify(JSON.parse(file.data).ressources)
     },
-    'content:file:beforeInsert': (document) => {
+    async 'content:file:beforeInsert'(document) {
       if (document.extension === '.md') {
+        /*
+         * Add reading time
+         */
         document.readingTime = require('reading-time')(document.text)
+
+        /*
+         * Add lexique terms and check if terms exist
+         */
+        // Search lexique terms
+        const lexiqueTerms = [
+          ...document.text.matchAll(
+            /<lexique[>]([^<]*)<\/|<lexique\s+title="(.*)"/g
+          ),
+        ].map((match) => {
+          return (match[2] || match[1]).toLowerCase() // Priority for term in title=""
+        })
+
+        if (lexiqueTerms.length) {
+          // Add terms to document
+          document.lexiqueTerms = [...new Set(lexiqueTerms)] // remove duplicate
+
+          const { $content } = require('@nuxt/content')
+          // Check if term exists
+          if ($content) {
+            for (let i = 0; i < document.lexiqueTerms.length; i++) {
+              const term = document.lexiqueTerms[i]
+              const lexique = await $content('lexique')
+                .where({
+                  $or: [
+                    { title: { $regex: [`^${term}$`, 'i'] } }, // case insensitive
+                    { synonyms: { $contains: term } }, // search in synonyms
+                  ],
+                })
+                .limit(1)
+                .fetch()
+
+              if (!lexique.length) {
+                // eslint-disable-next-line no-console
+                console.warn(
+                  `Term "${term}" in file ${document.path}.${document.extension} doesn't exist`
+                )
+              }
+            }
+          }
+        }
       }
     },
   },
diff --git a/pages/lexique/_slug.vue b/pages/lexique/_slug.vue
index 21bbf721..9e883d5c 100644
--- a/pages/lexique/_slug.vue
+++ b/pages/lexique/_slug.vue
@@ -12,7 +12,9 @@
       <PageToc v-if="document.toc" :document="document" class="mb-8" />
 
       <aside v-if="pagesWithTerm.length" class="mb-8">
-        <h1 class="text-2xl">Pages contenant "{{ document.title }}"</h1>
+        <h1 class="text-xl uppercase text-gray-400 font-bold mb-2">
+          Pages contenant "{{ document.title }}"
+        </h1>
 
         <nuxt-link
           v-for="(item, index) of pagesWithTerm"
@@ -31,29 +33,24 @@
 </template>
 
 <script>
-import { reduceResults, headDocument as head } from '~/libs/helpers'
+import { headDocument as head } from '~/libs/helpers'
 
 export default {
   name: 'LexiqueSlug',
   async asyncData({ $content, params, error }) {
     try {
       const document = await $content('lexique', params.slug).fetch()
-
-      const pagesWithSlot = await $content({ deep: true })
-        .search(`<lexique>${params.slug}</lexique>`)
-        .fetch()
-      const pagesWithProp = await $content({ deep: true })
-        .search(`<lexique title="${params.slug}">`)
+      const synonyms = document.synonyms || []
+
+      const pagesWithTerm = await $content({ deep: true })
+        .where({ $and: [{ extension: '.md' }, { dir: { $ne: '/ui' } }] })
+        .where({
+          lexiqueTerms: {
+            $containsAny: [document.title.toLowerCase(), ...synonyms],
+          },
+        })
         .fetch()
 
-      // Merge and deduplicate
-      const slugs = new Set(pagesWithSlot.map((item) => item.slug))
-      const results = [
-        ...pagesWithSlot,
-        ...pagesWithProp.filter((item) => !slugs.has(item.slug)),
-      ]
-      const pagesWithTerm = reduceResults(results)
-
       return { document, pagesWithTerm }
     } catch (err) {
       return error({ statusCode: 404, message: err.message })
diff --git a/static/admin/config.yml b/static/admin/config.yml
index 6a78fe35..654bc122 100644
--- a/static/admin/config.yml
+++ b/static/admin/config.yml
@@ -75,6 +75,7 @@ collections:
     fields:
       - { label: Titre, name: title, widget: string }
       - { label: Résumé, name: description, widget: string, hint: Le résumé est affiché dans le lexique et dans les bulles d'info. }
+      - { label: Synonymes, name: synonyms, widget: list, hint: Synonymes du titre. Séparé par des virgules. Insensible à la casse. }
       - { label: Définition, name: body, widget: markdown }
 
   - name: ressources
-- 
GitLab