Add skill projects modal with Headless UI (Story 2.5)

- 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>
This commit is contained in:
2026-02-06 10:44:45 +01:00
parent 4db96a0ded
commit 2b043674ca
12 changed files with 441 additions and 54 deletions

View File

@@ -31,6 +31,51 @@ class SkillController extends Controller
]);
}
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 = [

View File

@@ -11,3 +11,4 @@ Route::get('/health', function () {
Route::get('/projects', [ProjectController::class, 'index']);
Route::get('/projects/{slug}', [ProjectController::class, 'show']);
Route::get('/skills', [SkillController::class, 'index']);
Route::get('/skills/{slug}/projects', [SkillController::class, 'projects']);