Files
RepoHub/src/components/RecommendationCard.tsx
T
Yusuf İpek a5b65bb4df feat: Add package name formatting for Windows packages and update contribution docs
- Format Windows winget package IDs for better display (e.g., "Microsoft.VisualStudioCode" → "Visual Studio Code")
- Extract last segment from dotted package names and convert CamelCase to spaced format
- Add title attribute to package names for full name on hover
- Update README with optional icon addition step in contribution guide
- Document Simple Icons integration process for new package submissions
- Add package icons
2025-11-23 14:42:14 +03:00

90 lines
3.8 KiB
TypeScript

import { useState } from 'react'
import { Package as PackageIcon } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Card, CardContent } from '@/components/ui/card'
import { RecommendedPackage } from '@/types/recommendations'
import { useLocale } from '@/contexts/LocaleContext'
interface RecommendationCardProps {
pkg: RecommendedPackage
isSelected: boolean
onToggle: (pkg: RecommendedPackage) => void
}
export function RecommendationCard({ pkg, isSelected, onToggle }: RecommendationCardProps) {
const { t } = useLocale()
const [iconError, setIconError] = useState(false)
// Helper to format package name for display
const getDisplayName = (name: string, platformId?: string) => {
if (platformId === 'windows' && name.includes('.')) {
// Handle winget IDs like "Microsoft.VisualStudioCode" or "Git.Git"
const parts = name.split('.')
const appName = parts[parts.length - 1] // Take the last part
// Add spaces to CamelCase (e.g. "VisualStudioCode" -> "Visual Studio Code")
return appName.replace(/([A-Z])/g, ' $1').trim()
}
return name
}
const displayName = getDisplayName(pkg.name, pkg.platform_id)
return (
<Card
className={`relative overflow-hidden transition-all hover:shadow-lg cursor-pointer ${isSelected ? 'ring-2 ring-primary' : ''
}`}
onClick={() => onToggle(pkg)}
>
<CardContent className="p-4">
<div className="flex items-center gap-3 mb-3">
{pkg.icon && !iconError ? (
<>
<div
className="h-8 w-8 flex-shrink-0 bg-foreground"
style={{
maskImage: `url(https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/${pkg.icon}.svg)`,
WebkitMaskImage: `url(https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/${pkg.icon}.svg)`,
maskRepeat: 'no-repeat',
WebkitMaskRepeat: 'no-repeat',
maskSize: 'contain',
WebkitMaskSize: 'contain',
maskPosition: 'center',
WebkitMaskPosition: 'center'
}}
/>
{/* Hidden image to detect load errors */}
<img
src={`https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/${pkg.icon}.svg`}
alt=""
className="hidden"
onError={() => setIconError(true)}
/>
</>
) : (
<PackageIcon className="h-8 w-8 text-foreground flex-shrink-0" />
)}
<div className="flex-1 min-w-0">
<h3 className="font-semibold truncate" title={displayName}>{displayName}</h3>
<p className="text-xs text-muted-foreground">{pkg.version}</p>
</div>
</div>
<Button
variant={isSelected ? 'default' : 'outline'}
size="sm"
className="w-full mt-3"
onClick={(e) => {
e.stopPropagation()
onToggle(pkg)
}}
>
{isSelected ? '✓ Selected' : t('recommendations.add_to_selection')}
</Button>
</CardContent>
</Card>
)
}