|
import React from "react"; |
|
import { ProviderInfo } from "../types/heatmap"; |
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./ui/tooltip"; |
|
|
|
interface OrganizationHeaderProps { |
|
provider: ProviderInfo; |
|
calendarKey: string; |
|
providerName: string; |
|
totalCount: number; |
|
} |
|
|
|
const OrganizationHeader: React.FC<OrganizationHeaderProps> = ({ |
|
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> |
|
|
|
</div> |
|
</div> |
|
</TooltipProvider> |
|
); |
|
}; |
|
|
|
export default OrganizationHeader; |