Personal Website for @jaspermayone.com jaspermayone.com
at main 148 lines 4.4 kB view raw
1"use client"; 2 3import { Star, GitFork, ArrowUpRight } from "lucide-react"; 4import { ExternalLink } from "@/components/ExternalLink"; 5import { RepoStats } from "@/lib/types"; 6import { SiGithub } from "react-icons/si"; 7 8// GitHub linguist language colors 9const languageColors: Record<string, string> = { 10 TypeScript: "#3178c6", 11 JavaScript: "#f1e05a", 12 Python: "#3572A5", 13 Rust: "#dea584", 14 Go: "#00ADD8", 15 Ruby: "#701516", 16 Java: "#b07219", 17 "C++": "#f34b7d", 18 C: "#555555", 19 "C#": "#178600", 20 PHP: "#4F5D95", 21 Swift: "#F05138", 22 Kotlin: "#A97BFF", 23 HTML: "#e34c26", 24 CSS: "#663399", 25 Shell: "#89e051", 26 Lua: "#000080", 27 Dart: "#00B4AB", 28 Scala: "#c22d40", 29 Elixir: "#6e4a7e", 30 Haskell: "#5e5086", 31 Vue: "#41b883", 32 SCSS: "#c6538c", 33 Dockerfile: "#384d54", 34 Makefile: "#427819", 35}; 36 37function getLanguageColor(language: string | null): string { 38 if (!language) return "#56ba8e"; // Site green fallback 39 return languageColors[language] || "#56ba8e"; // Site green fallback 40} 41 42interface OpenSourceCardProps { 43 name: string; 44 description: string; 45 repo: string; 46 stats?: RepoStats; 47 loading?: boolean; 48 featured?: boolean; 49 status?: "active" | "beta" | "deprecated" | "alpha"; 50 liveUrl?: string; 51} 52 53export function OpenSourceCard({ 54 name, 55 description, 56 repo, 57 stats, 58 loading, 59 featured, 60 status, 61 liveUrl, 62}: OpenSourceCardProps) { 63 const repoUrl = `https://github.com/${repo}`; 64 65 return ( 66 <div 67 className={`rounded-lg border p-4 transition-all hover:shadow-md ${ 68 featured 69 ? "border-blue-200 bg-blue-50/50 dark:border-blue-800 dark:bg-blue-900/10" 70 : "border-gray-200 bg-white dark:border-gray-700 dark:bg-slate-800" 71 }`} 72 > 73 <div className="mb-2 flex items-start justify-between"> 74 <div className="flex items-center gap-2"> 75 <h3 className="font-semibold text-gray-900 dark:text-white"> 76 {name} 77 </h3> 78 {status === "alpha" && ( 79 <span className="rounded-full bg-purple-100 px-2 py-0.5 text-xs text-purple-800 dark:bg-purple-900/30 dark:text-purple-300"> 80 Alpha 81 </span> 82 )} 83 {status === "beta" && ( 84 <span className="rounded-full bg-yellow-100 px-2 py-0.5 text-xs text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300"> 85 Beta 86 </span> 87 )} 88 {status === "deprecated" && ( 89 <span className="rounded-full bg-red-100 px-2 py-0.5 text-xs text-red-800 dark:bg-red-900/30 dark:text-red-300"> 90 Deprecated 91 </span> 92 )} 93 </div> 94 <div className="flex gap-2"> 95 {liveUrl && ( 96 <ExternalLink 97 href={liveUrl} 98 className="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400" 99 aria-label="Visit live site" 100 > 101 <ArrowUpRight className="h-4 w-4" /> 102 </ExternalLink> 103 )} 104 <ExternalLink 105 href={repoUrl} 106 className="text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white" 107 aria-label="View repository" 108 > 109 <SiGithub className="h-4 w-4" /> 110 </ExternalLink> 111 </div> 112 </div> 113 114 <p className="mb-3 text-sm text-gray-600 dark:text-white/70"> 115 {description} 116 </p> 117 118 {/* Stats row */} 119 <div className="flex flex-wrap items-center gap-3 text-xs text-gray-500 dark:text-gray-400"> 120 {loading ? ( 121 <span className="animate-pulse">Loading stats...</span> 122 ) : stats && !stats.error ? ( 123 <> 124 {stats.language && ( 125 <span className="flex items-center gap-1"> 126 <span 127 className="h-2 w-2 rounded-full" 128 style={{ backgroundColor: getLanguageColor(stats.language) }} 129 /> 130 {stats.language} 131 </span> 132 )} 133 <span className="flex items-center gap-1"> 134 <Star className="h-3 w-3" /> 135 {stats.stars} 136 </span> 137 {stats.forks > 0 && ( 138 <span className="flex items-center gap-1"> 139 <GitFork className="h-3 w-3" /> 140 {stats.forks} 141 </span> 142 )} 143 </> 144 ) : null} 145 </div> 146 </div> 147 ); 148}