Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/components/common/CategoryTag.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { cn } from '@/lib/utils';

interface CategoryTagProps {
label?: string;
className?: string;
}

const CategoryTag: React.FC<CategoryTagProps> = ({
label = 'Uncategorized',
className,
}) => {
return (
<span
className={cn(
'inline-flex max-w-full items-center rounded-md border border-white/15 bg-white/10 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-[0.12em] text-white/80',
className
)}
title={label}
>
<span className="truncate">{label}</span>
</span>
);
};

export default CategoryTag;
33 changes: 33 additions & 0 deletions src/components/common/CompactEmptyWalletState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { WalletMinimal } from 'lucide-react';
import { cn } from '@/lib/utils';

interface CompactEmptyWalletStateProps {
title?: string;
description?: string;
className?: string;
}

const CompactEmptyWalletState: React.FC<CompactEmptyWalletStateProps> = ({
title = 'No wallet balance yet',
description = 'Connect and fund your wallet to unlock creator key purchases.',
className,
}) => {
return (
<div
className={cn(
'flex items-start gap-3 rounded-xl border border-amber-300/25 bg-amber-400/8 p-3 text-left',
className
)}
>
<div className="mt-0.5 rounded-lg bg-amber-300/20 p-1.5">
<WalletMinimal className="size-4 text-amber-200" />
</div>
<div>
<p className="text-sm font-semibold text-amber-100">{title}</p>
<p className="mt-0.5 text-xs text-amber-100/75">{description}</p>
</div>
</div>
);
};

export default CompactEmptyWalletState;
27 changes: 21 additions & 6 deletions src/components/common/CreatorCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { ShoppingCart, Wallet, Link as LinkIcon } from 'lucide-react';
import toast from 'react-hot-toast';
import showToast from '@/utils/toast.util';
import TransactionRetryNotice from '@/components/common/TransactionRetryNotice';
import VerifiedBadge from '@/components/common/VerifiedBadge';
import CategoryTag from '@/components/common/CategoryTag';
import CompactEmptyWalletState from '@/components/common/CompactEmptyWalletState';

interface CreatorCardProps {
creator: Course;
Expand Down Expand Up @@ -71,12 +74,21 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
</div>

<div className="mb-4">
<h3 className="font-jakarta text-lg font-bold text-white">
{creator.title}
</h3>
<div className="flex items-center gap-2">
<h3 className="font-jakarta text-lg font-bold text-white">
{creator.title}
</h3>
<VerifiedBadge
verified={Boolean(creator.isVerified)}
reserveSpace={true}
/>
</div>
<p className="font-jakarta text-sm text-white/50">
@{creator.instructorId || 'creator'}
</p>
<div className="mt-2">
<CategoryTag label={creator.category} />
</div>
{creator.socialHandle ? (
<div className="mt-2 flex items-center gap-1.5 text-xs text-white/60">
<LinkIcon className="size-3 text-amber-500/70" />
Expand Down Expand Up @@ -116,9 +128,12 @@ const CreatorCard: React.FC<CreatorCardProps> = ({ creator, className }) => {
</div>

{!isConnected && (
<div className="mt-3 flex items-center gap-2 text-[10px] font-medium uppercase tracking-widest text-amber-500/70">
<Wallet className="size-3" />
Wallet Required
<div className="mt-4 space-y-3">
<div className="flex items-center gap-2 text-[10px] font-medium uppercase tracking-widest text-amber-500/70">
<Wallet className="size-3" />
Wallet Required
</div>
<CompactEmptyWalletState />
</div>
)}

Expand Down
27 changes: 27 additions & 0 deletions src/components/common/InlineValidationMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { AlertCircle } from 'lucide-react';
import { cn } from '@/lib/utils';

interface InlineValidationMessageProps {
message: string;
className?: string;
}

const InlineValidationMessage: React.FC<InlineValidationMessageProps> = ({
message,
className,
}) => {
return (
<p
className={cn(
'mt-2 inline-flex items-start gap-1.5 text-xs font-medium text-amber-200/90',
className
)}
role="status"
>
<AlertCircle className="mt-0.5 size-3.5 shrink-0 text-amber-300" />
<span>{message}</span>
</p>
);
};

export default InlineValidationMessage;
32 changes: 22 additions & 10 deletions src/components/common/SearchBar.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import { Search } from 'lucide-react';
import { cn } from '@/lib/utils';
import InlineValidationMessage from '@/components/common/InlineValidationMessage';

interface SearchBarProps {
value: string;
onChange: (value: string) => void;
placeholder?: string;
className?: string;
validationMessage?: string;
}

const SearchBar: React.FC<SearchBarProps> = ({
value,
onChange,
placeholder = 'Search creators by name or handle...',
className,
validationMessage,
}) => {
return (
<div className={cn('relative w-full max-w-md', className)}>
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<Search className="size-5 text-white/50" aria-hidden="true" />
<div className={cn('w-full max-w-md', className)}>
<div className="relative">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<Search className="size-5 text-white/50" aria-hidden="true" />
</div>
<input
type="text"
className={cn(
'block w-full rounded-xl border border-white/10 bg-white/5 py-3 pl-10 pr-3 text-sm text-white placeholder:text-white/40 focus:border-amber-500/50 focus:bg-white/10 focus:outline-none focus:ring-2 focus:ring-amber-500/20',
validationMessage &&
'border-amber-400/45 focus:border-amber-400/65 focus:ring-amber-300/20'
)}
placeholder={placeholder}
value={value}
onChange={e => onChange(e.target.value)}
/>
</div>
<input
type="text"
className="block w-full rounded-xl border border-white/10 bg-white/5 py-3 pl-10 pr-3 text-sm text-white placeholder:text-white/40 focus:border-amber-500/50 focus:bg-white/10 focus:outline-none focus:ring-2 focus:ring-amber-500/20"
placeholder={placeholder}
value={value}
onChange={e => onChange(e.target.value)}
/>
{validationMessage && (
<InlineValidationMessage message={validationMessage} />
)}
</div>
);
};
Expand Down
33 changes: 33 additions & 0 deletions src/components/common/VerifiedBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { BadgeCheck } from 'lucide-react';
import { cn } from '@/lib/utils';

interface VerifiedBadgeProps {
verified?: boolean;
reserveSpace?: boolean;
className?: string;
}

const VerifiedBadge: React.FC<VerifiedBadgeProps> = ({
verified = false,
reserveSpace = false,
className,
}) => {
if (!verified && !reserveSpace) {
return null;
}

return (
<span
className={cn(
'inline-flex items-center gap-1 rounded-full border border-emerald-300/35 bg-emerald-400/12 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-[0.12em] text-emerald-200',
!verified && 'invisible',
className
)}
>
<BadgeCheck className="size-3" />
Verified
</span>
);
};

export default VerifiedBadge;
Loading
Loading