diff --git a/.gitignore b/.gitignore index d6b39456..efdff52f 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ dist-ssr # vercel /.vercel +.vercel diff --git a/src/App.tsx b/src/App.tsx index 94b3f452..21cbe7af 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,11 +16,11 @@ import PairDevice from './pages/PairDevice/PairDevice' import PinnedNotification from './components/General/PinnedNottification' import CreateLocalNodePage from './pages/CreateLocalNodePage/CreateLocalNodePage' import ValidatorOnboarding from './pages/ValidatorOnboarding/ValidatorOnboarding' -import { ethereumRopsten, wcV2InitOptions, apiKey } from './constants' import Dashboard from './pages/Dashboard/Dashboard' import ConnectExistingInstance from './pages/ConnectExistingInstance/ConnectExistingInstance' -import './App.css' import ValidatorManagement from './pages/ValidatorManagement/ValidatorManagement' +import { ethereumRopsten, wcV2InitOptions, apiKey } from './constants' +import './App.css' const injected = injectedModule() const walletConnect = walletConnectModule(wcV2InitOptions) diff --git a/src/components/General/AddCards/AddCardsContainer.stories.ts b/src/components/General/AddCards/AddCardsContainer.stories.ts index 2132cef6..11444395 100644 --- a/src/components/General/AddCards/AddCardsContainer.stories.ts +++ b/src/components/General/AddCards/AddCardsContainer.stories.ts @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react' import AddCardsContainer from './AddCardsContainer' const meta = { - title: 'Dashboard/AddCardsContainer', + title: 'General/AddCardsContainer', component: AddCardsContainer, parameters: { layout: 'centered', @@ -15,5 +15,13 @@ export default meta type Story = StoryObj export const Default: Story = { - args: {}, + args: { + cardsAmount: 2, + }, +} + +export const WithoutCards: Story = { + args: { + cardsAmount: 0, + }, } diff --git a/src/components/General/AddCards/AddCardsContainer.tsx b/src/components/General/AddCards/AddCardsContainer.tsx index 136a2468..736d3c91 100644 --- a/src/components/General/AddCards/AddCardsContainer.tsx +++ b/src/components/General/AddCards/AddCardsContainer.tsx @@ -4,14 +4,19 @@ import AddCard from './AddCard' import DashboardCardWrapper from '../../../pages/Dashboard/DashboardCardWrapper' import { getHeightPercentages } from '../../../utilities' -const AddCardsContainer = () => { - const cards = 2 +type AddCardsContainerProps = { + cardsAmount: number +} +const AddCardsContainer = ({ cardsAmount }: AddCardsContainerProps) => { return ( - {Array.from({ length: cards }).map((_, index) => ( - + {Array.from({ length: cardsAmount }).map((_, index) => ( + ))} diff --git a/src/components/General/RightSideBar/ValidatorsTabs/ValidatorsTabs.tsx b/src/components/General/RightSideBar/ValidatorsTabs/ValidatorsTabs.tsx index c3f53fe8..5e33a669 100644 --- a/src/components/General/RightSideBar/ValidatorsTabs/ValidatorsTabs.tsx +++ b/src/components/General/RightSideBar/ValidatorsTabs/ValidatorsTabs.tsx @@ -2,44 +2,23 @@ import { Tabs } from '@status-im/components' import { Stack } from 'tamagui' import ValidatorsList from './ValidatorsList' -import { useMemo } from 'react' +import { VALIDATOR_TABS_RIGHT_SIDEBAR } from '../../../../constants' const ValidatorsTabs = () => { - const VALIDATOR_TABS = useMemo( - () => [ - { - label: 'Active', - value: 'active', - children: , - }, - { - label: 'Pending', - value: 'pending', - children: , - }, - { - label: 'Inactive', - value: 'inactive', - children: , - }, - ], - [], - ) - return ( - + - {VALIDATOR_TABS.map(tab => ( - - {tab.label} + {VALIDATOR_TABS_RIGHT_SIDEBAR.map(tab => ( + + {tab} ))} - {VALIDATOR_TABS.map(tab => ( - - {tab.children} + {VALIDATOR_TABS_RIGHT_SIDEBAR.map(tab => ( + + ))} diff --git a/src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationSyncCard.stories.ts b/src/components/General/SyncStatusCard.stories.ts similarity index 86% rename from src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationSyncCard.stories.ts rename to src/components/General/SyncStatusCard.stories.ts index 7f06c630..5fb5bf2e 100644 --- a/src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationSyncCard.stories.ts +++ b/src/components/General/SyncStatusCard.stories.ts @@ -1,15 +1,15 @@ import type { Meta, StoryObj } from '@storybook/react' -import KeyGenerationSyncCard from './KeyGenerationSyncCard' +import SyncStatusCard from './SyncStatusCard' const meta = { - title: 'ValidatorOnboarding/KeyGenerationSyncCard', - component: KeyGenerationSyncCard, + title: 'General/SyncStatusCard', + component: SyncStatusCard, parameters: { layout: 'centered', }, tags: ['autodocs'], -} satisfies Meta +} satisfies Meta export default meta type Story = StoryObj diff --git a/src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationSyncCard.tsx b/src/components/General/SyncStatusCard.tsx similarity index 77% rename from src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationSyncCard.tsx rename to src/components/General/SyncStatusCard.tsx index 0fade5dd..b87da768 100644 --- a/src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationSyncCard.tsx +++ b/src/components/General/SyncStatusCard.tsx @@ -2,18 +2,18 @@ import { Stack, XStack, YStack } from 'tamagui' import { InfoBadgeIcon } from '@status-im/icons' import { Text } from '@status-im/components' -import StandardGauge from '../../../../components/Charts/StandardGauge' -import BorderBox from '../../../../components/General/BorderBox' -import { formatNumbersWithComa } from '../../../../utilities' +import StandardGauge from '../Charts/StandardGauge' +import BorderBox from './BorderBox' +import { formatNumbersWithComa } from '../../utilities' -type KeyGenerationSyncCardProps = { +type SyncStatusCardProps = { synced: number total: number title: string color: string } -const KeyGenerationSyncCard = ({ synced, total, title, color }: KeyGenerationSyncCardProps) => { +const SyncStatusCard = ({ synced, total, title, color }: SyncStatusCardProps) => { return ( @@ -54,4 +54,4 @@ const KeyGenerationSyncCard = ({ synced, total, title, color }: KeyGenerationSyn ) } -export default KeyGenerationSyncCard +export default SyncStatusCard diff --git a/src/pages/Dashboard/TitleLogo.stories.ts b/src/components/General/TitleLogo.stories.ts similarity index 72% rename from src/pages/Dashboard/TitleLogo.stories.ts rename to src/components/General/TitleLogo.stories.ts index 7d2a7b5a..8064f0c5 100644 --- a/src/pages/Dashboard/TitleLogo.stories.ts +++ b/src/components/General/TitleLogo.stories.ts @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react' import TitleLogo from './TitleLogo' const meta = { - title: 'Dashboard/TitleLogo', + title: 'General/TitleLogo', component: TitleLogo, parameters: { layout: 'centered', @@ -15,5 +15,11 @@ export default meta type Story = StoryObj export const Default: Story = { + args: { + subtitle: 'Node Management Dashboard', + }, +} + +export const WithoutSubtitle: Story = { args: {}, } diff --git a/src/pages/Dashboard/TitleLogo.tsx b/src/components/General/TitleLogo.tsx similarity index 83% rename from src/pages/Dashboard/TitleLogo.tsx rename to src/components/General/TitleLogo.tsx index 9c8a1164..b694bc90 100644 --- a/src/pages/Dashboard/TitleLogo.tsx +++ b/src/components/General/TitleLogo.tsx @@ -1,7 +1,11 @@ import { Avatar, Text } from '@status-im/components' import { Stack, XStack, YStack } from 'tamagui' -const TitleLogo = () => { +type TitleLogoProps = { + subtitle?: string +} + +const TitleLogo = ({ subtitle }: TitleLogoProps) => { return ( @@ -18,7 +22,7 @@ const TitleLogo = () => { Nimbus - Node Management Dashboard + {subtitle} diff --git a/src/components/General/ValidatorProfile.stories.tsx b/src/components/General/ValidatorProfile.stories.tsx new file mode 100644 index 00000000..04f5be39 --- /dev/null +++ b/src/components/General/ValidatorProfile.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ValidatorProfile from './ValidatorProfile' + +const meta = { + title: 'General/ValidatorProfile', + component: ValidatorProfile, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + number: 1, + address: 'zQ3asdf9d4Gs0', + }, +} diff --git a/src/components/General/ValidatorProfile.tsx b/src/components/General/ValidatorProfile.tsx new file mode 100644 index 00000000..5352fc07 --- /dev/null +++ b/src/components/General/ValidatorProfile.tsx @@ -0,0 +1,33 @@ +import { Avatar, Text } from '@status-im/components' +import { XStack, YStack } from 'tamagui' + +import { getFormattedValidatorAddress } from '../../utilities' + +type ValidatorProfileProps = { + number: number + address: string +} + +const ValidatorProfile = ({ number, address }: ValidatorProfileProps) => { + return ( + + + + + Validator {number} + + + {getFormattedValidatorAddress(address)} + + + + ) +} + +export default ValidatorProfile diff --git a/src/constants.ts b/src/constants.ts index c41bc024..42ed82d7 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -45,7 +45,6 @@ export const DEPOSIT_SUBTITLE = 'Connect you Wallet to stake required ETH for ne export const CLIENT_SETUP_SUBTITLE = 'How many Validators would you like to run?' // Dashboard - export const years = [ 'JAN', 'FEB', @@ -60,3 +59,57 @@ export const years = [ 'NOV', 'DEC', ] +export const VALIDATOR_TABS_RIGHT_SIDEBAR = ['Active', 'Pending', 'Inactive'] + +// Validator Management +export const VALIDATOR_TABS_MANAGEMENT = [ + 'Active', + 'Pending', + 'Inactive', + 'Exited', + 'Withdraw', + 'All', +] + +export const VALIDATORS_DATA = [ + { + number: 1, + address: 'zQ3asdf9d4Gs0', + balance: 32.0786, + income: 0.0786, + proposals: '1/102', + attestations: '1/102', + effectiveness: 98, + status: 'Active', + }, + { + number: 1, + address: 'zQ3asdf9d4Gs0', + balance: 32.0786, + income: 0.0786, + proposals: '1/102', + attestations: '1/102', + effectiveness: 98, + status: 'Active', + }, + { + number: 1, + address: 'zQ3asdf9d4Gs0', + balance: 32.0786, + income: 0.0786, + proposals: '1/102', + attestations: '1/102', + effectiveness: 98, + status: 'Active', + }, + { + number: 1, + address: 'zQ3asdf9d4Gs0', + balance: 32.0786, + income: 0.0786, + proposals: '1/102', + attestations: '1/102', + effectiveness: 98, + status: 'Active', + }, +] diff --git a/src/index.css b/src/index.css index d97ef17a..438e3bf0 100644 --- a/src/index.css +++ b/src/index.css @@ -62,7 +62,6 @@ body { margin: 0; display: flex; - min-width: 320px; min-height: 100vh; } h1, @@ -103,11 +102,13 @@ ul li { } .transparent-scrollbar::-webkit-scrollbar { width: 8px; + height: 8px; } .transparent-scrollbar::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.1); border-radius: 10px; + height: 8px; } .transparent-scrollbar::-webkit-scrollbar-thumb:hover { @@ -136,4 +137,10 @@ ul li { flex-direction: column; gap: 12px; } +} + +@media (max-width: 900px) { + .right-sidebar-wrapper { + display: none; + } } diff --git a/src/pages/Dashboard/DashboardContent.tsx b/src/pages/Dashboard/DashboardContent.tsx index 25214f11..a6fb228b 100644 --- a/src/pages/Dashboard/DashboardContent.tsx +++ b/src/pages/Dashboard/DashboardContent.tsx @@ -1,4 +1,5 @@ -import { Stack, YStack } from 'tamagui' +import { Stack, YStack, XStack } from 'tamagui' + import BasicInfoCards from './BasicInfoCards/BasicInfoCards' import AddCardsContainer from '../../components/General/AddCards/AddCardsContainer' import BalanceChartCard from './BalanceChartCard/BalanceChartCard' @@ -6,15 +7,16 @@ import CPUCard from './CPULoad/CPUCard' import ConsensusUptimeCard from './ConsensusUptime/ConsensusUptimeCard' import ExecutionUptime from './ExecutionUptime/ExecutionUptime' import DeviceUptime from './DeviceUptime/DeviceUptime' -import TitleLogo from './TitleLogo' +import TitleLogo from '../../components/General/TitleLogo' import StorageCard from './StorageCard/StorageCard' import NetworkCard from './NetworkCard/NetworkCard' -import SyncStatusCard from './SyncStatusCards/SyncStatusCards' +import SyncStatusCards from './SyncStatusCards/SyncStatusCards' import MemoryCard from './MemoryCard/MemoryCard' -import { XStack } from 'tamagui' + type DashboardContentProps = { windowWidth: number } + const DashboardContent = ({ windowWidth }: DashboardContentProps) => { return ( { }} className={'transparent-scrollbar'} > - + { gridAutoFlow: 'row', }} > - - + + {windowWidth < 1375 ? ( diff --git a/src/pages/Dashboard/SyncStatusCards/SyncStatusCards.tsx b/src/pages/Dashboard/SyncStatusCards/SyncStatusCards.tsx index 3c9743c8..2dfa2c1f 100644 --- a/src/pages/Dashboard/SyncStatusCards/SyncStatusCards.tsx +++ b/src/pages/Dashboard/SyncStatusCards/SyncStatusCards.tsx @@ -5,7 +5,7 @@ import DashboardCardWrapper from '../DashboardCardWrapper' import ExecutionClientCard from './ExecutionClientCard' import ConsensusCard from './ConsensusClientCard' -const SyncStatusCard = () => { +const SyncStatusCards = () => { return ( @@ -24,4 +24,4 @@ const SyncStatusCard = () => { ) } -export default SyncStatusCard +export default SyncStatusCards diff --git a/src/pages/ValidatorManagement/ManagementCard.stories.ts b/src/pages/ValidatorManagement/ManagementCard.stories.ts new file mode 100644 index 00000000..1fd17c36 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementCard.stories.ts @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ManagementCard from './ManagementCard' + +const meta = { + title: 'ValidatorManagement/ManagementCard', + component: ManagementCard, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: {}, +} diff --git a/src/pages/ValidatorManagement/ManagementCard.tsx b/src/pages/ValidatorManagement/ManagementCard.tsx new file mode 100644 index 00000000..72da8773 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementCard.tsx @@ -0,0 +1,31 @@ +import { Text } from '@status-im/components' +import { Separator, Stack, YStack } from 'tamagui' + +const ManagementCard = () => { + return ( + + + + Validators + + + + + + Total Balance + + + + + + Total Income + + + + ) +} + +export default ManagementCard diff --git a/src/pages/ValidatorManagement/ManagementHeader.stories.ts b/src/pages/ValidatorManagement/ManagementHeader.stories.ts new file mode 100644 index 00000000..ede987a7 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementHeader.stories.ts @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ManagementHeader from './ManagementHeader' + +const meta = { + title: 'ValidatorManagement/ManagementHeader', + component: ManagementHeader, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: {}, +} diff --git a/src/pages/ValidatorManagement/ManagementHeader.tsx b/src/pages/ValidatorManagement/ManagementHeader.tsx new file mode 100644 index 00000000..3a66480f --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementHeader.tsx @@ -0,0 +1,39 @@ +import { XStack } from 'tamagui' + +import TitleLogo from '../../components/General/TitleLogo' +import SyncStatusCard from '../../components/General/SyncStatusCard' + +const ManagementHeader = () => { + return ( + + + +
+ +
+
+ +
+
+
+ ) +} + +export default ManagementHeader diff --git a/src/pages/ValidatorManagement/ManagementTable/DropdownFilter.stories.tsx b/src/pages/ValidatorManagement/ManagementTable/DropdownFilter.stories.tsx new file mode 100644 index 00000000..baa09844 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/DropdownFilter.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import DropdownFilter from './DropdownFilter' + +const meta = { + title: 'ValidatorManagement/DropdownFilter', + component: DropdownFilter, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: {}, +} diff --git a/src/pages/ValidatorManagement/ManagementTable/DropdownFilter.tsx b/src/pages/ValidatorManagement/ManagementTable/DropdownFilter.tsx new file mode 100644 index 00000000..1742cb53 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/DropdownFilter.tsx @@ -0,0 +1,37 @@ +import { DropdownMenu } from '@status-im/components' +import { SortIcon } from '@status-im/icons' +import { Stack } from 'tamagui' + +const DropdownFilter = () => { + return ( + + + + + + + + ) +} + +export default DropdownFilter diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTable.css b/src/pages/ValidatorManagement/ManagementTable/ManagementTable.css new file mode 100644 index 00000000..2ed76751 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTable.css @@ -0,0 +1,18 @@ +table { + width: 100%; + border-spacing: 0; + margin: 20px 0; + font-size: 14px; + border: 1px solid #e7eaee; + border-radius: 16px; +} + +th { + border-bottom: 1px solid #e7eaee; +} + +th, +td { + padding: 9px 19px; + text-align: center; +} diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTable.stories.tsx b/src/pages/ValidatorManagement/ManagementTable/ManagementTable.stories.tsx new file mode 100644 index 00000000..f15ba6f0 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTable.stories.tsx @@ -0,0 +1,39 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { useState } from 'react' + +import ManagementTable from './ManagementTable' +import { VALIDATOR_TABS_MANAGEMENT } from '../../../constants' + +const meta = { + title: 'ValidatorManagement/ManagementTable', + component: ManagementTable, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = () => { + const [searchValue, setSearchValue] = useState('') + + const changeSearchValue = (os: string) => { + setSearchValue(os) + } + + return ( + + ) +} + +Default.args = { + tab: VALIDATOR_TABS_MANAGEMENT[0], + searchValue: '', + changeSearchValue: () => {}, +} diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTable.tsx b/src/pages/ValidatorManagement/ManagementTable/ManagementTable.tsx new file mode 100644 index 00000000..ee7306e5 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTable.tsx @@ -0,0 +1,92 @@ +import { useEffect, useMemo, useState } from 'react' +import { YStack, XStack } from 'tamagui' + +import { VALIDATORS_DATA, VALIDATOR_TABS_MANAGEMENT } from '../../../constants' +import SearchManagement from './SearchManagement' +import DropdownFilter from './DropdownFilter' +import ManagementTableHeader from './ManagementTableHeader' +import ManagementTableBody from './ManagementTableBody' +import './ManagementTable.css' + +type ManagementTableProps = { + tab: string + searchValue: string + changeSearchValue: (value: string) => void +} + +export type Validator = { + number: number + address: string + balance: number + income: number + proposals: string + attestations: string + effectiveness: number + status: string +} + +const isValidStatus = (validatorStatus: string, tabStatus: string) => { + if ( + validatorStatus === tabStatus || + tabStatus === VALIDATOR_TABS_MANAGEMENT[VALIDATOR_TABS_MANAGEMENT.length - 1] + ) { + return true + } + return false +} + +const isValidNumberOrAddress = ( + validatorNumber: number, + validatorAddress: string, + searchValue: string, +) => { + if (validatorNumber.toString().includes(searchValue) || validatorAddress.includes(searchValue)) { + return true + } + return false +} + +const ManagementTable = ({ tab, searchValue, changeSearchValue }: ManagementTableProps) => { + const [validators, setValidators] = useState([]) + const [isAllSelected, setIsAllSelected] = useState(false) + + useEffect(() => { + setValidators(VALIDATORS_DATA) + }, []) + + useEffect(() => { + setIsAllSelected(false) + }, [validators, tab, searchValue]) + + const filteredValidators = useMemo(() => { + return validators + .filter(validator => isValidStatus(validator.status, tab)) + .filter(validator => isValidNumberOrAddress(validator.number, validator.address, searchValue)) + }, [validators, tab, searchValue]) + + const handleSelectAll = () => { + setIsAllSelected(state => !state) + } + + return ( + + + + + + + + +
+
+ ) +} + +export default ManagementTable diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTableBody.stories.tsx b/src/pages/ValidatorManagement/ManagementTable/ManagementTableBody.stories.tsx new file mode 100644 index 00000000..3f5044b8 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTableBody.stories.tsx @@ -0,0 +1,30 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ManagementTableBody from './ManagementTableBody' +import { VALIDATORS_DATA } from '../../../constants' + +const meta = { + title: 'ValidatorManagement/ManagementTableBody', + component: ManagementTableBody, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + filteredValidators: VALIDATORS_DATA, + isAllSelected: false, + }, +} + +export const AllSelected: Story = { + args: { + filteredValidators: VALIDATORS_DATA, + isAllSelected: true, + }, +} diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTableBody.tsx b/src/pages/ValidatorManagement/ManagementTable/ManagementTableBody.tsx new file mode 100644 index 00000000..efd149cb --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTableBody.tsx @@ -0,0 +1,34 @@ +import { Text } from '@status-im/components' + +import { Validator } from './ManagementTable' +import ManagementTableRow from './ManagementTableRow' + +type ManagementTableBodyProps = { + filteredValidators: Validator[] + isAllSelected: boolean +} + +const ManagementTableBody = ({ filteredValidators, isAllSelected }: ManagementTableBodyProps) => { + return ( + + {filteredValidators.map(validator => ( + + ))} + {filteredValidators.length === 0 && ( + + + + No validators + + + + )} + + ) +} + +export default ManagementTableBody diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTableHeader.stories.tsx b/src/pages/ValidatorManagement/ManagementTable/ManagementTableHeader.stories.tsx new file mode 100644 index 00000000..c7644790 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTableHeader.stories.tsx @@ -0,0 +1,38 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { useState } from 'react' + +import ManagementTableHeader from './ManagementTableHeader' + +const meta = { + title: 'ValidatorManagement/ManagementTableHeader', + component: ManagementTableHeader, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = () => { + const [isAllSelected, setIsAllSelected] = useState(false) + + const handleSelectAll = () => { + setIsAllSelected(state => !state) + } + + return ( + + ) +} + +Default.args = { + isAllSelected: false, + validatorsAmount: 4, + handleSelectAll: () => {}, +} diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTableHeader.tsx b/src/pages/ValidatorManagement/ManagementTable/ManagementTableHeader.tsx new file mode 100644 index 00000000..77747b7c --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTableHeader.tsx @@ -0,0 +1,66 @@ +import { Checkbox, Text } from '@status-im/components' + +type ManagementTableHeaderProps = { + validatorsAmount: number + isAllSelected: boolean + handleSelectAll: () => void +} + +const ManagementTableHeader = ({ + validatorsAmount, + isAllSelected, + handleSelectAll, +}: ManagementTableHeaderProps) => { + return ( + + + + + + + + {validatorsAmount} Validators + + + + + Balance + + + + + Income + + + + + Proposals + + + + + Attestations + + + + + Effectiveness + + + + + Status + + + + + + ) +} + +export default ManagementTableHeader diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTableRow.stories.tsx b/src/pages/ValidatorManagement/ManagementTable/ManagementTableRow.stories.tsx new file mode 100644 index 00000000..9c2f88c2 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTableRow.stories.tsx @@ -0,0 +1,23 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ManagementTableRow from './ManagementTableRow' +import { VALIDATORS_DATA } from '../../../constants' + +const meta = { + title: 'ValidatorManagement/ManagementTableRow', + component: ManagementTableRow, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: { + validator: VALIDATORS_DATA[0], + isAllSelected: false, + }, +} diff --git a/src/pages/ValidatorManagement/ManagementTable/ManagementTableRow.tsx b/src/pages/ValidatorManagement/ManagementTable/ManagementTableRow.tsx new file mode 100644 index 00000000..7a21bafe --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/ManagementTableRow.tsx @@ -0,0 +1,74 @@ +import { useEffect, useState } from 'react' +import { Checkbox, Text } from '@status-im/components' +import { OptionsIcon } from '@status-im/icons' + +import ValidatorProfile from '../../../components/General/ValidatorProfile' +import { Validator } from './ManagementTable' + +type ManagementTableRowProps = { + validator: Validator + isAllSelected: boolean +} + +const ManagementTableRow = ({ validator, isAllSelected }: ManagementTableRowProps) => { + const [isSelected, setIsSelected] = useState(false) + + useEffect(() => { + setIsSelected(isAllSelected) + }, [isAllSelected]) + + const handleChangeIsSelected = () => { + setIsSelected(state => !state) + } + + return ( + + + + + + + + + + {validator.balance} + + + + + {validator.income} + + + + + {validator.proposals} + + + + + {validator.attestations} + + + + + {validator.effectiveness}% + + + + + {validator.status} + + + + + + + ) +} + +export default ManagementTableRow diff --git a/src/pages/ValidatorManagement/ManagementTable/SearchManagement.stories.tsx b/src/pages/ValidatorManagement/ManagementTable/SearchManagement.stories.tsx new file mode 100644 index 00000000..73e4c66a --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/SearchManagement.stories.tsx @@ -0,0 +1,26 @@ +import { useState } from 'react' +import type { Meta, StoryObj } from '@storybook/react' + +import SearchManagement from './SearchManagement' + +const meta = { + title: 'ValidatorManagement/SearchManagement', + component: SearchManagement, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = (args: { searchValue: string }) => { + const [searchValue, setSearchValue] = useState(args.searchValue) + + return +} + +Default.args = { + searchValue: '', +} diff --git a/src/pages/ValidatorManagement/ManagementTable/SearchManagement.tsx b/src/pages/ValidatorManagement/ManagementTable/SearchManagement.tsx new file mode 100644 index 00000000..f651175a --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTable/SearchManagement.tsx @@ -0,0 +1,24 @@ +import { Input } from '@status-im/components' +import { SearchIcon } from '@status-im/icons' + +type SearchManagementProps = { + searchValue: string + changeSearchValue: (value: string) => void +} + +const SearchManagement = ({ searchValue, changeSearchValue }: SearchManagementProps) => { + return ( +
+ } + onClear={() => changeSearchValue('')} + size={40} + /> +
+ ) +} + +export default SearchManagement diff --git a/src/pages/ValidatorManagement/ManagementTabs.stories.ts b/src/pages/ValidatorManagement/ManagementTabs.stories.ts new file mode 100644 index 00000000..899e1ec7 --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTabs.stories.ts @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ManagementTabs from './ManagementTabs' + +const meta = { + title: 'ValidatorManagement/ManagementTabs', + component: ManagementTabs, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: {}, +} diff --git a/src/pages/ValidatorManagement/ManagementTabs.tsx b/src/pages/ValidatorManagement/ManagementTabs.tsx new file mode 100644 index 00000000..0e4a2a2c --- /dev/null +++ b/src/pages/ValidatorManagement/ManagementTabs.tsx @@ -0,0 +1,43 @@ +import { Tabs } from '@status-im/components' +import { Stack } from 'tamagui' +import { useState } from 'react' + +import ManagementTable from './ManagementTable/ManagementTable' +import { VALIDATOR_TABS_MANAGEMENT } from '../../constants' + +const ManagementTabs = () => { + const [searchValue, setSearchValue] = useState('') + + const changeSearchValue = (value: string) => { + setSearchValue(value) + } + + return ( +
+ +
+ + + {VALIDATOR_TABS_MANAGEMENT.map(tab => ( + + {tab} + + ))} + + +
+ {VALIDATOR_TABS_MANAGEMENT.map(tab => ( + + + + ))} +
+
+ ) +} + +export default ManagementTabs diff --git a/src/pages/ValidatorManagement/ValidatorManagement.stories.ts b/src/pages/ValidatorManagement/ValidatorManagement.stories.ts new file mode 100644 index 00000000..e1e115cc --- /dev/null +++ b/src/pages/ValidatorManagement/ValidatorManagement.stories.ts @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ValidatorManagement from './ValidatorManagement' + +const meta = { + title: 'Pages/ValidatorManagement', + component: ValidatorManagement, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Page: Story = { + args: {}, +} diff --git a/src/pages/ValidatorManagement/ValidatorManagement.tsx b/src/pages/ValidatorManagement/ValidatorManagement.tsx index 5619fe4a..e23342ad 100644 --- a/src/pages/ValidatorManagement/ValidatorManagement.tsx +++ b/src/pages/ValidatorManagement/ValidatorManagement.tsx @@ -1,7 +1,20 @@ -import { YStack } from 'tamagui' +import { XStack } from 'tamagui' + +import ValidatorManagementContent from './ValidatorManagementContent' +import LeftSidebar from '../../components/General/LeftSidebar/LeftSidebar' +import RightSidebar from '../../components/General/RightSideBar/RightSidebar' +import './validatorManagement.css' const ValidatorManagement = () => { - return + return ( + + + +
+ +
+
+ ) } export default ValidatorManagement diff --git a/src/pages/ValidatorManagement/ValidatorManagementContent.stories.ts b/src/pages/ValidatorManagement/ValidatorManagementContent.stories.ts new file mode 100644 index 00000000..45f84738 --- /dev/null +++ b/src/pages/ValidatorManagement/ValidatorManagementContent.stories.ts @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from '@storybook/react' + +import ValidatorManagementContent from './ValidatorManagementContent' + +const meta = { + title: 'ValidatorManagement/ValidatorManagementContent', + component: ValidatorManagementContent, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta + +export default meta +type Story = StoryObj + +export const Default: Story = { + args: {}, +} diff --git a/src/pages/ValidatorManagement/ValidatorManagementContent.tsx b/src/pages/ValidatorManagement/ValidatorManagementContent.tsx new file mode 100644 index 00000000..25f5c53e --- /dev/null +++ b/src/pages/ValidatorManagement/ValidatorManagementContent.tsx @@ -0,0 +1,44 @@ +import { Text } from '@status-im/components' +import { YStack } from 'tamagui' + +import ManagementTabs from './ManagementTabs' +import AddCardsContainer from '../../components/General/AddCards/AddCardsContainer' +import ManagementHeader from './ManagementHeader' +import ManagementCard from './ManagementCard' + +const ValidatorManagementContent = () => { + return ( + + +
+ + + +
+ + Validators + + +
+ ) +} + +export default ValidatorManagementContent diff --git a/src/pages/ValidatorManagement/validatorManagement.css b/src/pages/ValidatorManagement/validatorManagement.css new file mode 100644 index 00000000..6ea6a853 --- /dev/null +++ b/src/pages/ValidatorManagement/validatorManagement.css @@ -0,0 +1,94 @@ +@media (max-width: 1130px) { + .sync-status-card-container-first { + display: none; + } +} + +@media (max-width: 900px) and (min-width: 810px) { + .sync-status-card-container-first { + display: block; + } +} + +@media (max-width: 590px) { + .sync-status-card-container-second { + display: none; + } +} + +@media (max-width: 600px) { + .tabs { + overflow-x: auto; + overflow-y: none; + } +} + +@media (max-width: 600px) { + .cards { + flex-direction: column; + } +} + +/* Hide Effectiveness */ +@media (max-width: 1300px) { + th:nth-child(7), + td:nth-child(7) { + display: none; + } +} + +/* Hide the Attestations */ +@media (max-width: 1200px) { + th:nth-child(6), + td:nth-child(6) { + display: none; + } +} + +/* Hide the Proposals */ +@media (max-width: 1100px) { + th:nth-child(5), + td:nth-child(5) { + display: none; + } +} + +/* Hide and show Proposals */ +@media (max-width: 900px) and (min-width: 800px) { + th:nth-child(5), + td:nth-child(5) { + display: table-cell; + } +} + +/* Hide the Income */ +@media (max-width: 1000px) { + th:nth-child(4), + td:nth-child(4) { + display: none; + } +} + +/* Hide and show Income */ +@media (max-width: 900px) and (min-width: 700px) { + th:nth-child(4), + td:nth-child(4) { + display: table-cell; + } +} + +/* Hide Status */ +@media (max-width: 560px) { + th:nth-child(8), + td:nth-child(8) { + display: none; + } +} + +/* Hide Balance */ +@media (max-width: 475px) { + th:nth-child(3), + td:nth-child(3) { + display: none; + } +} diff --git a/src/pages/ValidatorOnboarding/Deposit/ValidatorRequest/ValidatorRequest.tsx b/src/pages/ValidatorOnboarding/Deposit/ValidatorRequest/ValidatorRequest.tsx index a5eb6d4e..757907c3 100644 --- a/src/pages/ValidatorOnboarding/Deposit/ValidatorRequest/ValidatorRequest.tsx +++ b/src/pages/ValidatorOnboarding/Deposit/ValidatorRequest/ValidatorRequest.tsx @@ -1,8 +1,8 @@ -import { Avatar, DividerLine, Text } from '@status-im/components' +import { DividerLine, Text } from '@status-im/components' import { XStack, YStack } from 'tamagui' -import { getFormattedValidatorAddress } from '../../../../utilities' import TransactionStatus from './TransactionStatus' +import ValidatorProfile from '../../../../components/General/ValidatorProfile' type ValidatorRequestProps = { number: number @@ -17,23 +17,7 @@ const ValidatorRequest = ({ number, isTransactionConfirmation }: ValidatorReques - - - - - Validator {number} - - - {getFormattedValidatorAddress('zQ3asdf9d4Gs0')} - - - + Keys Generated diff --git a/src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationHeader.tsx b/src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationHeader.tsx index 9d10ad84..86d73792 100644 --- a/src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationHeader.tsx +++ b/src/pages/ValidatorOnboarding/KeyGeneration/KeyGenerationHeader/KeyGenerationHeader.tsx @@ -1,6 +1,6 @@ import { XStack } from 'tamagui' -import KeyGenerationSyncCard from './KeyGenerationSyncCard' +import SyncStatusCard from '../../../../components/General/SyncStatusCard' import KeyGenerationTitle from '../KeyGenerationTitle' const KeyGenerationHeader = () => { @@ -8,13 +8,13 @@ const KeyGenerationHeader = () => { - -