diff --git a/public/assets/chevron-down-purple.svg b/public/assets/chevron-down-purple.svg new file mode 100644 index 0000000..76e66fb --- /dev/null +++ b/public/assets/chevron-down-purple.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/public/assets/edit.svg b/public/assets/edit.svg new file mode 100644 index 0000000..3229df4 --- /dev/null +++ b/public/assets/edit.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/file-copy-purple.svg b/public/assets/file-copy-purple.svg new file mode 100644 index 0000000..f2bfd0f --- /dev/null +++ b/public/assets/file-copy-purple.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/public/assets/file-copy.svg b/public/assets/file-copy.svg new file mode 100644 index 0000000..f1080f6 --- /dev/null +++ b/public/assets/file-copy.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/plus.svg b/public/assets/plus.svg index 0536731..38d0b7e 100644 --- a/public/assets/plus.svg +++ b/public/assets/plus.svg @@ -1,3 +1,5 @@ - - + + + \ No newline at end of file diff --git a/public/dashboard/badges/1-year-streak.svg b/public/dashboard/badges/1-year-streak.svg new file mode 100644 index 0000000..847fcc1 --- /dev/null +++ b/public/dashboard/badges/1-year-streak.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/dashboard/badges/10-day-streak.svg b/public/dashboard/badges/10-day-streak.svg new file mode 100644 index 0000000..2882a64 --- /dev/null +++ b/public/dashboard/badges/10-day-streak.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/public/dashboard/badges/100-days-streak.svg b/public/dashboard/badges/100-days-streak.svg new file mode 100644 index 0000000..72e2b31 --- /dev/null +++ b/public/dashboard/badges/100-days-streak.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/public/dashboard/badges/badege-placeholder.svg b/public/dashboard/badges/badege-placeholder.svg new file mode 100644 index 0000000..a63f2e9 --- /dev/null +++ b/public/dashboard/badges/badege-placeholder.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/Dashboard/OperatorGrid/OperatorGrid.tsx b/src/components/Dashboard/OperatorGrid/OperatorGrid.tsx index 10b1047..9c06dda 100644 --- a/src/components/Dashboard/OperatorGrid/OperatorGrid.tsx +++ b/src/components/Dashboard/OperatorGrid/OperatorGrid.tsx @@ -18,13 +18,14 @@ const OperatorGrid: React.FC = ({
Operators - -

Filter

- chevron down - - - Settings - + + Add Operator + @@ -34,14 +35,14 @@ const OperatorGrid: React.FC = ({ + 6 + + + 1 - - 4,278 - - - + 912 @@ -83,7 +84,7 @@ const OperatorGrid: React.FC = ({ ) } const StyledOperatorGrid = styled.section` - margin-top: 94px; + margin-top: 116px; @media (max-width: ${breakpoints.md}px) { margin-top: 40px; @@ -109,24 +110,16 @@ const Controls = styled.div` align-items: center; ` -const FilterDropdown = styled.div` - background-color: transparent; - width: 128px; +const PlusIcon = styled.button` display: flex; align-items: center; - justify-content: space-between; + justify-content: center; + border: 1px solid rgb(var(--lsd-border-primary)); - border-right: none; - color: rgb(var(--lsd-text-primary)); - font-weight: 400; - font-size: 12px; - padding-left: 12px; + background-color: transparent; + width: 28px; height: 28px; cursor: pointer; - - img { - padding: 8px; - } ` const IconButton = styled.button` @@ -172,7 +165,7 @@ const Value = styled.div` const Grid = styled.div` display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 16px; ` @@ -201,6 +194,13 @@ const OperatorName = styled.div` font-weight: 400; font-size: 14px; line-height: 20px; + // 1 line of text + display: -webkit-inline-box; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + line-clamp: 1; + -webkit-line-clamp: 1; ` const PointsPerHour = styled.div` @@ -219,17 +219,22 @@ const Actions = styled.div` const ActionButton = styled.button<{ isStaked: boolean }>` flex: 1; background-color: ${(props) => - props.isStaked ? 'transparent' : 'rgb(var(--lsd-surface-secondary))'}; + props.isStaked ? 'rgb(var(--lsd-surface-secondary))' : 'transparent'}; color: ${(props) => props.isStaked - ? 'rgb(var(--lsd-text-primary))' - : 'rgb(var(--lsd-text-secondary))'}; + ? 'rgb(var(--lsd-text-secondary))' + : 'rgb(var(--lsd-text-primary))'}; border: none; font-weight: 400; font-size: 12px; line-height: 16px; padding: 6px 12px; + &:hover { + background-color: rgb(var(--lsd-surface-secondary)); + color: rgb(var(--lsd-text-secondary)); + } + cursor: pointer; ` diff --git a/src/components/Dashboard/OperatorPanel/OperatorPanel.tsx b/src/components/Dashboard/OperatorPanel/OperatorPanel.tsx index 95b6672..9460b31 100644 --- a/src/components/Dashboard/OperatorPanel/OperatorPanel.tsx +++ b/src/components/Dashboard/OperatorPanel/OperatorPanel.tsx @@ -1,3 +1,5 @@ +import { Collapse } from '@/components/common/Collapse' +import { truncateString } from '@/utils/general.utils' import styled from '@emotion/styled' import React from 'react' @@ -6,23 +8,19 @@ interface OperatorPanelProps {} const TEMP_BADGES = [ { id: 1, - image: '/dashboard/badges/badge-1.svg', + image: '/dashboard/badges/1-year-streak.svg', }, { id: 2, - image: '/dashboard/badges/badge-2.svg', + image: '/dashboard/badges/10-day-streak.svg', }, { id: 3, - image: '/dashboard/badges/badge-3.svg', + image: '/dashboard/badges/100-days-streak.svg', }, { id: 4, - image: '/dashboard/badges/badge-4.svg', - }, - { - id: 5, - image: '/dashboard/badges/badge-5.svg', + image: '/dashboard/badges/badege-placeholder.svg', }, ] @@ -35,8 +33,7 @@ const OperatorPanel: React.FC = () => { alt="Operator" /> - Operator - Memetic #1234 + Quantum Recursive Memetic @@ -45,17 +42,31 @@ const OperatorPanel: React.FC = () => { + + + {truncateString('bc1qaa13nskasjovehs9')} + + Copy wallet address + + RagingBull + + Edit callsign + + + + + Operator - - - #214 + + + @@ -72,6 +83,11 @@ const OperatorPanel: React.FC = () => { + + ) } @@ -90,21 +106,13 @@ const OperatorImage = styled.img` const OperatorInfo = styled.div` display: flex; - justify-content: space-between; + justify-content: center; padding: 16px 8px; margin-bottom: 2px; ` const OperatorType = styled.span`` -const OperatorName = styled.span`` - -const InfoColumn = styled.div` - display: flex; - flex-direction: column; - gap: 2px; -` - const InfoRow = styled.div` display: flex; justify-content: space-between; @@ -129,10 +137,13 @@ const CallSignContainer = styled.div` margin-bottom: 2px; ` -const Label = styled.span`` +const Label = styled.span` + width: 87px; +` const Value = styled.span` text-align: right; + width: 155px; ` const BadgesSection = styled.div` @@ -159,14 +170,23 @@ const BadgeIcon = styled.img` const BadgeList = styled.div` display: flex; flex-wrap: wrap; - gap: 8px; + gap: 16px; ` const Badge = styled.img` - width: 45px; - height: 42px; + width: 52px; + height: 52px; object-fit: contain; +` + +const ActionButton = styled.button` + display: flex; + align-items: center; + justify-content: center; + border: none; + background-color: transparent; cursor: pointer; + margin-left: 18px; ` export default OperatorPanel diff --git a/src/components/Dashboard/ProgressBar/ProgressBar.tsx b/src/components/Dashboard/ProgressBar/ProgressBar.tsx index f211f6a..af19ba5 100644 --- a/src/components/Dashboard/ProgressBar/ProgressBar.tsx +++ b/src/components/Dashboard/ProgressBar/ProgressBar.tsx @@ -1,3 +1,4 @@ +import { numberWithCommas } from '@/utils/general.utils' import styled from '@emotion/styled' import React, { useEffect, useState } from 'react' @@ -56,22 +57,30 @@ const ProgressBar: React.FC = ({ - Staking Rate: 100% - Bonus Reward: 20% + Current Rate: 100% + XP Bonus: +20% - {timeRemaining} + {`${numberWithCommas(1026)} blocks`} - - - - 200.12 - - + + + + + {`${numberWithCommas(40278)}`} + + + + + + {`${numberWithCommas(5020)}`} + + + ) @@ -99,6 +108,8 @@ const EpochLabel = styled.span` padding: 0 8px; line-height: 20px; font-size: 14px; + width: 72px; + text-align: center; ` const EpochInfo = styled.div` @@ -111,7 +122,7 @@ const NextEpoch = styled.span`` const ProgressTrack = styled.div` position: relative; height: 8px; - margin: 14px 0; + margin: 16px 0; border-bottom: 1px solid rgb(var(--lsd-border-primary)); border-right: 1px solid rgb(var(--lsd-border-primary)); ` @@ -165,19 +176,18 @@ const TimeRemaining = styled.div` border: 1px solid var(--dark-orange); display: flex; align-items: center; - gap: 16px; ` const EarnedReward = styled.div` border: 1px solid #320430; display: flex; align-items: center; - gap: 16px; ` const Label = styled.div` font-size: 12px; padding: 8px 16px; + line-height: 16px; ` const Value = styled.div<{ color: string; backgroundColor: string }>` @@ -185,6 +195,12 @@ const Value = styled.div<{ color: string; backgroundColor: string }>` color: ${(props) => props.color}; background-color: ${(props) => props.backgroundColor}; padding: 8px 16px; + line-height: 16px; + letter-spacing: 0.12px; +` + +const PointsRow = styled.div` + display: flex; ` export default ProgressBar diff --git a/src/components/Header/Navbar/Navbar.tsx b/src/components/Header/Navbar/Navbar.tsx index 0739719..f96e330 100644 --- a/src/components/Header/Navbar/Navbar.tsx +++ b/src/components/Header/Navbar/Navbar.tsx @@ -68,7 +68,7 @@ const Navigation = styled.ul` position: absolute; left: 50%; transform: translateX(-50%); - gap: 37px; + gap: 41px; list-style-type: none; a { diff --git a/src/components/common/Collapse/Collapse.tsx b/src/components/common/Collapse/Collapse.tsx new file mode 100644 index 0000000..9613ea1 --- /dev/null +++ b/src/components/common/Collapse/Collapse.tsx @@ -0,0 +1,97 @@ +import styled from '@emotion/styled' +import React, { useState } from 'react' + +interface CollapsibleProps { + header: string + content: string + enableCopy?: boolean +} + +const Collapse: React.FC = ({ + header, + content, + enableCopy = true, +}) => { + const [isOpen, setIsOpen] = useState(false) + + const toggleOpen = () => { + setIsOpen(!isOpen) + } + + const copyToClipboard = () => { + navigator.clipboard.writeText(content) + alert('Copied to clipboard!') + } + + return ( + +
+ {header} + + {isOpen ? ( + chevron up + ) : ( + chevron down + )} + +
+ + {content} + {enableCopy && ( + + file copy + + )} + +
+ ) +} + +const Container = styled.div` + background-color: #320430; + border-radius: 8px; + margin: 10px 0; + color: #f29ae9; + cursor: pointer; +` + +const Header = styled.div` + padding: 20px 16px 20px 8px; + font-size: 14px; + line-height: 20px; + display: flex; + justify-content: space-between; + align-items: center; +` + +const Body = styled.div<{ isOpen: boolean }>` + display: ${({ isOpen }) => (isOpen ? 'flex' : 'none')}; + justify-content: space-between; + max-height: ${({ isOpen }) => (isOpen ? '100px' : '0')}; + overflow: hidden; + transition: max-height 0.3s ease; + background-color: #320430; + padding: 20px 16px 20px 8px; + font-size: 14px; + line-height: 20px; + color: #f29ae9; + letter-spacing: 0.14px; +` + +const CopyButton = styled.button` + background: none; + border: none; + color: #f29ae9; + cursor: pointer; + font-size: 14px; + margin-left: 10px; +` + +const Chevron = styled.span<{ isExpanded: boolean }>` + display: inline-flex; + transition: transform 0.3s ease; + transform: ${({ isExpanded }) => + isExpanded ? 'rotate(180deg)' : 'rotate(0deg)'}; +` + +export default Collapse diff --git a/src/components/common/Collapse/index.ts b/src/components/common/Collapse/index.ts new file mode 100644 index 0000000..d0f23d4 --- /dev/null +++ b/src/components/common/Collapse/index.ts @@ -0,0 +1 @@ +export { default as Collapse } from './Collapse' diff --git a/src/utils/general.utils.ts b/src/utils/general.utils.ts index 81122a0..276b363 100644 --- a/src/utils/general.utils.ts +++ b/src/utils/general.utils.ts @@ -9,3 +9,16 @@ export function arrayIncludesAnyElementFromOtherArray(a: T[], b: T[]) { return a.some((el) => b.includes(el)) } + +export function numberWithCommas(x: number) { + return x?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') +} + +// truncates a string to first 5 and last 5, appends an ellipsis in the middle +export function truncateString(str: string, length = 10) { + if (str.length <= length) { + return str + } + + return `${str.slice(0, 5)}...${str.slice(-5)}` +}