This commit is contained in:
Julien Froidefond
2025-08-20 15:43:24 +02:00
commit 09d2c5cbe1
100 changed files with 12494 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
"use client";
import { getTechIcon } from "./tech-icons";
interface SmartTechIconProps {
techId: string;
techName: string;
className?: string;
fallbackStyle?: "initial" | "emoji" | "gradient";
}
// Emojis pour certaines technologies populaires
const TECH_EMOJIS: Record<string, string> = {
react: "⚛️",
vue: "💚",
typescript: "🔷",
nextjs: "▲",
nodejs: "🟢",
python: "🐍",
javascript: "💛",
html: "🧡",
css: "💙",
docker: "🐳",
kubernetes: "☸️",
aws: "☁️",
git: "🌿",
github: "🐙",
mongodb: "🍃",
postgresql: "🐘",
redis: "🔴",
mysql: "🐬",
flutter: "💙",
swift: "🦉",
kotlin: "🟣",
java: "☕",
php: "🐘",
ruby: "💎",
go: "🐹",
rust: "🦀",
csharp: "💜",
};
export function SmartTechIcon({
techId,
techName,
className = "w-6 h-6",
fallbackStyle = "initial",
}: SmartTechIconProps) {
// Essayer d'abord l'icône SVG locale
const TechIcon = getTechIcon(techId);
// Si ce n'est pas l'icône par défaut, l'utiliser
if (TechIcon !== getTechIcon("default")) {
return <TechIcon className={className} />;
}
// Sinon, utiliser le style de fallback
switch (fallbackStyle) {
case "emoji":
const emoji = TECH_EMOJIS[techId];
if (emoji) {
return (
<div
className={`${className} flex items-center justify-center text-lg`}
>
{emoji}
</div>
);
}
break;
case "gradient":
const gradientColors = [
"from-blue-500 to-purple-500",
"from-green-500 to-blue-500",
"from-red-500 to-pink-500",
"from-yellow-500 to-orange-500",
"from-purple-500 to-indigo-500",
];
const colorIndex = techId.length % gradientColors.length;
return (
<div
className={`${className} bg-gradient-to-br ${gradientColors[colorIndex]} rounded flex items-center justify-center text-white text-xs font-bold`}
>
{techName.charAt(0).toUpperCase()}
</div>
);
default: // "initial"
return (
<div
className={`${className} bg-muted rounded flex items-center justify-center text-xs font-bold`}
>
{techName.charAt(0).toUpperCase()}
</div>
);
}
// Fallback final
return (
<div
className={`${className} bg-muted rounded flex items-center justify-center text-xs font-bold`}
>
{techName.charAt(0).toUpperCase()}
</div>
);
}

View File

@@ -0,0 +1,77 @@
"use client";
import Image from "next/image";
import { useState } from "react";
interface TechIconExternalProps {
techId: string;
techName: string;
className?: string;
size?: number;
}
// Service Simple Icons pour récupérer les icônes SVG
export function TechIconExternal({
techId,
techName,
className = "w-6 h-6",
size = 24,
}: TechIconExternalProps) {
const [imageError, setImageError] = useState(false);
// URL pour Simple Icons
const simpleIconUrl = `https://cdn.jsdelivr.net/npm/simple-icons@v9/${techId}.svg`;
// URL pour DevIcons
const devIconUrl = `https://cdn.jsdelivr.net/gh/devicons/devicon/icons/${techId}/${techId}-original.svg`;
if (imageError) {
// Fallback vers initiale si l'icône n'existe pas
return (
<div
className={`${className} bg-muted rounded flex items-center justify-center text-xs font-bold`}
>
{techName.charAt(0).toUpperCase()}
</div>
);
}
return (
<Image
src={simpleIconUrl}
alt={`${techName} icon`}
width={size}
height={size}
className={className}
onError={() => setImageError(true)}
style={{ filter: "brightness(0) saturate(100%) invert(0.5)" }} // Pour s'adapter au thème
/>
);
}
// Mapping des IDs pour Simple Icons (ils utilisent parfois des noms différents)
export const SIMPLE_ICONS_MAPPING: Record<string, string> = {
react: "react",
vue: "vuedotjs",
typescript: "typescript",
nextjs: "nextdotjs",
nodejs: "nodedotjs",
python: "python",
express: "express",
postgresql: "postgresql",
mongodb: "mongodb",
redis: "redis",
docker: "docker",
kubernetes: "kubernetes",
aws: "amazonwebservices",
terraform: "terraform",
jenkins: "jenkins",
githubactions: "githubactions",
reactnative: "react",
flutter: "flutter",
swift: "swift",
kotlin: "kotlin",
expo: "expo",
tailwindcss: "tailwindcss",
webpack: "webpack",
};

View File

