init
This commit is contained in:
33
lib/data-loader.ts
Normal file
33
lib/data-loader.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { SkillCategory, Team } from "./types";
|
||||
|
||||
export async function loadSkillCategories(): Promise<SkillCategory[]> {
|
||||
const categories = ["frontend", "backend", "devops", "mobile"];
|
||||
const skillCategories: SkillCategory[] = [];
|
||||
|
||||
for (const category of categories) {
|
||||
try {
|
||||
const response = await fetch(`/data/skills/${category}.json`);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
skillCategories.push(data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to load ${category} skills:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return skillCategories;
|
||||
}
|
||||
|
||||
export async function loadTeams(): Promise<Team[]> {
|
||||
try {
|
||||
const response = await fetch("/data/teams.json");
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
return data.teams;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to load teams:", error);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
76
lib/evaluation-utils.ts
Normal file
76
lib/evaluation-utils.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
SkillLevel,
|
||||
SKILL_LEVEL_VALUES,
|
||||
CategoryEvaluation,
|
||||
RadarChartData,
|
||||
UserEvaluation,
|
||||
SkillCategory,
|
||||
} from "./types";
|
||||
|
||||
export function calculateCategoryScore(
|
||||
categoryEvaluation: CategoryEvaluation
|
||||
): number {
|
||||
if (categoryEvaluation.skills.length === 0) return 0;
|
||||
|
||||
const evaluatedSkills = categoryEvaluation.skills.filter(
|
||||
(skill) => skill.level !== null
|
||||
);
|
||||
if (evaluatedSkills.length === 0) return 0;
|
||||
|
||||
const totalScore = evaluatedSkills.reduce((sum, skill) => {
|
||||
return sum + SKILL_LEVEL_VALUES[skill.level!];
|
||||
}, 0);
|
||||
|
||||
return totalScore / evaluatedSkills.length;
|
||||
}
|
||||
|
||||
export function generateRadarData(
|
||||
evaluations: CategoryEvaluation[],
|
||||
categories: SkillCategory[]
|
||||
): RadarChartData[] {
|
||||
const maxScore = 3; // Expert level
|
||||
|
||||
return categories.map((category) => {
|
||||
const evaluation = evaluations.find(
|
||||
(e) => e.category === category.category
|
||||
);
|
||||
const score = evaluation ? calculateCategoryScore(evaluation) : 0;
|
||||
|
||||
return {
|
||||
category: category.category,
|
||||
score: Math.round(score * 10) / 10, // Round to 1 decimal
|
||||
maxScore,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function saveUserEvaluation(evaluation: UserEvaluation): void {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
"peakSkills_userEvaluation",
|
||||
JSON.stringify(evaluation)
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Failed to save user evaluation:", error);
|
||||
}
|
||||
}
|
||||
|
||||
export function loadUserEvaluation(): UserEvaluation | null {
|
||||
try {
|
||||
const saved = localStorage.getItem("peakSkills_userEvaluation");
|
||||
return saved ? JSON.parse(saved) : null;
|
||||
} catch (error) {
|
||||
console.error("Failed to load user evaluation:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function createEmptyEvaluation(
|
||||
categories: SkillCategory[]
|
||||
): CategoryEvaluation[] {
|
||||
return categories.map((category) => ({
|
||||
category: category.category,
|
||||
skills: [],
|
||||
selectedSkillIds: [],
|
||||
}));
|
||||
}
|
||||
44
lib/pattern-colors.ts
Normal file
44
lib/pattern-colors.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
export const getCategoryColor = (category: string) => {
|
||||
switch (category) {
|
||||
case "Creational":
|
||||
return "border-blue-400 dark:border-blue-400 text-blue-600 dark:text-blue-400 bg-blue-50/50 dark:bg-blue-950/30 hover:bg-blue-100/70 dark:hover:bg-blue-900/50 hover:border-blue-500 dark:hover:border-blue-300 hover:shadow-blue-500/20";
|
||||
case "Structural":
|
||||
return "border-green-400 dark:border-green-400 text-green-600 dark:text-green-400 bg-green-50/50 dark:bg-green-950/30 hover:bg-green-100/70 dark:hover:bg-green-900/50 hover:border-green-500 dark:hover:border-green-300 hover:shadow-green-500/20";
|
||||
case "Behavioral":
|
||||
return "border-purple-400 dark:border-purple-400 text-purple-600 dark:text-purple-400 bg-purple-50/50 dark:bg-purple-950/30 hover:bg-purple-100/70 dark:hover:bg-purple-900/50 hover:border-purple-500 dark:hover:border-purple-300 hover:shadow-purple-500/20";
|
||||
case "Architectural":
|
||||
return "border-orange-400 dark:border-orange-400 text-orange-600 dark:text-orange-400 bg-orange-50/50 dark:bg-orange-950/30 hover:bg-orange-100/70 dark:hover:bg-orange-900/50 hover:border-orange-500 dark:hover:border-orange-300 hover:shadow-orange-500/20";
|
||||
default:
|
||||
return "border-gray-400 dark:border-gray-400 text-gray-600 dark:text-gray-400 bg-gray-50/50 dark:bg-gray-950/30 hover:bg-gray-100/70 dark:hover:bg-gray-900/50 hover:border-gray-500 dark:hover:border-gray-300 hover:shadow-gray-500/20";
|
||||
}
|
||||
};
|
||||
|
||||
export const getDifficultyColor = (difficulty: string) => {
|
||||
switch (difficulty) {
|
||||
case "Facile":
|
||||
return "border-emerald-400 dark:border-emerald-400 text-emerald-600 dark:text-emerald-400 bg-emerald-50/50 dark:bg-emerald-950/30 hover:bg-emerald-100/70 dark:hover:bg-emerald-900/50 hover:border-emerald-500 dark:hover:border-emerald-300 hover:shadow-emerald-500/20";
|
||||
case "Moyen":
|
||||
return "border-amber-400 dark:border-amber-400 text-amber-600 dark:text-amber-400 bg-amber-50/50 dark:bg-amber-950/30 hover:bg-amber-100/70 dark:hover:bg-amber-900/50 hover:border-amber-500 dark:hover:border-amber-300 hover:shadow-amber-500/20";
|
||||
case "Difficile":
|
||||
return "border-red-400 dark:border-red-400 text-red-600 dark:text-red-400 bg-red-50/50 dark:bg-red-950/30 hover:bg-red-100/70 dark:hover:bg-red-900/50 hover:border-red-500 dark:hover:border-red-300 hover:shadow-red-500/20";
|
||||
default:
|
||||
return "border-gray-400 dark:border-gray-400 text-gray-600 dark:text-gray-400 bg-gray-50/50 dark:bg-gray-950/30 hover:bg-gray-100/70 dark:hover:bg-gray-900/50 hover:border-gray-500 dark:hover:border-gray-300 hover:shadow-gray-500/20";
|
||||
}
|
||||
};
|
||||
|
||||
export const getUsageColor = (usage: string) => {
|
||||
switch (usage) {
|
||||
case "Peu utilisé":
|
||||
return "border-red-400 dark:border-red-400 text-red-600 dark:text-red-400 bg-red-50/50 dark:bg-red-950/30 hover:bg-red-100/70 dark:hover:bg-red-900/50 hover:border-red-500 dark:hover:border-red-300 hover:shadow-red-500/20";
|
||||
case "Modérément utilisé":
|
||||
return "border-orange-400 dark:border-orange-400 text-orange-600 dark:text-orange-400 bg-orange-50/50 dark:bg-orange-950/30 hover:bg-orange-100/70 dark:hover:bg-orange-900/50 hover:border-orange-500 dark:hover:border-orange-300 hover:shadow-orange-500/20";
|
||||
case "Utilisé":
|
||||
return "border-amber-400 dark:border-amber-400 text-amber-600 dark:text-amber-400 bg-amber-50/50 dark:bg-amber-950/30 hover:bg-amber-100/70 dark:hover:bg-amber-900/50 hover:border-amber-500 dark:hover:border-amber-300 hover:shadow-amber-500/20";
|
||||
case "Assez utilisé":
|
||||
return "border-lime-400 dark:border-lime-400 text-lime-600 dark:text-lime-400 bg-lime-50/50 dark:bg-lime-950/30 hover:bg-lime-100/70 dark:hover:bg-lime-900/50 hover:border-lime-500 dark:hover:border-lime-300 hover:shadow-lime-500/20";
|
||||
case "Très utilisé":
|
||||
return "border-green-400 dark:border-green-400 text-green-600 dark:text-green-400 bg-green-50/50 dark:bg-green-950/30 hover:bg-green-100/70 dark:hover:bg-green-900/50 hover:border-green-500 dark:hover:border-green-300 hover:shadow-green-500/20";
|
||||
default:
|
||||
return "border-gray-400 dark:border-gray-400 text-gray-600 dark:text-gray-400 bg-gray-50/50 dark:bg-gray-950/30 hover:bg-gray-100/70 dark:hover:bg-gray-900/50 hover:border-gray-500 dark:hover:border-gray-300 hover:shadow-gray-500/20";
|
||||
}
|
||||
};
|
||||
61
lib/tech-colors.ts
Normal file
61
lib/tech-colors.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
// Couleurs officielle des technologies
|
||||
export const TECH_COLORS: Record<
|
||||
string,
|
||||
{ bg: string; text: string; border: string }
|
||||
> = {
|
||||
react: {
|
||||
bg: "bg-blue-500/10",
|
||||
text: "text-blue-600 dark:text-blue-400",
|
||||
border: "border-blue-500/20",
|
||||
},
|
||||
vue: {
|
||||
bg: "bg-green-500/10",
|
||||
text: "text-green-600 dark:text-green-400",
|
||||
border: "border-green-500/20",
|
||||
},
|
||||
typescript: {
|
||||
bg: "bg-blue-500/10",
|
||||
text: "text-blue-600 dark:text-blue-400",
|
||||
border: "border-blue-500/20",
|
||||
},
|
||||
nextjs: {
|
||||
bg: "bg-gray-500/10",
|
||||
text: "text-gray-900 dark:text-gray-100",
|
||||
border: "border-gray-500/20",
|
||||
},
|
||||
nodejs: {
|
||||
bg: "bg-green-500/10",
|
||||
text: "text-green-600 dark:text-green-400",
|
||||
border: "border-green-500/20",
|
||||
},
|
||||
python: {
|
||||
bg: "bg-yellow-500/10",
|
||||
text: "text-yellow-600 dark:text-yellow-400",
|
||||
border: "border-yellow-500/20",
|
||||
},
|
||||
docker: {
|
||||
bg: "bg-blue-500/10",
|
||||
text: "text-blue-600 dark:text-blue-400",
|
||||
border: "border-blue-500/20",
|
||||
},
|
||||
kubernetes: {
|
||||
bg: "bg-indigo-500/10",
|
||||
text: "text-indigo-600 dark:text-indigo-400",
|
||||
border: "border-indigo-500/20",
|
||||
},
|
||||
aws: {
|
||||
bg: "bg-orange-500/10",
|
||||
text: "text-orange-600 dark:text-orange-400",
|
||||
border: "border-orange-500/20",
|
||||
},
|
||||
// Couleur par défaut
|
||||
default: {
|
||||
bg: "bg-muted",
|
||||
text: "text-foreground",
|
||||
border: "border-muted",
|
||||
},
|
||||
};
|
||||
|
||||
export function getTechColors(techId: string) {
|
||||
return TECH_COLORS[techId] || TECH_COLORS.default;
|
||||
}
|
||||
67
lib/types.ts
Normal file
67
lib/types.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
export type SkillLevel =
|
||||
| "never"
|
||||
| "not-autonomous"
|
||||
| "autonomous"
|
||||
| "expert"
|
||||
| null;
|
||||
|
||||
export const SKILL_LEVELS: Record<Exclude<SkillLevel, null>, string> = {
|
||||
never: "Jamais pratiqué",
|
||||
"not-autonomous": "Pas autonome",
|
||||
autonomous: "Autonome",
|
||||
expert: "Maîtrise",
|
||||
};
|
||||
|
||||
export const SKILL_LEVEL_VALUES: Record<Exclude<SkillLevel, null>, number> = {
|
||||
never: 0,
|
||||
"not-autonomous": 1,
|
||||
autonomous: 2,
|
||||
expert: 3,
|
||||
};
|
||||
|
||||
export interface Skill {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
links: string[];
|
||||
}
|
||||
|
||||
export interface SkillCategory {
|
||||
category: string;
|
||||
skills: Skill[];
|
||||
}
|
||||
|
||||
export interface Team {
|
||||
id: string;
|
||||
name: string;
|
||||
direction: string;
|
||||
}
|
||||
|
||||
export interface UserProfile {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
teamId: string;
|
||||
}
|
||||
|
||||
export interface SkillEvaluation {
|
||||
skillId: string;
|
||||
level: SkillLevel;
|
||||
}
|
||||
|
||||
export interface CategoryEvaluation {
|
||||
category: string;
|
||||
skills: SkillEvaluation[];
|
||||
selectedSkillIds: string[]; // List of skill IDs the user wants to evaluate
|
||||
}
|
||||
|
||||
export interface UserEvaluation {
|
||||
profile: UserProfile;
|
||||
evaluations: CategoryEvaluation[];
|
||||
lastUpdated: string;
|
||||
}
|
||||
|
||||
export interface RadarChartData {
|
||||
category: string;
|
||||
score: number;
|
||||
maxScore: number;
|
||||
}
|
||||
6
lib/utils.ts
Normal file
6
lib/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
Reference in New Issue
Block a user