Merge pull request #362 from alisaweb3/v3-single

Governance Data Improve
This commit is contained in:
ping 2023-04-25 22:40:30 +08:00 committed by GitHub
commit 85f6e48c50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 671 additions and 420 deletions

9
.prettierrc.json Normal file
View File

@ -0,0 +1,9 @@
{
"tabWidth": 2,
"singleQuote": true,
"semi": true,
"endOfLine": "auto",
"bracketSpacing": true,
"TrailingCooma": true,
"arrowParens": "always"
}

View File

@ -37,10 +37,7 @@ const veto = computed(() =>
<div class="progress"> <div class="progress">
<div class="progress-bar bg-success" :style="`width: ${yes}`"></div> <div class="progress-bar bg-success" :style="`width: ${yes}`"></div>
<div class="progress-bar bg-error" :style="`width: ${no}`"></div> <div class="progress-bar bg-error" :style="`width: ${no}`"></div>
<div <div class="progress-bar bg-[#B71C1C]" :style="`width: ${veto};`"></div>
class="progress-bar"
:style="`width: ${veto}; background-color: #B71C1C;`"
></div>
<div class="progress-bar bg-secondary" :style="`width: ${abstain}`"></div> <div class="progress-bar bg-secondary" :style="`width: ${abstain}`"></div>
</div> </div>
</template> </template>

View File

@ -1,74 +1,87 @@
<script lang="ts" setup> <script lang="ts" setup>
import TxsElement from '@/components/dynamic/TxsElement.vue'; import TxsElement from "@/components/dynamic/TxsElement.vue";
import { useBlockModule } from './block' import { useBlockModule } from "./block";
import DynamicComponent from '@/components/dynamic/DynamicComponent.vue'; import DynamicComponent from "@/components/dynamic/DynamicComponent.vue";
import { computed } from '@vue/reactivity'; import { computed } from "@vue/reactivity";
import { toBase64, toHex } from '@cosmjs/encoding'; import { toBase64, toHex } from "@cosmjs/encoding";
import { useFormatter } from '@/stores'; import { useFormatter } from "@/stores";
const props = defineProps(["height", "chain"]); const props = defineProps(["height", "chain"]);
const store = useBlockModule() const store = useBlockModule();
store.fetchBlock(props.height) store.fetchBlock(props.height);
const tab = ref('blocks') const tab = ref("blocks");
const format = useFormatter() const format = useFormatter();
</script> </script>
<template> <template>
<VCard>
<VCard> <VCardTitle class="d-flex justify-space-between">
<VCardTitle class="d-flex justify-space-between"> <VTabs v-model="tab">
<VTabs v-model="tab"> <VTab value="blocks">Blocks</VTab>
<VTab value="blocks">Blocks</VTab> <VTab value="transactions">Transactions</VTab>
<VTab value="transactions">Transactions</VTab> </VTabs>
</VTabs> </VCardTitle>
</VCardTitle> <VWindow v-model="tab">
<VWindow v-model="tab"> <VWindowItem value="blocks">
<VWindowItem value="blocks"> <VTable>
<VTable> <thead>
<thead> <tr>
<tr> <th>Height</th>
<th>Height</th><th>Hash</th><th>Proposor</th><th>Txs</th><th>Time</th> <th>Hash</th>
</tr> <th>Proposor</th>
</thead> <th>Txs</th>
<tbody> <th>Time</th>
<tr v-for="item in store.recents"> </tr>
<td><RouterLink :to="`/${props.chain}/block/${item.block?.header?.height}`">{{ item.block?.header?.height }}</RouterLink></td> </thead>
<td>{{ toBase64(item.blockId?.hash) }}</td> <tbody>
<td>{{ format.validator(item.block?.header?.proposerAddress) }}</td> <tr v-for="item in store.recents">
<td>{{ item.block?.data?.txs.length }}</td> <td>
<td>{{ format.toDay(item.block?.header?.time, 'from') }}</td> <RouterLink
</tr> :to="`/${props.chain}/block/${item.block?.header?.height}`"
</tbody> >{{ item.block?.header?.height }}</RouterLink
</VTable> >
</VWindowItem> </td>
<VWindowItem value="transactions"> <td>{{ toBase64(item.blockId?.hash) }}</td>
<VTable> <td>
<thead> {{ format.validator(item.block?.header?.proposerAddress) }}
<tr> </td>
<th>Hash</th><th>Messages</th><th>Fees</th> <td>{{ item.block?.data?.txs.length }}</td>
</tr> <td>{{ format.toDay(item.block?.header?.time, "from") }}</td>
</thead> </tr>
<tbody> </tbody>
<tr v-for="item in store.txsInRecents"> </VTable>
<td><RouterLink :to="`/${props.chain}/tx/${item.hash}`">{{ item.hash }}</RouterLink></td> </VWindowItem>
<td>{{ format.messages(item.tx.body.messages) }}</td> <VWindowItem value="transactions">
<td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td> <VTable>
</tr> <thead>
</tbody> <tr>
</VTable> <th>Hash</th>
<th>Messages</th>
<th>Fees</th>
</tr>
</thead>
<tbody>
<tr v-for="item in store.txsInRecents">
<td>
<RouterLink :to="`/${props.chain}/tx/${item.hash}`">{{
item.hash
}}</RouterLink>
</td>
<td>{{ format.messages(item.tx.body.messages) }}</td>
<td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td>
</tr>
</tbody>
</VTable>
<VCardItem> <VCardItem>
<v-alert <v-alert
type="info" type="info"
text="Only show txs in recent blocks" text="Only show txs in recent blocks"
variant="tonal" variant="tonal"
></v-alert> ></v-alert>
</VCardItem> </VCardItem>
</VWindowItem> </VWindowItem>
</VWindow> </VWindow>
<VCardActions> <VCardActions> </VCardActions>
</VCard>
</VCardActions> </template>
</VCard>
</template>

View File

@ -1,40 +1,45 @@
<script lang="ts" setup> <script lang="ts" setup>
import MdEditor from 'md-editor-v3'; import MdEditor from "md-editor-v3";
import PriceMarketChart from '@/components/charts/PriceMarketChart.vue' import PriceMarketChart from "@/components/charts/PriceMarketChart.vue";
import { useBlockchain, useFormatter } from '@/stores'; import { useBlockchain, useFormatter } from "@/stores";
import { onMounted, ref } from 'vue'; import { onMounted, ref } from "vue";
import { useIndexModule } from './indexStore'; import { useIndexModule } from "./indexStore";
import { computed } from '@vue/reactivity'; import { computed } from "@vue/reactivity";
import CardStatisticsVertical from '@/components/CardStatisticsVertical.vue'; import CardStatisticsVertical from "@/components/CardStatisticsVertical.vue";
import ProposalProcess from '@/components/ProposalProcess.vue'; import ProposalProcess from "@/components/ProposalProcess.vue";
const blockchain = useBlockchain() const blockchain = useBlockchain();
const store = useIndexModule() const store = useIndexModule();
const coinInfo = computed(() => { const coinInfo = computed(() => {
return store.coinInfo return store.coinInfo;
}) });
onMounted(() => { onMounted(() => {
store.loadDashboard() store.loadDashboard();
}) });
const format = useFormatter() const format = useFormatter();
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]) const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
blockchain.$subscribe((m, s) => { blockchain.$subscribe((m, s) => {
console.log('index:', m) console.log("index:", m);
if(!Array.isArray(m.events) && ['chainName', 'endpoint'].includes(m.events.key)) { if (
console.log(m.events.key) !Array.isArray(m.events) &&
store.loadDashboard() ["chainName", "endpoint"].includes(m.events.key)
) {
console.log(m.events.key);
store.loadDashboard();
} }
}) });
function shortName(name: string, id: string) { function shortName(name: string, id: string) {
return name.toLowerCase().startsWith('ibc/') || name.toLowerCase().startsWith('0x') ? id: name return name.toLowerCase().startsWith("ibc/") ||
name.toLowerCase().startsWith("0x")
? id
: name;
} }
</script> </script>
<template> <template>
@ -44,58 +49,97 @@ function shortName(name: string, id: string) {
<VCol md="5"> <VCol md="5">
<VCardItem> <VCardItem>
<VCardTitle> <VCardTitle>
{{ coinInfo.name }} (<span class="text-uppercase">{{ coinInfo.symbol }}</span>) {{ coinInfo.name }} (<span class="text-uppercase">{{
coinInfo.symbol
}}</span
>)
</VCardTitle> </VCardTitle>
<VCardSubtitle> <VCardSubtitle>
Rank: <VChip color="error" size="x-small">#{{ coinInfo.market_cap_rank }}</VChip> Rank:
<VChip color="error" size="x-small"
>#{{ coinInfo.market_cap_rank }}</VChip
>
</VCardSubtitle> </VCardSubtitle>
</VCardItem> </VCardItem>
<VDivider/> <VDivider />
<VCardItem> <VCardItem>
<VBtn variant="text" size="small" :href="store.homepage" prependIcon="mdi-web">Website</VBtn> <VBtn
<VBtn variant="text" size="small" :href="store.twitter" prependIcon="mdi-twitter">Twitter</VBtn> variant="text"
<VBtn variant="text" size="small" :href="store.telegram" prependIcon="mdi-telegram">Telegram</VBtn> size="small"
<VBtn variant="text" size="small" :href="store.github" prependIcon="mdi-github">Github</VBtn> :href="store.homepage"
prependIcon="mdi-web"
>Website</VBtn
>
<VBtn
variant="text"
size="small"
:href="store.twitter"
prependIcon="mdi-twitter"
>Twitter</VBtn
>
<VBtn
variant="text"
size="small"
:href="store.telegram"
prependIcon="mdi-telegram"
>Telegram</VBtn
>
<VBtn
variant="text"
size="small"
:href="store.github"
prependIcon="mdi-github"
>Github</VBtn
>
</VCardItem> </VCardItem>
<VCardItem> <VCardItem>
<!-- SECTION upgrade plan banner --> <!-- SECTION upgrade plan banner -->
<div class="plan-upgrade-banner d-flex bg-light-secondary rounded align-center pa-3"> <div
<h3 class="plan-upgrade-banner d-flex bg-light-secondary rounded align-center pa-3"
class="plan-details me-3" >
:class="store.priceColor" <h3 class="plan-details me-3" :class="store.priceColor">
>
{{ store.priceChange }}<small>%</small> {{ store.priceChange }}<small>%</small>
</h3> </h3>
<VMenu open-on-hover> <VMenu open-on-hover>
<template #activator="{ props }"> <template #activator="{ props }">
<div class="d-flex flex-column align-start" v-bind="props"> <div class="d-flex flex-column align-start" v-bind="props">
<h3 class="text-base font-weight-semibold"> <h3 class="text-base font-weight-semibold">
{{ ticker?.market?.name || ''}} {{ ticker?.market?.name || "" }}
</h3> </h3>
<span class="text-primary text-xs">{{ shortName(ticker?.base, ticker.coin_id) }}/{{ shortName(ticker?.target, ticker.target_coin_id) }}</span> <span class="text-primary text-xs"
>{{ shortName(ticker?.base, ticker.coin_id) }}/{{
shortName(ticker?.target, ticker.target_coin_id)
}}</span
>
</div> </div>
</template> </template>
<VList style="max-height: 300px;"> <VList style="max-height: 300px">
<VListItem <VListItem
v-for="(item, i) in store.coinInfo.tickers" v-for="(item, i) in store.coinInfo.tickers"
:key="i" :key="i"
rounded rounded
@click="store.selectTicker(i)" @click="store.selectTicker(i)"
> >
<template #prepend> <template #prepend> </template>
</template>
<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component --> <!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
<VListItemTitle v-text="item.market.name" /> <VListItemTitle v-text="item.market.name" />
<VListItemSubtitle>{{ shortName(item?.base, item.coin_id) }}/{{ shortName(item?.target, item.target_coin_id) }}</VListItemSubtitle> <VListItemSubtitle
>{{ shortName(item?.base, item.coin_id) }}/{{
shortName(item?.target, item.target_coin_id)
}}</VListItemSubtitle
>
<template #append> <template #append>
<span class="ml-3" :class="`text-${store.tickerColor(item.trust_score)}`">{{ item.converted_last.usd }}</span> <span
class="ml-3"
:class="`text-${store.tickerColor(item.trust_score)}`"
>{{ item.converted_last.usd }}</span
>
</template> </template>
</VListItem> </VListItem>
</VList> </VList>
</VMenu> </VMenu>
<VSpacer /> <VSpacer />
<div class="d-flex align-center"> <div class="d-flex align-center">
@ -107,14 +151,14 @@ function shortName(name: string, id: string) {
</div> </div>
<!-- !SECTION --> <!-- !SECTION -->
<VSpacer /> <VSpacer />
<VBtn <VBtn
block block
:color="store.trustColor" :color="store.trustColor"
class="mt-3" class="mt-3"
:href="ticker.trade_url" :href="ticker.trade_url"
> >
Buy {{ coinInfo.symbol || '' }} Buy {{ coinInfo.symbol || "" }}
</VBtn> </VBtn>
</VCardItem> </VCardItem>
</VCol> </VCol>
<VCol md="7"> <VCol md="7">
@ -124,9 +168,16 @@ function shortName(name: string, id: string) {
</VCol> </VCol>
</VRow> </VRow>
<VDivider /> <VDivider />
<VCardText class="" style="max-height: 250px; overflow:auto;"><MdEditor :model-value="coinInfo.description?.en" previewOnly></MdEditor></VCardText> <VCardText class="" style="max-height: 250px; overflow: auto"
><MdEditor
:model-value="coinInfo.description?.en"
previewOnly
></MdEditor
></VCardText>
<VCardItem> <VCardItem>
<VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{ tag }}</VChip> <VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{
tag
}}</VChip>
</VCardItem> </VCardItem>
</VCard> </VCard>
@ -139,46 +190,56 @@ function shortName(name: string, id: string) {
</VRow> </VRow>
<VCard class="my-5"> <VCard class="my-5">
<VCardItem class="pb-0"> <VCardItem class="pb-0">
<VCardTitle>Active Proposals</VCardTitle> <VCardTitle>Active Proposals</VCardTitle>
</VCardItem> </VCardItem>
<VCardItem> <VCardItem>
<VExpansionPanels variant="accordion"> <VExpansionPanels variant="accordion">
<VExpansionPanel v-for="(x, i) in store.proposals"> <VExpansionPanel v-for="(x, i) in store.proposals">
<VExpansionPanelTitle disable-icon-rotate> <VExpansionPanelTitle disable-icon-rotate>
<VChip label color="primary" class="mr-2">{{x.proposalId}}</VChip> <VChip label color="primary" class="mr-2">{{
<div class="w-100">{{ x.content?.title }} x.proposalId
<div class="d-flex mt-1"> }}</VChip>
<small class="text-secondary me-auto"> {{ format.toDay(x.votingEndTime, 'from') }}</small> <div class="w-100">
<ProposalProcess style="width:300px;" :pool="store.pool" :tally="store.tally[Number(x.proposalId)]"></ProposalProcess> {{ x.content?.title }}
<span></span> <div class="d-flex mt-1">
</div> <small class="text-secondary me-auto">
</div> {{ format.toDay(x.votingEndTime, "from") }}</small
<template #actions> >
<VIcon <ProposalProcess
icon="mdi-check" style="width: 300px"
color="success" :pool="store.pool"
class="ml-2" :tally="store.tally[Number(x.proposalId)]"
/> ></ProposalProcess>
</template> <span></span>
</VExpansionPanelTitle> </div>
<VExpansionPanelText> </div>
<VCard class="card-box"> <template #actions>
<VCardText> <VIcon icon="mdi-check" color="success" class="ml-2" />
<MdEditor :model-value="x.content?.description" previewOnly></MdEditor> </template>
</VCardText> </VExpansionPanelTitle>
<div class="text-center w-100 my-2"> <VExpansionPanelText>
<VBtn color="primary" variant="flat">Vote</VBtn> <VCard class="card-box">
</div> <VCardText>
</VCard> <MdEditor
</VExpansionPanelText> :model-value="x.content?.description"
</VExpansionPanel> previewOnly
</VExpansionPanels> ></MdEditor>
</VCardItem> </VCardText>
<VCardText v-if="store.proposals.length === 0">No active proposals</VCardText> <div class="text-center w-100 my-2">
<VBtn color="primary" variant="flat">Vote</VBtn>
</div>
</VCard>
</VExpansionPanelText>
</VExpansionPanel>
</VExpansionPanels>
</VCardItem>
<VCardText v-if="store.proposals.length === 0"
>No active proposals</VCardText
>
</VCard> </VCard>
<VBtn block color='secondary' variant="outlined" class="mt-5"> <VBtn block color="secondary" variant="outlined" class="mt-5">
Connect Wallet Connect Wallet
</VBtn> </VBtn>
</div> </div>

View File

@ -1,29 +1,44 @@
<script setup lang="ts"> <script setup lang="ts">
import misc404 from '@images/pages/404.png' import misc404 from "@images/pages/404.png";
import miscObj from '@images/pages/misc-404-object.png' import miscObj from "@images/pages/misc-404-object.png";
import miscMaskDark from '@images/pages/misc-mask-dark.png' import miscMaskDark from "@images/pages/misc-mask-dark.png";
import miscMaskLight from '@images/pages/misc-mask-light.png' import miscMaskLight from "@images/pages/misc-mask-light.png";
import { useGenerateImageVariant } from '@/plugins/vuetify/@core/composable/useGenerateImageVariant' import { useGenerateImageVariant } from "@/plugins/vuetify/@core/composable/useGenerateImageVariant";
const miscThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark) const miscThemeMask = useGenerateImageVariant(miscMaskLight, miscMaskDark);
</script> </script>
<template> <template>
<div class="misc-wrapper"> <div class="misc-wrapper">
<ErrorHeader error-code="404" error-title="Page Not Found " <ErrorHeader
error-description="We couldn't find the page you are looking for." /> error-code="404"
error-title="Page Not Found ⚠️"
error-description="We couldn't find the page you are looking for."
/>
<!-- 👉 Image --> <!-- 👉 Image -->
<div class="misc-avatar w-100 text-center"> <div class="misc-avatar w-100 text-center">
<VImg :src="misc404" alt="Coming Soon" :height="$vuetify.display.xs ? 400 : 500" class="my-sm-4" /> <VImg
<VBtn to="/" class="mt-10"> :src="misc404"
Back to Home alt="Coming Soon"
</VBtn> :height="$vuetify.display.xs ? 400 : 500"
<VImg :src="miscThemeMask" class="d-none d-md-block footer-coming-soon" cover /> class="my-sm-4"
/>
<VBtn to="/" class="mt-10"> Back to Home </VBtn>
<VImg
:src="miscThemeMask"
class="d-none d-md-block footer-coming-soon"
cover
/>
<VImg :src="miscObj" class="d-none d-md-block footer-coming-soon-obj" :max-width="174" height="158" /> <VImg
:src="miscObj"
class="d-none d-md-block footer-coming-soon-obj"
:max-width="174"
height="158"
/>
</div> </div>
</div> </div>
</template> </template>

View File

@ -9,7 +9,7 @@
"add_to_favorite": "Add to favorite" "add_to_favorite": "Add to favorite"
}, },
"Ecosystem": "Ecosystem", "Ecosystem": "Ecosystem",
"All Blockchains": "All Blockchain222", "All Blockchains": "All Blockchain",
"Favorite": "Favorite" "Favorite": "Favorite"
} }

