SkateSpots/src/components/ProfileBadges.tsx
2026-10-31 09:24:32 +07:00

69 lines
2.1 KiB
TypeScript

import { BadgeCheck, ExternalLink } from 'lucide-react';
import { cn } from '@/lib/utils';
import { SKATESPOTS_SITE, type ProfileBadge } from '@/lib/badges';
import { sanitizeUrl } from '@/lib/sanitizeUrl';
/** The app logo is already served with the site. */
const SKATESPOTS_LOGO = '/images/logo-icon.png';
const isApp = (b: ProfileBadge) => b.label === 'SkateSpots' && b.url === SKATESPOTS_SITE;
/**
* ProfileBadges — the row of badges on a profile (own or someone else's).
* Clicking a badge with a link opens it in a new tab.
*/
export function ProfileBadges({
badges,
size = 'md',
className,
}: {
badges: ProfileBadge[];
size?: 'sm' | 'md';
className?: string;
}) {
if (badges.length === 0) return null;
return (
<div className={cn('flex flex-wrap items-center gap-2', className)}>
{badges.map((b, i) => {
const app = isApp(b);
const safeUrl = b.url ? sanitizeUrl(b.url) : null;
const cls = cn(
'inline-flex items-center gap-1.5 rounded-full border font-bold transition-colors',
size === 'sm' ? 'px-2 py-0.5 text-[11px]' : 'px-3 py-1.5 text-sm',
app
? 'border-[#ccff00]/40 bg-[#ccff00]/10 text-[#ccff00] hover:bg-[#ccff00]/20'
: 'border-pink-500/25 bg-pink-500/10 text-gray-100 hover:bg-pink-500/20',
);
const inner = (
<>
{app ? (
<img src={SKATESPOTS_LOGO} alt="" className="h-4 w-4 rounded-full object-contain" />
) : (
<BadgeCheck className={size === 'sm' ? 'h-3 w-3' : 'h-3.5 w-3.5'} />
)}
<span className="max-w-[12rem] truncate">{b.label}</span>
{safeUrl && !app && <ExternalLink className="h-3 w-3 opacity-60" />}
</>
);
return safeUrl ? (
<a
key={`${b.label}-${i}`}
href={safeUrl}
target="_blank"
rel="noopener noreferrer"
title={safeUrl}
className={cls}
>
{inner}
</a>
) : (
<span key={`${b.label}-${i}`} className={cls}>
{inner}
</span>
);
})}
</div>
);
}