From ed48e12d8adbdde9433c55dc20035f61778cdc48 Mon Sep 17 00:00:00 2001
From: paidge <paidge_cs@hotmail.com>
Date: Mon, 20 Dec 2021 04:56:35 +0100
Subject: [PATCH] =?UTF-8?q?am=C3=A9liorer=20la=20vue=20d'un=20membre?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 assets/css/_bootstrap.scss |   4 +-
 components/certif/List.vue |  33 +++++++++++
 layouts/default.vue        |   1 +
 nuxt.config.js             |   1 +
 package-lock.json          |  11 ++++
 package.json               |   1 +
 pages/hash/_hash.vue       | 103 ++++++++++++++++++++---------------
 pages/soin.vue             | 109 +++++++++++++++++++++++++++++++++++++
 plugins/filters.js         |   9 +++
 9 files changed, 225 insertions(+), 47 deletions(-)
 create mode 100644 components/certif/List.vue
 create mode 100644 pages/soin.vue
 create mode 100644 plugins/filters.js

diff --git a/assets/css/_bootstrap.scss b/assets/css/_bootstrap.scss
index 2b295d5..80d8123 100644
--- a/assets/css/_bootstrap.scss
+++ b/assets/css/_bootstrap.scss
@@ -18,10 +18,10 @@
 //  @import "~bootstrap/scss/custom-forms";
  @import "~bootstrap/scss/nav";
  @import "~bootstrap/scss/navbar";
-//  @import "~bootstrap/scss/card";
+ @import "~bootstrap/scss/card";
  @import "~bootstrap/scss/breadcrumb";
 //  @import "~bootstrap/scss/pagination";
-//  @import "~bootstrap/scss/badge";
+ @import "~bootstrap/scss/badge";
 //  @import "~bootstrap/scss/jumbotron";
 //  @import "~bootstrap/scss/alert";
 //  @import "~bootstrap/scss/progress";
diff --git a/components/certif/List.vue b/components/certif/List.vue
new file mode 100644
index 0000000..81a1875
--- /dev/null
+++ b/components/certif/List.vue
@@ -0,0 +1,33 @@
+<template>
+<ul class="list-group">
+    <li class="list-group-item" v-for="certif in certifs">
+        <div>{{ getNeighbor(certif) }}</div>
+        <small class="text-muted">Expire le {{ certif.expires_on | formatDate }}</small>
+    </li>
+</ul>
+</template>
+
+<script>
+export default {
+    props : {
+        certifs : Array,
+        type : {
+            type: String,
+            required: true,
+            validator: function (value) {
+                const types = ['recieved','sent']
+                return types.indexOf(value) !== -1
+            }
+        }
+    },
+    methods : {
+        getNeighbor(certif) {
+            return this.type == "recieved" ? certif.from.uid : certif.to.uid
+        }
+    }
+}
+</script>
+
+<style>
+
+</style>
\ No newline at end of file
diff --git a/layouts/default.vue b/layouts/default.vue
index 35fe300..6ea2fbb 100644
--- a/layouts/default.vue
+++ b/layouts/default.vue
@@ -16,6 +16,7 @@ export default {
           items : [
               {path: '/appolo',title: 'Appolo'},
               {path: '/chartjs',title: 'ChartJS'},
+              {path: '/soin',title: 'Prendre soin de ses contacts'}
           ]},
           {
           title: 'Un menu',
diff --git a/nuxt.config.js b/nuxt.config.js
index f1c0d97..1218695 100644
--- a/nuxt.config.js
+++ b/nuxt.config.js
@@ -26,6 +26,7 @@ export default {
 
   // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
   plugins: [
+    '~plugins/filters.js'
   ],
 
   // Auto import components: https://go.nuxtjs.dev/config-components
diff --git a/package-lock.json b/package-lock.json
index fcad10c..1b527d9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
         "bootstrap": "^4.6.0",
         "chart.js": "^3.6.2",
         "core-js": "^3.15.1",
+        "dayjs": "^1.10.7",
         "graphql-tag": "^2.12.6",
         "nuxt": "^2.15.8",
         "nuxt-purgecss": "^1.0.0"
@@ -9590,6 +9591,11 @@
       "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
       "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
     },
+    "node_modules/dayjs": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
+      "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
+    },
     "node_modules/de-indent": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
@@ -29164,6 +29170,11 @@
       "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz",
       "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw=="
     },
