Skip to content
Snippets Groups Projects
Commit f8c14a2a authored by Pierre-Jean CHANCELLIER's avatar Pierre-Jean CHANCELLIER
Browse files

Merge branch 'exitsPrevisions' into 'master'

Ajout d'une page de prévisions de sorties

See merge request !5
parents 4051ba17 593f092c
No related branches found
No related tags found
1 merge request!5Ajout d'une page de prévisions de sorties
<template>
<small>
<span
class="badge"
:class="'badge-' + $options.filters.dateStatus(date)">
{{ $d(new Date(date * 1000), styleDate) }}
</span>
</small>
</template>
<script>
export default {
props: {
date: {
type: Number,
required: true
},
styleDate: {
type: String,
required: true
},
}
}
</script>
......@@ -34,20 +34,12 @@
</div>
</th>
<td class="text-right py-1">
<small
><span
class="badge"
:class="
'badge-' + $options.filters.dateStatus(certif.expires_on)
"
>{{ $d(new Date(certif.expires_on * 1000), "short") }}</span
></small
>
<small class="d-block"
><span class="badge badge-secondary">{{
$t("traitement")
}}</span></small
>
<BadgeDate
:date="certif.expires_on"
:styleDate="'short'" />
<small class="d-block">
<span class="badge badge-secondary">{{ $t("traitement")}}</span>
</small>
</td>
</tr>
</tbody>
......@@ -134,15 +126,9 @@
</div>
</th>
<td class="text-right py-1">
<small
><span
class="badge"
:class="
'badge-' + $options.filters.dateStatus(certif.expires_on)
"
>{{ $d(new Date(certif.expires_on * 1000), "long") }}</span
></small
>
<BadgeDate
:date="certif.expires_on"
:styleDate="'long'" />
</td>
</tr>
</tbody>
......
......@@ -3,10 +3,52 @@
<table class="table table-striped table-hover">
<thead v-if="displayHead">
<tr>
<th scope="col">UID</th>
<th scope="col" class="d-none d-xl-table-cell" v-if="displayPubkey">
<th scope="col" @click="sort('uid')">
UID
<div class="d-inline-block position-absolute ml-2">
<div
class="up"
:class="{
sorted: currentSortDir == 'desc' && currentSort == 'uid',
invisible: currentSortDir == 'asc' && currentSort == 'uid'
}">
</div>
<div
class="down"
:class="{
sorted: currentSortDir == 'asc' && currentSort == 'uid',
invisible: currentSortDir == 'desc' && currentSort == 'uid'
}">
</div>
</div>
</th>
<th scope="col" class="d-none d-xl-table-cell" v-if="displayPubkey && !displayOnlyDate">
{{ $t("cle.publique.title") }}
</th>
<th scope="col" class="d-none d-xl-table-cell"
@click="sort('limit_date')" v-if="displayOnlyDate">
{{ $t("limitDate") }}
<div class="d-inline-block position-absolute ml-2">
<div
class="up"
:class="{
sorted: currentSortDir == 'desc' && currentSort == 'limit_date',
invisible: currentSortDir == 'asc' && currentSort == 'limit_date'
}">
</div>
<div
class="down"
:class="{
sorted: currentSortDir == 'asc' && currentSort == 'limit_date',
invisible: currentSortDir == 'desc' && currentSort == 'limit_date'
}">
</div>
</div>
</th>
<th scope="col" class="d-none d-sm-table-cell" v-if="displayDate">
{{ $t("membre.datelimpertestatut") }}
</th>
......@@ -14,7 +56,7 @@
</thead>
<tbody>
<tr
v-for="member in members"
v-for="member in membersSorted"
:key="member.uid"
@click="redirect(member.hash)">
<th scope="row">
......@@ -23,9 +65,15 @@
:limitDate="
Math.min(member.received_certifications.limit, member.limitDate)
"
:memberStatus="member.status" />
<BadgeStatus :membre="member" />
:memberStatus="member.status"
v-if="!displayOnlyDate" />
<BadgeStatus :membre="member" v-if="!displayOnlyDate"/>
</th>
<td class="d-none d-xl-table-cell" v-if="displayOnlyDate">
<BadgeDate
:date="adhesion ? member.limitDate : member.received_certifications.limit"
:styleDate="'long'" />
</td>
<td class="d-none d-xl-table-cell" v-if="displayPubkey">
{{ member.pubkey.substring(0, 10) }}
</td>
......@@ -41,6 +89,12 @@
<script>
export default {
data() {
return {
currentSort: "uid",
currentSortDir: "asc"
}
},
props: {
members: {
type: Array,
......@@ -57,7 +111,15 @@ export default {
displayDate: {
type: Boolean,
default: true
}
},
displayOnlyDate: {
type: Boolean,
default: false
},
adhesion: {
type: Boolean,
default: true
},
},
methods: {
redirect(hash) {
......@@ -77,7 +139,64 @@ export default {
"'>" +
this.$d(new Date(date * 1000), "long") +
"</span>"
},
sort(s) {
if (s === this.currentSort) {
this.currentSortDir = this.currentSortDir === "asc" ? "desc" : "asc"
}
this.currentSort = s
}
},
computed: {
membersSorted() {
return this.members
.slice()
.sort((a, b) => {
let modifier = this.currentSortDir === "desc" ? -1 : 1
if (this.currentSort == "limit_date") {
if(this.adhesion) {
if (a["limitDate"] < b["limitDate"]) return -1 * modifier
if (a["limitDate"] > b["limitDate"]) return 1 * modifier
} else {
if (a["received_certifications"]["limit"] < b["received_certifications"]["limit"]) return -1 * modifier
if (a["received_certifications"]["limit"] > b["received_certifications"]["limit"]) return 1 * modifier
}
} else {
if (a["uid"].toLowerCase() < b["uid"].toLowerCase())
return -1 * modifier
if (a["uid"].toLowerCase() > b["uid"].toLowerCase())
return 1 * modifier
}
return 0
})
}
}
}
</script>
<style lang="scss" scoped>
thead th {
position: relative;
cursor: pointer;
background: var(--background-color-secondary);
&:last-child {
padding-right: 1.5rem;
text-align: right;
}
}
.up,
.down {
line-height: 10px;
font-size: 1.1rem;
transform: scale(1.5, 1);
opacity: 0.3;
}
.sorted {
opacity: 1;
}
</style>
......@@ -595,7 +595,7 @@ type Forecast {
"Entry or exit of an identity"
type EventId {
id: Identity!
member: Identity!
"Entry or exit; true if entry"
inOut: Boolean!
......
......@@ -206,3 +206,21 @@ export const FAVORIS = gql`
}
}
`
// Pour la page index
export const NEXT_EXITS = gql`
query NextExits($group: [String!], $start: Int64, $period: Int64) {
memEnds (group: $group, startFromNow: $start, period: $period) {
__typename
pubkey
uid
status
hash
limitDate
received_certifications {
__typename
limit
}
}
}
`
......@@ -62,6 +62,10 @@
"title": "Duniter"
},
"expire": "Expires",
"error": {
"tooSmall": "{0} is too small. It must be between {1} and {2}. The value used is {1}",
"tooBig": "{0} is too big. It must be between {1} and {2}. The value used is {1}"
},
"favoris": {
"enregistre": "Saved to favorites&nbsp;!",
"none": "You don't have any favorites yet",
......@@ -69,10 +73,12 @@
"title": "My favourites"
},
"futuremembers": "Future members",
"futureexits": "Future exits",
"infos": "Informations",
"inout": "Entries and exits of the web of trust for the last 2 days",
"inpreparation": "In preparation",
"lexique": "Lexicon",
"limitDate": "Deadline",
"membre": {
"calculant": {
"desc": "Member using his private key to forge blocks thanks to Duniter installed on a node accessible on the Internet network",
......@@ -150,8 +156,13 @@
"previsions": {
"pardate": "Forecasts by dates",
"parmembre": "Forecasts by members",
"period": {
"title": "Search period",
"desc": "Select the desired number of days between 1 and 30"
},
"title": "Forecasts"
},
"pubkey": "Public key",
"recherche": {
"desc": "Enter the start of a nickname or public key",
"title": "Your search"
......@@ -184,6 +195,7 @@
"title": "Relative Theory of Money (RTM)"
},
"type": "Type",
"uid": "Unique identifier",
"valeur": "Value",
"wot": {
"desc": "Set of the individuals recognized as such by their peers including the links that bind them together through certifications",
......
......@@ -62,6 +62,10 @@
"title": "Duniter"
},
"expire": "Expira el",
"error": {
"tooSmall": "{0} es demasiado pequeño. Debe estar entre {1} y {2}. El valor utilizado es {1}",
"tooBig": "{0} es demasiado grande. Debe estar entre {1} y {2}. El valor utilizado es {1}"
},
"favoris": {
"enregistre": "¡Guardado en favoritos!",
"none": "Aún no tienes favoritos",
......@@ -69,10 +73,12 @@
"title": "Mis favoritos"
},
"futuremembers": "Futuros miembros",
"futureexits": "Futuras salidas",
"infos": "Informaciones",
"inout": "Entradas y salidas de la red de confianza en los últimos 2 días",
"inpreparation": "En preparación",
"lexique": "Léxico",
"limitDate": "Fecha límite",
"membre": {
"calculant": {
"desc": "Miembro usando su clave privada para falsificar bloques gracias a Duniter instalado en un nodo accesible en la red de Internet",
......@@ -150,8 +156,13 @@
"previsions": {
"pardate": "Previsiones por fecha",
"parmembre": "Previsiones por miembros",
"period": {
"title": "Período de búsqueda",
"desc": "Seleccione el número de días deseado entre 1 y 30"
},
"title": "Pronósticos"
},
"pubkey": "Llave pública",
"recherche": {
"desc": "Introduce el comienzo de un pseudónimo o llave pública",
"title": "Buscar"
......@@ -184,6 +195,7 @@
"title": "Teoría relativa del dinero (TRD)"
},
"type": "Tipo",
"uid": "Identificador único",
"valeur": "Valor",
"wot": {
"desc": "Conjunto de las personas reconocidas como tales por sus pares incluyendo los vínculos que las unen a través de certificaciones",
......
......@@ -62,6 +62,10 @@
"title": "Duniter"
},
"expire": "Expire le",
"error": {
"tooSmall": "{0} est trop petit. Il doit être compris entre {1} et {2}. La valeur utilisée est {1}",
"tooBig": "{0} est trop grand. Il doit être compris entre {1} et {2}. La valeur utilisée est {2}"
},
"favoris": {
"enregistre": "Enregistré dans les favoris&nbsp;!",
"none": "Vous n'avez pas encore de favoris",
......@@ -69,10 +73,12 @@
"title": "Mes favoris"
},
"futuremembers": "Futurs membres",
"futureexits": "Futures sorties",
"infos": "Informations",
"inout": "Entrées et sorties de la toile de confiance des 2 derniers jours",
"inpreparation": "En préparation",
"lexique": "Lexique",
"limitDate": "Date limite",
"membre": {
"calculant": {
"desc": "Membre utilisant sa clé privée pour forger des blocs grâce à Duniter installé sur un noeud accessible sur le réseau Internet",
......@@ -150,8 +156,13 @@
"previsions": {
"pardate": "Prévisions par date",
"parmembre": "Prévisions par membres",
"period": {
"title": "Période de recherche",
"desc": "Sélectionnez le nombre de jours souhaités entre 1 et 30"
},
"title": "Prévisions"
},
"pubkey": "Clef publique",
"recherche": {
"desc": "Saisissez le début d'un pseudo ou d'une clé publique",
"title": "Votre recherche"
......@@ -184,6 +195,7 @@
"title": "Théorie Relative de la Monnaie (TRM)"
},
"type": "Type",
"uid": "Identifiant unique",
"valeur": "Valeur",
"wot": {
"desc": "Ensemble des membres et des certifications qui les relient entre eux",
......
......@@ -21,7 +21,10 @@ export default {
},
{
title: "previsions.title",
items: [{ path: "/previsions", title: "futuremembers" }]
items: [
{ path: "/previsions", title: "futuremembers" },
{ path: "/previsions/futures_sorties", title: "futureexits" },
]
},
{
title: "infos",
......
<template>
<main class="container">
<h2 class="text-center my-5 font-weight-light">{{ $t("futureexits") }}</h2>
<div class="row mb-4">
<div class="col-6 m-auto text-center">
<label for="period" class="form-label">{{ $t("previsions.period.title") }}</label>
<input
type="number"
class="form-control"
:class="{ invalid:periodIsInvalid() }"
id="period"
aria-describedby="periodHelp"
v-model="period"
autocomplete="off"
min="1" max="30"
@keyup="save" />
<small id="periodHelp" class="form-text text-muted">{{ $t("previsions.period.desc") }}</small>
</div>
</div>
<NavigationLoader :isLoading="$apollo.queries.wwResult.loading" />
<transition name="fade">
<div class="alert alert-danger" v-if="error && !$apollo.queries.wwResult.loading">{{ error }}</div>
</transition>
<transition name="fade">
<div v-if="wwResult && !$apollo.queries.wwResult.loading">
<div class="row text-center">
<div class="col-md-6 col-lg-6">
<h2 class="h4 text-danger">{{ $t("statut.renew") }}</h2>
<MemberList
:members="wwResult['membership']"
:displayPubkey="false"
:displayOnlyDate="true"
:displayDate="false" />
</div>
<div class="col-md-6 col-lg-6">
<h2 class="h4 text-danger">{{ $t("statut.manquecertif") }}</h2>
<MemberList
:members="wwResult['outOfCerts']"
:displayPubkey="false"
:displayOnlyDate="true"
:displayDate="false"
:adhesion="false" />
</div>
</div>
</div>
</transition>
</main>
</template>
<script>
import { NEXT_EXITS } from "@/graphql/queries.js"
const day = 24*60*60
const defaultPeriod = 30*day
export default {
data() {
return {
breadcrumb: [
{
text: this.$t('accueil'),
to: '/'
},
{
text: this.$t('previsions.title'),
to: '/previsions'
},
{
text: this.$t('futureexits'),
active: true
}
],
error: null,
period: 30,
display: 'forecastsByNames'
}
},
methods: {
save() {
this.error = null
localStorage.setItem('previsions_sorties', this.display)
localStorage.setItem('previsions_period', this.getPeriod()/day)
},
addValue(arr, val) {
if (
arr.filter(function (e) {
return e.uid === val.uid
}).length == 0
) {
arr.push(val)
}
return arr
},
getPeriod() {
if( this.period != "") {
let tempPeriod = parseInt(this.period)
if(tempPeriod < 1) {
this.error = this.$t("error.tooSmall", [ tempPeriod, 1, 30 ])
return day
}
if(tempPeriod > 30) {
this.error = this.$t('error.tooBig', [ tempPeriod, 1, 30 ])
return 30 * day
}
return this.period*day
}
return defaultPeriod
},
periodIsInvalid() {
return this.period != "" && (this.period < 1 || this.period > 30)
}
},
apollo: {
wwResult : {
query: NEXT_EXITS,
variables() {
return { period: this.getPeriod()}
},
update (data) {
let result = { membership: [], outOfCerts: [] }
for (let i = 0; i < data.memEnds.length; i++) {
let identity = data.memEnds[i]
if(['danger', 'warning'].includes(this.$options.filters.dateStatus(identity.limitDate))) {
this.addValue(result["membership"], identity)
}
if(['danger', 'warning'].includes(this.$options.filters.dateStatus(identity.received_certifications.limit))) {
this.addValue(result["outOfCerts"], identity)
}
}
return result
},
error (err) {this.error = err.message}
}
},
nuxtI18n: {
paths: {
fr: '/previsions/futures_sorties',
en: '/forecasts/future_exits',
es: '/pronosticos/futuras_salidas'
}
},
mounted () {
$nuxt.$emit('changeRoute',this.breadcrumb)
if (localStorage.previsions_sorties) {
this.display = localStorage.getItem('previsions_sorties')
this.period = localStorage.getItem('previsions_period')
}
}
}
</script>
<style lang="scss" scoped>
.list-group-item {
background: transparent;
&:hover {
background: rgba(0, 0, 255, 0.075);
color: var(--text-primary-color);
}
}
.forecast_date {
min-width: 150px;
}
.invalid {
border: 4px solid #ec0404;
}
</style>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment