Merge pull request #360 from alisaweb3/v3-single

Improved Governance List Style
This commit is contained in:
ping 2023-04-25 08:54:49 +08:00 committed by GitHub
commit 8d14f342d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1596 additions and 9759 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules/
**/.vscode
**/.vscode
yarn-error.log

View File

@ -4,6 +4,7 @@
"private": true,
"target": "",
"scripts": {
"dev": "vite",
"serve": "vite",
"build": "run-p type-check build-only",
"preview": "vite preview",
@ -28,6 +29,7 @@
"@vueuse/core": "^9.12.0",
"@vueuse/math": "^9.12.0",
"apexcharts": "^3.37.1",
"autoprefixer": "^10.4.14",
"axios": "^1.3.2",
"cross-fetch": "^3.1.5",
"dayjs": "^1.11.7",
@ -36,11 +38,16 @@
"numeral": "^2.0.6",
"osmojs": "^14.0.0-rc.0",
"pinia": "^2.0.28",
"postcss": "^8.4.23",
"prismjs": "^1.29.0",
"tailwindcss": "^3.3.1",
"vite-plugin-vue-layouts": "^0.7.0",
"vite-plugin-vuetify": "^1.0.2",
"vue": "^3.2.45",
"vue-flatpickr-component": "^11.0.3",
"vue-i18n": "^9.2.2",
"vue-json-pretty": "^2.2.4",
"vue-prism-component": "^2.0.0",
"vue-router": "^4.1.6",
"vue3-apexcharts": "^1.4.1",
"vue3-flip-countdown": "^0.1.6",
@ -69,7 +76,7 @@
"unplugin-auto-import": "^0.13.0",
"unplugin-vue-components": "^0.23.0",
"unplugin-vue-define-options": "1.1.4",
"vite": "^4.0.0",
"vite": "^4.3.1",
"vite-plugin-pages": "^0.28.0",
"vue-tsc": "^1.0.12"
}

View File

@ -1,58 +1,67 @@
<script lang="ts" setup>
import { useFormatter } from '@/stores';
import { computed } from '@vue/reactivity';
import { ref, type PropType } from 'vue';
import { useFormatter } from "@/stores";
import { computed } from "@vue/reactivity";
import { ref, type PropType } from "vue";
const props = defineProps({
tally: { type: Object as PropType<{
yes: string,
no: string,
noWithVeto: string,
abstain: string
}>},
pool: {
tally: {
type: Object as PropType<{
notBondedTokens: string;
bondedTokens: string;
yes: string;
no: string;
noWithVeto: string;
abstain: string;
}>,
},
})
const format = useFormatter()
const yes = computed(() => (format.calculatePercent(props.tally?.yes, props.pool?.bondedTokens)))
const no = computed(() => ref(format.calculatePercent(props.tally?.no, props.pool?.bondedTokens)))
const abstain = computed(() => (format.calculatePercent(props.tally?.abstain, props.pool?.bondedTokens)))
const veto = computed(() => (format.calculatePercent(props.tally?.noWithVeto, props.pool?.bondedTokens)))
console.log(yes.value, no.value, abstain.value, veto.value)
pool: {
type: Object as PropType<{
notBondedTokens: string;
bondedTokens: string;
}>,
},
});
const format = useFormatter();
const yes = computed(() =>
format.calculatePercent(props.tally?.yes, props.pool?.bondedTokens)
);
const no = computed(() =>
ref(format.calculatePercent(props.tally?.no, props.pool?.bondedTokens))
);
const abstain = computed(() =>
format.calculatePercent(props.tally?.abstain, props.pool?.bondedTokens)
);
const veto = computed(() =>
format.calculatePercent(props.tally?.noWithVeto, props.pool?.bondedTokens)
);
</script>
<template>
<div class="progress">
<div class="progress-bar bg-success" :style="`width: ${yes}`"></div>
<div class="progress-bar bg-error" :style="`width: ${no}`"></div>
<div class="progress-bar " :style="`width: ${veto}; background-color: #B71C1C;`"></div>
<div class="progress-bar bg-secondary" :style="`width: ${abstain}`"></div>
</div>
<div class="progress">
<div class="progress-bar bg-success" :style="`width: ${yes}`"></div>
<div class="progress-bar bg-error" :style="`width: ${no}`"></div>
<div
class="progress-bar"
:style="`width: ${veto}; background-color: #B71C1C;`"
></div>
<div class="progress-bar bg-secondary" :style="`width: ${abstain}`"></div>
</div>
</template>
<style scoped>
.progress {
height: 0.8rem;
overflow: hidden;
background-color: rgba(128, 128, 128, 0.178);
height: 0.8rem;
overflow: hidden;
background-color: rgba(128, 128, 128, 0.178);
}
.progress-bar {
display: inline-block;
height: 100%;
display: inline-block;
height: 100%;
}
.progress :first-child {
border-radius: 0px !important;
border-top-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: 0;
border-radius: 0px !important;
border-top-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: 0;
}
.progress :last-child {
border-radius: 0px !important;
border-radius: 0px !important;
}
</style>
</style>