+    "dayjs": {
+      "version": "1.10.7",
+      "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.10.7.tgz",
+      "integrity": "sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig=="
+    },
     "de-indent": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
diff --git a/package.json b/package.json
index 65cfb1d..569e357 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
     "bootstrap": "^4.6.0",
     "chart.js": "^3.6.2",
     "core-js": "^3.15.1",
+    "dayjs": "^1.10.7",
     "graphql-tag": "^2.12.6",
     "nuxt": "^2.15.8",
     "nuxt-purgecss": "^1.0.0"
diff --git a/pages/hash/_hash.vue b/pages/hash/_hash.vue
index db76383..9621541 100644
--- a/pages/hash/_hash.vue
+++ b/pages/hash/_hash.vue
@@ -1,5 +1,5 @@
 <template>
-  <main class="content">
+  <main class="content container">
     <div
       class="spinner-border text-primary mx-auto d-block mb-3"
       role="status"
@@ -7,31 +7,38 @@
       <span class="sr-only">Loading...</span>
     </div>
     <div v-if="!$apollo.queries.idFromHash.loading">
-      <h2 class="display-2 text-center mb-5">{{ idFromHash.uid }}</h2>
       <div class="row">
-        <div class="col-8 m-auto">
-          <div class="table-responsive">
-            <table class="table striped">
-              <thead>
-                <th>Champ</th>
-                <th>Valeur</th>
-              </thead>
-              <tbody>
-                <tr v-for="(value, propertyName) in idFromHash" :key="propertyName">
-                  <td>{{ propertyName }}</td>
-                  <td>{{ value }}</td>
-                </tr>
-              </tbody>
-            </table>
+        <div class="col-6 mx-auto mt-3">
+          <div class="card">
+            <div class="card-body">
+              <h2 class="card-title">{{ idFromHash.uid }} <small><span class="badge badge-secondary">{{ idFromHash.status }}</span></small></h2>
+              <div class="card-subtitle mb-2 text-muted">{{ idFromHash.pubkey }}</div>
+              <div> Référent : {{ isReferent ? 'Oui' : 'Non' }}</div>
+              <div> Qualité : {{ Math.round(idFromHash.quality.ratio*100)/100 }}</div>
+              <div> Date limite d'adhésion : {{ idFromHash.limitDate | formatDate }}</div>
+              <div> Date avant de manquer de certifs : {{ idFromHash.received_certifications.limit | formatDate }}</div>
+              <div> Pourra certifier à partir du : {{ idFromHash.minDatePassed || 'Déjà dispo' }}</div>
+              <div> Nbre de certifs disponibles  : {{ 100-idFromHash.sent_certifications.length }}</div>
+            </div>
           </div>
         </div>
       </div>
+      <div class="row mt-3">
+        <div class="col-6">
+          <h3>Certifications reçues</h3>
+          <CertifList :certifs="idFromHash.received_certifications.certifications" type="recieved" />
+        </div>
+        <div class="col-6">
+          <h3>Certifications envoyées</h3>
+          <CertifList :certifs="idFromHash.sent_certifications" type="sent" />
+        </div>
+      </div>
     </div>
   </main>
 </template>
 
 <script>
