Spaces:
Running
Running
import React from "react"; | |
import { ProviderInfo } from "../types/heatmap"; | |
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip"; | |
interface OrganizationCardProps { | |
provider: ProviderInfo; | |
calendarKey: string; | |
providerName: string; | |
totalCount: number; | |
} | |
const OrganizationCard: React.FC<OrganizationCardProps> = ({ | |
provider, | |
calendarKey, | |
providerName, | |
totalCount, | |
}) => { | |
return ( | |
<TooltipProvider> | |
<div className="mb-4"> | |
{/* Organization Name & Stats Badge */} | |
<div className="text-center bg-muted/20 rounded-lg p-3 border border-border/30"> | |
{/* Avatar and Name Row */} | |
<div className="flex items-center justify-center gap-2 mb-2"> | |
{/* Multi-author avatars and names */} | |
{provider.authorsData && provider.authorsData.length > 1 ? ( | |
<div className="flex items-center gap-2 flex-wrap justify-center"> | |
{provider.authorsData.map((authorData, index) => ( | |
<React.Fragment key={authorData.author}> | |
{index > 0 && ( | |
<span className="text-muted-foreground text-sm font-medium mx-1">+</span> | |
)} | |
<div className="flex items-center gap-1"> | |
<div className="relative"> | |
<img | |
src={authorData.avatarUrl || `https://huggingface.co/${authorData.author}/avatar.jpg`} | |
alt={`${authorData.fullName} logo`} | |
className="w-8 h-8 rounded-md shadow-sm border border-border/50" | |
onError={(e) => { | |
const target = e.target as HTMLImageElement; | |
target.src = `https://ui-avatars.com/api/?name=${encodeURIComponent(authorData.fullName)}&background=random`; | |
}} | |
/> | |
{authorData.isVerified && ( | |
<div className="absolute -bottom-0.5 -right-0.5 w-3 h-3 bg-blue-500 rounded-full flex items-center justify-center"> | |
<svg className="w-2 h-2 text-white" fill="currentColor" viewBox="0 0 20 20"> | |
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" /> | |
</svg> | |
</div> | |
)} | |
</div> | |
<span className="text-base font-bold text-foreground"> | |
<a | |
href={`https://huggingface.co/${authorData.author}`} | |
target="_blank" | |
rel="noopener noreferrer" | |
className="hover:text-blue-500 hover:underline transition-colors duration-200" | |
> | |
{authorData.author} | |
</a> | |
</span> | |
</div> | |
</React.Fragment> | |
))} | |
</div> | |
) : ( | |
<div className="flex items-center gap-2"> | |
{provider.avatarUrl && ( | |
<div className="relative"> | |
<img | |
src={provider.avatarUrl} | |
alt={`${providerName} logo`} | |
className="w-8 h-8 rounded-md shadow-sm border border-border/50" | |
/> | |
{provider.isVerified && ( | |
<div className="absolute -bottom-0.5 -right-0.5 w-3 h-3 bg-blue-500 rounded-full flex items-center justify-center"> | |
<svg className="w-2 h-2 text-white" fill="currentColor" viewBox="0 0 20 20"> | |
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" /> | |
</svg> | |
</div> | |
)} | |
</div> | |
)} | |
<h3 className="text-base font-bold text-foreground"> | |
<a | |
href={`https://huggingface.co/${calendarKey}`} | |
target="_blank" | |
rel="noopener noreferrer" | |
className="hover:text-blue-500 hover:underline transition-colors duration-200" | |
> | |
{providerName} | |
</a> | |
</h3> | |
</div> | |
)} | |
</div> | |
{/* Compact Organization Stats */} | |
<div className="flex flex-wrap justify-center gap-2 text-xs mb-3"> | |
{provider.authorsData && provider.authorsData.length > 1 ? ( | |
<Tooltip> | |
<TooltipTrigger asChild> | |
<div className="bg-background/60 rounded px-2 py-1 border border-border/40 cursor-pointer"> | |
<span className="font-semibold text-foreground">{(provider.numModels || 0).toLocaleString()}</span> | |
<span className="text-muted-foreground ml-1">models</span> | |
</div> | |
</TooltipTrigger> | |
<TooltipContent side="bottom"> | |
<div className="text-xs"> | |
{provider.authorsData.map(author => ( | |
<div key={`models-${author.author}`}> | |
{author.author}: {author.numModels.toLocaleString()} | |
</div> | |
))} | |
</div> | |
</TooltipContent> | |
</Tooltip> | |
) : ( | |
<div className="bg-background/60 rounded px-2 py-1 border border-border/40"> | |
<span className="font-semibold text-foreground">{(provider.numModels || 0).toLocaleString()}</span> | |
<span className="text-muted-foreground ml-1">models</span> | |
</div> | |
)} | |
{provider.authorsData && provider.authorsData.length > 1 ? ( | |
<Tooltip> | |
<TooltipTrigger asChild> | |
<div className="bg-background/60 rounded px-2 py-1 border border-border/40 cursor-pointer"> | |
<span className="font-semibold text-foreground">{(provider.numDatasets || 0).toLocaleString()}</span> | |
<span className="text-muted-foreground ml-1">datasets</span> | |
</div> | |
</TooltipTrigger> | |
<TooltipContent side="bottom"> | |
<div className="text-xs"> | |
{provider.authorsData.map(author => ( | |
<div key={`datasets-${author.author}`}> | |
{author.author}: {author.numDatasets.toLocaleString()} | |
</div> | |
))} | |
</div> | |
</TooltipContent> | |
</Tooltip> | |
) : ( | |
<div className="bg-background/60 rounded px-2 py-1 border border-border/40"> | |
<span className="font-semibold text-foreground">{(provider.numDatasets || 0).toLocaleString()}</span> | |
<span className="text-muted-foreground ml-1">datasets</span> | |
</div> | |
)} | |
{provider.authorsData && provider.authorsData.length > 1 ? ( | |
<Tooltip> | |
<TooltipTrigger asChild> | |
<div className="bg-background/60 rounded px-2 py-1 border border-border/40 cursor-pointer"> | |
<span className="font-semibold text-foreground">{(provider.numSpaces || 0).toLocaleString()}</span> | |
<span className="text-muted-foreground ml-1">spaces</span> | |
</div> | |
</TooltipTrigger> | |
<TooltipContent side="bottom"> | |
<div className="text-xs"> | |
{provider.authorsData.map(author => ( | |
<div key={`spaces-${author.author}`}> | |
{author.author}: {author.numSpaces.toLocaleString()} | |
</div> | |
))} | |
</div> | |
</TooltipContent> | |
</Tooltip> | |
) : ( | |
<div className="bg-background/60 rounded px-2 py-1 border border-border/40"> | |
<span className="font-semibold text-foreground">{(provider.numSpaces || 0).toLocaleString()}</span> | |
<span className="text-muted-foreground ml-1">spaces</span> | |
</div> | |
)} | |
{provider.authorsData && provider.authorsData.length > 1 ? ( | |
<Tooltip> | |
<TooltipTrigger asChild> | |
<div className="bg-background/60 rounded px-2 py-1 border border-border/40 cursor-pointer"> | |
<span className="font-semibold text-foreground">{(provider.numFollowers || 0).toLocaleString()}</span> | |
<span className="text-muted-foreground ml-1">followers</span> | |
</div> | |
</TooltipTrigger> | |
<TooltipContent side="bottom"> | |
<div className="text-xs"> | |
{provider.authorsData.map(author => ( | |
<div key={`followers-${author.author}`}> | |
{author.author}: {author.numFollowers.toLocaleString()} | |
</div> | |
))} | |
</div> | |
</TooltipContent> | |
</Tooltip> | |
) : ( | |
<div className="bg-background/60 rounded px-2 py-1 border border-border/40"> | |
<span className="font-semibold text-foreground">{(provider.numFollowers || 0).toLocaleString()}</span> | |
<span className="text-muted-foreground ml-1">followers</span> | |
</div> | |
)} | |
</div> | |
{/* Divider */} | |
<div className="border-t border-border/20 mb-3"></div> | |
{/* Releases Past Year */} | |
<div className="text-xs text-muted-foreground italic"> | |
<span className="font-bold text-foreground">{totalCount.toLocaleString()}</span> in the last year | |
</div> | |
</div> | |
</div> | |
</TooltipProvider> | |
); | |
}; | |
export default OrganizationCard; | |