diff --git a/frontend/assets/css/colors.scss b/frontend/assets/css/colors.scss index c7ef22b75..d623a5c47 100644 --- a/frontend/assets/css/colors.scss +++ b/frontend/assets/css/colors.scss @@ -7,6 +7,8 @@ --light-grey: #f0f0f0; --light-grey-2: #dfdfdf; --light-grey-3: #d3d3d3; + --light-grey-5: #eaeaea; + --light-grey-6: #a8a8a8; --dark-grey: #5c4e4e; --very-dark-grey: #362f32; --asphalt: #484f56; @@ -29,12 +31,13 @@ --primary-orange-pressed: #ffb346; --primary-contrast-color: var(--grey-4); + --primary-contrast-color-discreet: var(--light-grey-5); --background-color: var(--grey-1); --text-color: var(--light-black); --text-color-inverted: var(--light-grey); --text-color-disabled: var(--grey); - --text-color-discreet: var(--grey); + --text-color-discreet: var(--dark-grey); --button-color-active: var(--primary-orange); --button-color-hover: var(--primary-orange-hover); @@ -65,15 +68,22 @@ --megamenu-text-color: var(--grey); --megamenu-hover-color: var(--light-grey-3); + --list-highlight-background: var(--primary-orange-hover); + --list-hover-background: var(--light-grey-6); + --list-hover-color: var(--light-grey); + --list-hover-descrete-color: var(--light-grey-5); + &.dark-mode { --primary-color: var(--primary-orange); --primary-contrast-color: var(--light-black); + --primary-contrast-color-discreet: var(--dark-grey); --background-color: var(--almost-black); --text-color: var(--light-grey); --text-color-inverted: var(--light-black); --text-color-disabled: var(--grey); + --text-color-discreet: var(--grey); --button-color-active: var(--primary-orange); --button-color-hover: var(--primary-orange-hover); @@ -103,5 +113,9 @@ --megamenu-panel-color: var(--graphite); --megamenu-text-color: var(--grey); --megamenu-hover-color: var(--asphalt); + + --list-highlight-background: var(--primary-orange); + --list-hover-background: var(--dark-grey); + --list-hover-descrete-color: var(--grey); } } diff --git a/frontend/assets/css/main.scss b/frontend/assets/css/main.scss index ed3d0b72b..4698c63a9 100644 --- a/frontend/assets/css/main.scss +++ b/frontend/assets/css/main.scss @@ -1,6 +1,7 @@ @import "~/assets/css/colors.scss"; @import "~/assets/css/variables.scss"; @import "~/assets/css/fonts.scss"; +@import "~/assets/css/utils.scss"; html { margin: 0; @@ -40,12 +41,6 @@ a { @include container; } -@mixin truncate-text() { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - .text-positive{ color: var(--positive-color) } diff --git a/frontend/assets/css/prime.scss b/frontend/assets/css/prime.scss index aa228d6b4..920f3ea34 100644 --- a/frontend/assets/css/prime.scss +++ b/frontend/assets/css/prime.scss @@ -1,4 +1,5 @@ @use "~/assets/css/fonts.scss"; +@use "~/assets/css/utils.scss"; @import "~/assets/css/prime_datatable.scss"; @import "~/assets/css/prime_megamenu.scss"; @@ -204,3 +205,88 @@ .p-toast .p-toast-message.p-toast-message-error .p-toast-icon-close { color: #ff5757; } + +/*** +* Dropdown +* https://primevue.org/dropdown/ +* different Dropdown variant's: +* - 'default' (=default) : default style for dropdown +* - 'table' : dropdown style within a data table +***/ + +.p-dropdown { + background: var(--input-background); + border: 1px solid var(--input-border-color); + color: var(--input-active-text-color); + transition: background-color 0.2s, color 0.2s, border-color 0.2s; + border-radius: var(--border-radius); + padding: var(--padding); + + &.table { + background: var(--background-color); + } + + &:not(.p-disabled).p-focus { + outline: 0 none; + outline-offset: 0; + } + &.p-overlay-open { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + .p-dropdown-label { + background: transparent; + border: 0 none; + padding: 0; + &.p-placeholder { + color: var(--input-placeholder-text-color); + } + &:focus, + &:enabled:focus { + outline: 0 none; + box-shadow: none; + } + } + .p-dropdown-trigger { + margin-left: var(--padding); + background: transparent; + border-top-right-radius: var(--border-radius); + border-bottom-right-radius: var(--border-radius); + } +} + +.p-dropdown-panel { + background: var(--input-background); + border: 1px solid var(--input-border-color); + color: var(--input-active-text-color); + border-radius: 0 0 var(--border-radius) var(--border-radius); + transform: translateY(-1px); + &.table { + background: var(--background-color); + } + .p-dropdown-items { + padding: var(--padding-small); + .p-dropdown-item { + @include utils.truncate-text; + padding: var(--padding-small) 4px; + border-radius: var(--border-radius); + .discreet { + color: var(--text-color-discreet); + } + &.p-highlight:not(:hover) { + background: var(--list-highlight-background); + color: var(--primary-contrast-color); + .discreet { + color: var(--primary-contrast-color-discreet); + } + } + &:hover { + background: var(--list-hover-background); + color: var(--list-hover-color); + .discreet { + color: var(--list-hover-descrete-color); + } + } + } + } +} diff --git a/frontend/assets/css/utils.scss b/frontend/assets/css/utils.scss new file mode 100644 index 000000000..b6b23ee35 --- /dev/null +++ b/frontend/assets/css/utils.scss @@ -0,0 +1,9 @@ +@mixin truncate-text() { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.truncate-text{ + @include truncate-text; +} \ No newline at end of file diff --git a/frontend/components/bc/BcDropdown.vue b/frontend/components/bc/BcDropdown.vue new file mode 100644 index 000000000..58c3b89bc --- /dev/null +++ b/frontend/components/bc/BcDropdown.vue @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/frontend/components/dashboard/ValidatorOverview.vue b/frontend/components/dashboard/ValidatorOverview.vue index 0c2d26dbe..bf6cfe5ff 100644 --- a/frontend/components/dashboard/ValidatorOverview.vue +++ b/frontend/components/dashboard/ValidatorOverview.vue @@ -1,6 +1,6 @@ + + + {{ name }} (ID: {{ group.id }}) + diff --git a/frontend/components/dashboard/group/GroupSelection.vue b/frontend/components/dashboard/group/GroupSelection.vue new file mode 100644 index 000000000..5d7097dee --- /dev/null +++ b/frontend/components/dashboard/group/GroupSelection.vue @@ -0,0 +1,47 @@ + + + + + + {{ $t('dashboard.group.selection.group') }}: + + + + + + + + + + + diff --git a/frontend/components/dashboard/table/DashboardTableEfficiency.vue b/frontend/components/dashboard/table/DashboardTableEfficiency.vue index ad3ec3dd3..3b1e80b2b 100644 --- a/frontend/components/dashboard/table/DashboardTableEfficiency.vue +++ b/frontend/components/dashboard/table/DashboardTableEfficiency.vue @@ -33,10 +33,10 @@ const data = computed(() => { diff --git a/frontend/components/playground/DashboardValidatorSummary.vue b/frontend/components/playground/DashboardValidatorSummary.vue index d526896f2..557470bc6 100644 --- a/frontend/components/playground/DashboardValidatorSummary.vue +++ b/frontend/components/playground/DashboardValidatorSummary.vue @@ -1,6 +1,7 @@ diff --git a/frontend/components/playground/PlaygroundStyling.vue b/frontend/components/playground/PlaygroundStyling.vue index f2ea02248..e9d6a8d5b 100644 --- a/frontend/components/playground/PlaygroundStyling.vue +++ b/frontend/components/playground/PlaygroundStyling.vue @@ -5,7 +5,7 @@ import { import { faChartColumn } from '@fortawesome/pro-regular-svg-icons' -import { BcToggleMultiBar, IconSlotBlockProposal } from '#components' +import { IconSlotBlockProposal } from '#components' const emptyModalVisibility = ref(false) const headerPropModalVisibility = ref(false) @@ -24,6 +24,9 @@ const selected = ref(true) const completeList = ref([{ value: 'attestation' }, { value: 'proposal', component: IconSlotBlockProposal }, { value: 'sync' }, { value: 'chart', icon: faChartColumn }]) const selectedList = ref(['attestation', 'proposal']) +const dropodownSelection = ref() +const dropdownList = [{ value: 'yes', label: 'Yes' }, { value: 'no', label: 'No' }, { value: 'maybe', label: 'Maybe we need a bigger label' }] + @@ -46,6 +49,7 @@ const selectedList = ref(['attestation', 'proposal']) + Utilizing the footer slot for custom content @@ -92,6 +96,7 @@ const selectedList = ref(['attestation', 'proposal']) + @@ -100,13 +105,14 @@ const selectedList = ref(['attestation', 'proposal']) Selected: {{ selected }} - + + @@ -115,6 +121,29 @@ const selectedList = ref(['attestation', 'proposal']) + + + + + Selected: {{ dropodownSelection }} + + Toggle loading @@ -147,4 +176,5 @@ const selectedList = ref(['attestation', 'proposal']) width: 200px; height: 200px; background-color: antiquewhite; -} +} + diff --git a/frontend/composables/useCustomFetch.ts b/frontend/composables/useCustomFetch.ts index ad09f71bd..362b80a22 100644 --- a/frontend/composables/useCustomFetch.ts +++ b/frontend/composables/useCustomFetch.ts @@ -4,6 +4,7 @@ import type { LoginResponse } from '~/types/user' export enum API_PATH { AD_CONFIGURATIONs = '/adConfigurations', + USER_DASHBOARDS = '/user/dashboards', DASHBOARD_SUMMARY = '/dashboard/validatorSummary', DASHBOARD_SUMMARY_DETAILS = '/dashboard/validatorSummaryDetails', DASHBOARD_SUMMARY_CHART = '/dashboard/validatorSummaryChart', @@ -42,6 +43,10 @@ const mapping: Record = { getPath: values => `/ad-configurations?keys=${values?.keys}`, mock: true }, + [API_PATH.USER_DASHBOARDS]: { + path: '/users/me/dashboards', + mock: false + }, [API_PATH.DASHBOARD_SUMMARY_DETAILS]: { path: '/validator-dashboards/{dashboardKey}/groups/{group_id}/summary', getPath: values => `/validator-dashboards/${values?.dashboardKey}/groups/${values?.groupId}/summary`, diff --git a/frontend/i18n/en.json b/frontend/i18n/en.json index c5b5196db..e9f997cf8 100644 --- a/frontend/i18n/en.json +++ b/frontend/i18n/en.json @@ -98,6 +98,14 @@ }, "dashboard": { "title": "Dashboard", + "group": { + "selection": { + "group": "Group", + "all": "All groups", + "default": "Default", + "placeholder": "Select group" + } + }, "validator": { "overview": { "your_online_validators": "Your Online Validators", diff --git a/frontend/pages/playground.vue b/frontend/pages/playground.vue index 2dc6361db..b60732d4f 100644 --- a/frontend/pages/playground.vue +++ b/frontend/pages/playground.vue @@ -71,6 +71,9 @@ onMounted(async () => { + + + diff --git a/frontend/stores/dashboard/useUserDashboardStore.ts b/frontend/stores/dashboard/useUserDashboardStore.ts new file mode 100644 index 000000000..8378feba5 --- /dev/null +++ b/frontend/stores/dashboard/useUserDashboardStore.ts @@ -0,0 +1,14 @@ +import { defineStore } from 'pinia' +import { useCustomFetch } from '~/composables/useCustomFetch' +import type { GetUserDashboardsResponse, UserDashboardsData } from '~/types/api/dashboard' + +export const useUserDashboardStore = defineStore('user_dashboards', () => { + const dashboards = ref() + async function getDashboards () { + const res = await useCustomFetch(API_PATH.USER_DASHBOARDS) + dashboards.value = res.data + return dashboards.value + } + + return { dashboards, getDashboards } +}) diff --git a/frontend/stores/dashboard/useValidatorDashboardOverviewStore.ts b/frontend/stores/dashboard/useValidatorDashboardOverviewStore.ts index e83f616c4..0d0245e2d 100644 --- a/frontend/stores/dashboard/useValidatorDashboardOverviewStore.ts +++ b/frontend/stores/dashboard/useValidatorDashboardOverviewStore.ts @@ -2,7 +2,7 @@ import { defineStore } from 'pinia' import { useCustomFetch } from '~/composables/useCustomFetch' import type { VDBOverviewData, InternalGetValidatorDashboardResponse } from '~/types/api/validator_dashboard' -export const useValidatorDashboardOverview = defineStore('validator_overview', () => { +export const useValidatorDashboardOverviewStore = defineStore('validator_overview', () => { const overview = ref() async function getOverview () { const res = await useCustomFetch(API_PATH.DASHBOARD_OVERVIEW) diff --git a/frontend/stores/useUserStore.ts b/frontend/stores/useUserStore.ts index d6628d4af..1f3be793a 100644 --- a/frontend/stores/useUserStore.ts +++ b/frontend/stores/useUserStore.ts @@ -3,6 +3,8 @@ import type { LoginResponse } from '~/types/user' import { useCustomFetch } from '~/composables/useCustomFetch' export const useUserStore = defineStore('user-store', () => { + const { public: { xUserId } } = useRuntimeConfig() + async function doLogin (email: string, password: string) { await useCustomFetch(API_PATH.LOGIN, { body: { @@ -12,5 +14,12 @@ export const useUserStore = defineStore('user-store', () => { }) } - return { doLogin } + // TODO: Faking logged in User for now, if xUserId is set + const user = computed(() => { + return xUserId ? { user_id: xUserId, user_name: `Test User [${xUserId}]` } : undefined + }) + + const isLoggedIn = computed(() => !!user.value) + + return { doLogin, user, isLoggedIn } }) diff --git a/frontend/types/dashboard/index.ts b/frontend/types/dashboard/index.ts index 30d6bd8ab..83771b88c 100644 --- a/frontend/types/dashboard/index.ts +++ b/frontend/types/dashboard/index.ts @@ -1,2 +1,6 @@ // can be ether a dashboard id or a list of validators export type DashboardKey = number | string + +export type DashboardType = 'validator' | 'account' + +export const DAHSHBOARDS_ALL_GROUPS_ID = -1