From 8caeaa89c13bdadf5d238536ea5d14958ec83089 Mon Sep 17 00:00:00 2001 From: Dopey Date: Thu, 26 Mar 2026 02:05:58 +0100 Subject: [PATCH 1/3] feat(common): add FollowerCountPill component for creator cards Resolves #21 --- src/components/common/FollowerCountPill.tsx | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/components/common/FollowerCountPill.tsx diff --git a/src/components/common/FollowerCountPill.tsx b/src/components/common/FollowerCountPill.tsx new file mode 100644 index 00000000..23ea379d --- /dev/null +++ b/src/components/common/FollowerCountPill.tsx @@ -0,0 +1,37 @@ +import { Users } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface FollowerCountPillProps { + count?: number | null; + className?: string; +} + +function formatCount(count: number): string { + if (count >= 1_000_000) { + return `${(count / 1_000_000).toFixed(1).replace(/\.0$/, '')}M`; + } + if (count >= 1_000) { + return `${(count / 1_000).toFixed(1).replace(/\.0$/, '')}K`; + } + return count.toString(); +} + +const FollowerCountPill: React.FC = ({ count, className }) => { + if (count == null || count < 0) { + return null; + } + + return ( + + + {formatCount(count)} + + ); +}; + +export default FollowerCountPill; From 2df48901a107637945083719a65e2bd14191d4d1 Mon Sep 17 00:00:00 2001 From: Dopey Date: Thu, 26 Mar 2026 02:06:12 +0100 Subject: [PATCH 2/3] feat(common): add BackHeader component for detail pages Resolves #22 --- src/components/common/BackHeader.tsx | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/components/common/BackHeader.tsx diff --git a/src/components/common/BackHeader.tsx b/src/components/common/BackHeader.tsx new file mode 100644 index 00000000..018bdb82 --- /dev/null +++ b/src/components/common/BackHeader.tsx @@ -0,0 +1,44 @@ +import { useNavigate } from 'react-router-dom'; +import { ArrowLeft } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface BackHeaderProps { + title: string; + subtitle?: string; + onBack?: () => void; + className?: string; +} + +const BackHeader: React.FC = ({ title, subtitle, onBack, className }) => { + const navigate = useNavigate(); + + const handleBack = () => { + if (onBack) { + onBack(); + } else { + navigate(-1); + } + }; + + return ( +
+ +
+

+ {title} +

+ {subtitle && ( +

{subtitle}

+ )} +
+
+ ); +}; + +export default BackHeader; From c1756ad712ae467540696e4109fc57bc94d09977 Mon Sep 17 00:00:00 2001 From: Dopey Date: Thu, 26 Mar 2026 02:06:23 +0100 Subject: [PATCH 3/3] feat(common): add TruncatedAddress component for wallet addresses Resolves #23 --- src/components/common/BackHeader.tsx | 2 +- src/components/common/TruncatedAddress.tsx | 57 ++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 src/components/common/TruncatedAddress.tsx diff --git a/src/components/common/BackHeader.tsx b/src/components/common/BackHeader.tsx index 018bdb82..cec071b4 100644 --- a/src/components/common/BackHeader.tsx +++ b/src/components/common/BackHeader.tsx @@ -1,4 +1,4 @@ -import { useNavigate } from 'react-router-dom'; +import { useNavigate } from 'react-router'; import { ArrowLeft } from 'lucide-react'; import { cn } from '@/lib/utils'; diff --git a/src/components/common/TruncatedAddress.tsx b/src/components/common/TruncatedAddress.tsx new file mode 100644 index 00000000..91bb4ab4 --- /dev/null +++ b/src/components/common/TruncatedAddress.tsx @@ -0,0 +1,57 @@ +import { useState } from 'react'; +import { Copy, Check } from 'lucide-react'; +import { cn } from '@/lib/utils'; + +interface TruncatedAddressProps { + address: string; + prefixChars?: number; + suffixChars?: number; + copyable?: boolean; + className?: string; +} + +function truncate(address: string, prefix: number, suffix: number): string { + if (address.length <= prefix + suffix + 3) { + return address; + } + return `${address.slice(0, prefix)}...${address.slice(-suffix)}`; +} + +const TruncatedAddress: React.FC = ({ + address, + prefixChars = 6, + suffixChars = 4, + copyable = false, + className, +}) => { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(address); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( + + {truncate(address, prefixChars, suffixChars)} + {copyable && ( + + )} + + ); +}; + +export default TruncatedAddress;