diff --git a/packages/common/src/utils/formatUtil.ts b/packages/common/src/utils/formatUtil.ts index 8fb130716f..044cef053e 100644 --- a/packages/common/src/utils/formatUtil.ts +++ b/packages/common/src/utils/formatUtil.ts @@ -34,6 +34,36 @@ export const formatCount = (count: number) => { } } +/** + * The format any currency should be: + * - show 0 if 0 + * - don't show decimal places if input is a round number + * - show only up to 2 decimal places if input is not a round number + * - round down to nearest thousand if input is greater than 10000 + * ie. + * 0 => 0 + * 8 => 8 + * 8.01 => 8.01 + * 8.10 => 8.10 + * 4,210 => 4210 + * 9,999.99 => 9999.99 + * 56,010 => 56K + * 443,123 => 443K + */ +export const formatCurrencyBalance = (amount: number) => { + if (amount === 0) { + return '0' + } else if (amount >= 9999.995) { + const roundedAmount = Math.floor(amount / 1000) + return `${roundedAmount}k` + } else if (Number.isInteger(amount)) { + return amount.toString() + } else { + const decimalCount = amount > 10000 ? 0 : 2 + return amount.toFixed(decimalCount) + } +} + /** * Formats a number of bytes into a nice looking string. * ie. diff --git a/packages/stems/src/assets/icons/iconQuestionCircle.svg b/packages/stems/src/assets/icons/iconQuestionCircle.svg new file mode 100644 index 0000000000..8997330945 --- /dev/null +++ b/packages/stems/src/assets/icons/iconQuestionCircle.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/stems/src/assets/styles/fonts.css b/packages/stems/src/assets/styles/fonts.css index bdf3f373c9..19ff8afeff 100644 --- a/packages/stems/src/assets/styles/fonts.css +++ b/packages/stems/src/assets/styles/fonts.css @@ -18,6 +18,7 @@ root { --font-2xl: 24px; --font-3xl: 32px; --font-4xl: 36px; + --font-5xl: 40px; --font-ultra-light: 100; --font-regular: 400; diff --git a/packages/stems/src/components/Icons/index.ts b/packages/stems/src/components/Icons/index.ts index 086e5fcee7..b41434ee87 100644 --- a/packages/stems/src/components/Icons/index.ts +++ b/packages/stems/src/components/Icons/index.ts @@ -66,6 +66,7 @@ export { ReactComponent as IconIndent } from '../../assets/icons/iconIndent.svg' export { ReactComponent as IconInfo } from '../../assets/icons/iconInfo.svg' export { ReactComponent as IconInstagram } from '../../assets/icons/iconInstagram.svg' export { ReactComponent as IconKebabHorizontal } from '../../assets/icons/iconKebabHorizontal.svg' +export { ReactComponent as IconQuestionCircle } from '../../assets/icons/iconQuestionCircle.svg' export { ReactComponent as IconKebabVertical } from '../../assets/icons/iconKebabVertical.svg' export { ReactComponent as IconLink } from '../../assets/icons/iconLink.svg' export { ReactComponent as IconListens } from '../../assets/icons/iconListens.svg' diff --git a/packages/stems/src/styles/colors.ts b/packages/stems/src/styles/colors.ts index 94c1357b8b..c378297ded 100644 --- a/packages/stems/src/styles/colors.ts +++ b/packages/stems/src/styles/colors.ts @@ -39,3 +39,4 @@ export type ColorValue = | 'accentBlue' | 'specialLightGreen' | 'darkmodeStaticWhite' + | 'staticWhite' diff --git a/packages/web/src/components/typography/types.ts b/packages/web/src/components/typography/types.ts index b239589fbd..2df4b2fd2e 100644 --- a/packages/web/src/components/typography/types.ts +++ b/packages/web/src/components/typography/types.ts @@ -12,7 +12,13 @@ export type FontWeight = export type TextStrength = 'weak' | 'default' | 'strong' -export type TextSize = 'xLarge' | 'large' | 'medium' | 'small' | 'xSmall' +export type TextSize = + | 'xxLarge' + | 'xLarge' + | 'large' + | 'medium' + | 'small' + | 'xSmall' export type TextVariant = | 'display' diff --git a/packages/web/src/components/typography/typography.module.css b/packages/web/src/components/typography/typography.module.css index e8fc6eb4de..ff34aab3cb 100644 --- a/packages/web/src/components/typography/typography.module.css +++ b/packages/web/src/components/typography/typography.module.css @@ -67,6 +67,12 @@ font-weight: var(--font-bold); } +/* 40 / 44 */ +.headingXxLarge { + font-size: var(--unit-10); + line-height: var(--unit-11); +} + /* 36 / 40 */ .headingXLarge { font-size: var(--unit-9); diff --git a/packages/web/src/pages/artist-dashboard-page/ArtistDashboardPage.module.css b/packages/web/src/pages/artist-dashboard-page/ArtistDashboardPage.module.css index 505ff0b9ee..7d6d069bcb 100644 --- a/packages/web/src/pages/artist-dashboard-page/ArtistDashboardPage.module.css +++ b/packages/web/src/pages/artist-dashboard-page/ArtistDashboardPage.module.css @@ -159,3 +159,62 @@ justify-content: stretch; margin: 12px 0px 36px; } + +.usdcContainer { + width: 100%; + flex-direction: column; + border-radius: var(--unit-3); + background: var(--static-white); + overflow: hidden; +} + +.backgroundBlueGradient { + display: flex; + width: 100%; + flex-direction: column; + align-items: center; + justify-content: space-between; + gap: var(--unit-4); + padding: var(--unit-6); + background: linear-gradient( + 315deg, + rgba(0, 0, 0, 0.1) 0%, + rgba(255, 255, 255, 0.1) 100% + ), + #2775ca; +} + +.usdcTitleContainer { + display: flex; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.usdcTitle { + display: flex; + gap: var(--unit-2); +} + +.usdc { + opacity: 0.8; +} + +.usdcInfo { + display: flex; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.withdrawContainer { + display: flex; + width: 100%; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: var(--unit-6); + gap: var(--unit-6); +} diff --git a/packages/web/src/pages/artist-dashboard-page/ArtistDashboardPage.tsx b/packages/web/src/pages/artist-dashboard-page/ArtistDashboardPage.tsx index af7e1827a2..4ccdb6febb 100644 --- a/packages/web/src/pages/artist-dashboard-page/ArtistDashboardPage.tsx +++ b/packages/web/src/pages/artist-dashboard-page/ArtistDashboardPage.tsx @@ -13,10 +13,24 @@ import { Theme, Track, User, + formatCurrencyBalance, formatCount, - themeSelectors + themeSelectors, + FeatureFlags } from '@audius/common' -import { IconFilter, IconNote, IconHidden } from '@audius/stems' +import { + IconFilter, + IconNote, + IconHidden, + IconKebabHorizontal, + IconQuestionCircle, + HarmonyButton, + HarmonyButtonType, + PopupMenu, + PopupMenuItem, + HarmonyPlainButton, + HarmonyPlainButtonType +} from '@audius/stems' import cn from 'classnames' import { push as pushRoute } from 'connected-react-router' import { each } from 'lodash' @@ -25,12 +39,15 @@ import { connect, useDispatch, useSelector } from 'react-redux' import { withRouter, RouteComponentProps } from 'react-router-dom' import { Dispatch } from 'redux' +import { Icon } from 'components/Icon' import Header from 'components/header/desktop/Header' import { Input } from 'components/input' import LoadingSpinner from 'components/loading-spinner/LoadingSpinner' import Page from 'components/page/Page' import { TracksTable, TracksTableColumn } from 'components/tracks-table' +import { Text } from 'components/typography' import useTabs, { useTabRecalculator } from 'hooks/useTabs/useTabs' +import { getFeatureEnabled } from 'services/remote-config/featureFlagHelpers' import { AppState } from 'store/types' import lazyWithPreload from 'utils/lazyWithPreload' import { profilePage, TRENDING_PAGE } from 'utils/route' @@ -86,7 +103,13 @@ export const messages = { publicTracksTabTitle: 'PUBLIC TRACKS', unlistedTracksTabTitle: 'HIDDEN TRACKS', filterInputPlacehoder: 'Filter Tracks', - thisYear: 'This Year' + thisYear: 'This Year', + usdc: 'USDC', + earn: 'Earn USDC by selling your music', + learnMore: 'Learn More', + withdraw: 'Withdraw Funds', + salesSummary: 'Sales Summary', + withdrawalHistory: 'Withdrawal History' } const tableColumns: TracksTableColumn[] = [ @@ -231,6 +254,89 @@ const TracksTableContainer = ({ ) } +const USDCSection = ({ account }: { account: User }) => { + if (!account) return null + + // TODO: wire up balance https://linear.app/audius/issue/PAY-1761/wire-up-usdc-balance-in-artist-dashboard + const balance = 10.29 + + const menuItems: PopupMenuItem[] = [ + { + text: messages.salesSummary, + // TODO: link to sales page https://linear.app/audius/issue/PAY-1763/wire-up-salespurchases-pages-on-artist-dashboard + onClick: () => {} + }, + { + text: messages.withdrawalHistory, + // TODO: link to withdraw history page https://linear.app/audius/issue/PAY-1763/wire-up-salespurchases-pages-on-artist-dashboard + onClick: () => {} + } + ] + + return ( +
+
+
+
+ {/* TODO: update icon https://linear.app/audius/issue/PAY-1764/update-icons-in-usdc-tile */} + +
+ + {messages.usdc} + +
+
+ + ${formatCurrencyBalance(balance)} + +
+
+ {messages.earn} + {}} + iconLeft={IconQuestionCircle} + variant={HarmonyPlainButtonType.INVERTED} + text={messages.learnMore} + /> +
+
+
+ } + onClick={() => {}} + /> + ( + + )} + /> +
+
+ ) +} + type ArtistDashboardPageProps = ReturnType & ReturnType> & RouteComponentProps @@ -383,6 +489,7 @@ export class ArtistDashboardPage extends Component< render() { const { account, status } = this.props const header =
+ const isUSDCEnabled = getFeatureEnabled(FeatureFlags.USDC_PURCHASES) return ( {this.renderProfileSection()} + {isUSDCEnabled ? : null} {this.renderCreatorContent()} )}