-import gql from "graphql-tag";
+import gql from "graphql-tag"
 
 export default {
   data() {
@@ -39,53 +46,53 @@ export default {
     return {};
   },
   // Fonctions locales
-  methods: {},
+  methods: {
+
+  },
   apollo: {
     idFromHash: {
       query: gql`
         query Search($hash: Hash!) {
           idFromHash(hash: $hash) {
+            ...memberAttributes
             pubkey
-            uid
-            status
-            hash
-            membership_pending
-            membership_pending_block {
-              number
-            }
-            membership_pending_limitDate
-            id_written_block {
-              number
-            }
-            lastApplication {
-              number
-            }
-            limitDate
             isLeaving
             sentry
-            received_certifications {
-              limit
+            minDate
+            minDatePassed
+            membership_pending
+            limitDate
+            quality {
+              ratio
             }
             distance {
               value {
                 ratio
               }
+              dist_ok
             }
-            distanceE {
-              value {
-                ratio
+            received_certifications {
+              limit
+              certifications {
+                from {
+                  ...memberAttributes
+                }
+                expires_on
               }
             }
-            quality {
-              ratio
-            }
-            qualityE {
-              ratio
+            sent_certifications {
+              to {
+                ...memberAttributes
+              }
+              expires_on
             }
-            minDate
-            minDatePassed
           }
         }
+        fragment memberAttributes on Identity {
+          uid
+          hash
+          status
+        }
       `,
       variables() {
         return { hash: this.$route.params.hash };
@@ -99,6 +106,12 @@ export default {
     // Mise à jour du fil d'ariane au chargement
     $nuxt.$emit("changeRoute", this.breadcrumb);
   },
+  computed: {
+    isReferent () {
+      const nb_certifs_referent = 5
+      return this.idFromHash.received_certifications.certifications.length > nb_certifs_referent && this.idFromHash.sent_certifications.length > nb_certifs_referent
+    }
+  }
 };
 </script>
 
diff --git a/pages/soin.vue b/pages/soin.vue
new file mode 100644
index 0000000..e8d99b8
--- /dev/null
+++ b/pages/soin.vue
@@ -0,0 +1,109 @@
+<template>
+<main class="content">
+  <h2 class="display-2 text-center mb-5">Prendre soin de ses contacts</h2>
+  <div class="row mb-4">
+    <div class="col-6 m-auto text-center">
+      <label for="rech" class="form-label">Votre recherche</label>
+      <input type="text" class="form-control" id="rech" aria-describedby="rechHelp" v-model="param" autocomplete="off">
+      <small id="rechHelp" class="form-text text-muted">Saisissez le début d'un pseudo ou d'une clé publique</small>
+    </div>
+  </div>
+  <NavigationLoader :isLoading="$apollo.queries.idSearch.loading" class="d-block mx-auto" />
+  <div class="row" v-if="idSearch && !$apollo.queries.idSearch.loading && param.length > 2">
+    <div class="col-8 m-auto">
+      <div class="table-responsive">
+        <table class="table striped">
+          <thead>
+            <tr>
+              <th scope="col">UID</th>
+              <th scope="col" class="d-none d-xl-table-cell">PUBKEY</th>
+              <th scope="col" class="d-none d-sm-table-cell">STATUS</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr v-for="member in idSearch.ids" :key="member.uid"
+              @click="redirect('/hash/' + member.hash)"
+              :class="{
+                'table-danger' : member.status == 'REVOKED',
+                'table-success' : member.status == 'MEMBER',
+                'table-info' : member.status == 'NEWCOMER',
+                'table-warning' : member.status == 'MISSING',
+            }">
+              <th scope="row">{{ member.uid }}</th>
+              <td class="d-none d-xl-table-cell">{{ member.pubkey }}</td>
+              <td class="d-none d-sm-table-cell">{{ member.status }}</td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+  </div>
+</main>
+</template>
+
+<script>
+import gql from 'graphql-tag'
+
+export default {
+  data() {
+    // Variables locales
+    return {
+      // Fil d'ariane
+      breadcrumb: [
+        {
+          text: 'Accueil',
+          to: '/'
+        },
+        {
+          text: 'Prendre soin de ses contacts',
+          active: true
+        }
+      ],
+      // Requête graphQL
+      param: '',
+    }
+  },
+  // Fonctions locales
+  methods: {
+    redirect(path) {
+      this.$router.push({ path: path })
+    }
+  },
+  apollo: {
+    idSearch : {
+      query: gql`
+      query Search($hint: String) {
+        idSearch(with: {hint: $hint}) {
+          ids {
+            pubkey
+            uid
+            status
+            hash
+          }
+        }
+      } `,
+      variables(){return {hint:this.param}},
+      skip() {return this.param.length < 3}
+    } 
+  },
+  mounted () {
+    // Mise à jour du fil d'ariane au chargement
+    $nuxt.$emit('changeRoute',this.breadcrumb)
+  }
+}
+</script>
+
+<style lang="sass" scoped>
+// CSS Lié au composant
+.table.striped tbody
+  color: var(--dark)
+  tr
+    opacity: .9
+    cursor: pointer
+
+  &:nth-child(2n+1)
+    opacity: .7
+
+  &:hover
+    opacity: 1
+</style>
\ No newline at end of file
diff --git a/plugins/filters.js b/plugins/filters.js
new file mode 100644
index 0000000..1b62a42
--- /dev/null
+++ b/plugins/filters.js
@@ -0,0 +1,9 @@
+import Vue from 'vue'
+import dayjs from 'dayjs'
+import 'dayjs/locale/fr'
+
+dayjs.locale('fr')
+
+Vue.filter('formatDate', (value) => {
+    return dayjs(value*1000).format('D MMMM YYYY')
+})
\ No newline at end of file
-- 
GitLab