build ok,

This commit is contained in:
louiscklaw
2025-04-14 09:26:24 +08:00
commit 6c931c1fe8
770 changed files with 63959 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Card from '@mui/material/Card';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { Lightning as LightningIcon } from '@phosphor-icons/react/dist/ssr/Lightning';
export function AccountUpgrade(): React.JSX.Element {
return (
<Card>
<Stack spacing={2} sx={{ alignItems: 'center', p: 3 }}>
<Box sx={{ flex: '0 0 auto', width: '100px' }}>
<Box alt="Tick" component="img" src="/assets/iconly-glass-tick.svg" sx={{ height: 'auto', width: '100%' }} />
</Box>
<Stack spacing={1}>
<Typography sx={{ textAlign: 'center' }} variant="h6">
Upgrade your account to PRO.
</Typography>
<Typography color="text.secondary" sx={{ textAlign: 'center' }} variant="body2">
Unlock exclusive features like Test Networks, Test Swaps, and more.
</Typography>
</Stack>
<Button color="secondary" startIcon={<LightningIcon />} variant="contained">
Upgrade
</Button>
</Stack>
</Card>
);
}

View File

@@ -0,0 +1,78 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
export type Brand = 'mastercard' | 'visa';
const brandBgs: Record<Brand, string> = {
mastercard: '/assets/card-background-1.png',
visa: '/assets/card-background-2.png',
};
const brandIcons: Record<Brand, string> = { mastercard: '/assets/logo-mastercard.svg', visa: '/assets/logo-visa.svg' };
export interface CreditCardProps {
card: { id: string; brand: Brand; cardNumber: string; expiryDate: string; holderName: string };
}
export function CreditCard({ card }: CreditCardProps): React.JSX.Element {
return (
<Stack
spacing={4}
sx={{
bgcolor: 'var(--mui-palette-primary-main)',
backgroundImage: `url("${brandBgs[card.brand]}")`,
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
p: '32px 24px',
borderRadius: '20px',
}}
>
<Box sx={{ alignItems: 'center', display: 'flex', justifyContent: 'space-between' }}>
<Box alt="Contactless" component="img" src="/assets/contactless.svg" sx={{ height: 'auto', width: '24px' }} />
<Box alt={card.brand} component="img" src={brandIcons[card.brand]} sx={{ height: 'auto', width: '56px' }} />
</Box>
<Typography
sx={{
background: 'linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, #FFFFFF 100%)',
backgroundClip: 'text',
fontSize: '1.25rem',
fontWeight: 700,
letterSpacing: '0.3em',
lineHeight: 1.2,
textFillColor: 'transparent',
}}
>
{card.cardNumber}
</Typography>
<Stack direction="row" spacing={2} sx={{ alignItems: 'center', justifyContent: 'space-between' }}>
<div>
<Typography color="white" variant="caption">
Card holder
</Typography>
<Typography color="white" variant="subtitle2">
{card.holderName}
</Typography>
</div>
<div>
<Typography color="white" variant="caption">
Expiry date
</Typography>
<Typography color="white" variant="subtitle2">
{card.expiryDate}
</Typography>
</div>
<div>
<Box
alt="Sim card"
component="img"
src="/assets/sim.svg"
sx={{ display: 'block', height: 'auto', width: '48px' }}
/>
</div>
</Stack>
</Stack>
);
}

View File