@@ -0,0 +1,66 @@
import React from "react";
interface TechIconProps {
className?: string;
}
export const ReactIcon = ({ className = "w-6 h-6" }: TechIconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M12 10.11c1.03 0 1.87.84 1.87 1.89s-.84 1.89-1.87 1.89c-1.03 0-1.87-.84-1.87-1.89s.84-1.89 1.87-1.89M7.37 20c.63.38 2.01-.2 3.6-1.7-.52-.59-1.03-1.23-1.51-1.9a22.7 22.7 0 0 1-2.4-.36c-.51 2.14-.32 3.61.31 3.96m.71-5.74l-.29-.51c-.11.29-.22.58-.29.86.27.06.57.11.88.16l-.3-.51m6.54-.76l.81-1.5-.81-1.5c-.3-.53-.62-1-.91-1.47C13.17 9 12.6 9 12 9s-1.17 0-1.71.03c-.29.47-.61.94-.91 1.47L8.57 12l.81 1.5c.3.53.62 1 .91 1.47.54.03 1.11.03 1.71.03s1.17 0 1.71-.03c.29-.47.61-.94.91-1.47M12 6.78c-.19.22-.39.45-.59.72h1.18c-.2-.27-.4-.5-.59-.72m0 10.44c.19-.22.39-.45.59-.72h-1.18c.2.27.4.5.59.72M16.62 4c-.62-.38-2 .2-3.59 1.7.52.59 1.03 1.23 1.51 1.9.82.08 1.63.2 2.4.36.51-2.14.32-3.61-.32-3.96m-.7 5.74l.29.51c.11-.29.22-.58.29-.86-.27-.06-.57-.11-.88-.16l.3.51m1.45-7.05c1.47.84 1.63 3.05 1.01 5.63 2.54.75 4.37 1.99 4.37 3.68s-1.83 2.93-4.37 3.68c.62 2.58.46 4.79-1.01 5.63-1.46.84-3.45-.12-5.37-1.95-1.92 1.83-3.91 2.79-5.37 1.95-1.47-.84-1.63-3.05-1.01-5.63-2.54-.75-4.37-1.99-4.37-3.68s1.83-2.93 4.37-3.68c-.62-2.58-.46-4.79 1.01-5.63 1.46-.84 3.45.12 5.37 1.95 1.92-1.83 3.91-2.79 5.37-1.95z" />
</svg>
);
export const VueIcon = ({ className = "w-6 h-6" }: TechIconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M2,3L12,21L22,3H18.5L12,14L5.5,3H2Z" />
</svg>
);
export const TypeScriptIcon = ({ className = "w-6 h-6" }: TechIconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M1.5,5.5V18.5H22.5V5.5H1.5M3,7H21V17H3V7M13.5,8.5V15.5H15.5V8.5H13.5M8.5,8.5V10H11V11.5H8.5V13H12V14.5H7V8.5H8.5Z" />
</svg>
);
export const NextJSIcon = ({ className = "w-6 h-6" }: TechIconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M11.572 0c-.176 0-.31.001-.358.007a19.76 19.76 0 0 1-.364.033C7.443.346 4.25 2.185 2.228 5.012a11.875 11.875 0 0 0-2.119 5.243c-.096.659-.108.854-.108 1.747s.012 1.089.108 1.748c.652 4.506 3.86 8.292 8.209 9.695.779.25 1.6.422 2.534.525.363.04 1.935.04 2.299 0 1.611-.178 2.977-.577 4.323-1.264.207-.106.246-.134.218-.157-.02-.013-.9-1.193-1.955-2.62l-1.919-2.592-2.404-3.558a338.739 338.739 0 0 0-2.422-3.556c-.009-.002-.018 1.579-.023 3.51-.007 3.38-.01 3.515-.052 3.595a.426.426 0 0 1-.206.214c-.075.037-.14.044-.495.044H7.81l-.108-.068a.438.438 0 0 1-.157-.171l-.05-.106.006-4.703.007-4.705.072-.092a.645.645 0 0 1 .174-.143c.096-.047.134-.051.54-.051.478 0 .558.018.682.154.035.038 1.337 1.999 2.895 4.361a10760.433 10760.433 0 0 0 4.735 7.17l1.9 2.879.096-.063a12.317 12.317 0 0 0 2.466-2.163 11.944 11.944 0 0 0 2.824-6.134c.096-.66.108-.854.108-1.748 0-.893-.012-1.088-.108-1.747C19.777 4.249 16.569.346 12.282.033a19.555 19.555 0 0 0-.364-.033C11.87.001 11.736 0 11.572 0zm4.069 7.217c.347 0 .408.005.486.047a.473.473 0 0 1 .237.277c.018.06.023 1.365.018 4.304l-.006 4.218-.744-1.14-.746-1.14v-3.066c0-1.982.01-3.097.023-3.15a.478.478 0 0 1 .233-.296c.096-.05.13-.054.5-.054z" />
</svg>
);
export const NodeJSIcon = ({ className = "w-6 h-6" }: TechIconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M12,1.85C11.73,1.85 11.45,1.92 11.22,2.05L3.78,6.35C3.32,6.61 3.03,7.12 3.03,7.67V16.33C3.03,16.88 3.32,17.39 3.78,17.65L5.73,18.77C6.68,19.23 7.17,19.24 7.17,19.24C8.1,19.24 8.62,18.6 8.62,17.79V9.19C8.62,9.08 8.53,8.99 8.42,8.99H7.6C7.49,8.99 7.4,9.08 7.4,9.19V17.79C7.4,18.05 7.16,18.23 6.94,18.11L4.95,16.96C4.9,16.93 4.87,16.88 4.87,16.82V7.18C4.87,7.12 4.9,7.07 4.95,7.04L12.39,2.74C12.44,2.71 12.53,2.71 12.58,2.74L20.02,7.04C20.07,7.07 20.1,7.12 20.1,7.18V16.82C20.1,16.88 20.07,16.93 20.02,16.96L12.58,21.26C12.53,21.29 12.44,21.29 12.39,21.26L10.57,20.15C10.5,20.11 10.42,20.11 10.35,20.15C9.87,20.41 9.78,20.46 9.26,20.65C9.11,20.71 8.91,20.81 9.18,20.97L11.22,21.95C11.45,22.08 11.73,22.15 12,22.15C12.27,22.15 12.55,22.08 12.78,21.95L20.22,17.65C20.68,17.39 20.97,16.88 20.97,16.33V7.67C20.97,7.12 20.68,6.61 20.22,6.35L12.78,2.05C12.55,1.92 12.27,1.85 12,1.85M14.1,8.14C11.86,8.14 10.95,9.17 10.95,10.45C10.95,11.96 12,12.31 14.05,12.6C16.65,12.96 16.92,13.44 16.92,13.92C16.92,14.7 16.32,15.04 14.91,15.04C12.84,15.04 12.24,14.38 12.1,13.15C12.08,13.05 12,12.97 11.9,12.97H11.05C10.95,12.97 10.86,13.06 10.86,13.16C10.86,14.71 11.69,16.1 14.91,16.1C17.5,16.1 18.78,15.05 18.78,13.92C18.78,12.39 17.78,12.05 15.54,11.7C13.28,11.35 13.09,11.14 13.09,10.64C13.09,10.21 13.29,9.58 14.1,9.58C14.8,9.58 15.21,9.84 15.37,10.76C15.39,10.85 15.47,10.92 15.57,10.92H16.42C16.47,10.92 16.52,10.9 16.55,10.86C16.58,10.82 16.59,10.77 16.58,10.72C16.39,9.05 15.27,8.14 14.1,8.14Z" />
</svg>
);
export const PythonIcon = ({ className = "w-6 h-6" }: TechIconProps) => (
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
<path d="M19.14,7.5A2.86,2.86 0 0,1 22,10.36V14.14A2.86,2.86 0 0,1 19.14,17H12C12,17.39 12.32,17.96 12.71,17.96H17V19.64A2.86,2.86 0 0,1 14.14,22.5H9.86A2.86,2.86 0 0,1 7,19.64V15.89C7,14.31 8.28,13.04 9.86,13.04H15.11C16.69,13.04 17.96,11.76 17.96,10.18V7.5H19.14M14.86,19.29C14.46,19.29 14.14,19.59 14.14,20S14.46,20.68 14.86,20.68A0.71,0.71 0 0,0 15.57,20C15.57,19.59 15.25,19.29 14.86,19.29M4.86,17H12C12,16.61 11.68,16.04 11.29,16.04H7V14.36A2.86,2.86 0 0,1 9.86,11.5H14.14A2.86,2.86 0 0,1 17,14.36V18.11C17,19.69 15.72,20.96 14.14,20.96H8.89C7.31,20.96 6.04,19.68 6.04,18.1V17H4.86A2.86,2.86 0 0,1 2,14.14V10.36A2.86,2.86 0 0,1 4.86,7.5M9.14,4.71C9.54,4.71 9.86,4.41 9.86,4S9.54,3.32 9.14,3.32A0.71,0.71 0 0,0 8.43,4C8.43,4.41 8.75,4.71 9.14,4.71Z" />
</svg>
);
// Map des icônes par ID de technologie
export const TECH_ICONS: Record<string, React.ComponentType<TechIconProps>> = {
react: ReactIcon,
vue: VueIcon,
typescript: TypeScriptIcon,
nextjs: NextJSIcon,
nodejs: NodeJSIcon,
python: PythonIcon,
// Icône par défaut pour les technologies sans icône spécifique
default: ({ className = "w-6 h-6" }: TechIconProps) => (
<div
className={`${className} bg-muted rounded flex items-center justify-center text-xs font-bold`}
>
?
</div>
),
};
// Fonction utilitaire pour obtenir l'icône d'une technologie
export const getTechIcon = (
techId: string
): React.ComponentType<TechIconProps> => {
return TECH_ICONS[techId] || TECH_ICONS.default;
};