diff --git a/src/grants/components/grant-stat-item/grantee-stat-item.tsx b/src/grants/components/grant-stat-item/grantee-stat-item.tsx index 79636a26..fab88aec 100644 --- a/src/grants/components/grant-stat-item/grantee-stat-item.tsx +++ b/src/grants/components/grant-stat-item/grantee-stat-item.tsx @@ -1,8 +1,7 @@ -import React from 'react'; - -import { Skeleton } from '@nextui-org/react'; +import { Button, Skeleton } from '@nextui-org/react'; import { cn } from '@/shared/utils/cn'; +import { copyToClipboard } from '@/shared/utils/copy-to-clipboard'; import { GranteeProjectStat } from '@/grants/core/schemas'; @@ -16,13 +15,28 @@ export const GranteeStatItem = ({ granteeStat, level = 1 }: Props) => { const hasChildren = !!stats && stats.length > 0; const hasGap = hasChildren || level === 1; + const text = formatStat(value); + + const onCopy = () => { + copyToClipboard({ text: value, options: { icon: '✅' } }); + }; + return ( {label} - {trimStat(value)} +
+ + {text} + + {text.includes('...') && ( + + )} +
{hasChildren && ( @@ -111,12 +125,40 @@ export const GranteeStatsSkeleton = () => ( ); -export const trimStat = (input: string): string => { - const num = parseFloat(input); +// Regular expression to match a valid number (integer, float, or scientific notation) +const NUM_REGEX = /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/; + +export const formatStat = (input: string): string => { + const trimmed = input.trim(); - if (!isNaN(num)) { + if (NUM_REGEX.test(trimmed)) { + const num = parseFloat(trimmed); return num.toFixed(2); } + if (trimmed.startsWith('0x')) { + return `${trimmed.slice(0, 6)}... ${trimmed.slice(-4)}`; + } + return input; }; + +const CopyIcon = () => ( + +); diff --git a/src/grants/components/toaster.tsx b/src/grants/components/toaster.tsx index b04b201b..7f65ba02 100644 --- a/src/grants/components/toaster.tsx +++ b/src/grants/components/toaster.tsx @@ -9,7 +9,7 @@ export const Toaster = () => { className: 'lg:ml-[248px]', style: { color: 'white', - backgroundColor: 'rgba(255,255,255,0.10)', + backgroundColor: '#2b2b2b', }, success: { icon: '🎉', diff --git a/src/shared/utils/copy-to-clipboard.ts b/src/shared/utils/copy-to-clipboard.ts new file mode 100644 index 00000000..43a42f15 --- /dev/null +++ b/src/shared/utils/copy-to-clipboard.ts @@ -0,0 +1,20 @@ +import toast, { ToastOptions } from 'react-hot-toast'; + +interface Props { + text: string; + message?: string; + options?: ToastOptions; +} + +export const copyToClipboard = ({ + text, + message = 'Copied to clipboard!', + options, +}: Props) => { + if (!navigator.clipboard) { + return; + } + + navigator.clipboard.writeText(text); + toast.success(message, options); +};