View File

@ -124,7 +124,7 @@ function shortName(name: string, id: string) {
</VCol>
</VRow>
<VDivider />
<VCardText style="max-height: 250px; overflow:scroll;"><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>
<VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{ tag }}</VChip>
</VCardItem>

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@ -6,57 +6,60 @@ import ProposalProcess from './ProposalProcess.vue';
import type { PropType } from 'vue';
const props = defineProps({
proposals: { type: Object as PropType<PaginatedProposals>},
})
proposals: { type: Object as PropType<PaginatedProposals> },
});
// const list = computed(()=> proposl)
const format = useFormatter()
const staking = useStakingStore()
const chain = useBlockchain()
function showType(v: string){
if(v) {
return v.substring(v.lastIndexOf('.')+1)
}
return v
const format = useFormatter();
const staking = useStakingStore();
const chain = useBlockchain();
function showType(v: string) {
if (v) {
return v.substring(v.lastIndexOf('.') + 1);
}
return v;
}
const statusMap: Record<string, string> = {
PROPOSAL_STATUS_VOTING_PERIOD: 'VOTING',
PROPOSAL_STATUS_PASSED: 'PASSED',
PROPOSAL_STATUS_REJECTED: 'REJECTED',
};
</script>
<template>
<VExpansionPanels variant="accordion">
<VExpansionPanel v-for="(x, i) in proposals?.proposals">
<VExpansionPanelTitle disable-icon-rotate>
<VChip label color="primary" class="mr-2">{{x.proposal_id}}</VChip>
<div class="w-100"><VChip label>{{ showType(x.content['@type']) }}</VChip> {{ x.content?.title }}
<div class="d-flex mt-1">
<small class="text-secondary me-auto"> {{ format.toDay(x.voting_end_time, 'from') }}</small>
<ProposalProcess style="width:300px;" :pool="staking.pool" :tally="x.final_tally_result"></ProposalProcess>
<span></span>
</div>
</div>
<template #actions>
<VIcon
v-if="x.status === 'PROPOSAL_STATUS_PASSED'"
icon="mdi-check"
color="success"
class="ml-2"
/>
<VIcon
v-if="x.status === 'PROPOSAL_STATUS_REJECTED'"
icon="mdi-multiply"
color="error"
class="ml-2"
/>
</template>
</VExpansionPanelTitle>
<VExpansionPanelText>
<VCard class="card-box">
<VCardText>
<MdEditor :model-value="format.multiLine(x.content?.description)" previewOnly></MdEditor>
</VCardText>
<div class="text-center w-100 my-2">
<VBtn :to="`/${chain.chainName}/gov/${x.proposal_id}`" color="primary" variant="flat" size="small">Detail</VBtn>
<VBtn color="primary" variant="flat" class="ml-2" size="small">Vote</VBtn>
</div>
</VCard>
</VExpansionPanelText>
</VExpansionPanel>
</VExpansionPanels>
</template>
<div class="bg-white dark:bg-[#28334e] rounded text-sm">
<RouterLink
:to="`/${chain.chainName}/gov/${item?.proposal_id}`"
v-for="(item, index) in proposals?.proposals"
:key="index"
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="text-textMain dark:text-white mb-3">#{{ item?.proposal_id }}</div>
<div class="col-span-5 md:pr-10 text-textMain dark:text-white truncate">
{{ item?.content?.title }}
</div>
<div class="col-span-3 mb-3 truncate">
<div
class="bg-[#f6f2ff] text-[#9c6cff] dark:bg-gray-600 dark:text-gray-300 inline-block rounded-full px-2 py-[1px] text-xs"
>
{{ showType(item.content['@type']) }}
</div>
</div>
<div class="text-yes flex items-center mb-3">
<div class="w-1 h-1 bg-yes rounded-full mr-2"></div>
<div class="text-xs">{{ statusMap?.[item?.status] || item?.status }}</div>
</div>
<div
class="truncate mb-3 col-span-2 md:col-span-1 text-xs text-gray-500 dark:text-gray-400 text-right md:flex md:justify-start"
>
{{ format.toDay(item.voting_end_time, 'from') }}
</div>
</div>
<ProposalProcess :pool="staking.pool" :tally="item.final_tally_result"></ProposalProcess>
</RouterLink>
</div>
</template>

View File

@ -2,52 +2,36 @@
import { useFormatter } from '@/stores';
import type { Tally } from '@/types';
import { computed } from '@vue/reactivity';
import { ref, type PropType } from 'vue';
import type { PropType } from 'vue';
const props = defineProps({
tally: { type: Object as PropType<Tally>},
tally: { type: Object as PropType<Tally> },
pool: {
type: Object as PropType<{
not_bonded_tokens: string;
bonded_tokens: string;
not_bonded_tokens: string;
bonded_tokens: string;
}>,
},
})
const total = computed(() => props.pool?.bonded_tokens)
const format = useFormatter()
const yes = computed(() => (format.calculatePercent(props.tally?.yes, total.value)))
const no = computed(() => ref(format.calculatePercent(props.tally?.no, total.value)))
const abstain = computed(() => (format.calculatePercent(props.tally?.abstain, total.value)))
const veto = computed(() => (format.calculatePercent(props.tally?.no_with_veto, total.value)))
});
const total = computed(() => props.pool?.bonded_tokens);
const format = useFormatter();
const yes = computed(() => format.calculatePercent(props.tally?.yes, total.value));
const no = computed(() => format.calculatePercent(props.tally?.no, total.value));
const abstain = computed(() => format.calculatePercent(props.tally?.abstain, total.value));
const veto = computed(() => format.calculatePercent(props.tally?.no_with_veto, total.value));
</script>
<template>
<div class="progress">
<div class="progress-bar bg-success" :style="`width: ${yes}`"> {{ yes }}</div>
<div class="progress-bar bg-error" :style="`width: ${no}`">{{ no }} </div>
<div class="progress-bar " :style="`width: ${veto}; background-color: #B71C1C;`"> {{ veto }} </div>
<div class="progress-bar bg-secondary" :style="`width: ${abstain}`"> </div>
</div>
<div class="progress rounded-full h-1 text-xs flex items-center">
<div class="h-1 bg-yes" :style="`width: ${yes}`"></div>
<div class="h-1 bg-no" :style="`width: ${no}`"></div>
<div class="h-1" :style="`width: ${veto}; background-color: #B71C1C;`"></div>
<div class="h-1 bg-secondary" :style="`width: ${abstain}`"></div>
</div>
</template>
<style scoped>
.progress {
height: 0.8rem;
overflow: hidden;
background-color: rgba(128, 128, 128, 0.178);
overflow: hidden;
background-color: rgba(128, 128, 128, 0.178);
}
.progress-bar {
display: inline-block;
height: 100%;
}
.progress :first-child {
border-radius: 0px !important;
border-top-right-radius: 0;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-top-left-radius: 0;
}
.progress :last-child {
border-radius: 0px !important;
}
</style>
</style>

