Merge pull request #379 from alisaweb3/v3-single

UI refactor: Uptime, Staking,Dashboard,Validator Detail,Tx
This commit is contained in:
ping 2023-05-13 07:40:57 +08:00 committed by GitHub
commit e7a21a9db9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 758 additions and 627 deletions

View File

@ -23,15 +23,13 @@ onMounted(() => {
</script>
<template>
<VLocaleProvider :rtl="isAppRtl">
<!-- This is required to set the background color of active nav link based on currently active global theme's primary -->
<VApp
:style="`--v-global-theme-primary: ${hexToRgb(
global.current.value.colors.primary
)}`"
>
<RouterView />
<TxDialog/>
</VApp>
</VLocaleProvider>
<!-- This is required to set the background color of active nav link based on currently active global theme's primary -->
<VApp
:style="`--v-global-theme-primary: ${hexToRgb(
global.current.value.colors.primary
)}`"
>
<RouterView />
<TxDialog />
</VApp>
</template>

View File

View File

@ -1,55 +0,0 @@
<script setup lang="ts">
import { controlledComputed } from '@vueuse/shared';
interface Props {
title: string;
color?: string;
icon: string;
stats: number;
change?: number;
}
const props = withDefaults(defineProps<Props>(), {
color: 'primary',
});
const isPositive = controlledComputed(
() => props.change,
() => Math.sign(props.change || 0) === 1
);
</script>
<template>
<VCard>
<VCardText class="d-flex align-center">
<VAvatar
size="40"
rounded
:color="props.color"
variant="tonal"
class="me-4"
>
<VIcon :icon="props.icon" size="24" />
</VAvatar>
<div class="d-flex flex-column">
<div class="d-flex align-center flex-wrap">
<h6 class="text-h6">
{{ props.stats }}
</h6>
<div
v-if="props.change"
:class="`${isPositive ? 'text-success' : 'text-error'}`"
>
<VIcon
size="24"
:icon="isPositive ? 'mdi-chevron-up' : 'mdi-chevron-down'"
/>
<span class="text-caption">{{ Math.abs(props.change) }}%</span>
</div>
</div>
<span class="text-caption">{{ props.title }}</span>
</div>
</VCardText>
</VCard>
</template>

View File

@ -1,4 +1,6 @@
<script setup lang="ts">
import { Icon } from '@iconify/vue';
interface Props {
title: string;
color?: string;
@ -19,40 +21,40 @@ const isPositive = controlledComputed(
</script>
<template>
<VCard class="h-full flex-col content-between">
<VCardText class="d-flex align-center justify-between">
<VAvatar
<div class="bg-base-100 shadow rounded p-4">
<div class="flex items-center justify-between">
<div
v-if="props.icon"
rounded
size="38"
variant="tonal"
:color="props.color"
class="relative w-9 h-9 rounded overflow-hidden flex items-center justify-center"
>
<VIcon :icon="props.icon" size="24" />
</VAvatar>
<Icon :class="[`text-${props?.color}`]" :icon="props.icon" size="32" />
<div
class="absolute top-0 left-0 bottom-0 right-0 opacity-20"
:class="[`bg-${props?.color}`]"
></div>
</div>
<div
v-if="props.change"
:class="isPositive ? 'text-success' : 'text-error'"
class="d-flex align-center text-sm font-weight-medium mt-n4"
class="flex items-center text-sm font-semibold"
>
<span>{{ isPositive ? `+${props.change}` : props.change }}%</span>
<VIcon :icon="isPositive ? 'mdi-chevron-up' : 'mdi-chevron-down'" />
<Icon :icon="isPositive ? 'mdi-chevron-up' : 'mdi-chevron-down'" />
</div>
</VCardText>
</div>
<VCardText class="d-flex flex-col">
<h6 class="text-h6 me-2 mt-2 mb-1">
<div class="">
<h6 class="text-lg font-semibold mt-2 mb-1">
{{ props.stats }}
</h6>
<p class="text-sm">
{{ props.title }}
</p>
<VChip v-if="props.subtitle" size="x-small" class="font-weight-medium">
<div v-if="props.subtitle" size="x-small" class="font-semibold">
<span class="text-truncate">{{ props.subtitle }}</span>
</VChip>
</VCardText>
</VCard>
</div>
</div>
</div>
</template>

View File

@ -1,64 +0,0 @@
<script setup lang="ts">
interface Props {
title: string;
subtitle: string;
stats: string;
change: number;
image: string;
imgWidth: number;
color?: string;
}
const props = withDefaults(defineProps<Props>(), {
color: 'primary',
});
const isPositive = controlledComputed(
() => props.change,
() => Math.sign(props.change) === 1
);
</script>
<template>
<VCard>
<VCardText>
<h6 class="text-base font-weight-semibold mb-2">
{{ props.title }}
</h6>
<VChip
v-if="props.subtitle"
size="x-small"
:color="props.color"
class="mb-5"
>
{{ props.subtitle }}
</VChip>
<div class="d-flex align-center flex-wrap">
<h5 class="text-h5 me-2">
{{ props.stats }}
</h5>
<span
class="text-caption"
:class="isPositive ? 'text-success' : 'text-error'"
>
{{ isPositive ? `+${props.change}` : props.change }}%
</span>
</div>
</VCardText>
<VSpacer />
<div class="illustrator-img">
<VImg v-if="props.image" :src="props.image" :width="props.imgWidth" />
</div>
</VCard>
</template>
<style lang="scss">
.illustrator-img {
position: absolute;
inset-block-end: 0;
inset-inline-end: 5%;
}
</style>

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { getLogo, useDashboard } from '@/stores/useDashboard';
import { useDashboard } from '@/stores/useDashboard';
import { computed } from 'vue';
import { Icon } from '@iconify/vue';

View File

@ -1,12 +1,12 @@
<script lang="ts" setup>
import { useTxDialog } from '@/stores'
const store = useTxDialog()
import { useTxDialog } from '@/stores';
const store = useTxDialog();
</script>
<template>
<ping-tx-dialog
:type="store.type"
:sender="store.sender"
:endpoint="store.endpoint"
:params="store.params"
></ping-tx-dialog>
</template>
<ping-tx-dialog
:type="store.type"
:sender="store.sender"
:endpoint="store.endpoint"
:params="store.params"
></ping-tx-dialog>
</template>

View File

@ -23,15 +23,16 @@ const bars = computed(() => {
});
</script>
<template>
<div class="d-flex justify-evenly">
<span v-for="(b, i) in bars" :key="i" :class="b.color" style="width: 1.5%"
>&nbsp;
<v-tooltip
v-if="Number(b.height) > 0"
activator="parent"
location="top"
>{{ b.height }}</v-tooltip
>
</span>
<div class="d-flex items-center justify-evenly">
<div class="cursor-default" v-for="(item, index) in bars" :key="index">
<div class="tooltip" :data-tip="item.height">
<span
:class="item.color"
class="rounded"
style="width: 1.5%"
>&nbsp;
</span>
</div>
</div>
</div>
</template>

View File

@ -136,35 +136,27 @@ const chartConfig = computed(() => {
</script>
<template>
<VCard
title="Commission Rate"
:subtitle="`Updated at ${format.toDay(
props.commission?.update_time,
'short'
)}`"
>
<VCardText>
<VueApexCharts
type="donut"
height="257"
:options="chartConfig"
:series="series"
/>
<div class="d-flex align-center justify-center flex-wrap mx-2 gap-x-6">
<div class="d-flex align-center gap-2">
<VBadge dot color="success" />
<span class="mt-1 text-caption">Rate:{{ rate }}%</span>
<div class="bg-base-100 rounded shadow p-4">
<div class="text-lg text-main font-semibold mb-1">Commission Rate</div>
<div class="text-sm text-gray-500 dark:text-gray-400">
{{ `Updated at ${format.toDay(props.commission?.update_time, 'short')}` }}
</div>
<VueApexCharts type="donut" :options="chartConfig" :series="series" />
<div>
<div class="flex items-center justify-center flex-wrap gap-x-3">
<div class="flex items-center gap-x-2">
<div class="bg-success w-[6px] h-[6px] rounded-full"></div>
<span class="text-caption">Rate:{{ rate }}%</span>
</div>
<div class="d-flex align-center gap-2">
<VBadge dot color="success" style="opacity: 0.2" />
<span class="mt-1 text-caption">24h: ±{{ change }}%</span>
<div class="flex items-center gap-x-2">
<div class="bg-success w-[6px] h-[6px] rounded-full opacity-60"></div>
<span class="text-caption">24h: ±{{ change }}%</span>
</div>
<div class="d-flex align-center gap-2">
<VBadge dot color="secondary" />
<span class="mt-1 text-caption">Max:{{ max }}%</span>
<div class="flex items-center gap-x-2">
<div class="bg-secondary w-[6px] h-[6px] rounded-full"></div>
<span class="text-caption">Max:{{ max }}%</span>
</div>
</div>
</VCardText>
</VCard>
</div>
</div>
</template>

View File

@ -1,9 +1,4 @@
<script lang="ts" setup>
import { toBase64 } from '@cosmjs/encoding';
import type { PropType } from 'vue';
import { computed } from '@vue/reactivity';
import DynamicComponentVue from './DynamicComponent.vue';
import { select } from './index';
import ArrayBytesElement from './ArrayBytesElement.vue';
import ArrayObjectElement from './ArrayObjectElement.vue';
import TextElement from './TextElement.vue';

View File

@ -1,5 +1,4 @@
<script lang="ts" setup>
import DynamicComponent from './DynamicComponent.vue';
import { select } from './index';
const props = defineProps(['value']);

View File

@ -1,7 +1,6 @@
<script lang="ts" setup>
import DynamicComponent from './DynamicComponent.vue';
import { select } from './index';
import { ref} from 'vue'
import { ref } from 'vue';
const props = defineProps(['value']);
const tab = ref(Object.keys(props.value)[0] || '');
@ -12,16 +11,19 @@ const changeTab = (val: string) => {
<template>
<div>
<div class="tabs">
<a class="tab tab-bordered text-gray-400 uppercase"
v-for="(item, index) of value" :value="index"
:class="{ 'tab-active':tab === String(index) }"
<a
class="tab tab-bordered text-gray-400 uppercase"
v-for="(item, index) of value"
:value="index"
:class="{ 'tab-active': tab === String(index) }"
@click="changeTab(String(index))"
>{{ index }}</a>
>{{ index }}</a
>
</div>
<div class="min-h-[25px] mt-4">
<div v-for="(v, k) of value" :value="k">
<DynamicComponent :value="v" v-show="tab === String(k)"/>
</div>
<div v-for="(v, k) of value" :value="k">
<DynamicComponent :value="v" v-show="tab === String(k)" />
</div>
</div>
</div>
</template>

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import { useBlockchain } from '@/stores/useBlockchain';
import { Icon } from '@iconify/vue';
import { computed } from '@vue/reactivity';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
@ -20,10 +21,10 @@ const items = computed(() => [
<template>
<div class="d-flex flex-rows align-center">
<span class="text-h5 mr-3">{{ moduleName }}</span>
<v-icon icon="mdi-dots-vertical" />
<Icon icon="mdi-dots-vertical"/>
<VBreadcrumbs :items="items">
<template v-slot:divider>
<v-icon icon="mdi-chevron-right"></v-icon>
<Icon icon="mdi-chevron-right"/>
</template>
</VBreadcrumbs>
</div>

View File

@ -16,7 +16,7 @@ chainStore.$subscribe((m, s) => {
<div tabindex="0" class="p-1 relative mr-3 cursor-pointer">
<img v-lazy="chainStore.logo" class="w-9 h-9 rounded-full" />
<div
class="w-2 h-2 rounded-full bg-yes absolute right-0 bottom-0"
class="w-2 h-2 rounded-full bg-yes absolute right-0 bottom-0 shadow"
></div>
</div>
<div

View File

@ -4,20 +4,15 @@ import { ref } from 'vue';
import { useThemeConfig } from '@/plugins/vuetify/@core/composable/useThemeConfig';
// Components
// import Footer from '@/layouts/components/Footer.vue';
import newFooter from '@/layouts/components/newFooter.vue';
import newFooter from '@/layouts/components/NavFooter.vue';
import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue';
import UserProfile from '@/layouts/components/ChainProfile.vue';
import { useDashboard } from '@/stores/useDashboard';
// @layouts plugin
import NavBarI18n from './NavBarI18n.vue';
import NavSearchBar from './NavSearchBar.vue';
import NavBarNotifications from './NavBarNotifications.vue';
import NavBarWallet from './NavBarWallet.vue';
import TheCustomizer from '@/plugins/vuetify/@core/components/TheCustomizer.vue';
import Breadcrumbs from './Breadcrumbs.vue';
import { useBlockchain } from '@/stores';
const { appRouteTransition } = useThemeConfig();
@ -44,7 +39,9 @@ const sidebarShow = ref(false);
>
<div class="flex items-center pl-4 py-4 mb-1">
<img class="w-10 h-10" src="../../assets/logo.svg" />
<h1 class="flex-1 ml-3 text-2xl font-semibold dark:text-white">Ping.pub</h1>
<h1 class="flex-1 ml-3 text-2xl font-semibold dark:text-white">
Ping.pub
</h1>
<div class="pr-4 cursor-pointer xl:hidden" @click="sidebarShow = false">
<Icon icon="mdi-close" class="text-3xl" />
</div>
@ -178,7 +175,8 @@ const sidebarShow = ref(false);
<NavBarNotifications class="hidden md:inline-block" />
<NavBarI18n class="hidden md:inline-block" />
<NavbarThemeSwitcher class="hidden md:inline-block" />
<NavBarWallet class="md:inline-block" />
<NavBarWallet class="block truncate md:inline-block text-xs md:text-sm" />
</div>
<!-- 👉 Pages -->

View File

@ -1,11 +1,10 @@
<script lang="ts" setup>
import NavBarI18n from '@core/components/I18n.vue';
import { useThemeConfig } from '@core/composable/useThemeConfig';
import type { I18nLanguage } from '@layouts/types';
const { isAppRtl } = useThemeConfig();
const i18nCompLanguages: I18nLanguage[] = [
const i18nCompLanguages: Array<{ label: string; i18nLang: string }> = [
{
label: 'English',
i18nLang: 'en',
@ -14,10 +13,6 @@ const i18nCompLanguages: I18nLanguage[] = [
label: '中文',
i18nLang: 'cn',
},
{
label: 'Arabic',
i18nLang: 'ar',
},
];
const handleLangChange = (lang: string) => {

View File

@ -1,5 +1,5 @@
<template>
<footer class="footer items-center p-4 text-base mb-4">
<footer class="footer items-center p-4 text-sm mb-4">
<div class="items-center grid-flow-col">
&copy;
{{ new Date().getFullYear() }}

View File

@ -1,7 +1,6 @@
<script setup lang="ts">
import type { ThemeSwitcherTheme } from '@layouts/types';
import NewThemeSwitcher from '@/components/ThemeSwitcher.vue';
const themes: ThemeSwitcherTheme[] = [
const themes: Array<{ name: string; icon: string }> = [
{
name: 'system',
icon: 'mdi-laptop',

View File

@ -1,10 +1,6 @@
<script lang="ts" setup>
import { useSkins } from '@core/composable/useSkins';
import { useThemeConfig } from '@core/composable/useThemeConfig';
const DefaultLayout = defineAsyncComponent(
() => import('./components/DefaultLayout.vue')
);
import DefaultLayout from './components/DefaultLayout.vue';
const { layoutAttrs, injectSkinClasses } = useSkins();
@ -14,8 +10,3 @@ injectSkinClasses();
<template>
<DefaultLayout v-bind="layoutAttrs" />
</template>
<style lang="scss">
// As we are using `layouts` plugin we need its styles to be imported
@use '@layouts/styles/default-layout';
</style>

View File

@ -1,8 +1,14 @@
<script lang="ts" setup>
import { useBlockchain, useFormatter, useStakingStore, useTxDialog } from '@/stores';
import {
useBlockchain,
useFormatter,
useStakingStore,
useTxDialog,
} from '@/stores';
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import DonutChart from '@/components/charts/DonutChart.vue';
import { computed, ref } from '@vue/reactivity';
import { Icon } from '@iconify/vue';
import 'vue-json-pretty/lib/styles.css';
import type {
AuthAccount,
@ -85,24 +91,168 @@ loadAccount(props.address);
</script>
<template>
<div v-if="account">
<VCard>
<VList>
<VListItem>
<template #prepend>
<VAvatar rounded variant="tonal" size="45" color="primary">
<VIcon icon="mdi-qrcode" />
</VAvatar>
</template>
<!-- address -->
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<div class="d-flex items-center">
<!-- img -->
<div class="inline-flex relative w-11 h-11 rounded-md">
<div
class="w-11 h-11 absolute rounded-md opacity-10 bg-primary"
></div>
<div
class="w-full inline-flex items-center align-middle flex-none justify-center"
>
<Icon
icon="mdi-qrcode"
class="text-primary"
style="width: 27px; height: 27px"
/>
</div>
</div>
<!-- content -->
<div class="flex flex-1 flex-col truncate pl-4">
<h2 class="text-sm card-title">Address:</h2>
<span class="text-xs truncate"> {{ address }}</span>
</div>
</div>
</div>
<VListItemTitle class="text-sm font-weight-semibold">
Address:
</VListItemTitle>
<VListItemSubtitle class="text-xs">
{{ address }}
</VListItemSubtitle>
</VListItem>
</VList>
</VCard>
<!-- Assets -->
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title mb-4">Assets</h2>
<div class="grid md:grid-cols-3">
<div class="md:col-span-1">
<DonutChart :series="totalAmountByCategory" :labels="labels" />
</div>
<div class="mt-4 md:col-span-2 md:mt-0 md:ml-4">
<!-- button -->
<div class="d-flex justify-end mb-4">
<label
for="send"
class="btn btn-primary btn-sm mr-2"
@click="dialog.open('send', {})"
>Send</label
>
<label
for="transfer"
class="btn btn-primary btn-sm"
@click="
dialog.open('transfer', {
chain_name: blockchain.current?.prettyName,
})
"
>transfer</label
>
</div>
<!-- -->
<div class="">
<VListItem v-for="v in balances">
<template #prepend>
<VAvatar rounded variant="tonal" size="35" color="info">
<VIcon icon="mdi-account-cash" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken(v) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs"> ${{ 0 }} </VListItemSubtitle>
<template #append>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-primary mr-2"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary"
></span>
{{ format.calculatePercent(v.amount, totalAmount) }}
</div>
</template>
</VListItem>
<VListItem v-for="v in delegations">
<template #prepend>
<VAvatar rounded variant="tonal" size="35" color="warning">
<VIcon icon="mdi-user-clock" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken(v.balance) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs"> ${{ 0 }} </VListItemSubtitle>
<template #append>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-primary mr-2"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary"
></span>
{{ format.calculatePercent(v.balance.amount, totalAmount) }}
</div>
</template>
</VListItem>
<VListItem v-for="v in rewards.total">
<template #prepend>
<VAvatar rounded variant="tonal" size="35" color="success">
<VIcon icon="mdi-account-arrow-up" size="20" />
</VAvatar>
</template>
<VListItemTitle class="text-sm font-weight-semibold">
{{ format.formatToken(v) }}
</VListItemTitle>
<VListItemSubtitle class="text-xs"> ${{ 0 }} </VListItemSubtitle>
<template #append>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-primary mr-2"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary"
></span>
{{ format.calculatePercent(v.amount, totalAmount) }}
</div>
</template>
</VListItem>
<div class="flex items-center px-4">
<div
class="w-9 h-9 rounded overflow-hidden flex items-center justify-center relative mr-4"
>
<Icon
icon="mdi-account-arrow-right"
class="text-error"
size="20"
/>
<div
class="absolute top-0 bottom-0 left-0 right-0 bg-error opacity-20"
></div>
</div>
<div class="flex-1">
<div class="text-base font-semibold">
{{
format.formatToken({
amount: String(unbondingTotal),
denom: stakingStore.params.bond_denom,
})
}}
</div>
<div class="text-sm">${{ 0 }}</div>
</div>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-primary mr-2"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary"
></span>
{{ format.calculatePercent(unbondingTotal, totalAmount) }}
</div>
</div>
</div>
<div class="divider"></div>
{{ totalAmount }}
</div>
</div>
</div>
<VCard class="mt-5">
<VCardTitle>Assets</VCardTitle>
@ -114,8 +264,22 @@ loadAccount(props.address);
<VCol cols="12" md="8">
<VList class="card-list">
<VListItem>
<label for="transfer" class="btn btn-primary float-right btn-sm" @click="dialog.open('transfer', {chain_name: blockchain.current?.prettyName})">transfer</label>
<label for="send" class="btn btn-primary float-right btn-sm" @click="dialog.open('send', {})">Send</label>
<label
for="transfer"
class="btn btn-primary float-right btn-sm"
@click="
dialog.open('transfer', {
chain_name: blockchain.current?.prettyName,
})
"
>transfer</label
>
<label
for="send"
class="btn btn-primary float-right btn-sm"
@click="dialog.open('send', {})"
>Send</label
>
</VListItem>
<VListItem v-for="v in balances">
<template #prepend>
@ -130,9 +294,14 @@ loadAccount(props.address);
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{
format.calculatePercent(v.amount, totalAmount)
}}</VChip>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-primary mr-2"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary"
></span>
{{ format.calculatePercent(v.amount, totalAmount) }}
</div>
</template>
</VListItem>
<VListItem v-for="v in delegations">
@ -149,9 +318,14 @@ loadAccount(props.address);
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{
format.calculatePercent(v.balance.amount, totalAmount)
}}</VChip>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-primary mr-2"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary"
></span>
{{ format.calculatePercent(v.balance.amount, totalAmount) }}
</div>
</template>
</VListItem>
<VListItem v-for="v in rewards.total">
@ -168,9 +342,14 @@ loadAccount(props.address);
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{
format.calculatePercent(v.amount, totalAmount)
}}</VChip>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-primary mr-2"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary"
></span>
{{ format.calculatePercent(v.amount, totalAmount) }}
</div>
</template>
</VListItem>
@ -193,9 +372,14 @@ loadAccount(props.address);
${{ 0 }}
</VListItemSubtitle>
<template #append>
<VChip color="primary">{{
format.calculatePercent(unbondingTotal, totalAmount)
}}</VChip>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit text-primary mr-2"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute bg-primary"
></span>
{{ format.calculatePercent(unbondingTotal, totalAmount) }}
</div>
</template>
</VListItem>
</VList>
@ -206,25 +390,34 @@ loadAccount(props.address);
</VCardItem>
</VCard>
<VCard class="my-5">
<VCardItem>
<VCardTitle>
Delegations
<div>
<label for="delegate" class="btn btn-primary float-right btn-sm" @click="dialog.open('delegate', {})">Delegate</label>
<label for="withdraw" class="btn btn-primary float-right btn-sm" @click="dialog.open('withdraw', {})">Withdraw</label>
</div>
</VCardTitle>
<VTable>
<!-- Delegations -->
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title mb-4">Delegations</h2>
<div class="d-flex justify-end mb-4">
<label
for="delegate"
class="btn btn-primary btn-sm mr-2"
@click="dialog.open('delegate', {})"
>Delegate</label
>
<label
for="withdraw"
class="btn btn-primary btn-sm"
@click="dialog.open('withdraw', {})"
>Withdraw</label
>
</div>
<div class="overdflow-x-auto">
<table class="table w-full">
<thead>
<tr>
<th>Validator</th>
<th style="position: relative">Validator</th>
<th>Delegation</th>
<th>Rewards</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tbody class="text-sm">
<tr v-for="v in delegations">
<td class="text-caption text-primary">
<RouterLink
@ -246,28 +439,62 @@ loadAccount(props.address);
}}
</td>
<td>
<label for="delegate" class="btn btn-primary float-right btn-sm" @click="dialog.open('delegate', {validator_address: v.delegation.validator_address})">delegate</label>
<label for="redelegate" class="btn btn-primary float-right btn-sm" @click="dialog.open('redelegate', {validator_address: v.delegation.validator_address})">Redelegate</label>
<label for="unbond" class="btn btn-primary float-right btn-sm" @click="dialog.open('unbond', {validator_address: v.delegation.validator_address})">Unbond</label>
<div class="d-flex justify-end">
<label
for="delegate"
class="btn btn-primary btn-sm mr-2"
@click="
dialog.open('delegate', {
validator_address: v.delegation.validator_address,
})
"
>delegate</label
>
<label
for="redelegate"
class="btn btn-primary btn-sm mr-2"
@click="
dialog.open('redelegate', {
validator_address: v.delegation.validator_address,
})
"
>Redelegate</label
>
<label
for="unbond"
class="btn btn-primary btn-sm"
@click="
dialog.open('unbond', {
validator_address: v.delegation.validator_address,
})
"
>Unbond</label
>
</div>
</td>
</tr>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard class="my-5" v-if="unbonding && unbonding.length > 0">
<VCardItem>
<VCardTitle>Unbonding Delegations</VCardTitle>
<VTable>
</table>
</div>
</div>
<!-- Unbonding Delegations -->
<div
class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow"
v-if="unbonding && unbonding.length > 0"
>
<h2 class="card-title mb-4">Unbonding Delegations</h2>
<div class="overflow-x-auto">
<table class="table">
<thead>
<tr>
<th>Creation Height</th>
<th style="position: relative">Creation Height</th>
<th>Initial Balance</th>
<th>Balance</th>
<th>Completion Time</th>
</tr>
</thead>
<tbody>
<tbody class="text-sm">
<div v-for="v in unbonding">
<tr>
<td class="text-caption text-primary">
@ -309,22 +536,24 @@ loadAccount(props.address);
</tr>
</div>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard class="my-5">
<VCardItem>
<VCardTitle>Transactions</VCardTitle>
<VTable>
</table>
</div>
</div>
<!-- Transactions -->
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title mb-4">Transactions</h2>
<div class="overflow-x-auto">
<table class="table w-full">
<thead>
<tr>
<th>Height</th>
<th style="position: relative">Height</th>
<th>Hash</th>
<th>Messages</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<tbody class="text-sm">
<tr v-for="v in txs">
<td class="text-sm text-primary">
<RouterLink :to="`/${chain}/block/${v.height}`">{{
@ -346,15 +575,15 @@ loadAccount(props.address);
<td>{{ format.toDay(v.timestamp, 'from') }}</td>
</tr>
</tbody>
</VTable>
</VCardItem>
</VCard>
<VCard>
<VCardItem>
<VCardTitle>Account</VCardTitle>
<DynamicComponent :value="account" />
</VCardItem>
</VCard>
</table>
</div>
</div>
<!-- Account -->
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title mb-4">Account</h2>
<DynamicComponent :value="account" />
</div>
</div>
<div v-else>Account does not exists on chain</div>
</template>

View File

@ -1,5 +1,4 @@
<script lang="ts" setup>
import { fromHex } from '@cosmjs/encoding';
import { useWasmStore } from '../WasmStore';
import { ref } from 'vue';
import type {
@ -8,7 +7,6 @@ import type {
PaginabledContracts,
} from '../types';
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import type CustomRadiosVue from '@/plugins/vuetify/@core/components/CustomRadios.vue';
import type { CustomInputContent } from '@/plugins/vuetify/@core/types';
import { useFormatter } from '@/stores';
@ -94,12 +92,14 @@ const result = ref('');
<template>
<div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title truncate w-full">Contract List of Code: {{ props.code_id }}</h2>
<h2 class="card-title truncate w-full">
Contract List of Code: {{ props.code_id }}
</h2>
<div class="overflow-x-auto">
<table class="table w-full mt-4">
<table class="table w-full mt-4">
<thead>
<tr>
<th style="position: relative;">Contract List</th>
<th style="position: relative">Contract List</th>
<th>Actions</th>
</tr>
</thead>
@ -110,22 +110,28 @@ const result = ref('');
<button
class="btn btn-primary btn-sm text-xs mr-2"
@click="showInfo(v)"
>contract</button>
>
contract
</button>
<button
class="btn btn-primary btn-sm text-xs mr-2"
@click="showState(v)"
>States</button>
>
States
</button>
<button
class="btn btn-primary btn-sm text-xs"
@click="showQuery(v)"
>Query</button>
>
Query
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<v-dialog v-model="infoDialog" width="auto">
<v-card>
<VCardTitle>Contract Detail</VCardTitle>
@ -160,7 +166,7 @@ const result = ref('');
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="queryDialog" width="auto">
<v-card>
<VCardTitle>Query Contract</VCardTitle>

View File

@ -20,7 +20,7 @@ wasmStore.wasmClient.getWasmCodeList().then((x) => {
<table class="table w-full mt-4 text-sm">
<thead>
<tr>
<th style="position: relative;">Code Id</th>
<th style="position: relative">Code Id</th>
<th>Code Hash</th>
<th>Creator</th>
<th>Permissions</th>

View File

@ -1,12 +1,6 @@
<script lang="ts" setup>
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue';
import { useBaseStore, useBlockchain, useFormatter } from '@/stores';
import type {
ClientStateWithProof,
Connection,
ClientState,
Channel,
} from '@/types';
import { useBaseStore, useBlockchain } from '@/stores';
import type { Connection, ClientState, Channel } from '@/types';
import { onMounted } from 'vue';
import { ref } from 'vue';
@ -136,16 +130,16 @@ function color(v: string) {
<td>{{ v.version }}</td>
<td>{{ v.ordering }}</td>
<td>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit"
:class="`text-${color(v.state)}`"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute"
:class="`bg-${color(v.state)}`"
></span>
{{ v.state }}
</div>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit"
:class="`text-${color(v.state)}`"
>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute"
:class="`bg-${color(v.state)}`"
></span>
{{ v.state }}
</div>
</td>
</tr>
</tbody>

View File

@ -48,11 +48,11 @@ function color(v: string) {
</td>
<td>{{ v.delay_period }}</td>
<td>
<div
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit"
:class="`text-${color(v.state)}`"
>
<span
<span
class="inset-x-0 inset-y-0 opacity-10 absolute"
:class="`bg-${color(v.state)}`"
></span>

View File

@ -135,29 +135,18 @@ onMounted(() => {
</script>
<template>
<div>
<div style="border-width:1px" class="bg-base-100 px-4 pt-3 pb-4 rounded shadow border-indigo-500">
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow border-indigo-500">
<div class="flex flex-col lg:flex-row">
<div class="flex-1">
<div class="flex">
<!-- avator -->
<!-- <VAvatar
icon="mdi-help-circle-outline"
:image="avatars[identity]"
size="90"
rounded
variant="outlined"
color="secondary"
/> -->
<div
class="avatar mr-4 relative w-24 rounded-lg overflow-hidden"
>
<div
class="w-24 rounded-lg absolute opacity-10"
></div>
<div class="avatar mr-4 relative w-24 rounded-lg overflow-hidden">
<div class="w-24 rounded-lg absolute opacity-10"></div>
<div class="w-24 rounded-lg">
<img
v-if="avatars[identity] !== 'undefined'"
:src="`https://s3.amazonaws.com/keybase_processed_uploads/${avatars[identity]}`"
v-lazy="
`https://s3.amazonaws.com/keybase_processed_uploads/${avatars[identity]}`
"
class="object-contain"
/>
<Icon
@ -172,212 +161,226 @@ onMounted(() => {
<div class="text-sm mb-2">
{{ v.description?.identity || '-' }}
</div>
<label for="delegate" class="btn btn-primary btn-sm w-full" @click="dialog.open('delegate', {validator_address: v.operator_address})">Delegate</label>
<label
for="delegate"
class="btn btn-primary btn-sm w-full"
@click="
dialog.open('delegate', {
validator_address: v.operator_address,
})
"
>Delegate</label
>
</div>
</div>
<div class="m-4 text-sm">
<p class="text-md">About Us</p>
<VList class="card-list">
<VListItem prepend-icon="mdi-web">
<span>Website: </span
><span> {{ v.description?.website || '-' }}</span>
</VListItem>
<VListItem prepend-icon="mdi-email-outline">
<span>Contact: </span
><span> {{ v.description?.security_contact }}</span>
</VListItem>
</VList>
<VListItem prepend-icon="mdi-web">
<span>Website: </span
><span> {{ v.description?.website || '-' }}</span>
</VListItem>
<VListItem prepend-icon="mdi-email-outline">
<span>Contact: </span
><span> {{ v.description?.security_contact }}</span>
</VListItem>
</VList>
<p class="text-md mt-3">Validator Status</p>
<VList class="card-list">
<VListItem prepend-icon="mdi-shield-account-outline">
<span>Status: </span
><span>
{{ String(v.status).replace('BOND_STATUS_', '') }}
</span>
</VListItem>
<VListItem prepend-icon="mdi-shield-alert-outline">
<span>Jailed: </span><span> {{ v.jailed || '-' }} </span>
</VListItem>
</VList>
<VListItem prepend-icon="mdi-shield-account-outline">
<span>Status: </span
><span>
{{ String(v.status).replace('BOND_STATUS_', '') }}
</span>
</VListItem>
<VListItem prepend-icon="mdi-shield-alert-outline">
<span>Jailed: </span><span> {{ v.jailed || '-' }} </span>
</VListItem>
</VList>
</div>
</div>
<div class="flex-1">
<div class="d-flex flex-column py-3 justify-space-between">
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-coin"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{
format.formatToken2({
amount: v.tokens,
denom: staking.params.bond_denom,
})
}}
</h4>
<span class="text-sm">Total Bonded Tokens</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-percent"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{ format.formatToken(selfBonded.balance) }} ({{
selfRate
}})
</h4>
<span class="text-sm">Self Bonded</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-account-tie"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{ v.min_self_delegation }} {{ staking.params.bond_denom }}
</h4>
<span class="text-sm">Min Self Delegation:</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-finance"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ apr }}</h4>
<span class="text-sm">Annual Profit</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-stairs-up"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.unbonding_height }}</h4>
<span class="text-sm">Unbonding Height</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-clock"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.toDay(v.unbonding_time, 'from') }}</h4>
<span class="text-sm">Unbonding Time</span>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-coin"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{
format.formatToken2({
amount: v.tokens,
denom: staking.params.bond_denom,
})
}}
</h4>
<span class="text-sm">Total Bonded Tokens</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-percent"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{ format.formatToken(selfBonded.balance) }} ({{ selfRate }})
</h4>
<span class="text-sm">Self Bonded</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-account-tie"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>
{{ v.min_self_delegation }} {{ staking.params.bond_denom }}
</h4>
<span class="text-sm">Min Self Delegation:</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-finance"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ apr }}</h4>
<span class="text-sm">Annual Profit</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-stairs-up"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ v.unbonding_height }}</h4>
<span class="text-sm">Unbonding Height</span>
</div>
</div>
<div class="d-flex">
<VAvatar
color="secondary"
rounded
variant="outlined"
icon="mdi-clock"
></VAvatar>
<div class="ml-3 d-flex flex-column justify-center">
<h4>{{ format.toDay(v.unbonding_time, 'from') }}</h4>
<span class="text-sm">Unbonding Time</span>
</div>
</div>
</div>
</div>
</div>
<div class="divider"></div>
<div class="text-sm px-4">{{ v.description?.details }}</div>
</div>
<VRow class="mt-3">
<VCol md="4" sm="12" class="h-100">
<div class="mt-3 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="h-100">
<ValidatorCommissionRate
:commission="v.commission"
></ValidatorCommissionRate>
</VCol>
<VCol md="4" sm="12">
<VCard class="h-100">
<VCardTitle>Commissions & Rewards</VCardTitle>
<VCardItem class="pt-0 pb-0">
</div>
<div>
<div class="h-100 bg-base-100 rounded shadow">
<div class="text-lg font-semibold text-main px-4 pt-4">
Commissions & Rewards
</div>
<div class="px-4 mt-1">
<div class="overflow-auto" style="max-height: 280px">
<VCardSubtitle>
Commissions
</VCardSubtitle>
<VDivider class="mb-2"></VDivider>
<VChip
<div class="text-sm mb-2">Commissions</div>
<div
v-for="(i, k) in commission"
:key="`reward-${k}`"
color="info"
label
variant="outlined"
class="mr-1 mb-1"
class="mr-1 mb-1 badge text-xs"
>
{{ format.formatToken2(i) }}
</VChip>
<VCardSubtitle class="mt-2">Outstanding Rewards</VCardSubtitle>
<VDivider class="mb-2"></VDivider>
<VChip
</div>
<div class="text-sm mb-2 mt-2">Outstanding Rewards</div>
<div
v-for="(i, k) in rewards"
:key="`reward-${k}`"
color="success"
label
variant="outlined"
class="mr-1 mb-1"
class="mr-1 mb-1 badge text-xs"
>
{{ format.formatToken2(i) }}
</VChip>
</div>
</div>
<label for="withdraw_commission" class="btn btn-primary mt-2 w-full btn-sm" @click="dialog.open('withdraw_commission', {validator_address: v.operator_address})">Withdraw</label>
</VCardItem>
</VCard>
</VCol>
<VCol md="4" sm="12">
<VCard title="Addresses" class="h-100">
<VList class="pt-0">
<VListItem>
<VListItemTitle>Account</VListItemTitle>
<VListItemSubtitle class="text-caption text-primary"
><RouterLink :to="`/${chain}/account/${addresses.account}`">{{
addresses.account
}}</RouterLink></VListItemSubtitle
<label
for="withdraw_commission"
class="btn btn-primary mt-4 w-full btn-sm"
@click="
dialog.open('withdraw_commission', {
validator_address: v.operator_address,
})
"
>Withdraw</label
>
</div>
</div>
</div>
<div>
<div class="h-100 bg-base-100 rounded shadow">
<div class="px-4 pt-4 mb-2 text-main font-lg font-semibold">
Addresses
</div>
<div class="px-4">
<div class="mb-3">
<div class="text-sm">Account</div>
<RouterLink
class="text-xs text-primary"
:to="`/${chain}/account/${addresses.account}`"
>
</VListItem>
<VListItem>
<VListItemTitle>Operator Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{
v.operator_address
}}</VListItemSubtitle>
</VListItem>
<VListItem>
<VListItemTitle>Hex Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{
addresses.hex
}}</VListItemSubtitle>
</VListItem>
<VListItem>
<VListItemTitle>Signer Address</VListItemTitle>
<VListItemSubtitle class="text-caption">{{
addresses.valCons
}}</VListItemSubtitle>
</VListItem>
</VList>
</VCard>
</VCol>
</VRow>
<VCard title="Transactions" class="mt-5">
<VCardItem class="pt-0">
<VTable>
{{ addresses.account }}
</RouterLink>
</div>
<div class="mb-3">
<div class="text-sm">Operator Address</div>
<div class="text-xs">
{{ v.operator_address }}
</div>
</div>
<div class="mb-3">
<div class="text-sm">Hex Address</div>
<div class="text-xs">{{ addresses.hex }}</div>
</div>
<div>
<div class="text-sm">Signer Address</div>
<div class="text-xs">{{ addresses.valCons }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-5 bg-base-100 shadow rounded p-4">
<div class="text-lg mb-4 font-semibold">Transactions</div>
<div class="rounded overflow-auto">
<table class="table validatore-table w-full">
<thead>
<th class="text-left pl-4">Height</th>
<th class="text-left pl-4" style="position: relative; z-index: 2">
Height
</th>
<th class="text-left pl-4">Hash</th>
<th class="text-left pl-4" width="40%">Messages</th>
<th class="text-left pl-4">Time</th>
@ -393,24 +396,30 @@ onMounted(() => {
{{ item.txhash }}
</td>
<td>
{{ format.messages(item.tx.body.messages) }}
<VIcon
v-if="item.code === 0"
icon="mdi-check"
color="success"
></VIcon>
<VIcon v-else icon="mdi-multiply" color="error"></VIcon>
<div class="flex items-center">
<span class="mr-2">{{
format.messages(item.tx.body.messages)
}}</span>
<Icon
v-if="item.code === 0"
icon="mdi-check"
class="text-yes"
/>
<Icon v-else icon="mdi-multiply" class="text-no" />
</div>
</td>
<td width="150">{{ format.toDay(item.timestamp, 'from') }}</td>
</tr>
</tbody>
</VTable>
</VCardItem>
</VCard>
</table>
</div>
</div>
</div>
</template>
<style>
.card-list {
--v-card-list-gap: 10px;
.validatore-table.table :where(th, td) {
padding: 0.6rem 1rem;
font-size: 14px;
}
</style>

View File

@ -1,5 +1,10 @@
<script lang="ts" setup>
import { useBaseStore, useFormatter, useStakingStore, useTxDialog } from '@/stores';
import {
useBaseStore,
useFormatter,
useStakingStore,
useTxDialog,
} from '@/stores';
import { computed } from '@vue/reactivity';
import { onMounted, ref, type DebuggerEvent } from 'vue';
import { Icon } from '@iconify/vue';
@ -7,7 +12,7 @@ import type { Key, Validator } from '@/types';
const staking = useStakingStore();
const format = useFormatter();
const dialog = useTxDialog()
const dialog = useTxDialog();
const cache = JSON.parse(localStorage.getItem('avatars') || '{}');
const avatars = ref(cache || {});
@ -189,7 +194,7 @@ const rank = function (position: number) {
<div class="bg-base-100 px-4 pt-3 pb-4 rounded shadow">
<div class="overflow-x-auto">
<table class="table w-full">
<table class="table staking-table w-full">
<thead>
<tr>
<th scope="col" style="width: 3rem; position: relative">#</th>
@ -201,11 +206,15 @@ const rank = function (position: number) {
</tr>
</thead>
<tbody>
<tr v-for="(v, i) in list" :key="v.operator_address">
<tr
v-for="(v, i) in list"
:key="v.operator_address"
class="hover:bg-gray-100 dark:hover:bg-[#384059]"
>
<!-- 👉 rank -->
<td>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit"
class="text-xs truncate relative px-2 py-1 rounded-full w-fit"
:class="`text-${rank(i)}`"
>
<span
@ -218,18 +227,18 @@ const rank = function (position: number) {
<!-- 👉 Validator -->
<td>
<div
class="d-flex align-center overflow-hidden"
class="flex items-center overflow-hidden"
style="max-width: 400px"
>
<div
class="avatar mr-4 relative w-9 rounded-full overflow-hidden"
class="avatar mr-4 relative w-8 h-8 rounded-full overflow-hidden"
>
<div
class="w-9 rounded-full bg-gray-400 absolute opacity-10"
class="w-8 h-8 rounded-full bg-gray-400 absolute opacity-10"
></div>
<div class="w-9 rounded-full">
<div class="w-8 h-8 rounded-full">
<img
v-if="logo(v.description?.identity) !== ''"
v-if="v.description?.identity"
v-lazy="logo(v.description?.identity)"
class="object-contain"
/>
@ -241,7 +250,7 @@ const rank = function (position: number) {
</div>
</div>
<div class="d-flex flex-column">
<div class="flex flex-col">
<h6 class="text-sm text-primary">
<RouterLink
:to="{
@ -262,7 +271,7 @@ const rank = function (position: number) {
<!-- 👉 Voting Power -->
<td class="text-right">
<div class="d-flex flex-column">
<div class="flex flex-col">
<h6 class="text-sm font-weight-medium">
{{
format.formatToken(
@ -309,7 +318,16 @@ const rank = function (position: number) {
</td>
<!-- 👉 Action -->
<td>
<label for="delegate" class="btn btn-xs bg-primary" @click="dialog.open('delegate', {validator_address: v.operator_address})">Delegate</label>
<label
for="delegate"
class="btn btn-xs bg-primary"
@click="
dialog.open('delegate', {
validator_address: v.operator_address,
})
"
>Delegate</label
>
</td>
</tr>
</tbody>
@ -345,3 +363,10 @@ const rank = function (position: number) {
}
}
</route>
<style>
.staking-table.table :where(th, td) {
padding: 8px 5px;
background: transparent;
}
</style>

View File

@ -39,9 +39,7 @@ onMounted(() => {
<template>
<div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title truncate mb-2">
What's State Sync?
</h2>
<h2 class="card-title truncate mb-2">What's State Sync?</h2>
<div class="text-sm">
The Tendermint Core 0.34 release includes support for state sync, which
allows a new node to join a network by fetching a snapshot of the
@ -58,14 +56,17 @@ onMounted(() => {
</div>
<div class="bg-base-100 px-4 pt-3 pb-4 rounded mb-4 shadow">
<h2 class="card-title truncate mb-2">Starting New Node From State Sync</h2>
<h2 class="card-title truncate mb-2">
Starting New Node From State Sync
</h2>
<div class="text-sm">
1. Install Binary ({{ appName }} Version:
{{ nodeInfo.application_version?.version || '' }})
<br />
We need to install the binary first and make sure that the version is
the one currently in use on mainnet.
<br /> <br />
<br />
<br />
2. Enable State Sync<br />
We can configure Tendermint to use state sync in
$DAEMON_HOME/config/config.toml.
@ -86,7 +87,7 @@ onMounted(() => {
<div class="text-sm">
To make state sync works, we can enable snapshot in
$DAEMON_HOME/config/app.toml
<br/><br/>
<br /><br />
<VTextarea
auto-grow
model-value="[state-sync]

View File

@ -25,9 +25,13 @@ const messages = computed(() => {
</script>
<template>
<div>
<VCard v-if="tx.tx_response" title="Summary">
<VCardItem class="pt-0">
<VTable>
<div
v-if="tx.tx_response"
class="bg-base-100 px-4 pt-3 pb-4 rounded shadow mb-4"
>
<h2 class="card-title truncate mb-2">Summary</h2>
<div class="overflow-auto-x">
<table class="table text-sm">
<tbody>
<tr>
<td>Tx Hash</td>
@ -45,10 +49,21 @@ const messages = computed(() => {
<tr>
<td>Status</td>
<td>
<VChip v-if="tx.tx_response.code === 0" color="success"
>Success</VChip
<VChip color="primary">oioio</VChip>
<div
class="text-xs truncate relative py-2 px-4 rounded-full w-fit mr-2"
:class="`text-${
tx.tx_response.code === 0 ? 'success' : 'error'
}`"
>
<span v-else><VChip color="error">Failded</VChip></span>
<span
class="inset-x-0 inset-y-0 opacity-10 absolute"
:class="`bg-${
tx.tx_response.code === 0 ? 'success' : 'error'
}`"
></span>
{{ tx.tx_response.code === 0 ? 'Success' : 'Failded' }}
</div>
</td>
</tr>
<tr>
@ -82,23 +97,30 @@ const messages = computed(() => {
<td>{{ tx.tx.body.memo }}</td>
</tr>
</tbody>
</VTable>
</VCardItem>
</VCard>
</table>
</div>
</div>
<VCard :title="`Messages: (${messages.length})`" class="my-5">
<VCardItem class="pt-0" style="border-top: 2px dotted gray">
<div v-for="(msg, i) in messages">
<div><DynamicComponent :value="msg" /></div>
</div>
<div v-if="messages.length === 0">No messages</div>
</VCardItem>
</VCard>
<div
v-if="tx.tx_response"
class="bg-base-100 px-4 pt-3 pb-4 rounded shadow mb-4"
>
<h2 class="card-title truncate mb-2">
Messages: ({{ messages.length }})
</h2>
<div class="divider"></div>
<div v-for="(msg, i) in messages">
<div><DynamicComponent :value="msg" /></div>
</div>
<div v-if="messages.length === 0">No messages</div>
</div>
<VCard title="JSON">
<VCardItem class="pt-0">
<vue-json-pretty :data="tx" :deep="3" />
</VCardItem>
</VCard>
<div
v-if="tx.tx_response"
class="bg-base-100 px-4 pt-3 pb-4 rounded shadow"
>
<h2 class="card-title truncate mb-2">JSON</h2>
<vue-json-pretty :data="tx" :deep="3" />
</div>
</div>
</template>

View File

@ -1,6 +1,7 @@
<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,
@ -88,35 +89,23 @@ watchEffect(() => {
</script>
<template>
<div>
<VRow>
<VCol cols="12" md="4">
<VCard class="h-full self-center d-flex p-4">
<div class="self-center">
Current Height: {{ latest.block?.header?.height }}
</div>
</VCard>
</VCol>
<VCol cols="12" md="8" class="">
<VTextField
v-model="keyword"
label="Keywords to filter validators"
variant="outlined"
>
<template v-slot:append>
<VBtn
><VIcon icon="mdi-star" /><span class="d-none d-md-block"
>Favorite</span
></VBtn
>
</template>
</VTextField>
</VCol>
</VRow>
<div class="bg-base-100 px-5 pt-5">
<div class="flex items-center gap-x-4">
<div class="text-main text-lg">Current Height: {{ latest.block?.header?.height }}</div>
<input
type="text"
v-model="keyword"
placeholder="Keywords to filter validators"
class="input input-sm w-full flex-1"
/>
<button class="btn btn-primary btn-sm">
<Icon icon="mdi-star" class="mr-2 text-lg" /> <span class="">Favorite</span>
</button>
</div>
<VRow>
<VCol v-for="(v, i) in validators" cols="12" md="3" xl="2" class="py-0">
<div class="d-flex justify-between">
<div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4 mt-4">
<div v-for="(v, i) in validators">
<div class="flex items-center justify-between">
<VCheckbox
v-model="selected"
color="warning"
@ -128,26 +117,26 @@ watchEffect(() => {
>
</template>
</VCheckbox>
<VChip
<div
v-if="
Number(
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
?.missed_blocks_counter || 0
) > 0
"
size="small"
class="mt-1"
label
color="error"
>{{
class="badge badge-error badge-sm text-white"
>
{{
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
?.missed_blocks_counter
}}</VChip
>
<VChip v-else size="small" class="mt-1" label color="success">{{
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
?.missed_blocks_counter
}}</VChip>
}}
</div>
<div v-else class="mt-1 badge badge-sm text-white bg-yes">
{{
signingInfo[consensusPubkeyToHexAddress(v.consensus_pubkey)]
?.missed_blocks_counter
}}
</div>
</div>
<UptimeBar
:blocks="commits"
@ -155,8 +144,10 @@ watchEffect(() => {
toBase64(fromHex(consensusPubkeyToHexAddress(v.consensus_pubkey)))
"
/>
</VCol>
</VRow>
</div>
</div>
<div class="h-6"></div>
</div>
</template>
<route>