Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"NotificationsTableEmpty",
"PremiumProductBox",
"PricingPremiumCompare",
"README.md",
"TheFooter",
"a11y",
"app",
Expand Down
12 changes: 6 additions & 6 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Beaconcha.in Good to know

## Usefull VSC Plugins
## Useful VSC Plugins

- Nuxtr
- EsLint
- Prettier -COde Formatter
- TypeScript Vue Plugin (Volar)
- Vue language Features (Volar)
- Tailwind CSS IntelliSense
- GitLens
- Conventional Commits

# Nuxt 3 Minimal Starter
# Nuxt 4 Minimal Starter

Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more about this framework.
Look at the [Nuxt 4 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more about this framework.

## Setup

Expand Down
124 changes: 76 additions & 48 deletions frontend/components/bc/BcTranslation.vue
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
<script lang="ts" setup>
/**
* This component provides two approaches for translation interpolation:
* 1. Props (simple patterns): suppath, linkpath, boldpath, listpath
* 2. Named slots (complex/dynamic content): for computed values or multiple interpolations
*
* @example Simple - using props
* <BcTranslation
* keypath="base.common.beaconscore"
* suppath="base.common.registered_trademark_symbol"
* />
*
* @example Complex - using named slots
* <BcTranslation keypath="dashboard.validator.summary.tooltip.higher">
* <template #sup><sup>®</sup></template>
* <template #name>{{ groupName }}</template>
* <template #average>{{ formatPercent(value) }}</template>
* </BcTranslation>
*/
defineProps<{
/** Translation key for bold text content */
boldpath?: TranslationKey,
/**
* The path to the key in the translation file (e.g. en.json)
*/
/** Translation key (required) */
keypath: TranslationKey,
/** Translation key for link text (requires `to` prop) */
linkpath?: TranslationKey,
/** Translation key for newline-separated list items */
listpath?: TranslationKey,
/** Translation key for superscript content (e.g., trademark symbols) */
suppath?: TranslationKey,
/** HTML tag to wrap translation (default: 'span') */
tag?: keyof HTMLElementTagNameMap,
/**
* URL to link to
*
* @example
*
* Translation key has to be under `${keypath}.link`
*
* // en.json
* {
* "notifications": {
* "template": "For further information {link}"
* "link": "Click here"
* }
*/
/** URL for link (requires `linkpath` prop) */
to?: string,
}>()

const slots = useSlots()
</script>

<template>
Expand All @@ -32,40 +43,57 @@ defineProps<{
scope="global"
:tag="tag || 'span'"
>
<template #bold>
<span
v-if="boldpath"
class="bc-translation-bold"
>{{ $t(boldpath) }}</span>
</template>
<template #link>
<!-- Forward custom named slots (use only for dynamic/complex content) -->
<template
v-for="(_, name) in slots"
:key="String(name)"
#[name]
>
<slot
v-if="to && linkpath"
name="link"
>
<BcLink
class="link"
target="_blank"
:to
>
{{ $t(linkpath) }}
</BcLink>
</slot>
:name="String(name)"
/>
</template>
<template #list>
<slot
name="list"
:listpath

<!-- Prop-based templates (preferred - use these via props when possible) -->
<template
v-if="suppath && !slots.sup"
#sup
>
<sup>{{ $t(suppath) }}</sup>
</template>

<template
v-if="boldpath && !slots.bold"
#bold
>
<span class="bc-translation-bold">{{ $t(boldpath) }}</span>
</template>

<template
v-if="linkpath && to && !slots.link"
#link
>
<BcLink
:to
class="link"
target="_blank"
>
<ul v-if="listpath">
<li
v-for="item in $t(listpath).split('\n')"
:key="item"
>
{{ item }}
</li>
</ul>
</slot>
{{ $t(linkpath) }}
</BcLink>
</template>

<template
v-if="listpath && !slots.list"
#list
>
<ul>
<li
v-for="item in $t(listpath).split('\n')"
:key="item"
>
{{ item }}
</li>
</ul>
</template>
</I18nT>
</template>
Expand Down
8 changes: 7 additions & 1 deletion frontend/components/dashboard/DashboardValidatorOverview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,13 @@ const aprInfos = [
</DashboardValidatorOverviewItem>
<DashboardValidatorOverviewItem
:infos="efficiencyInfos"
:title="$t('dashboard.validator.overview.24h_beaconscore')"
>
<template #title>
<BcTranslation
keypath="dashboard.validator.overview.24h_beaconscore"
suppath="base.common.registered_trademark_symbol"
/>
</template>
<BcTooltip
tooltip-class="tooltip"
>
Expand All @@ -151,6 +156,7 @@ const aprInfos = [
<BcTranslation
keypath="dashboard.beaconscore.template"
linkpath="dashboard.beaconscore.link"
suppath="base.common.registered_trademark_symbol"
:to="externalLink.knowledgeBase.beaconScore"
/>
</template>
Expand Down
10 changes: 8 additions & 2 deletions frontend/components/dashboard/DashboardValidatorOverviewItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,21 @@ const props = defineProps<{
label: string,
value: NumberOrString,
}[],
title: string,
title?: string,
}>()
</script>

<template>
<div class="box">
<div class="main">
<div class="big_text_label">
{{ props.title }}
<slot
v-if="$slots.title"
name="title"
/>
<template v-else>
{{ props.title }}
</template>
</div>
<div class="big_text dashbaord-validator-overview-item__value">
<slot />
Expand Down
18 changes: 15 additions & 3 deletions frontend/components/dashboard/chart/DashboardChartSummary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,20 @@ const formatTimestamp = (value: string) => {
}
}
const isTriggeringOnMouseMove = ref(true)
const efficiencyLabel = computed(() => {
switch (props.filter?.efficiency) {
case 'all':
return `${$t('base.common.beaconscore')}®`
case 'attestation':
return $t('dashboard.validator.summary.chart.efficiency.attestation')
case 'proposal':
return $t('dashboard.validator.summary.chart.efficiency.proposal')
case 'sync':
return $t('dashboard.validator.summary.chart.efficiency.sync')
default:
return ''
}
})
const option = computed<EChartsOption>(() => {
return {
color: colors.value.groups,
Expand Down Expand Up @@ -409,9 +423,7 @@ const option = computed<EChartsOption>(() => {
? Math.max(0, 10 * Math.ceil(range.min / 10 - 1))
: 10 * Math.ceil(range.min / 10 - 1),
minInterval: 10,
name: $t(
`dashboard.validator.summary.chart.efficiency.${props.filter?.efficiency}`,
),
name: efficiencyLabel.value,
nameLocation: 'middle',
nameTextStyle: {
padding: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,33 @@ const aggregationDisabled = ({ disabled }: { disabled: boolean }) => disabled

/** efficiency */
const efficiency = ref<EfficiencyType>(chartFilter.value.efficiency)

const efficiencyList = EfficiencyTypes.map(e => ({
id: e,
label: $t(`dashboard.validator.summary.chart.efficiency.${e}`),
}))
const efficiencyList = EfficiencyTypes.map((efficiencyType) => {
let efficiencyTranslationKey: TranslationKey
switch (efficiencyType) {
case 'all':
efficiencyTranslationKey = 'base.common.beaconscore'
break
case 'attestation':
efficiencyTranslationKey = 'dashboard.validator.summary.chart.efficiency.attestation'
break
case 'proposal':
efficiencyTranslationKey = 'dashboard.validator.summary.chart.efficiency.proposal'
break
case 'sync':
efficiencyTranslationKey = 'dashboard.validator.summary.chart.efficiency.sync'
break
}
return {
efficiencyTranslationKey,
id: efficiencyType,
}
})
watch(efficiency, (e) => {
chartFilter.value.efficiency = e
})
const getEfficiencyTranslationKey = (id: EfficiencyType) => {
return efficiencyList.find(efficiencyType => efficiencyType.id === id)!.efficiencyTranslationKey
}

/** groups */
const total = ref(
Expand Down Expand Up @@ -156,9 +175,23 @@ const selectedLabel = computed(() => {
v-model="efficiency"
:options="efficiencyList"
option-value="id"
option-label="label"
option-label="labelTranslationKey"
class="small"
/>
>
<template #option="{ efficiencyTranslationKey }">
<BcTranslation
:keypath="efficiencyTranslationKey"
suppath="base.common.registered_trademark_symbol"
/>
</template>

<template #value="{ value }">
<BcTranslation
:keypath="getEfficiencyTranslationKey(value)"
suppath="base.common.registered_trademark_symbol"
/>
</template>
</BcDropdown>

<BcMultiSelect
v-model="selectedGroups"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,18 @@ const epochText = computed(() => {
})

const title = computed(() => {
if (props.efficiencyType) {
return props.t(
`dashboard.validator.summary.chart.efficiency.${props.efficiencyType}`,
)
switch (props.efficiencyType) {
case 'all':
return `${props.t('base.common.beaconscore')}®`
case 'attestation':
return props.t('dashboard.validator.summary.chart.efficiency.attestation')
case 'proposal':
return props.t('dashboard.validator.summary.chart.efficiency.proposal')
case 'sync':
return props.t('dashboard.validator.summary.chart.efficiency.sync')
default:
return ''
}
return undefined
})
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,10 @@ watch(
>
<template #header>
<div class="validators-header">
<div>{{ $t("dashboard.validator.col.beaconscore") }}</div>
<BcTranslation
keypath="base.common.beaconscore"
suppath="base.common.registered_trademark_symbol"
/>
<BcTooltip
class="info"
tooltip-class="summary-info-tooltip"
Expand All @@ -270,6 +273,7 @@ watch(
<BcTranslation
keypath="dashboard.beaconscore.template"
linkpath="dashboard.beaconscore.link"
suppath="base.common.registered_trademark_symbol"
:to="externalLink.knowledgeBase.beaconScore"
/>
</template>
Expand Down
Loading