View File

@ -1,41 +1,42 @@
<script lang="ts" setup>
import { useThemeConfig } from '@/plugins/vuetify/@core/composable/useThemeConfig'
import { useThemeConfig } from '@/plugins/vuetify/@core/composable/useThemeConfig';
// Components
import Footer from '@/layouts/components/Footer.vue'
import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue'
import UserProfile from '@/layouts/components/ChainProfile.vue'
import Footer from '@/layouts/components/Footer.vue';
import NavbarThemeSwitcher from '@/layouts/components/NavbarThemeSwitcher.vue';
import UserProfile from '@/layouts/components/ChainProfile.vue';
import { useDashboard } from '@/stores/useDashboard'
import { useDashboard } from '@/stores/useDashboard';
// @layouts plugin
import { VerticalNavLayout } from '@layouts'
import NavBarI18n from './NavBarI18n.vue'
import NavSearchBar from './NavSearchBar.vue'
import NavBarNotifications from './NavBarNotifications.vue'
import TheCustomizer from '@/plugins/vuetify/@core/components/TheCustomizer.vue'
import Breadcrumbs from './Breadcrumbs.vue'
import { useBlockchain } from '@/stores'
import { VerticalNavLayout } from '@layouts';
import NavBarI18n from './NavBarI18n.vue';
import NavSearchBar from './NavSearchBar.vue';
import NavBarNotifications from './NavBarNotifications.vue';
import TheCustomizer from '@/plugins/vuetify/@core/components/TheCustomizer.vue';
import Breadcrumbs from './Breadcrumbs.vue';
import { useBlockchain } from '@/stores';
const { appRouteTransition, isLessThanOverlayNavBreakpoint, isVerticalNavCollapsed } = useThemeConfig()
const { width: windowWidth } = useWindowSize()
const { appRouteTransition, isLessThanOverlayNavBreakpoint, isVerticalNavCollapsed } =
useThemeConfig();
const { width: windowWidth } = useWindowSize();
// 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 => {
verticalNavHeaderActionAnimationName.value = val ? 'rotate-180' : 'rotate-back-180'
})
verticalNavHeaderActionAnimationName.value = val ? 'rotate-180' : 'rotate-back-180';
});
const dashboard = useDashboard()
dashboard.initial()
const blockchain = useBlockchain()
const dashboard = useDashboard();
dashboard.initial();
const blockchain = useBlockchain();
// blockchain.initial()
blockchain.$subscribe((m, s) => {
if(!Array.isArray(m.events) && m.events.key === 'chainName') {
blockchain.initial()
if (!Array.isArray(m.events) && m.events.key === 'chainName') {
blockchain.initial();
}
})
});
</script>
<template>
@ -43,8 +44,11 @@ blockchain.$subscribe((m, s) => {
<!-- 👉 navbar -->
<template #navbar="{ toggleVerticalOverlayNavActive }">
<div class="d-flex h-100 align-center">
<IconBtn v-if="isLessThanOverlayNavBreakpoint(windowWidth)" class="ms-n3"
@click="toggleVerticalOverlayNavActive(true)">
<IconBtn
v-if="isLessThanOverlayNavBreakpoint(windowWidth)"
class="ms-n3"
@click="toggleVerticalOverlayNavActive(true)"
>
<VIcon icon="mdi-menu" />
</IconBtn>
@ -52,9 +56,9 @@ blockchain.$subscribe((m, s) => {
<VSpacer />
<!-- <NavSearchBar />-->
<NavBarNotifications />
<NavBarI18n />
<NavbarThemeSwitcher />
<NavBarNotifications class="hidden md:inline-block" />
<NavBarI18n class="hidden md:inline-block" />
<NavbarThemeSwitcher class="hidden md:inline-block" />
</div>
</template>

View File

@ -1,32 +1,32 @@
/* eslint-disable import/order */
import "@/plugins/vuetify/@iconify/icons-bundle";
import App from "@/App.vue";
import layoutsPlugin from "@/plugins/vuetify/layouts";
import vuetify from "@/plugins/vuetify";
import i18n from "@/plugins/i18n";
import { loadFonts } from "@/plugins/vuetify/webfontloader";
import "@/plugins/vuetify/@core/scss/template/index.scss";
import "@/plugins/vuetify/styles/styles.scss";
import { createApp } from "vue";
import { createPinia } from "pinia";
import '@/plugins/vuetify/@iconify/icons-bundle';
import App from '@/App.vue';
import layoutsPlugin from '@/plugins/vuetify/layouts';
import vuetify from '@/plugins/vuetify';
import i18n from '@/plugins/i18n';
import { loadFonts } from '@/plugins/vuetify/webfontloader';
import '@/plugins/vuetify/@core/scss/template/index.scss';
import '@/plugins/vuetify/styles/styles.scss';
import '@/style.css';
import { createApp } from 'vue';
import { createPinia } from 'pinia';
// import router from "@/plugins/vuetify/router";
import router from "./router";
import { useBaseStore } from "./stores/useBaseStore";
import router from './router';
import { useBaseStore } from './stores/useBaseStore';
loadFonts();
// Create vue app
const app = createApp(App);
// Use plugins
app.use(i18n)
app.use(i18n);
app.use(vuetify);
app.use(createPinia());
app.use(layoutsPlugin);
app.use(router);
// Mount vue app
app.mount("#app");
app.mount('#app');
// fetch latest block every 6s
// const blockStore = useBaseStore()

View File

@ -124,7 +124,7 @@ function shortName(name: string, id: string) {
</VCol>
</VRow>
<VDivider />
<VCardText style="max-height: 250px; overflow:scroll;"><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>
<VChip v-for="tag in coinInfo.categories" size="x-small" class="mr-2">{{ tag }}</VChip>
</VCardItem>

View File

@ -1,31 +1,52 @@
<script setup lang="ts">
import { useThemeConfig } from '@core/composable/useThemeConfig'
import type { ThemeSwitcherTheme } from '@layouts/types'
import { useThemeConfig } from '@core/composable/useThemeConfig';
import type { ThemeSwitcherTheme } from '@layouts/types';
import { onMounted } from 'vue';
const props = defineProps<{
themes: ThemeSwitcherTheme[]
}>()
themes: ThemeSwitcherTheme[];
}>();
const { theme } = useThemeConfig()
const { state: currentThemeName, next: getNextThemeName, index: currentThemeIndex } = useCycleList(props.themes.map(t => t.name), { initialValue: theme.value })
const { theme } = useThemeConfig();
const {
state: currentThemeName,
next: getNextThemeName,
index: currentThemeIndex,
} = useCycleList(
props.themes.map(t => t.name),
{ initialValue: theme.value }
);
const changeTheme = () => {
theme.value = getNextThemeName()
}
theme.value = getNextThemeName();
};
const changeMode = (val: 'dark' | 'light') => {
if (val === 'dark') {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
} else {
document.documentElement.classList.add('light');
document.documentElement.classList.remove('dark');
}
};
// Update icon if theme is changed from other sources
watch(theme, val => {
currentThemeName.value = val
})
watch(theme, (val: 'dark' | 'light') => {
currentThemeName.value = val;
changeMode(val);
});
onMounted(() => {
if (currentThemeName.value) {
changeMode(currentThemeName.value);
}
});
</script>
<template>
<IconBtn @click="changeTheme">
<VIcon :icon="props.themes[currentThemeIndex].icon" />
<VTooltip
activator="parent"
open-delay="1000"
>
<VTooltip activator="parent" open-delay="1000">
<span class="text-capitalize">{{ currentThemeName }}</span>
</VTooltip>
</IconBtn>

3
src/style.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

17
tailwind.config.js Normal file
View File

@ -0,0 +1,17 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ['class'],
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
theme: {
extend: {
colors: {
main: '#5973fe',
yes: '#3fb68b',
no: '#ff5353',
info: '#00b2ff',
textMain: '#333',
},
},
},
plugins: [],
};

File diff suppressed because it is too large Load Diff

2730
yarn.lock

File diff suppressed because it is too large Load Diff