@@ -0,0 +1,81 @@
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Card from '@mui/material/Card';
import CardContent from '@mui/material/CardContent';
import CardHeader from '@mui/material/CardHeader';
import Divider from '@mui/material/Divider';
import FormControl from '@mui/material/FormControl';
import IconButton from '@mui/material/IconButton';
import InputLabel from '@mui/material/InputLabel';
import OutlinedInput from '@mui/material/OutlinedInput';
import Stack from '@mui/material/Stack';
import Tab from '@mui/material/Tab';
import Tabs from '@mui/material/Tabs';
import Typography from '@mui/material/Typography';
import { ArrowsDownUp as ArrowsDownUpIcon } from '@phosphor-icons/react/dist/ssr/ArrowsDownUp';
import { CoinVertical as CoinVerticalIcon } from '@phosphor-icons/react/dist/ssr/CoinVertical';
export function CurrencyConverter(): React.JSX.Element {
return (
<Card>
<CardHeader
avatar={
<Avatar>
<CoinVerticalIcon fontSize="var(--Icon-fontSize)" />
</Avatar>
}
title="Operation"
/>
<Tabs sx={{ px: 3 }} value="buy">
<Tab label="Buy" tabIndex={0} value="buy" />
<Tab label="Sell" tabIndex={0} value="sell" />
</Tabs>
<Divider />
<CardContent>
<Stack spacing={2}>
<Stack spacing={1}>
<FormControl>
<InputLabel>From</InputLabel>
<OutlinedInput
startAdornment={
<Box
alt="ETH"
component="img"
src="/assets/logo-eth.svg"
sx={{ height: '24px', mr: 1, width: '24px' }}
/>
}
value="0.4567"
/>
</FormControl>
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
<IconButton>
<ArrowsDownUpIcon />
</IconButton>
</Box>
<FormControl>
<InputLabel>To</InputLabel>
<OutlinedInput
startAdornment={
<Box
alt="BTC"
component="img"
src="/assets/logo-btc.svg"
sx={{ height: '24px', mr: 1, width: '24px' }}
/>
}
value="5.9093"
/>
</FormControl>
</Stack>
<Typography color="text.secondary" variant="body2">
1 BTC = {new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(20024.9)}
</Typography>
<Button variant="contained">Buy Bitcoin</Button>
</Stack>
</CardContent>
</Card>
);
}

View File

@@ -0,0 +1,149 @@
'use client';
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Card from '@mui/material/Card';
import CardActions from '@mui/material/CardActions';
import CardContent from '@mui/material/CardContent';
import CardHeader from '@mui/material/CardHeader';
import Divider from '@mui/material/Divider';
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { ArrowDownRight as ArrowDownRightIIcon } from '@phosphor-icons/react/dist/ssr/ArrowDownRight';
import { ArrowUpRight as ArrowUpRightIcon } from '@phosphor-icons/react/dist/ssr/ArrowUpRight';
import { Wallet as WalletIcon } from '@phosphor-icons/react/dist/ssr/Wallet';
import { Cell, Pie, PieChart, Tooltip } from 'recharts';
import { NoSsr } from '@/components/core/no-ssr';
export type CurrencyCode = 'BTC' | 'ETH' | 'BNB' | 'USD';
const currencyNames: Record<CurrencyCode, string> = {
BTC: 'Bitcoin',
ETH: 'Ethereum',
BNB: 'Binance',
USD: 'US Dollar',
};
export interface CurrentBalanceProps {
data: { name: CurrencyCode; value: number; color: string }[];
}
export function CurrentBalance({ data }: CurrentBalanceProps): React.JSX.Element {
const chartSize = 200;
const chartTickness = 30;
const total = data.reduce((acc, curr) => acc + curr.value, 0);
return (
<Card>
<CardHeader
avatar={
<Avatar>
<WalletIcon fontSize="var(--Icon-fontSize)" />
</Avatar>
}
subheader="Balance across all your accounts"
title="Current balance"
/>
<CardContent>
<Stack direction="row" spacing={3} sx={{ alignItems: 'center', flexWrap: 'wrap' }}>
<NoSsr fallback={<Box sx={{ height: `${chartSize}px`, width: `${chartSize}px` }} />}>
<PieChart height={chartSize} margin={{ top: 0, right: 0, bottom: 0, left: 0 }} width={chartSize}>
<Pie
animationDuration={300}
cx={chartSize / 2}
cy={chartSize / 2}
data={data}
dataKey="value"
innerRadius={chartSize / 2 - chartTickness}
nameKey="name"
outerRadius={chartSize / 2}
strokeWidth={0}
>
{data.map(
(entry): React.JSX.Element => (
<Cell fill={entry.color} key={entry.name} />
)
)}
</Pie>
<Tooltip animationDuration={50} content={<TooltipContent />} />
</PieChart>
</NoSsr>
<Stack spacing={3} sx={{ flex: '1 1 auto' }}>
<Stack spacing={1}>
<Typography color="text.secondary" variant="overline">
Total balance
</Typography>
<Typography variant="h4">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(total)}
</Typography>
</Stack>
<Stack spacing={1}>
<Typography color="text.secondary" variant="overline">
Available currency
</Typography>
<Stack component="ul" spacing={2} sx={{ listStyle: 'none', m: 0, p: 0 }}>
{data.map((entry) => (
<Stack component="li" direction="row" key={entry.name} spacing={1} sx={{ alignItems: 'center' }}>
<Box sx={{ bgcolor: entry.color, borderRadius: '2px', height: '4px', width: '16px' }} />
<Typography sx={{ flex: '1 1 auto' }} variant="subtitle2">
{currencyNames[entry.name]}
</Typography>
<Typography color="text.secondary">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(entry.value)}
</Typography>
</Stack>
))}
</Stack>
</Stack>
</Stack>
</Stack>
</CardContent>
<Divider />
<CardActions>
<Button color="secondary" endIcon={<ArrowUpRightIcon />} size="small">
Add funds
</Button>
<Button color="secondary" endIcon={<ArrowDownRightIIcon />} size="small">
Transfer funds
</Button>
</CardActions>
</Card>
);
}
interface TooltipContentProps {
active?: boolean;
payload?: { name: string; payload: { fill: string }; value: number }[];
label?: string;
}
function TooltipContent({ active, payload }: TooltipContentProps): React.JSX.Element | null {
if (!active) {
return null;
}
return (
<Paper sx={{ border: '1px solid var(--mui-palette-divider)', boxShadow: 'var(--mui-shadows-16)', p: 1 }}>
<Stack spacing={2}>
{payload?.map(
(entry): React.JSX.Element => (
<Stack direction="row" key={entry.name} spacing={3} sx={{ alignItems: 'center' }}>
<Stack direction="row" spacing={1} sx={{ alignItems: 'center', flex: '1 1 auto' }}>
<Box sx={{ bgcolor: entry.payload.fill, borderRadius: '2px', height: '8px', width: '8px' }} />
<Typography sx={{ whiteSpace: 'nowrap' }}>{currencyNames[entry.name as CurrencyCode]}</Typography>
</Stack>
<Typography color="text.secondary" variant="body2">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(entry.value)}
</Typography>
</Stack>
)
)}
</Stack>
</Paper>
);
}