View File

@ -1,38 +1,30 @@
<script setup lang="ts"> <script setup lang="ts">
interface Props { interface Props {
title: string title: string;
color?: string color?: string;
icon: string icon: string;
stats: string stats: string;
change?: number change?: number;
subtitle?: string subtitle?: string;
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
color: 'primary', color: 'primary',
}) });
const isPositive = controlledComputed(() => props.change, () => Math.sign(props.change||0) === 1) const isPositive = controlledComputed(
() => props.change,
() => Math.sign(props.change || 0) === 1
);
</script> </script>
<template> <template>
<VCard> <VCard class="h-full flex-col content-between">
<VCardText class="d-flex align-center justify-center"> <VCardText class="d-flex align-center justify-between">
<VAvatar <VAvatar v-if="props.icon" rounded size="38" variant="tonal" :color="props.color">
v-if="props.icon" <VIcon :icon="props.icon" size="24" />
rounded
size="38"
variant="tonal"
:color="props.color"
>
<VIcon
:icon="props.icon"
size="24"
/>
</VAvatar> </VAvatar>
<VSpacer />
<div <div
v-if="props.change" v-if="props.change"
:class="isPositive ? 'text-success' : 'text-error'" :class="isPositive ? 'text-success' : 'text-error'"
@ -44,7 +36,7 @@ const isPositive = controlledComputed(() => props.change, () => Math.sign(props.
</div> </div>
</VCardText> </VCardText>
<VCardText> <VCardText class="d-flex flex-col">
<h6 class="text-h6 me-2 mt-2 mb-1"> <h6 class="text-h6 me-2 mt-2 mb-1">
{{ props.stats }} {{ props.stats }}
</h6> </h6>
@ -52,11 +44,7 @@ const isPositive = controlledComputed(() => props.change, () => Math.sign(props.
{{ props.title }} {{ props.title }}
</p> </p>
<VChip <VChip v-if="props.subtitle" size="x-small" class="font-weight-medium">
v-if="props.subtitle"
size="x-small"
class="font-weight-medium"
>
<span class="text-truncate">{{ props.subtitle }}</span> <span class="text-truncate">{{ props.subtitle }}</span>
</VChip> </VChip>
</VCardText> </VCardText>

View File

@ -1,14 +1,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import MdEditor from 'md-editor-v3';
import { useBlockchain, useFormatter, useStakingStore } from '@/stores'; import { useBlockchain, useFormatter, useStakingStore } from '@/stores';
import type { GovProposal, PaginatedProposals } from '@/types'; import type { PaginatedProposals } from '@/types';
import ProposalProcess from './ProposalProcess.vue'; import ProposalProcess from './ProposalProcess.vue';
import type { PropType } from 'vue'; import type { PropType } from 'vue';
const props = defineProps({ defineProps({
proposals: { type: Object as PropType<PaginatedProposals> }, proposals: { type: Object as PropType<PaginatedProposals> },
}); });
// const list = computed(()=> proposl)
const format = useFormatter(); const format = useFormatter();
const staking = useStakingStore(); const staking = useStakingStore();
const chain = useBlockchain(); const chain = useBlockchain();
@ -34,9 +33,11 @@ const statusMap: Record<string, string> = {
class="py-4 px-4 hover:bg-gray-100 dark:hover:bg-[#353f5a] block rounded cursor-pointer" class="py-4 px-4 hover:bg-gray-100 dark:hover:bg-[#353f5a] block rounded cursor-pointer"
> >
<div class="grid grid-cols-6 md:grid-cols-11 flex-1"> <div class="grid grid-cols-6 md:grid-cols-11 flex-1">
<div class="text-textMain dark:text-white mb-3">#{{ item?.proposal_id }}</div> <div class="text-main dark:text-white mb-3">
#{{ item?.proposal_id }}
</div>
<div class="col-span-5 md:pr-10 text-textMain dark:text-white truncate"> <div class="col-span-5 md:pr-10 text-main dark:text-white truncate">
{{ item?.content?.title }} {{ item?.content?.title }}
</div> </div>
@ -48,9 +49,29 @@ const statusMap: Record<string, string> = {
</div> </div>
</div> </div>
<div class="text-yes flex items-center mb-3"> <div
<div class="w-1 h-1 bg-yes rounded-full mr-2"></div> class="flex items-center mb-3"
<div class="text-xs">{{ statusMap?.[item?.status] || item?.status }}</div> :class="
statusMap?.[item?.status] === 'PASSED'
? 'text-yes'
: statusMap?.[item?.status] === 'REJECTED'
? 'text-no'
: 'text-info'
"
>
<div
class="w-1 h-1 rounded-full mr-2"
:class="
statusMap?.[item?.status] === 'PASSED'
? 'bg-yes'
: statusMap?.[item?.status] === 'REJECTED'
? 'bg-no'
: 'bg-info'
"
></div>
<div class="text-xs">
{{ statusMap?.[item?.status] || item?.status }}
</div>
</div> </div>
<div <div
@ -59,7 +80,10 @@ const statusMap: Record<string, string> = {
{{ format.toDay(item.voting_end_time, 'from') }} {{ format.toDay(item.voting_end_time, 'from') }}
</div> </div>
</div> </div>
<ProposalProcess :pool="staking.pool" :tally="item.final_tally_result"></ProposalProcess> <ProposalProcess
:pool="staking.pool"
:tally="item.final_tally_result"
></ProposalProcess>
</RouterLink> </RouterLink>
</div> </div>
</template> </template>

View File

@ -17,15 +17,22 @@ import TheCustomizer from '@/plugins/vuetify/@core/components/TheCustomizer.vue'
import Breadcrumbs from './Breadcrumbs.vue'; import Breadcrumbs from './Breadcrumbs.vue';
import { useBlockchain } from '@/stores'; import { useBlockchain } from '@/stores';
const { appRouteTransition, isLessThanOverlayNavBreakpoint, isVerticalNavCollapsed } = const {
useThemeConfig(); appRouteTransition,
isLessThanOverlayNavBreakpoint,
isVerticalNavCollapsed,
} = useThemeConfig();
const { width: windowWidth } = useWindowSize(); const { width: windowWidth } = useWindowSize();
// Provide animation name for vertical nav collapse icon. // Provide animation name for vertical nav collapse icon.
const verticalNavHeaderActionAnimationName = ref<null | 'rotate-180' | 'rotate-back-180'>(null); const verticalNavHeaderActionAnimationName = ref<
null | 'rotate-180' | 'rotate-back-180'
>(null);
watch(isVerticalNavCollapsed, val => { watch(isVerticalNavCollapsed, (val) => {
verticalNavHeaderActionAnimationName.value = val ? 'rotate-180' : 'rotate-back-180'; verticalNavHeaderActionAnimationName.value = val
? 'rotate-180'
: 'rotate-back-180';
}); });
const dashboard = useDashboard(); const dashboard = useDashboard();

View File

@ -1,66 +1,126 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SearchHeader, SearchItem } from '@/@fake-db/types' import type { SearchHeader, SearchItem } from '@/@fake-db/types';
import axios from 'axios' import axios from 'axios';
import { useThemeConfig } from '@core/composable/useThemeConfig' import { useThemeConfig } from '@core/composable/useThemeConfig';
interface Suggestion { interface Suggestion {
icon: string icon: string;
title: string title: string;
url: object url: object;
} }
const { appContentLayoutNav } = useThemeConfig() const { appContentLayoutNav } = useThemeConfig();
interface SuggestionGroup { interface SuggestionGroup {
title: string title: string;
content: Suggestion[] content: Suggestion[];
} }
defineOptions({ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}) });
// 👉 Is App Search Bar Visible // 👉 Is App Search Bar Visible
const isAppSearchBarVisible = ref(false) const isAppSearchBarVisible = ref(false);
// 👉 Default suggestions // 👉 Default suggestions
const suggestionGroups: SuggestionGroup[] = [ const suggestionGroups: SuggestionGroup[] = [
{ {
title: 'Popular Searches', title: 'Popular Searches',
content: [ content: [
{ icon: 'mdi-chart-donut', title: 'Analytics', url: { name: 'dashboards-analytics' } }, {
{ icon: 'mdi-chart-bubble', title: 'CRM', url: { name: 'dashboards-crm' } }, icon: 'mdi-chart-donut',
{ icon: 'mdi-file-outline', title: 'Invoice List', url: { name: 'apps-invoice-list' } }, title: 'Analytics',
{ icon: 'mdi-account-group-outline', title: 'User List', url: { name: 'apps-user-list' } }, url: { name: 'dashboards-analytics' },
},
{
icon: 'mdi-chart-bubble',
title: 'CRM',
url: { name: 'dashboards-crm' },
},
{
icon: 'mdi-file-outline',
title: 'Invoice List',
url: { name: 'apps-invoice-list' },
},
{
icon: 'mdi-account-group-outline',
title: 'User List',
url: { name: 'apps-user-list' },
},
], ],
}, },
{ {
title: 'Apps & Pages', title: 'Apps & Pages',
content: [ content: [
{ icon: 'mdi-calendar', title: 'Calendar', url: { name: 'apps-calendar' } }, {
{ icon: 'mdi-file-plus-outline', title: 'Invoice Add', url: { name: 'apps-invoice-add' } }, icon: 'mdi-calendar',
{ icon: 'mdi-currency-usd', title: 'Pricing', url: { name: 'pages-pricing' } }, title: 'Calendar',
{ icon: 'mdi-account-cog-outline', title: 'Account Settings', url: { name: 'pages-account-settings-tab', params: { tab: 'account' } } }, url: { name: 'apps-calendar' },
},
{
icon: 'mdi-file-plus-outline',
title: 'Invoice Add',
url: { name: 'apps-invoice-add' },
},
{
icon: 'mdi-currency-usd',
title: 'Pricing',
url: { name: 'pages-pricing' },
},
{
icon: 'mdi-account-cog-outline',
title: 'Account Settings',
url: { name: 'pages-account-settings-tab', params: { tab: 'account' } },
},
], ],
}, },
{ {
title: 'User Interface', title: 'User Interface',
content: [ content: [
{ icon: 'mdi-alpha-a-box-outline', title: 'Typography', url: { name: 'pages-typography' } }, {
icon: 'mdi-alpha-a-box-outline',
title: 'Typography',
url: { name: 'pages-typography' },
},
{ icon: 'mdi-tab', title: 'Tabs', url: { name: 'components-tabs' } }, { icon: 'mdi-tab', title: 'Tabs', url: { name: 'components-tabs' } },
{ icon: 'mdi-gesture-tap-button', title: 'Buttons', url: { name: 'components-button' } }, {
{ icon: 'mdi-keyboard-settings-outline', title: 'Statistics', url: { name: 'pages-cards-card-statistics' } }, icon: 'mdi-gesture-tap-button',
title: 'Buttons',
url: { name: 'components-button' },
},
{
icon: 'mdi-keyboard-settings-outline',
title: 'Statistics',
url: { name: 'pages-cards-card-statistics' },
},
], ],
}, },
{ {
title: 'Popular Searches', title: 'Popular Searches',
content: [ content: [
{ icon: 'mdi-format-list-checkbox', title: 'Select', url: { name: 'forms-select' } }, {
{ icon: 'mdi-lastpass', title: 'Combobox', url: { name: 'forms-combobox' } }, icon: 'mdi-format-list-checkbox',
{ icon: 'mdi-calendar-range-outline', title: 'Date & Time Picker', url: { name: 'forms-date-time-picker' } }, title: 'Select',
{ icon: 'mdi-hexagram-outline', title: 'Rating', url: { name: 'forms-rating' } }, url: { name: 'forms-select' },
},
{
icon: 'mdi-lastpass',
title: 'Combobox',
url: { name: 'forms-combobox' },
},
{
icon: 'mdi-calendar-range-outline',
title: 'Date & Time Picker',
url: { name: 'forms-date-time-picker' },
},
{
icon: 'mdi-hexagram-outline',
title: 'Rating',
url: { name: 'forms-rating' },
},
], ],
}, },
] ];
// 👉 No Data suggestion // 👉 No Data suggestion
const noDataSuggestions: Suggestion[] = [ const noDataSuggestions: Suggestion[] = [
@ -79,32 +139,36 @@ const noDataSuggestions: Suggestion[] = [
icon: 'mdi-cash', icon: 'mdi-cash',
url: { name: 'pages-pricing' }, url: { name: 'pages-pricing' },
}, },
] ];
const searchQuery = ref('') const searchQuery = ref('');
const searchResult = ref<(SearchItem | SearchHeader)[]>([]) const searchResult = ref<(SearchItem | SearchHeader)[]>([]);
const router = useRouter() const router = useRouter();
// 👉 fetch search result API // 👉 fetch search result API
watchEffect(() => { watchEffect(() => {
axios.get('/app-bar/search', { axios
params: { .get('/app-bar/search', {
q: searchQuery.value, params: {
}, q: searchQuery.value,
}).then(response => { },
searchResult.value = response.data })
}) .then((response) => {
}) searchResult.value = response.data;
});
});
// 👉 redirect the selected page // 👉 redirect the selected page
const redirectToSuggestedOrSearchedPage = (selected: Suggestion) => { const redirectToSuggestedOrSearchedPage = (selected: Suggestion) => {
router.push(selected.url) router.push(selected.url);
isAppSearchBarVisible.value = false isAppSearchBarVisible.value = false;
searchQuery.value = '' searchQuery.value = '';
} };
const LazyAppBarSearch = defineAsyncComponent(() => import('@core/components/AppBarSearch.vue')) const LazyAppBarSearch = defineAsyncComponent(
() => import('@core/components/AppBarSearch.vue')
);
</script> </script>
<template> <template>
@ -157,7 +221,7 @@ const LazyAppBarSearch = defineAsyncComponent(() => import('@core/components/App
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@use "@styles/variables/_vuetify.scss"; @use '@styles/variables/_vuetify.scss';
.meta-key { .meta-key {
border: thin solid rgba(var(--v-border-color), var(--v-border-opacity)); border: thin solid rgba(var(--v-border-color), var(--v-border-opacity));

View File

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

View File

@ -0,0 +1,12 @@
<template>
<div class="text-main text-h5 mb-4">Parameters</div>
<div class="bg-card px-4 pt-3 pb-4 rounded-sm">
<div class="text-base mb-3 text-main">Minting Parameters</div>
<div class="grid grid-cols-5 gap-4">
<div v-for="item in 10" :key="item" class="rounded-sm bg-active px-4 py-2">
<div class="text-xs mb-2 text-secondary">Blocks Per Year</div>
<div class="text-base text-main">4,360,000</div>
</div>
</div>
</div>
</template>

View File

@ -1,73 +1,86 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useBlockModule } from './block' import { useBlockModule } from './block';
import { computed, ref } from '@vue/reactivity'; import { computed, ref } from '@vue/reactivity';
import { useFormatter } from '@/stores'; import { useFormatter } from '@/stores';
const props = defineProps(["height", "chain"]); const props = defineProps(['height', 'chain']);
const store = useBlockModule() const store = useBlockModule();
// store.fetchBlock(props.height) // store.fetchBlock(props.height)
const tab = ref('blocks') const tab = ref('blocks');
const format = useFormatter() const format = useFormatter();
</script> </script>
<template> <template>
<VCard>
<VCard> <VCardTitle class="d-flex justify-space-between">
<VCardTitle class="d-flex justify-space-between"> <VTabs v-model="tab">
<VTabs v-model="tab"> <VTab value="blocks">Blocks</VTab>
<VTab value="blocks">Blocks</VTab> <VTab value="transactions">Transactions</VTab>
<VTab value="transactions">Transactions</VTab> </VTabs>
</VTabs> </VCardTitle>
</VCardTitle> <VWindow v-model="tab">
<VWindow v-model="tab"> <VWindowItem value="blocks">
<VWindowItem value="blocks"> <VTable>
<VTable> <thead>
<thead> <tr>
<tr> <th>Height</th>
<th>Height</th><th>Hash</th><th>Proposer</th><th>Txs</th><th>Time</th> <th>Hash</th>
</tr> <th>Proposer</th>
</thead> <th>Txs</th>
<tbody> <th>Time</th>
<tr v-for="item in store.recents"> </tr>
<td class="text-sm text-primary"><RouterLink :to="`/${props.chain}/block/${item.block?.header?.height}`">{{ item.block?.header?.height }}</RouterLink></td> </thead>
<td>{{ item.block_id?.hash }}</td> <tbody>
<td>{{ format.validator(item.block?.header?.proposer_address) }}</td> <tr v-for="item in store.recents">
<td>{{ item.block?.data?.txs.length }}</td> <td class="text-sm text-primary">
<td>{{ format.toDay(item.block?.header?.time, 'from') }}</td> <RouterLink
</tr> :to="`/${props.chain}/block/${item.block?.header?.height}`"
</tbody> >{{ item.block?.header?.height }}</RouterLink
</VTable> >
</VWindowItem> </td>
<VWindowItem value="transactions"> <td>{{ item.block_id?.hash }}</td>
<VTable> <td>
<thead> {{ format.validator(item.block?.header?.proposer_address) }}
<tr> </td>
<th>Hash</th><th>Messages</th><th>Fees</th> <td>{{ item.block?.data?.txs.length }}</td>
</tr> <td>{{ format.toDay(item.block?.header?.time, 'from') }}</td>
</thead> </tr>
<tbody> </tbody>
<tr v-for="item in store.txsInRecents"> </VTable>
<td><RouterLink :to="`/${props.chain}/tx/${item.hash}`">{{ item.hash }}</RouterLink></td> </VWindowItem>
<td>{{ format.messages(item.tx.body.messages) }}</td> <VWindowItem value="transactions">
<td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td> <VTable>
</tr> <thead>
</tbody> <tr>
</VTable> <th>Hash</th>
<th>Messages</th>
<th>Fees</th>
</tr>
</thead>
<tbody>
<tr v-for="item in store.txsInRecents">
<td>
<RouterLink :to="`/${props.chain}/tx/${item.hash}`">{{
item.hash
}}</RouterLink>
</td>
<td>{{ format.messages(item.tx.body.messages) }}</td>
<td>{{ format.formatTokens(item.tx.authInfo.fee?.amount) }}</td>
</tr>
</tbody>
</VTable>
<VCardItem> <VCardItem>
<v-alert <v-alert
type="info" type="info"
text="Only show txs in recent blocks" text="Only show txs in recent blocks"
variant="tonal" variant="tonal"
></v-alert> ></v-alert>
</VCardItem> </VCardItem>
</VWindowItem> </VWindowItem>
</VWindow> </VWindow>
<VCardActions> <VCardActions> </VCardActions>
</VCard>
</VCardActions>
</VCard>
</template> </template>
<route> <route>
@ -76,4 +89,4 @@ const format = useFormatter()
i18n: 'blocks' i18n: 'blocks'
} }
} }
</route> </route>

View File

@ -2,7 +2,7 @@
import ObjectElement from '@/components/dynamic/ObjectElement.vue'; import ObjectElement from '@/components/dynamic/ObjectElement.vue';
import { useBaseStore, useFormatter, useGovStore, useStakingStore } from '@/stores'; import { useBaseStore, useFormatter, useGovStore, useStakingStore } from '@/stores';
import type { GovProposal, GovVote, PaginabledAccounts, PaginatedProposalDeposit, PaginatedProposalVotes, Pagination } from '@/types'; import type { GovProposal, GovVote, PaginabledAccounts, PaginatedProposalDeposit, PaginatedProposalVotes, Pagination } from '@/types';
import { ref } from 'vue'; import { ref , reactive} from 'vue';
import Countdown from '@/components/Countdown.vue'; import Countdown from '@/components/Countdown.vue';
import { computed } from '@vue/reactivity'; import { computed } from '@vue/reactivity';
@ -12,7 +12,16 @@ const props = defineProps(["proposal_id", "chain"]);
const proposal = ref({} as GovProposal) const proposal = ref({} as GovProposal)
const format = useFormatter() const format = useFormatter()
const store = useGovStore() const store = useGovStore()
store.fetchProposal(props.proposal_id).then((x) => proposal.value = x.proposal) store.fetchProposal(props.proposal_id).then((res) => {
const proposalDetail = reactive(res.proposal)
// when status under the voting, final_tally_result are no data, should request fetchTally
if (res.proposal?.status === 'PROPOSAL_STATUS_VOTING_PERIOD'){
store.fetchTally(props.proposal_id).then((tallRes)=>{
proposalDetail.final_tally_result = tallRes?.tally
})
}
proposal.value = proposalDetail
})
const color = computed(() => { const color = computed(() => {
if (proposal.value.status==='PROPOSAL_STATUS_PASSED') { if (proposal.value.status==='PROPOSAL_STATUS_PASSED') {

View File

@ -1,10 +1,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { useGovStore } from '@/stores'; import { useGovStore } from '@/stores';
import ProposalListItem from '@/components/ProposalListItem.vue'; import ProposalListItem from '@/components/ProposalListItem.vue';
import { ref, onMounted } from 'vue'
const tab = ref("") const tab = ref("")
const store = useGovStore() const store = useGovStore()
store.fetchProposals("2")
onMounted(()=>{
store.fetchProposals('2')
})
</script> </script>
<template> <template>
<div> <div>

View File

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import MdEditor from 'md-editor-v3'; import MdEditor from 'md-editor-v3';
import PriceMarketChart from '@/components/charts/PriceMarketChart.vue' import PriceMarketChart from '@/components/charts/PriceMarketChart.vue';
import { useBlockchain, useFormatter } from '@/stores'; import { useBlockchain, useFormatter } from '@/stores';
import { onMounted, ref } from 'vue'; import { onMounted, ref } from 'vue';
@ -10,31 +10,30 @@ import { computed } from '@vue/reactivity';
import CardStatisticsVertical from '@/components/CardStatisticsVertical.vue'; import CardStatisticsVertical from '@/components/CardStatisticsVertical.vue';
import ProposalListItem from '@/components/ProposalListItem.vue'; import ProposalListItem from '@/components/ProposalListItem.vue';
const blockchain = useBlockchain() const blockchain = useBlockchain();
const store = useIndexModule() const store = useIndexModule();
const coinInfo = computed(() => { const coinInfo = computed(() => {
return store.coinInfo return store.coinInfo;
}) });
onMounted(() => { onMounted(() => {
store.loadDashboard() store.loadDashboard();
}) });
const format = useFormatter() const format = useFormatter();
const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]) const ticker = computed(() => store.coinInfo.tickers[store.tickerIndex]);
blockchain.$subscribe((m, s) => { blockchain.$subscribe((m, s) => {
console.log('index:', m) console.log('index:', m);
if(!Array.isArray(m.events) && ['chainName', 'endpoint'].includes(m.events.key)) { if (!Array.isArray(m.events) && ['chainName', 'endpoint'].includes(m.events.key)) {
console.log(m.events.key) console.log(m.events.key);
store.loadDashboard() store.loadDashboard();
} }
}) });
function shortName(name: string, id: string) { function shortName(name: string, id: string) {
return name.toLowerCase().startsWith('ibc/') || name.toLowerCase().startsWith('0x') ? id: name return name.toLowerCase().startsWith('ibc/') || name.toLowerCase().startsWith('0x') ? id : name;
} }
</script> </script>
<template> <template>
@ -44,58 +43,75 @@ function shortName(name: string, id: string) {
<VCol md="5"> <VCol md="5">
<VCardItem> <VCardItem>
<VCardTitle> <VCardTitle>
{{ coinInfo.name }} (<span class="text-uppercase">{{ coinInfo.symbol }}</span>) {{ coinInfo.name }} (
<span class="text-uppercase">{{ coinInfo.symbol }}</span>
)
</VCardTitle> </VCardTitle>
<VCardSubtitle> <VCardSubtitle>
Rank: <VChip color="error" size="x-small">#{{ coinInfo.market_cap_rank }}</VChip> Rank:
<VChip color="error" size="x-small">#{{ coinInfo.market_cap_rank }}</VChip>
</VCardSubtitle> </VCardSubtitle>
</VCardItem> </VCardItem>
<VDivider/> <VDivider />
<VCardItem> <VCardItem>
<VBtn variant="text" size="small" :href="store.homepage" prependIcon="mdi-web">Website</VBtn> <VBtn variant="text" size="small" :href="store.homepage" prependIcon="mdi-web">
<VBtn variant="text" size="small" :href="store.twitter" prependIcon="mdi-twitter">Twitter</VBtn> Website
<VBtn variant="text" size="small" :href="store.telegram" prependIcon="mdi-telegram">Telegram</VBtn> </VBtn>
<VBtn variant="text" size="small" :href="store.github" prependIcon="mdi-github">Github</VBtn> <VBtn variant="text" size="small" :href="store.twitter" prependIcon="mdi-twitter">
Twitter
</VBtn>
<VBtn variant="text" size="small" :href="store.telegram" prependIcon="mdi-telegram">
Telegram
</VBtn>
<VBtn variant="text" size="small" :href="store.github" prependIcon="mdi-github">
Github
</VBtn>
</VCardItem> </VCardItem>
<VCardItem> <VCardItem>
<!-- SECTION upgrade plan banner --> <!-- SECTION upgrade plan banner -->
<div class="plan-upgrade-banner d-flex bg-light-secondary rounded align-center pa-3"> <div class="plan-upgrade-banner d-flex bg-light-secondary rounded align-center pa-3">
<h3 <h3 class="plan-details me-3" :class="store.priceColor">
class="plan-details me-3" {{ store.priceChange }}
:class="store.priceColor" <small>%</small>
>
{{ store.priceChange }}<small>%</small>
</h3> </h3>
<VMenu open-on-hover> <VMenu open-on-hover>
<template #activator="{ props }"> <template #activator="{ props }">
<div class="d-flex flex-column align-start" v-bind="props"> <div class="d-flex flex-column align-start" v-bind="props">
<h3 class="text-base font-weight-semibold"> <h3 class="text-base font-weight-semibold">
{{ ticker?.market?.name || ''}} {{ ticker?.market?.name || '' }}
</h3> </h3>
<span class="text-primary text-xs">{{ shortName(ticker?.base, ticker.coin_id) }}/{{ shortName(ticker?.target, ticker.target_coin_id) }}</span> <span class="text-primary text-xs">
{{ shortName(ticker?.base, ticker.coin_id) }}/{{
shortName(ticker?.target, ticker.target_coin_id)
}}
</span>
</div> </div>
</template> </template>
<VList style="max-height: 300px;"> <VList style="max-height: 300px">
<VListItem <VListItem
v-for="(item, i) in store.coinInfo.tickers" v-for="(item, i) in store.coinInfo.tickers"
:key="i" :key="i"
rounded rounded
@click="store.selectTicker(i)" @click="store.selectTicker(i)"
> >
<template #prepend> <template #prepend></template>
</template>
<!-- eslint-disable-next-line vue/no-v-text-v-html-on-component --> <!-- eslint-disable-next-line vue/no-v-text-v-html-on-component -->
<VListItemTitle v-text="item.market.name" /> <VListItemTitle v-text="item.market.name" />
<VListItemSubtitle>{{ shortName(item?.base, item.coin_id) }}/{{ shortName(item?.target, item.target_coin_id) }}</VListItemSubtitle> <VListItemSubtitle>
{{ shortName(item?.base, item.coin_id) }}/{{
shortName(item?.target, item.target_coin_id)
}}
</VListItemSubtitle>
<template #append> <template #append>
<span class="ml-3" :class="`text-${store.tickerColor(item.trust_score)}`">{{ item.converted_last.usd }}</span> <span class="ml-3" :class="`text-${store.tickerColor(item.trust_score)}`">
{{ item.converted_last.usd }}
</span>
</template> </template>
</VListItem> </VListItem>
</VList> </VList>
</VMenu> </VMenu>
<VSpacer /> <VSpacer />
<div class="d-flex align-center"> <div class="d-flex align-center">
@ -107,14 +123,9 @@ function shortName(name: string, id: string) {
</div> </div>
<!-- !SECTION --> <!-- !SECTION -->
<VSpacer /> <VSpacer />
<VBtn <VBtn block :color="store.trustColor" class="mt-3" :href="ticker.trade_url">
block Buy {{ coinInfo.symbol || '' }}
:color="store.trustColor" </VBtn>
class="mt-3"
:href="ticker.trade_url"
>
Buy {{ coinInfo.symbol || '' }}
</VBtn>
</VCardItem> </VCardItem>
</VCol> </VCol>
<VCol md="7"> <VCol md="7">
@ -124,33 +135,31 @@ function shortName(name: string, id: string) {
</VCol> </VCol>
</VRow> </VRow>
<VDivider /> <VDivider />
<VCardText style="max-height: 250px; overflow:auto;"><MdEditor :model-value="coinInfo.description?.en" previewOnly></MdEditor></VCardText> <VCardText style="max-height: 250px; overflow: auto">
<MdEditor :model-value="coinInfo.description?.en" previewOnly></MdEditor>
</VCardText>
<VCardItem> <VCardItem>
<VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{ tag }}</VChip> <VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{ tag }}</VChip>
</VCardItem> </VCardItem>
</VCard> </VCard>
<VRow> <div class="grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-6">
<VCol v-for="item in store.stats" cols="12" sm="6" md="2"> <div v-for="item in store.stats">
<VCard> <CardStatisticsVertical v-bind="item" />
<CardStatisticsVertical v-bind="item" /> </div>
</VCard> </div>
</VCol>
</VRow>
<VCard class="my-5"> <VCard class="my-5">
<VCardItem class="pb-0"> <VCardItem class="pb-0">
<VCardTitle>Active Proposals</VCardTitle> <VCardTitle>Active Proposals</VCardTitle>
</VCardItem> </VCardItem>
<VCardItem> <VCardItem>
<ProposalListItem :proposals="store.proposals"/> <ProposalListItem :proposals="store.proposals" />
</VCardItem> </VCardItem>
<VCardText v-if="store.proposals.length === 0">No active proposals</VCardText> <VCardText v-if="store.proposals.length === 0">No active proposals</VCardText>
</VCard> </VCard>
<VBtn block color='secondary' variant="outlined" class="mt-5"> <VBtn block color="secondary" variant="outlined" class="mt-5">Connect Wallet</VBtn>
Connect Wallet
</VBtn>
</div> </div>
</template> </template>

View File

@ -11,7 +11,7 @@
"add_to_favorite": "Add to favorite" "add_to_favorite": "Add to favorite"
}, },
"Ecosystem": "Ecosystem", "Ecosystem": "Ecosystem",
"All Blockchains": "All Blockchain222", "All Blockchains": "All Blockchain",
"Favorite": "Favorite" "Favorite": "Favorite"
} }

View File

@ -127,7 +127,6 @@ export const useFormatter = defineStore('formatter', {
calculatePercent(input?: string|number, total?: string|number ) { calculatePercent(input?: string|number, total?: string|number ) {
if(!input || !total) return '0' if(!input || !total) return '0'
const percent = Number(input)/Number(total) const percent = Number(input)/Number(total)
console.log(input, total, percent);
return numeral(percent>0.0001?percent: 0).format("0.[00]%") return numeral(percent>0.0001?percent: 0).format("0.[00]%")
}, },
formatDecimalToPercent(decimal: string) { formatDecimalToPercent(decimal: string) {

View File

@ -2,6 +2,7 @@ import { defineStore } from "pinia";
import { useBlockchain } from "./useBlockchain"; import { useBlockchain } from "./useBlockchain";
import type { PageRequest, PaginatedProposals } from "@/types"; import type { PageRequest, PaginatedProposals } from "@/types";
import { LoadingStatus } from "./useDashboard"; import { LoadingStatus } from "./useDashboard";
import {reactive} from 'vue'
export const useGovStore = defineStore('govStore', { export const useGovStore = defineStore('govStore', {
state: () => { state: () => {
@ -27,18 +28,16 @@ export const useGovStore = defineStore('govStore', {
async fetchProposals( status: string, pagination?: PageRequest ) { async fetchProposals( status: string, pagination?: PageRequest ) {
if(!this.loading[status]) { if(!this.loading[status]) {
this.loading[status] = LoadingStatus.Loading this.loading[status] = LoadingStatus.Loading
const proposals = await this.blockchain.rpc.getGovProposals(status) const proposals = reactive(await this.blockchain.rpc.getGovProposals(status))
if(status === '2') {
proposals.proposals.forEach(async(x1) => {
await this.fetchTally(x1.proposal_id).then(res => {
x1.final_tally_result = res?.tally
})
})
}
this.loading[status] = LoadingStatus.Loaded this.loading[status] = LoadingStatus.Loaded
this.proposals[status] = proposals this.proposals[status] = proposals
if(status === '2') {
proposals.proposals.forEach(x1 => {
this.fetchTally(x1.proposal_id).then(t => {
x1.final_tally_result = t.tally
this.proposals[status] = proposals
})
})
}
} }
return this.proposals[status] return this.proposals[status]
}, },
@ -48,7 +47,7 @@ export const useGovStore = defineStore('govStore', {
// }) // })
}, },
async fetchTally(proposalId: string) { async fetchTally(proposalId: string) {
return this.blockchain.rpc.getGovProposalTally(proposalId) return await this.blockchain.rpc.getGovProposalTally(proposalId)
}, },
async fetchProposal(proposalId: string) { async fetchProposal(proposalId: string) {
return this.blockchain.rpc.getGovProposal(proposalId) return this.blockchain.rpc.getGovProposal(proposalId)
@ -59,5 +58,6 @@ export const useGovStore = defineStore('govStore', {
async fetchProposalVotes(proposalId: string, next_key?: string) { async fetchProposalVotes(proposalId: string, next_key?: string) {
return this.blockchain.rpc.getGovProposalVotes(proposalId, next_key) return this.blockchain.rpc.getGovProposalVotes(proposalId, next_key)
} }
} },
}) })

View File

@ -1,3 +1,21 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@layer base {
:root {
--text-main: #333;
--text-secondary: #4b525d;
--bg-card: #fff;
--bg-active: #fbfbfc;
--bg-hover: #eee;
}
html.dark {
--text-main: #f7f7f7;
--text-secondary: #6f6e84;
--bg-card: #28334e;
--bg-active: #242b40;
--bg-hover: #303044;
}
}

View File

@ -27,6 +27,10 @@ export interface GovProposal {
"@type": string, "@type": string,
"title": string, "title": string,
"description": string, "description": string,
"plan"?: {
'height'?: string | number,
'time'?: string | number,
}
}, },
"status": string, "status": string,
"final_tally_result": { "final_tally_result": {

View File

@ -5,12 +5,15 @@ module.exports = {
theme: { theme: {
extend: { extend: {
colors: { colors: {
main: '#5973fe',
yes: '#3fb68b', yes: '#3fb68b',
no: '#ff5353', no: '#ff5353',
info: '#00b2ff', info: '#00b2ff',
textMain: '#333',
primary: '#666cff', primary: '#666cff',
main: 'var(--text-main)',
secondary: 'var(--text-secondary)',
card: 'var(--bg-card)',
hover: 'var(--bg-hover)',
active: 'var(--bg-active)',
}, },
}, },
}, },