- Add GET /skills/{slug}/projects endpoint with level progression
- Install @headlessui/vue for accessible modal
- Create SkillProjectsModal with Dialog component:
- Focus trap and keyboard navigation (automatic)
- Fade + scale transitions with backdrop blur
- prefers-reduced-motion support
- Create ProjectListItem with thumbnail and level display
- Integrate modal in competences.vue page
- Add translations for related projects UI
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
91 lines
3.1 KiB
PHP
91 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Resources\SkillResource;
|
|
use App\Models\Skill;
|
|
|
|
class SkillController extends Controller
|
|
{
|
|
public function index()
|
|
{
|
|
$lang = app()->getLocale();
|
|
$skills = Skill::with('projects')->ordered()->get();
|
|
$grouped = $skills->groupBy('category');
|
|
|
|
$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' => $data,
|
|
'meta' => [
|
|
'lang' => $lang,
|
|
'total' => $skills->count(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function projects(string $slug)
|
|
{
|
|
$lang = app()->getLocale();
|
|
|
|
$skill = Skill::with('projects')->where('slug', $slug)->first();
|
|
|
|
if (!$skill) {
|
|
return response()->json([
|
|
'error' => [
|
|
'code' => 'SKILL_NOT_FOUND',
|
|
'message' => 'Skill not found',
|
|
],
|
|
], 404);
|
|
}
|
|
|
|
return response()->json([
|
|
'data' => [
|
|
'skill' => [
|
|
'id' => $skill->id,
|
|
'slug' => $skill->slug,
|
|
'name' => $skill->getTranslated('name_key'),
|
|
'description' => $skill->getTranslated('description_key'),
|
|
'level' => $skill->getCurrentLevel(),
|
|
'max_level' => $skill->max_level,
|
|
],
|
|
'projects' => $skill->projects->map(function ($project) {
|
|
return [
|
|
'id' => $project->id,
|
|
'slug' => $project->slug,
|
|
'title' => $project->getTranslated('title_key'),
|
|
'short_description' => $project->getTranslated('short_description_key'),
|
|
'image' => $project->image,
|
|
'date_completed' => $project->date_completed?->format('Y-m-d'),
|
|
'level_before' => $project->pivot->level_before,
|
|
'level_after' => $project->pivot->level_after,
|
|
'level_description' => $project->pivot->level_description_key
|
|
? $project->getTranslated($project->pivot->level_description_key)
|
|
: null,
|
|
];
|
|
}),
|
|
],
|
|
'meta' => ['lang' => $lang],
|
|
]);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|