View File

@@ -0,0 +1,118 @@
'use client';
import * as React from 'react';
import Box from '@mui/material/Box';
import Card from '@mui/material/Card';
import IconButton from '@mui/material/IconButton';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import { DotsThree as DotsThreeIcon } from '@phosphor-icons/react/dist/ssr/DotsThree';
import { TrendDown as TrendDownIcon } from '@phosphor-icons/react/dist/ssr/TrendDown';
import { TrendUp as TrendUpIcon } from '@phosphor-icons/react/dist/ssr/TrendUp';
import { Area, AreaChart, ResponsiveContainer, XAxis, YAxis } from 'recharts';
import { NoSsr } from '@/components/core/no-ssr';
export type CurrencyCode = 'BTC' | 'ETH' | 'BNB';
const currencyIcons: Record<CurrencyCode, string> = {
BTC: '/assets/logo-btc.svg',
ETH: '/assets/logo-eth.svg',
BNB: '/assets/logo-bnb.svg',
};
export interface DigitalWalletProps {
amount: number;
color: string;
currency: CurrencyCode;
data: number[];
diff: number;
trend: 'up' | 'down';
value: number;
}
export function DigitalWallet({
amount,
color,
data: dataRaw,
currency,
diff,
trend,
value,
}: DigitalWalletProps): React.JSX.Element {
const chartHeight = 100;
const data = dataRaw.map((item, index) => ({ name: index, value: item }));
return (
<Card>
<Stack direction="row" spacing={3} sx={{ alignItems: 'flex-start', pt: 2, px: 2 }}>
<Stack spacing={1} sx={{ flex: '1 1 auto' }}>
<Typography color="text.secondary" variant="h6">
<Typography color="text.primary" component="span" variant="inherit">
{amount}
</Typography>{' '}
{currency}
</Typography>
<Typography color="text.secondary" variant="body2">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(value)}
</Typography>
</Stack>
<IconButton>
<DotsThreeIcon weight="bold" />
</IconButton>
</Stack>
<Box sx={{ pt: 3 }}>
<NoSsr fallback={<Box sx={{ height: `${chartHeight}px` }} />}>
<ResponsiveContainer height={chartHeight} width="100%">
<AreaChart data={data} margin={{ top: 0, right: 0, bottom: 0, left: 0 }}>
<defs>
<linearGradient id={`area-wallet-${currency}`} x1="0" x2="0" y1="0" y2="1">
<stop offset="0" stopColor={color} stopOpacity={0.1} />
<stop offset="100%" stopColor={color} stopOpacity={0} />
</linearGradient>
</defs>
<XAxis axisLine={false} dataKey="name" hide type="category" />
<YAxis axisLine={false} hide type="number" />
<Area
animationDuration={300}
dataKey="value"
fill={`url(#${`area-wallet-${currency}`})`}
fillOpacity={1}
name="Income"
stroke={color}
strokeWidth={2}
type="monotone"
/>
</AreaChart>
</ResponsiveContainer>
</NoSsr>
</Box>
<Box sx={{ pb: 2, px: 2 }}>
<Stack direction="row" spacing={2} sx={{ alignItems: 'center' }}>
<Box component="img" src={currencyIcons[currency]} sx={{ height: 'auto', flex: '0 0 auto', width: '40px' }} />
<div>
<Typography variant="subtitle2">{currency}/USD</Typography>
<Stack
direction="row"
spacing={0.5}
sx={{
alignItems: 'center',
color: trend === 'up' ? 'var(--mui-palette-success-main)' : 'var(--mui-palette-error-main)',
}}
>
{trend === 'up' ? (
<TrendUpIcon fontSize="var(--icon-fontSize-md)" />
) : (
<TrendDownIcon fontSize="var(--icon-fontSize-md)" />
)}
<Typography color="inherit" variant="body2">
{new Intl.NumberFormat('en-US', { style: 'percent', maximumFractionDigits: 2 }).format(diff / 100)}
</Typography>
</Stack>
</div>
</Stack>
</Box>
</Card>
);
}

