Skip to content

Commit 8afce2e

Browse files
authored
Merge branch 'main' into fix/issues-260-261-262-263
2 parents e1d5d22 + 65f2d22 commit 8afce2e

28 files changed

Lines changed: 975 additions & 256 deletions

src/components/common/CompactEmptyWalletState.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { WalletMinimal } from 'lucide-react';
22
import { cn } from '@/lib/utils';
3+
import { EMPTY_STATE_ILLUSTRATION_SIZES } from './emptyStateIllustration.config';
34

45
interface CompactEmptyWalletStateProps {
56
title?: string;
@@ -19,8 +20,18 @@ const CompactEmptyWalletState: React.FC<CompactEmptyWalletStateProps> = ({
1920
className
2021
)}
2122
>
22-
<div className="mt-0.5 rounded-lg bg-amber-300/20 p-1.5">
23-
<WalletMinimal className="size-4 text-amber-200" />
23+
<div
24+
className={cn(
25+
'mt-0.5 flex items-center justify-center rounded-lg bg-amber-300/20',
26+
EMPTY_STATE_ILLUSTRATION_SIZES.compactBadgeFrame
27+
)}
28+
>
29+
<WalletMinimal
30+
className={cn(
31+
'text-amber-200',
32+
EMPTY_STATE_ILLUSTRATION_SIZES.compactBadgeIcon
33+
)}
34+
/>
2435
</div>
2536
<div>
2637
<p className="text-sm font-semibold text-amber-100">{title}</p>

src/components/common/CreatorBio.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ interface CreatorBioProps {
77
fallback?: string;
88
/** Variant — `card` is muted/italic for list rows, `profile` is slightly more prominent for the detail header. */
99
variant?: 'card' | 'profile';
10+
/** If true, returns null instead of a fallback when bio is missing. */
11+
allowEmpty?: boolean;
1012
className?: string;
1113
}
1214

@@ -33,12 +35,17 @@ const CreatorBio: React.FC<CreatorBioProps> = ({
3335
bio,
3436
fallback = DEFAULT_FALLBACK,
3537
variant = 'card',
38+
allowEmpty = false,
3639
className,
3740
}) => {
3841
const trimmed = bio?.trim();
3942
const styles = variantClasses[variant];
4043

4144
if (!trimmed) {
45+
if (allowEmpty) {
46+
return null;
47+
}
48+
4249
return (
4350
<p className={cn(styles.fallback, className)} aria-label="Bio not provided">
4451
{fallback}

src/components/common/CreatorCard.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ interface CreatorCardProps {
3232
className?: string;
3333
}
3434

35+
const creatorBadgeRowClass = 'mt-2 flex items-center gap-1.5';
36+
3537
const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
3638
const { isConnected } = useAccount();
3739
const { isMismatch: isNetworkMismatch, expectedChainName } = useNetworkMismatch();
@@ -116,7 +118,11 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
116118
className
117119
)}
118120
>
119-
<div className="relative mb-4 aspect-square overflow-hidden rounded-xl">
121+
<div
122+
className="relative mb-4 aspect-square overflow-hidden rounded-xl"
123+
role="img"
124+
aria-labelledby={`creator-name-${creator.id}`}
125+
>
120126
<CreatorInitialsAvatar
121127
name={creator.title}
122128
creatorId={creator.id}
@@ -126,7 +132,7 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
126132
<div className="absolute inset-0 bg-gradient-to-t from-slate-950/80 via-transparent to-transparent opacity-0 transition-opacity duration-300 md:group-hover:opacity-100" />
127133
{creator.volume24h !== undefined && (
128134
<div className="absolute right-3 top-3 z-10 flex items-center gap-1.5 rounded-full bg-slate-950/75 border border-white/10 px-2.5 py-1 backdrop-blur-md">
129-
<TrendingUp className="size-3 text-emerald-400" />
135+
<TrendingUp className="creator-action-icon text-emerald-400" />
130136
<span className="text-xs font-bold text-white/90">
131137
{creator.volume24h > 0
132138
? `${formatCompactNumber(creator.volume24h)} ETH`
@@ -138,7 +144,10 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
138144

139145
<div className="mb-4">
140146
<div className="flex items-center gap-2 flex-wrap">
141-
<h3 className="font-jakarta text-lg font-bold text-white">
147+
<h3
148+
id={`creator-name-${creator.id}`}
149+
className="font-jakarta text-lg font-bold text-white"
150+
>
142151
{creator.title}
143152
</h3>
144153
<VerifiedBadge
@@ -156,12 +165,17 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
156165

157166
{creator.socialHandle ? (
158167
<div className="marketplace-label-muted mt-2 flex items-center gap-1.5 text-xs">
159-
<LinkIcon className="size-3 text-amber-500/70" />
168+
<LinkIcon className="creator-action-icon text-amber-500/70" />
160169
<span className="truncate">@{creator.socialHandle}</span>
161170
</div>
162171
) : (
163-
<div className="mt-2 flex items-center gap-1.5 text-xs text-white/30 italic">
164-
<LinkIcon className="size-3 opacity-50" />
172+
<div
173+
className={cn(
174+
creatorBadgeRowClass,
175+
'text-xs text-white/30 italic'
176+
)}
177+
>
178+
<LinkIcon className="creator-action-icon opacity-50" />
165179
<span>No public handle</span>
166180
</div>
167181
)}
@@ -184,7 +198,7 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
184198
<CardMetaRow
185199
label={
186200
<span className="inline-flex items-center gap-1.5">
187-
<LinkIcon className="size-3 text-amber-500/70" />
201+
<LinkIcon className="creator-action-icon text-amber-500/70" />
188202
Handle
189203
</span>
190204
}
@@ -242,7 +256,7 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
242256
{transactionState === 'failed' && (
243257
<TransactionStatusIcon status="failed" />
244258
)}
245-
<ShoppingCart className="size-4" />
259+
<ShoppingCart className="creator-action-icon" />
246260
{transactionState === 'submitting'
247261
? 'Processing...'
248262
: transactionState === 'success'

src/components/common/CreatorProfileHeader.tsx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ interface CreatorProfileHeaderProps {
1717
className?: string;
1818
}
1919

20+
const CREATOR_PROFILE_SUBTITLE_WRAP_CLASS_NAME =
21+
'max-w-full whitespace-normal break-words [overflow-wrap:anywhere]';
22+
2023
const CreatorProfileHeader: React.FC<CreatorProfileHeaderProps> = ({
2124
name,
2225
handle,
@@ -67,17 +70,35 @@ const CreatorProfileHeader: React.FC<CreatorProfileHeaderProps> = ({
6770
)}
6871
>
6972
<div className="flex flex-col gap-4 md:flex-row md:items-center md:gap-6">
70-
<div className="size-24 overflow-hidden rounded-2xl border-4 border-white/10 shadow-xl md:size-32">
73+
<div
74+
className="size-24 overflow-hidden rounded-2xl border-4 border-white/10 shadow-xl md:size-32"
75+
role="img"
76+
aria-labelledby="creator-profile-name"
77+
>
7178
<CreatorInitialsAvatar name={name} creatorId={creatorId} imageSrc={avatarUrl} />
7279
</div>
7380
<div className="min-w-0 space-y-1">
7481
<div className="flex items-center gap-2 overflow-hidden">
75-
<h1 className="truncate font-grotesque text-3xl font-black tracking-tight text-white md:text-4xl">
82+
<h1
83+
id="creator-profile-name"
84+
className="truncate font-grotesque text-3xl font-black tracking-tight text-white md:text-4xl"
85+
>
7686
{name}
7787
</h1>
78-
{isVerified && <div className="shrink-0"><VerifiedBadge verified={true} /></div>}
88+
{isVerified && (
89+
<div className="shrink-0">
90+
<VerifiedBadge verified={true} />
91+
</div>
92+
)}
7993
</div>
80-
<p className="truncate font-jakarta text-lg text-white/50">@{handle}</p>
94+
<p
95+
className={cn(
96+
'font-jakarta text-lg text-white/50',
97+
CREATOR_PROFILE_SUBTITLE_WRAP_CLASS_NAME
98+
)}
99+
>
100+
@{handle}
101+
</p>
81102
<CreatorBio bio={bio} variant="profile" className="mt-2 max-w-md" />
82103
</div>
83104
</div>

src/components/common/CreatorProfileInfoGrid.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import CreatorProfileStatItem from './CreatorProfileStatItem';
55
interface CreatorProfileInfoItem {
66
label: string;
77
value: ReactNode;
8+
helperText?: ReactNode;
89
}
910

1011
interface CreatorProfileInfoGridProps {
@@ -28,6 +29,7 @@ const CreatorProfileInfoGrid: React.FC<CreatorProfileInfoGridProps> = ({
2829
key={item.label}
2930
label={item.label}
3031
value={item.value}
32+
helperText={item.helperText}
3133
/>
3234
))}
3335
</div>

src/components/common/CreatorProfileStatItem.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,41 @@ import { cn } from '@/lib/utils';
44
interface CreatorProfileStatItemProps {
55
label: string;
66
value: ReactNode;
7+
helperText?: ReactNode;
78
className?: string;
89
}
910

1011
const CreatorProfileStatItem: React.FC<CreatorProfileStatItemProps> = ({
1112
label,
1213
value,
14+
helperText,
1315
className,
1416
}) => {
17+
const accessibleLabel = typeof value === 'string' ? `${label}: ${value}` : undefined;
18+
1519
return (
1620
<div
1721
className={cn(
1822
'group relative overflow-hidden rounded-2xl border border-white/10 bg-white/[0.03] px-4 py-4 backdrop-blur-md transition-all duration-300 hover:border-amber-500/30 hover:bg-white/[0.06] hover:shadow-[0_8px_30px_rgb(0,0,0,0.12)]',
1923
className
2024
)}
25+
aria-label={accessibleLabel}
2126
>
2227
<div className="absolute inset-0 bg-gradient-to-br from-white/[0.02] to-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
23-
<p className="relative z-10 text-[0.65rem] font-bold uppercase tracking-[0.22em] text-white/40 transition-colors duration-300 group-hover:text-amber-200/50">
28+
<p
29+
className="relative z-10 text-[0.65rem] font-bold uppercase tracking-[0.22em] text-white/40 transition-colors duration-300 group-hover:text-amber-200/50"
30+
aria-hidden={accessibleLabel ? 'true' : 'false'}
31+
>
2432
{label}
2533
</p>
2634
<div className="relative z-10 mt-2.5 min-w-[4rem] font-jakarta text-base font-bold text-white md:text-[1.05rem]">
2735
{value}
2836
</div>
37+
{helperText && (
38+
<p className="relative z-10 mt-1.5 text-xs text-white/45">
39+
{helperText}
40+
</p>
41+
)}
2942
</div>
3043
);
3144
};

src/components/common/CreatorSocialLinksList.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ interface CreatorSocialLinksListProps {
1212
handle?: string;
1313
/** Override the default empty-state placeholder copy. */
1414
emptyPlaceholder?: string;
15+
/** If true, returns null instead of a placeholder when handle is missing. */
16+
allowEmpty?: boolean;
1517
className?: string;
1618
}
1719

@@ -27,9 +29,14 @@ interface SocialLinkItem {
2729
const CreatorSocialLinksList: React.FC<CreatorSocialLinksListProps> = ({
2830
handle,
2931
emptyPlaceholder = DEFAULT_EMPTY_PLACEHOLDER,
32+
allowEmpty = false,
3033
className,
3134
}) => {
3235
if (!handle?.trim()) {
36+
if (allowEmpty) {
37+
return null;
38+
}
39+
3340
return (
3441
<div
3542
role="note"
@@ -80,11 +87,11 @@ const CreatorSocialLinksList: React.FC<CreatorSocialLinksListProps> = ({
8087
href={url}
8188
target="_blank"
8289
rel="noreferrer"
83-
className="link-action-chip group inline-flex w-full items-center justify-center gap-1.5 rounded-lg border border-white/10 bg-white/[0.03] px-2.5 py-2 text-[11px] font-semibold text-white/80 transition-colors hover:border-amber-400/40 hover:bg-amber-400/10 hover:text-amber-100"
90+
className="link-action-chip group inline-flex w-full items-center justify-center gap-1.5 rounded-lg border border-white/10 bg-white/[0.03] px-2.5 py-2 text-[11px] font-semibold text-white/80 transition-all hover:border-amber-400/40 hover:bg-amber-400/10 hover:text-amber-100 focus-visible:border-amber-400/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-400/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background"
8491
>
8592
<Icon className="size-3.5" />
8693
<span>{label}</span>
87-
<LinkIcon className="size-3 opacity-0 transition-opacity group-hover:opacity-80" />
94+
<LinkIcon className="size-3 opacity-0 transition-opacity group-hover:opacity-80 group-focus-visible:opacity-80" />
8895
</a>
8996
</li>
9097
))}

src/components/common/EmptyState.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { cn } from '@/lib/utils';
22
import { Button } from '@/components/ui/button';
33
import { RotateCcw } from 'lucide-react';
4+
import { EMPTY_STATE_ILLUSTRATION_SIZES } from './emptyStateIllustration.config';
45

56
interface EmptyStateProps {
67
image: string;
@@ -26,11 +27,19 @@ const EmptyState: React.FC<EmptyStateProps> = ({
2627
role="status"
2728
aria-label={title}
2829
>
29-
<div className="relative mb-6">
30+
<div
31+
className={cn(
32+
'relative mb-6 flex items-center justify-center',
33+
EMPTY_STATE_ILLUSTRATION_SIZES.heroFrame
34+
)}
35+
>
3036
<div className="absolute inset-0 size-full rounded-full bg-amber-500/10 blur-2xl" />
3137
<img
3238
src={image}
33-
className="relative z-10 size-[180px] object-contain opacity-60 grayscale transition-all duration-500 hover:opacity-80 hover:grayscale-0"
39+
className={cn(
40+
'relative z-10 opacity-60 grayscale transition-all duration-500 hover:opacity-80 hover:grayscale-0',
41+
EMPTY_STATE_ILLUSTRATION_SIZES.heroVisual
42+
)}
3443
alt=""
3544
aria-hidden="true"
3645
/>

0 commit comments

Comments
 (0)