feat: footer fixed
This commit is contained in:
parent
29809b5dee
commit
42e4664744
@ -152,7 +152,7 @@ const showDiscord = window.location.host.search('ping.pub') > -1;
|
||||
:to="item?.to"
|
||||
v-if="item?.title && !item?.children?.length && item?.to"
|
||||
@click="sidebarShow = false"
|
||||
class=" cursor-pointer rounded-lg px-4 flex items-center py-2 hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
class="cursor-pointer rounded-lg px-4 flex items-center py-2 hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<Icon
|
||||
v-if="item?.icon?.icon"
|
||||
@ -210,7 +210,7 @@ const showDiscord = window.location.host.search('ping.pub') > -1;
|
||||
<a
|
||||
href="https://becole.com"
|
||||
target="_blank"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
class="py-2 px-4 flex items-center cursor-pointer rounded-lg hover:bg-gray-100 dark:hover:bg-[#373f59]"
|
||||
>
|
||||
<img
|
||||
src="https://becole.com/static/logo/logo_becole.png"
|
||||
|
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="h-10 bg-gray-100 dark:bg-[#171d30] w-full"></div>
|
||||
<footer
|
||||
class="footer items-center pt-4 pb-8 text-sm bg-gray-100 dark:bg-[#171d30] sticky bottom-0 z-10 mt-8"
|
||||
class="footer items-center h-10 text-sm bg-gray-100 dark:bg-[#171d30] fixed bottom-0 pr-14 pl-4 z-10"
|
||||
>
|
||||
<div class="items-center grid-flow-col">
|
||||
©
|
||||
|
@ -2,10 +2,10 @@
|
||||
import { ref, onMounted, computed, onUnmounted } from 'vue';
|
||||
import { fromHex, toBase64 } from '@cosmjs/encoding';
|
||||
import {
|
||||
useFormatter,
|
||||
useStakingStore,
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
useFormatter,
|
||||
useStakingStore,
|
||||
useBaseStore,
|
||||
useBlockchain,
|
||||
} from '@/stores';
|
||||
import UptimeBar from '@/components/UptimeBar.vue';
|
||||
import type { Commit, SlashingParam, SigningInfo } from '@/types';
|
||||
@ -21,187 +21,242 @@ const latest = ref(0);
|
||||
const commits = ref([] as Commit[]);
|
||||
const keyword = ref('');
|
||||
const live = ref(true);
|
||||
const slashingParam = ref({} as SlashingParam)
|
||||
const slashingParam = ref({} as SlashingParam);
|
||||
|
||||
const signingInfo = ref({} as Record<string, SigningInfo>);
|
||||
|
||||
const filterOptout = ref(false)
|
||||
const filterOptout = ref(false);
|
||||
// filter validators by keywords
|
||||
const validators = computed(() => {
|
||||
if (keyword)
|
||||
return stakingStore.validators.filter(
|
||||
(x) => x.description.moniker.indexOf(keyword.value) > -1
|
||||
);
|
||||
return stakingStore.validators;
|
||||
if (keyword)
|
||||
return stakingStore.validators.filter(
|
||||
(x) => x.description.moniker.indexOf(keyword.value) > -1
|
||||
);
|
||||
return stakingStore.validators;
|
||||
});
|
||||
|
||||
const list = computed(() => {
|
||||
const window = Number(slashingParam.value.signed_blocks_window || 0)
|
||||
const vset = validators.value.map(v => {
|
||||
const signing = signingInfo.value[consensusPubkeyToHexAddress(v.consensus_pubkey)]
|
||||
return {
|
||||
v,
|
||||
signing,
|
||||
hex: toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey))),
|
||||
uptime: signing && window > 0 ? (window - Number(signing.missed_blocks_counter)) / window : undefined
|
||||
}
|
||||
})
|
||||
return filterOptout.value ? vset.filter(x => x.signing) : vset
|
||||
})
|
||||
const window = Number(slashingParam.value.signed_blocks_window || 0);
|
||||
const vset = validators.value.map((v) => {
|
||||
const signing =
|
||||
signingInfo.value[consensusPubkeyToHexAddress(v.consensus_pubkey)];
|
||||
return {
|
||||
v,
|
||||
signing,
|
||||
hex: toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey))),
|
||||
uptime:
|
||||
signing && window > 0
|
||||
? (window - Number(signing.missed_blocks_counter)) / window
|
||||
: undefined,
|
||||
};
|
||||
});
|
||||
return filterOptout.value ? vset.filter((x) => x.signing) : vset;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
live.value = true;
|
||||
live.value = true;
|
||||
|
||||
baseStore.fetchLatest().then(l => {
|
||||
let b = l
|
||||
if (baseStore.recents?.findIndex(x => x.block_id.hash === l.block_id.hash) > -1) {
|
||||
b = baseStore.recents?.at(0) || l
|
||||
}
|
||||
latest.value = Number(b.block.header.height);
|
||||
commits.value.unshift(b.block.last_commit);
|
||||
const height = Number(b.block.header?.height || 0);
|
||||
if (height > 50) {
|
||||
// constructs sequence for loading blocks
|
||||
let promise = Promise.resolve();
|
||||
for (let i = height - 1; i > height - 50; i -= 1) {
|
||||
promise = promise.then(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (live.value && commits2.value.length < 50) {
|
||||
// continue only if the page is living
|
||||
baseStore.fetchBlock(i).then((x) => {
|
||||
commits.value.unshift(x.block.last_commit);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
baseStore.fetchLatest().then((l) => {
|
||||
let b = l;
|
||||
if (
|
||||
baseStore.recents?.findIndex((x) => x.block_id.hash === l.block_id.hash) >
|
||||
-1
|
||||
) {
|
||||
b = baseStore.recents?.at(0) || l;
|
||||
}
|
||||
latest.value = Number(b.block.header.height);
|
||||
commits.value.unshift(b.block.last_commit);
|
||||
const height = Number(b.block.header?.height || 0);
|
||||
if (height > 50) {
|
||||
// constructs sequence for loading blocks
|
||||
let promise = Promise.resolve();
|
||||
for (let i = height - 1; i > height - 50; i -= 1) {
|
||||
promise = promise.then(
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
if (live.value && commits2.value.length < 50) {
|
||||
// continue only if the page is living
|
||||
baseStore.fetchBlock(i).then((x) => {
|
||||
commits.value.unshift(x.block.last_commit);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chainStore.rpc.getSlashingSigningInfos().then((x) => {
|
||||
x.info?.forEach((i) => {
|
||||
signingInfo.value[valconsToBase64(i.address)] = i;
|
||||
});
|
||||
});
|
||||
|
||||
chainStore.rpc.getSlashingSigningInfos().then((x) => {
|
||||
x.info?.forEach((i) => {
|
||||
signingInfo.value[valconsToBase64(i.address)] = i;
|
||||
});
|
||||
});
|
||||
|
||||
chainStore.rpc.getSlashingParams().then(x => {
|
||||
slashingParam.value = x.params
|
||||
})
|
||||
chainStore.rpc.getSlashingParams().then((x) => {
|
||||
slashingParam.value = x.params;
|
||||
});
|
||||
});
|
||||
|
||||
const commits2 = computed(() => {
|
||||
const la = baseStore.recents.map(b => b.block.last_commit)
|
||||
const all = [...commits.value, ...la]
|
||||
return all.length > 50 ? all.slice(all.length - 50) : all
|
||||
})
|
||||
const la = baseStore.recents.map((b) => b.block.last_commit);
|
||||
const all = [...commits.value, ...la];
|
||||
return all.length > 50 ? all.slice(all.length - 50) : all;
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
live.value = false;
|
||||
live.value = false;
|
||||
});
|
||||
|
||||
//const tab = ref(window.location.hash.search("block")>-1?"2":"3")
|
||||
const tab = ref("2")
|
||||
const tab = ref('2');
|
||||
function changeTab(v: string) {
|
||||
tab.value = v
|
||||
tab.value = v;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a class="tab text-gray-400 capitalize" :class="{ 'tab-active': tab === '3' }"
|
||||
@click="changeTab('3')">Overall</a>
|
||||
<a class="tab text-gray-400 capitalize" :class="{ 'tab-active': tab === '2' }"
|
||||
@click="changeTab('2')">Blocks</a>
|
||||
<RouterLink :to="`/${chain}/uptime/customize`">
|
||||
<a class="tab text-gray-400 capitalize">Customize</a>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div class="bg-base-100 px-5 pt-5">
|
||||
<div class="flex items-center gap-x-4">
|
||||
<label v-if="chainStore.isConsumerChain" class="text-center">
|
||||
<input type="checkbox" v-model="filterOptout" class="checkbox" />
|
||||
Only Consumer Set
|
||||
</label>
|
||||
<input type="text" v-model="keyword" placeholder="Keywords to filter validators"
|
||||
class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600" />
|
||||
</div>
|
||||
<!-- grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 mt-4 -->
|
||||
<div class="flex flex-row flex-wrap gap-x-4 mt-4" :class="tab === '2' ? '' : 'hidden'">
|
||||
<div v-for="({ v, signing, hex }, i) in list" :key="i">
|
||||
<!-- flex items-center justify-between py-0 -->
|
||||
<div class="flex items-center justify-between py-0 w-72">
|
||||
<label class="truncate text-sm">
|
||||
<span class="ml-1 text-black dark:text-white">{{ i + 1 }}.{{ v.description.moniker }}</span>
|
||||
</label>
|
||||
<div v-if="Number(signing?.missed_blocks_counter || 0) > 10"
|
||||
class="badge badge-sm bg-transparent border-0 text-red-500">
|
||||
{{ signing?.missed_blocks_counter }}
|
||||
</div>
|
||||
<div v-else class="badge badge-sm bg-transparent text-green-600 border-0">
|
||||
{{ signing?.missed_blocks_counter }}
|
||||
</div>
|
||||
</div>
|
||||
<UptimeBar :blocks="commits2" :validator="hex" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="tab === '3' ? '' : 'hidden'" class="overflow-x-auto">
|
||||
<table class="table table-compact w-full mt-5">
|
||||
<thead class=" capitalize">
|
||||
<tr>
|
||||
<td>Validator</td>
|
||||
<td class="text-right">Uptime</td>
|
||||
<td>Last Jailed Time</td>
|
||||
<td class="text-right">Signed Precommits</td>
|
||||
<td class="text-right">Start Height</td>
|
||||
<td>Tombstoned</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="({ v, signing, uptime }, i) in list" class="hover">
|
||||
<td>
|
||||
<div class="truncate max-w-sm">{{ i + 1 }}. {{ v.description.moniker }}</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span v-if="signing" class=""
|
||||
:class="uptime && uptime > 0.95 ? 'text-green-500' : 'text-red-500'">
|
||||
<div class="tooltip" :data-tip="`${signing.missed_blocks_counter} missing blocks`"> {{
|
||||
format.percent(uptime) }} </div>
|
||||
</span>
|
||||
</td>
|
||||
<td><span v-if="signing && !signing.jailed_until.startsWith('1970')">
|
||||
<div class="tooltip" :data-tip="format.toDay(signing?.jailed_until, 'long')">
|
||||
<span>{{ format.toDay(signing?.jailed_until, "from") }}</span>
|
||||
</div>
|
||||
</span></td>
|
||||
<td class="text-xs text-right">
|
||||
|
||||
<span v-if="signing && signing.jailed_until.startsWith('1970')" class="text-right">{{
|
||||
format.percent(Number(signing.index_offset) / (latest - Number(signing.start_height)))
|
||||
}}</span>
|
||||
{{ signing?.index_offset }}
|
||||
</td>
|
||||
<td class="text-right">{{ signing?.start_height }}</td>
|
||||
<td class=" capitalize">{{ signing?.tombstoned }}</td>
|
||||
</tr>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="2" class="text-right"> Minimum uptime per window: <span class="lowercase tooltip"
|
||||
:data-tip="`Window size: ${slashingParam.signed_blocks_window}`"><span
|
||||
class="ml-2 btn btn-error btn-xs">{{
|
||||
format.percent(slashingParam.min_signed_per_window) }}</span>
|
||||
</span></td>
|
||||
<td colspan="8"></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="h-6"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="tabs tabs-boxed bg-transparent mb-4">
|
||||
<a
|
||||
class="tab text-gray-400 capitalize"
|
||||
:class="{ 'tab-active': tab === '3' }"
|
||||
@click="changeTab('3')"
|
||||
>Overall</a
|
||||
>
|
||||
<a
|
||||
class="tab text-gray-400 capitalize"
|
||||
:class="{ 'tab-active': tab === '2' }"
|
||||
@click="changeTab('2')"
|
||||
>Blocks</a
|
||||
>
|
||||
<RouterLink :to="`/${chain}/uptime/customize`">
|
||||
<a class="tab text-gray-400 capitalize">Customize</a>
|
||||
</RouterLink>
|
||||
</div>
|
||||
<div class="bg-base-100 px-5 pt-5">
|
||||
<div class="flex items-center gap-x-4">
|
||||
<label v-if="chainStore.isConsumerChain" class="text-center">
|
||||
<input type="checkbox" v-model="filterOptout" class="checkbox" />
|
||||
Only Consumer Set
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="keyword"
|
||||
placeholder="Keywords to filter validators"
|
||||
class="input input-sm w-full flex-1 border border-gray-200 dark:border-gray-600"
|
||||
/>
|
||||
</div>
|
||||
<!-- grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 mt-4 -->
|
||||
<div
|
||||
class="flex flex-row flex-wrap gap-x-4 mt-4"
|
||||
:class="tab === '2' ? '' : 'hidden'"
|
||||
>
|
||||
<div v-for="({ v, signing, hex }, i) in list" :key="i">
|
||||
<div class="flex items-center justify-between py-0 w-72">
|
||||
<label class="truncate text-sm">
|
||||
<span class="ml-1 text-black dark:text-white"
|
||||
>{{ i + 1 }}.{{ v.description.moniker }}</span
|
||||
>
|
||||
</label>
|
||||
<div
|
||||
v-if="Number(signing?.missed_blocks_counter || 0) > 10"
|
||||
class="badge badge-sm bg-transparent border-0 text-red-500"
|
||||
>
|
||||
{{ signing?.missed_blocks_counter }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="badge badge-sm bg-transparent text-green-600 border-0"
|
||||
>
|
||||
{{ signing?.missed_blocks_counter }}
|
||||
</div>
|
||||
</div>
|
||||
<UptimeBar :blocks="commits2" :validator="hex" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="tab === '3' ? '' : 'hidden'" class="overflow-x-auto">
|
||||
<table class="table table-compact w-full mt-5">
|
||||
<thead class="capitalize">
|
||||
<tr>
|
||||
<td>Validator</td>
|
||||
<td class="text-right">Uptime</td>
|
||||
<td>Last Jailed Time</td>
|
||||
<td class="text-right">Signed Precommits</td>
|
||||
<td class="text-right">Start Height</td>
|
||||
<td>Tombstoned</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="({ v, signing, uptime }, i) in list" class="hover">
|
||||
<td>
|
||||
<div class="truncate max-w-sm">
|
||||
{{ i + 1 }}. {{ v.description.moniker }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span
|
||||
v-if="signing"
|
||||
class=""
|
||||
:class="
|
||||
uptime && uptime > 0.95 ? 'text-green-500' : 'text-red-500'
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="tooltip"
|
||||
:data-tip="`${signing.missed_blocks_counter} missing blocks`"
|
||||
>
|
||||
{{ format.percent(uptime) }}
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="signing && !signing.jailed_until.startsWith('1970')">
|
||||
<div
|
||||
class="tooltip"
|
||||
:data-tip="format.toDay(signing?.jailed_until, 'long')"
|
||||
>
|
||||
<span>{{ format.toDay(signing?.jailed_until, 'from') }}</span>
|
||||
</div>
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-xs text-right">
|
||||
<span
|
||||
v-if="signing && signing.jailed_until.startsWith('1970')"
|
||||
class="text-right"
|
||||
>{{
|
||||
format.percent(
|
||||
Number(signing.index_offset) /
|
||||
(latest - Number(signing.start_height))
|
||||
)
|
||||
}}</span
|
||||
>
|
||||
{{ signing?.index_offset }}
|
||||
</td>
|
||||
<td class="text-right">{{ signing?.start_height }}</td>
|
||||
<td class="capitalize">{{ signing?.tombstoned }}</td>
|
||||
</tr>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="2" class="text-right">
|
||||
Minimum uptime per window:
|
||||
<span
|
||||
class="lowercase tooltip"
|
||||
:data-tip="`Window size: ${slashingParam.signed_blocks_window}`"
|
||||
><span class="ml-2 btn btn-error btn-xs">{{
|
||||
format.percent(slashingParam.min_signed_per_window)
|
||||
}}</span>
|
||||
</span>
|
||||
</td>
|
||||
<td colspan="8"></td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="h-6"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<route>
|
||||
{
|
||||
@ -212,6 +267,8 @@ function changeTab(v: string) {
|
||||
}
|
||||
</route>
|
||||
|
||||
<style lang="scss">.v-field--variant-outlined .v-field__outline__notch {
|
||||
border-width: 0 !important;
|
||||
}</style>
|
||||
<style lang="scss">
|
||||
.v-field--variant-outlined .v-field__outline__notch {
|
||||
border-width: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user