Add skills page with category grouping (Story 2.4)

- Enhance Skill model with getCurrentLevel() and ordered scope
- Update SkillController to group by category with translated labels
- Add level and project_count to SkillResource
- Create skill.ts types (Skill, SkillCategory, SkillsResponse)
- Create useFetchSkills composable
- Create SkillCard component with animated progress bar
- Implement competences.vue with:
  - Responsive grid (2/3/4 columns)
  - Category sections with icons
  - Stagger animations (respects prefers-reduced-motion)
  - Loading/error/empty states
  - Placeholder for vis.js skill tree (Epic 3)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 10:37:10 +01:00
parent 2269ecdb62
commit 4db96a0ded
11 changed files with 414 additions and 65 deletions

View File

@@ -10,13 +10,36 @@ class SkillController extends Controller
{
public function index()
{
$skills = Skill::ordered()->get()->groupBy('category');
$lang = app()->getLocale();
$skills = Skill::with('projects')->ordered()->get();
$grouped = $skills->groupBy('category');
$grouped = $skills->map(fn ($group) => SkillResource::collection($group));
$data = $grouped->map(function ($categorySkills, $category) use ($lang) {
return [
'category' => $category,
'category_label' => $this->getCategoryLabel($category, $lang),
'skills' => SkillResource::collection($categorySkills),
];
})->values();
return response()->json([
'data' => $grouped,
'meta' => ['lang' => app()->getLocale()],
'data' => $data,
'meta' => [
'lang' => $lang,
'total' => $skills->count(),
],
]);
}
private function getCategoryLabel(string $category, string $lang): string
{
$labels = [
'frontend' => ['fr' => 'Frontend', 'en' => 'Frontend'],
'backend' => ['fr' => 'Backend', 'en' => 'Backend'],
'tools' => ['fr' => 'Outils', 'en' => 'Tools'],
'soft_skills' => ['fr' => 'Soft Skills', 'en' => 'Soft Skills'],
];
return $labels[strtolower($category)][$lang] ?? ucfirst($category);
}
}

View File

@@ -16,7 +16,10 @@ class SkillResource extends JsonResource
'description' => $this->getTranslated('description_key'),
'icon' => $this->icon,
'category' => $this->category,
'level' => $this->getCurrentLevel(),
'max_level' => $this->max_level,
'display_order' => $this->display_order,
'project_count' => $this->whenLoaded('projects', fn () => $this->projects->count()),
'pivot' => $this->when($this->pivot, fn () => [
'level_before' => $this->pivot->level_before,
'level_after' => $this->pivot->level_after,