add uptime overview
This commit is contained in:
parent
c35b07ee49
commit
8f1561dd74
@ -23,16 +23,6 @@ const commits = ref([] as Commit[]);
|
||||
const keyword = ref('');
|
||||
const live = ref(true);
|
||||
|
||||
// storage local favorite validator ids
|
||||
const local = ref(
|
||||
JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<
|
||||
string,
|
||||
string[]
|
||||
>
|
||||
);
|
||||
const currentPined = local.value[chainStore.chainName]
|
||||
const selected = ref(currentPined || []); // favorite validators on selected blockchain
|
||||
|
||||
const signingInfo = ref({} as Record<string, SigningInfo>);
|
||||
|
||||
// filter validators by keywords
|
||||
@ -90,10 +80,17 @@ onUnmounted(() => {
|
||||
});
|
||||
|
||||
|
||||
watchEffect(() => {
|
||||
local.value[chainStore.chainName] = selected.value;
|
||||
localStorage.setItem('uptime-validators', JSON.stringify(local.value));
|
||||
});
|
||||
// watchEffect((x) => {
|
||||
// const list = selected.value.map(x => {
|
||||
// const val = stakingStore.validators.find(v => v.operator_address === x)
|
||||
// return {
|
||||
// name: val?.description.moniker || "",
|
||||
// address: x
|
||||
// }
|
||||
// })
|
||||
// local.value[chainStore.chainName] = list;
|
||||
// localStorage.setItem('uptime-validators', JSON.stringify(local.value));
|
||||
// });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -105,10 +102,10 @@ watchEffect(() => {
|
||||
placeholder="Keywords to filter validators"
|
||||
class="input input-sm w-full flex-1"
|
||||
/>
|
||||
<button class="btn btn-primary btn-sm">
|
||||
<RouterLink class="btn btn-primary btn-sm" :to="`/${chain}/uptime/overview`">
|
||||
<Icon icon="mdi-star" class="mr-2 text-lg" />
|
||||
<span class="">Favorite</span>
|
||||
</button>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div
|
||||
class="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-x-4 mt-4"
|
||||
@ -116,10 +113,6 @@ watchEffect(() => {
|
||||
<div v-for="(v, i) in validators" :key="i" >
|
||||
<div class="flex items-center justify-between py-0">
|
||||
<label class="text-truncate text-sm">
|
||||
<input type="checkbox"
|
||||
v-model="selected"
|
||||
:value="v.operator_address"
|
||||
/>
|
||||
<span class="ml-1 text-black dark:text-white">{{ i + 1 }}.{{ v.description.moniker }}</span>
|
||||
</label>
|
||||
<div
|
||||
|
188
src/modules/[chain]/uptime/overview.vue
Normal file
188
src/modules/[chain]/uptime/overview.vue
Normal file
@ -0,0 +1,188 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, computed, watchEffect } from 'vue';
|
||||
import { fromHex, toBase64 } from '@cosmjs/encoding';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import {
|
||||
useFormatter,
|
||||
useStakingStore,
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
useDashboard,
|
||||
} from '@/stores';
|
||||
import UptimeBar from '@/components/UptimeBar.vue';
|
||||
import type { Block, Commit } from '@/types';
|
||||
import { consensusPubkeyToHexAddress, valconsToBase64 } from '@/libs';
|
||||
import type { SigningInfo } from '@/types/slashing';
|
||||
import { CosmosRestClient } from '@/libs/client';
|
||||
|
||||
const props = defineProps(['chain']);
|
||||
|
||||
const stakingStore = useStakingStore();
|
||||
const format = useFormatter();
|
||||
const chainStore = useBlockchain();
|
||||
const dashboard = useDashboard()
|
||||
// storage local favorite validator ids
|
||||
const local = ref(JSON.parse(localStorage.getItem('uptime-validators') || '{}') as Record<string, {name: string, address: string}[]>)
|
||||
const signingInfo = ref({} as Record<string, SigningInfo[]>);
|
||||
const selected = ref([] as string[])
|
||||
const selectChain = ref(chainStore.chainName)
|
||||
const validators = ref(stakingStore.validators)
|
||||
const keyword = ref("")
|
||||
|
||||
if(local.value) Object.keys(local.value).map(chainName => {
|
||||
const chain = dashboard.chains[chainName]
|
||||
if(chain && chain.endpoints.rest) {
|
||||
const client = CosmosRestClient.newDefault(chain.endpoints.rest[0].address)
|
||||
client.getSlashingSigningInfos().then( resp => {
|
||||
signingInfo.value[chainName] = resp.info
|
||||
})
|
||||
}
|
||||
if(chainName === selectChain.value) {
|
||||
const vals = local.value[chainName]
|
||||
if(vals) {
|
||||
selected.value = vals.map(x => x.address)
|
||||
}
|
||||
}
|
||||
|
||||
return chain
|
||||
})
|
||||
|
||||
const filterValidators = computed(() => {
|
||||
if(keyword.value) {
|
||||
return validators.value.filter(x => x.description.moniker.indexOf(keyword.value) > -1)
|
||||
}
|
||||
return validators.value
|
||||
})
|
||||
|
||||
const list = computed(() => {
|
||||
const list = [] as any[]
|
||||
if(local.value) Object.keys(local.value).map( chainName => {
|
||||
const vals = local.value[chainName]
|
||||
const info = signingInfo.value[chainName]
|
||||
if(vals && info) {
|
||||
vals.forEach(v => {
|
||||
const sigingInfo = info.find(x => valconsToBase64(x.address) === v.address)
|
||||
list.push( {
|
||||
chainName,
|
||||
v,
|
||||
sigingInfo,
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
return list
|
||||
})
|
||||
|
||||
function add() {
|
||||
const newList = [] as { name: string; address: string; }[]
|
||||
selected.value.forEach(x => {
|
||||
const validator = validators.value.find(v => (consensusPubkeyToHexAddress(v.consensus_pubkey) === x))
|
||||
if(validator) newList.push({
|
||||
name: validator.description.moniker || x,
|
||||
address: x
|
||||
})
|
||||
})
|
||||
if(!local.value) local.value = {}
|
||||
local.value[selectChain.value] = newList
|
||||
localStorage.setItem("uptime-validators", JSON.stringify(local.value))
|
||||
}
|
||||
|
||||
function changeChain() {
|
||||
validators.value = []
|
||||
const endpoint = dashboard.chains[selectChain.value].endpoints.rest?.at(0)?.address
|
||||
if(!endpoint) return
|
||||
|
||||
const client = CosmosRestClient.newDefault(endpoint)
|
||||
client.getStakingValidators("BOND_STATUS_BONDED").then(x => {
|
||||
validators.value = x.validators
|
||||
})
|
||||
|
||||
const vals = local.value[selectChain.value]
|
||||
if(vals) {
|
||||
selected.value = vals.map(x => x.address)
|
||||
} else {
|
||||
selected.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function color(v: string) {
|
||||
if(v) {
|
||||
const n = Number(v)
|
||||
if(n < 10) return " badge-success"
|
||||
if(n > 1000) return " badge-error"
|
||||
if(n > 0) return " badge-warning"
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<table class="table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Blockchain</th>
|
||||
<th>Validator</th>
|
||||
<th>Missing Blocks</th>
|
||||
<th>Signed Blocks</th>
|
||||
<th>Last Jailed Time</th>
|
||||
<th>Tombstoned</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="v in list">
|
||||
<td class=" capitalize">{{ v.chainName }}</td>
|
||||
<td>{{ v.v.name }}</td>
|
||||
<td><span class="badge " :class="color( v.sigingInfo?.missed_blocks_counter)">{{ v.sigingInfo?.missed_blocks_counter }}</span></td>
|
||||
<td><span v-if="v.sigingInfo">{{ Number(v.sigingInfo.index_offset) - Number(v.sigingInfo.start_height) }}</span></td>
|
||||
<td>
|
||||
<div v-if="v.sigingInfo && !v.sigingInfo?.jailed_until.startsWith('1970')" class="text-xs flex flex-col">
|
||||
<div class="badge">{{ format.toDay(v.sigingInfo.jailed_until, 'from') }}</div>
|
||||
<div>{{v.sigingInfo?.jailed_until }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class=" capitalize">{{ v.sigingInfo?.tombstoned }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<label for="add-validator" class="btn btn-primary mt-5">Add Validators</label>
|
||||
|
||||
<!-- Put this part before </body> tag -->
|
||||
<input type="checkbox" id="add-validator" class="modal-toggle" />
|
||||
<div class="modal">
|
||||
<div class="modal-box relative">
|
||||
<label for="add-validator" class="btn btn-sm btn-circle absolute right-2 top-2">✕</label>
|
||||
<h3 class="text-lg font-bold">Add Validators</h3>
|
||||
<div class="py-4 max-h-60 overflow-y-auto">
|
||||
<div class="form-control my-5 border-2">
|
||||
<div class="input-group input-group-md">
|
||||
<select v-model="selectChain" class="select select-bordered capitalize" @change="changeChain">
|
||||
<option v-for="v in dashboard.chains" :value="v.chainName">
|
||||
{{ v.chainName }}
|
||||
</option>
|
||||
</select>
|
||||
<input v-model="keyword" type="text" class="input w-full" placeholder="keywords to filter validator">
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-compact w-full hover">
|
||||
<thead>
|
||||
<tr><th>Validator</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="v in filterValidators">
|
||||
<td><label :for="v.operator_address"><div class=" w-full">{{ v.description.moniker }}</div></label></td>
|
||||
<td><input :id="v.operator_address" v-model="selected" class="checkbox" type="checkbox" :value="consensusPubkeyToHexAddress(v.consensus_pubkey)"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<label for="add-validator" class="btn" @click="add">add</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-6"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
Loading…
Reference in New Issue
Block a user