refactor(links,tech-stack): pure fontawesome svg icons and streamline links directory

This commit is contained in:
ssbg04 committed 2026-10-18 10:48:32 +08:00
1 parent 585b76137e
commit 19b4f11fc9
2 files changed
+56 -171

No files matched your search

+55 -113
View File
@@ -6,6 +6,7 @@ import {
faPhp,
faPython,
faJs,
faSquareJs,
faHtml5,
faCss3Alt,
faTailwindCss,
@@ -23,7 +24,8 @@ import {
faRust,
faGolang,
faFigma,
faWordpress
faWordpress,
faGoogle
} from '@fortawesome/free-brands-svg-icons';
import {
faDatabase,
@@ -34,7 +36,9 @@ import {
faCode,
faCloud,
faServer,
faNetworkWired
faNetworkWired,
faRocket,
faCubes
} from '@fortawesome/free-solid-svg-icons';
interface TechIconProps {
@@ -47,44 +51,33 @@ export default function TechIcon({ name, icon, className = 'w-5 h-5' }: TechIcon
// Combine both name and Sanity icon string (e.g. "React" + "fa-brands fa-react")
const n = `${name} ${icon || ''}`.toLowerCase().trim();
// 1. Tailwind CSS (Official FontAwesome v7 brand icon)
// 1. Tailwind CSS
if (n.includes('tailwind')) {
return <FontAwesomeIcon icon={faTailwindCss} className={className} />;
}
// 2. TypeScript (Specific check before JavaScript)
// 2. TypeScript & JavaScript
if (n.includes('typescript') || n === 'ts') {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M2 3h20v18H2V3zm11.5 8.5h-2v6h2v-6zm-.5-1.5a1.25 1.25 0 100-2.5 1.25 1.25 0 000 2.5zm7 5.5c-.3-.2-.8-.4-1.4-.4-.7 0-1.1.3-1.1.7 0 .4.4.6 1.2.9 1.3.5 2 1.1 2 2.2 0 1.4-1.1 2.3-2.8 2.3-.9 0-1.7-.2-2.3-.6l.5-1.5c.5.3 1.1.5 1.8.5.8 0 1.2-.3 1.2-.7 0-.4-.4-.6-1.3-.9-1.2-.5-1.9-1.1-1.9-2.1 0-1.4 1.1-2.2 2.6-2.2.8 0 1.5.2 2 .5l-.5 1.6z" />
</svg>
);
return <FontAwesomeIcon icon={faSquareJs} className={className} />;
}
if (n.includes('js') || n.includes('javascript')) {
return <FontAwesomeIcon icon={faJs} className={className} />;
}
// 3. React Native (Specific check before React)
// 3. React Native & React
if (n.includes('react native')) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
<rect x="5" y="2" width="14" height="20" rx="3" />
<ellipse cx="12" cy="12" rx="4.5" ry="2" />
<ellipse cx="12" cy="12" rx="4.5" ry="2" transform="rotate(60 12 12)" />
<ellipse cx="12" cy="12" rx="4.5" ry="2" transform="rotate(120 12 12)" />
<circle cx="12" cy="12" r="1" fill="currentColor" />
</svg>
);
return <FontAwesomeIcon icon={faMobileScreenButton} className={className} />;
}
// 4. React (FontAwesome brand icon)
if (n.includes('react')) {
return <FontAwesomeIcon icon={faReact} className={className} />;
}
// 5. Node.js (FontAwesome brand icon)
// 4. Node.js
if (n.includes('node')) {
return <FontAwesomeIcon icon={faNodeJs} className={className} />;
}
// 6. PHP & Laravel (FontAwesome brand icons)
// 5. PHP & Laravel
if (n.includes('laravel')) {
return <FontAwesomeIcon icon={faLaravel} className={className} />;
}
@@ -92,17 +85,17 @@ export default function TechIcon({ name, icon, className = 'w-5 h-5' }: TechIcon
return <FontAwesomeIcon icon={faPhp} className={className} />;
}
// 7. Python (FontAwesome brand icon)
// 6. Python
if (n.includes('python')) {
return <FontAwesomeIcon icon={faPython} className={className} />;
}
// 8. Docker (FontAwesome brand icon)
// 7. Docker
if (n.includes('docker')) {
return <FontAwesomeIcon icon={faDocker} className={className} />;
}
// 9. Git / GitHub (FontAwesome brand icons)
// 8. Git & GitHub
if (n.includes('github')) {
return <FontAwesomeIcon icon={faGithub} className={className} />;
}
@@ -110,17 +103,17 @@ export default function TechIcon({ name, icon, className = 'w-5 h-5' }: TechIcon
return <FontAwesomeIcon icon={faGitAlt} className={className} />;
}
// 10. Linux (FontAwesome brand icon)
// 9. Linux
if (n.includes('linux')) {
return <FontAwesomeIcon icon={faLinux} className={className} />;
}
// 11. Java (FontAwesome brand icon)
// 10. Java
if (n.includes('java')) {
return <FontAwesomeIcon icon={faJava} className={className} />;
}
// 12. Rust & Go (FontAwesome brand icons)
// 11. Rust & Go
if (n.includes('rust')) {
return <FontAwesomeIcon icon={faRust} className={className} />;
}
@@ -128,7 +121,7 @@ export default function TechIcon({ name, icon, className = 'w-5 h-5' }: TechIcon
return <FontAwesomeIcon icon={faGolang} className={className} />;
}
// 13. Figma & WordPress (FontAwesome brand icons)
// 12. Figma & WordPress
if (n.includes('figma')) {
return <FontAwesomeIcon icon={faFigma} className={className} />;
}
@@ -136,7 +129,7 @@ export default function TechIcon({ name, icon, className = 'w-5 h-5' }: TechIcon
return <FontAwesomeIcon icon={faWordpress} className={className} />;
}
// 14. Vue & Angular (FontAwesome brand icons)
// 13. Vue & Angular
if (n.includes('vue')) {
return <FontAwesomeIcon icon={faVuejs} className={className} />;
}
@@ -144,7 +137,15 @@ export default function TechIcon({ name, icon, className = 'w-5 h-5' }: TechIcon
return <FontAwesomeIcon icon={faAngular} className={className} />;
}
// 15. Bootstrap & Sass (FontAwesome brand icons)
// 14. HTML5 & CSS3
if (n.includes('html')) {
return <FontAwesomeIcon icon={faHtml5} className={className} />;
}
if (n.includes('css')) {
return <FontAwesomeIcon icon={faCss3Alt} className={className} />;
}
// 15. Bootstrap & Sass
if (n.includes('bootstrap')) {
return <FontAwesomeIcon icon={faBootstrap} className={className} />;
}
@@ -152,52 +153,49 @@ export default function TechIcon({ name, icon, className = 'w-5 h-5' }: TechIcon
return <FontAwesomeIcon icon={faSass} className={className} />;
}
// 16. AWS (FontAwesome brand icon)
// 16. Cloud Providers & DevOps Platforms
if (n.includes('aws')) {
return <FontAwesomeIcon icon={faAws} className={className} />;
}
// 17. HTML / HTML5 (FontAwesome brand icon)
if (n.includes('html')) {
return <FontAwesomeIcon icon={faHtml5} className={className} />;
if (n.includes('google cloud') || n.includes('gcp') || n.includes('google')) {
return <FontAwesomeIcon icon={faGoogle} className={className} />;
}
if (n.includes('vercel')) {
return <FontAwesomeIcon icon={faRocket} className={className} />;
}
if (n.includes('netlify')) {
return <FontAwesomeIcon icon={faCloud} className={className} />;
}
if (n.includes('render')) {
return <FontAwesomeIcon icon={faServer} className={className} />;
}
// 18. CSS / CSS3 (FontAwesome brand icon)
if (n.includes('css')) {
return <FontAwesomeIcon icon={faCss3Alt} className={className} />;
// 17. Database (MySQL, PostgreSQL, SQLite, Mongo, SQL)
if (n.includes('sql') || n.includes('database') || n.includes('postgres') || n.includes('sqlite') || n.includes('mongo')) {
return <FontAwesomeIcon icon={faDatabase} className={className} />;
}
// 19. JavaScript (FontAwesome brand icon)
if (n.includes('js') || n.includes('javascript')) {
return <FontAwesomeIcon icon={faJs} className={className} />;
}
// 20. Flutter / Mobile (FontAwesome solid icon)
// 18. Flutter & Mobile
if (n.includes('flutter') || n.includes('mobile')) {
return <FontAwesomeIcon icon={faMobileScreenButton} className={className} />;
}
// 21. Database / MySQL / SQL (FontAwesome solid icon)
if (n.includes('mysql') || n.includes('sql') || n.includes('database') || n.includes('mongo')) {
return <FontAwesomeIcon icon={faDatabase} className={className} />;
}
// 22. Security / Cyber / Shield (FontAwesome solid icon)
// 19. Cybersecurity & Shield
if (n.includes('secur') || n.includes('cyber') || n.includes('shield')) {
return <FontAwesomeIcon icon={faShieldHalved} className={className} />;
}
// 23. Terminal / CLI / Bash (FontAwesome solid icon)
// 20. Terminal / CLI / Bash
if (n.includes('terminal') || n.includes('bash') || n.includes('cli')) {
return <FontAwesomeIcon icon={faTerminal} className={className} />;
}
// 24. AI / ML / Bot (FontAwesome solid icon)
// 21. AI & Bots
if (n.includes('ai') || n.includes('ml') || n.includes('bot')) {
return <FontAwesomeIcon icon={faRobot} className={className} />;
}
// 25. Server & Networking (FontAwesome solid icons)
// 22. Server & Networking
if (n.includes('server')) {
return <FontAwesomeIcon icon={faServer} className={className} />;
}
@@ -205,67 +203,11 @@ export default function TechIcon({ name, icon, className = 'w-5 h-5' }: TechIcon
return <FontAwesomeIcon icon={faNetworkWired} className={className} />;
}
// 26. Vercel (Official brand vector)
if (n.includes('vercel')) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M24 22.525H0l12-21.05 12 21.05z" />
</svg>
);
}
// 27. Netlify (Official brand vector)
if (n.includes('netlify')) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M6.49 19.09l.36.21 4.14 2.4 1.01.58.5-.29 4.14-2.4.36-.21v-.42V8.94v-.42l-.36-.21-4.14-2.4-.5-.29-1.01.58-4.14 2.4-.36.21v.42v10.15v.42zm5.51-11.97l3.65 2.11v7.54l-3.65 2.11-3.65-2.11V9.23l3.65-2.11z" />
</svg>
);
}
// 28. Render (Official brand vector)
if (n.includes('render')) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M19.98 9.53a8.03 8.03 0 00-6.1-5.46 8.08 8.08 0 00-7.39 2.53 8.04 8.04 0 00-2.47 5.74V20h4.43v-7.66c0-2.94 2.38-5.33 5.33-5.33 2.94 0 5.33 2.39 5.33 5.33V20h4.43v-7.66a8 8 0 00-3.56-2.81z" />
</svg>
);
}
// 29. Google Cloud / GCP (Official brand vector)
if (n.includes('google cloud') || n.includes('gcp') || n.includes('google cloud console')) {
return (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3z" />
</svg>
);
}
// 30. PostgreSQL / Postgres (Official brand vector)
if (n.includes('postgres') || n.includes('postgresql')) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 16.93c-3.96-.46-7-3.83-7-7.93 0-4.41 3.59-8 8-8 1.84 0 3.53.63 4.88 1.68l-1.45 1.45A5.96 5.96 0 0014 5.5c-3.31 0-6 2.69-6 6 0 2.97 2.16 5.43 5 5.91v1.52z" />
</svg>
);
}
// 31. SQLite (Official brand vector)
if (n.includes('sqlite')) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<ellipse cx="12" cy="5" rx="9" ry="3" />
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
</svg>
);
}
// 32. Generic Cloud (FontAwesome solid icon)
// 23. Generic Cloud
if (n.includes('cloud')) {
return <FontAwesomeIcon icon={faCloud} className={className} />;
}
// Default: Code (FontAwesome solid icon)
// Default: Code
return <FontAwesomeIcon icon={faCode} className={className} />;
}
+1 -58
View File
@@ -16,10 +16,6 @@ const links = allLinks.length > 0
{ platform: 'Facebook', url: 'https://facebook.com', icon: 'facebook', order: 5 },
];
const resumeUrl = settings.resumeFile && settings.resumeFile !== '#' ? settings.resumeFile : '/CV-Cris-Charles-Garcia.pdf';
const downloadUrl = resumeUrl.includes('cdn.sanity.io')
? `${resumeUrl}${resumeUrl.includes('?') ? '&' : '?'}dl=CV-Cris-Charles-Garcia.pdf`
: resumeUrl;
const profileAvatar = settings.heroImage || settings.logoImage || '/logo.png';
const formatDisplayUrl = (url: string) => {
@@ -98,7 +94,7 @@ const getIconSvg = (platform: string) => {
</a>
{settings.isAvailable !== false && (
<span class="text-[10px] font-mono text-emerald-600 dark:text-emerald-400 flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/25 font-medium">
<span class="text-[10px] font-mono text-emerald-700 dark:text-emerald-300 flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/25 font-bold">
<span class="flex h-1.5 w-1.5 relative">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-1.5 w-1.5 bg-emerald-500"></span>
@@ -134,33 +130,6 @@ const getIconSvg = (platform: string) => {
<p class="text-xs text-muted-foreground-custom max-w-xs mt-2 leading-relaxed">
{settings.valueProposition || 'Direct links to my official profiles, repositories, and credentials.'}
</p>
{/* Quick CV Actions */}
<div class="w-full grid grid-cols-2 gap-2 mt-4 pt-3.5 border-t border-border-custom">
<a
href={resumeUrl}
target="_blank"
rel="noopener noreferrer"
class="inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-xl bg-primary-custom text-white text-xs font-medium hover:bg-primary-custom/90 transition-all text-center"
>
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
View CV ↗
</a>
<a
href={downloadUrl}
download="CV-Cris-Charles-Garcia.pdf"
class="inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-xl border border-border-custom hover:border-primary-custom/40 text-foreground-custom text-xs font-medium hover:bg-primary-custom/5 transition-all text-center"
>
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75V16.5M16.5 12 12 16.5m0 0L7.5 12m4.5 4.5V3" />
</svg>
Download PDF
</a>
</div>
</div>
{/* Right Column: Social Links List */}
@@ -190,32 +159,6 @@ const getIconSvg = (platform: string) => {
</a>
))}
{/* Main Portfolio Destination Card */}
<a
href="/"
class="bento-card p-3 sm:p-3.5 md:p-4 flex items-center justify-between group hover:border-primary-custom/40 transition-all mt-0.5"
>
<div class="flex items-center gap-3.5">
<div class="w-9 h-9 rounded-xl bg-primary-custom/10 text-primary-custom flex items-center justify-center shrink-0">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>
</div>
<div class="flex flex-col">
<span class="text-xs sm:text-sm font-bold font-heading text-foreground-custom group-hover:text-primary-custom transition-colors">
Main Portfolio
</span>
<span class="text-[10px] sm:text-[11px] font-mono text-muted-foreground-custom">
Explore full case studies &amp; skills
</span>
</div>
</div>
<span class="text-xs text-muted-foreground-custom group-hover:text-primary-custom group-hover:translate-x-0.5 transition-all">
→
</span>
</a>
{/* Minimal Footer Stamp */}
<p class="text-[10px] font-mono text-muted-foreground-custom text-center md:text-left mt-3 pt-2">
&copy; {new Date().getFullYear()} {settings.fullName} • Official Directory