View File

@@ -0,0 +1,95 @@
import * as React from 'react';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import Card from '@mui/material/Card';
import CardActions from '@mui/material/CardActions';
import CardHeader from '@mui/material/CardHeader';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemAvatar from '@mui/material/ListItemAvatar';
import ListItemText from '@mui/material/ListItemText';
import Typography from '@mui/material/Typography';
import { ArrowRight as ArrowRightIcon } from '@phosphor-icons/react/dist/ssr/ArrowRight';
import { ArrowsDownUp as ArrowsDownUpIcon } from '@phosphor-icons/react/dist/ssr/ArrowsDownUp';
import { TrendDown as TrendDownIcon } from '@phosphor-icons/react/dist/ssr/TrendDown';
import { TrendUp as TrendUpIcon } from '@phosphor-icons/react/dist/ssr/TrendUp';
import { dayjs } from '@/lib/dayjs';
export type CurrencyCode = 'BTC' | 'ETH' | 'BNB';
export interface Transaction {
id: string;
type: 'add' | 'sub';
description: string;
balance: number;
currency: CurrencyCode;
amount: number;
createdAt: Date;
}
export interface TransactionsProps {
transactions: Transaction[];
}
export function Transactions({ transactions }: TransactionsProps): React.JSX.Element {
return (
<Card>
<CardHeader
avatar={
<Avatar>
<ArrowsDownUpIcon fontSize="var(--Icon-fontSize)" />
</Avatar>
}
title="Transactions"
/>
<List disablePadding sx={{ '& .MuiListItem-root': { py: 2 } }}>
{transactions.map((transaction) => (
<ListItem divider key={transaction.id}>
<ListItemAvatar>
<Avatar
sx={{
bgcolor: transaction.type === 'add' ? 'var(--mui-palette-success-50)' : 'var(--mui-palette-error-50)',
color:
transaction.type === 'add' ? 'var(--mui-palette-success-main)' : 'var(--mui-palette-error-main)',
}}
>
{transaction.type === 'add' ? (
<TrendUpIcon fontSize="var(--Icon-fontSize)" />
) : (
<TrendDownIcon fontSize="var(--Icon-fontSize)" />
)}
</Avatar>
</ListItemAvatar>
<ListItemText
disableTypography
primary={<Typography variant="subtitle2">{transaction.description}</Typography>}
secondary={
<Typography color="text.secondary" variant="body2">
{dayjs(transaction.createdAt).format('MM.DD.YYYY / hh:mm A')}
</Typography>
}
/>
<div>
<Typography
color={transaction.type === 'add' ? 'var(--mui-palette-success-main)' : 'var(--mui-palette-error-main)'}
sx={{ textAlign: 'right', whiteSpace: 'nowrap' }}
variant="subtitle2"
>
{transaction.type === 'add' ? '+' : '-'} {transaction.amount} {transaction.currency}
</Typography>
<Typography color="text.secondary" sx={{ textAlign: 'right' }} variant="body2">
{new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(transaction.balance)}
</Typography>
</div>
</ListItem>
))}
</List>
<CardActions>
<Button color="secondary" endIcon={<ArrowRightIcon />} size="small">
See all
</Button>
</CardActions>
</Card>
);
}