- 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>
69 lines
2.1 KiB
Vue
69 lines
2.1 KiB
Vue
<template>
|
|
<NuxtLink
|
|
:to="localePath(`/projets/${project.slug}`)"
|
|
class="block bg-sky-dark/50 rounded-lg p-4 hover:bg-sky-dark transition-colors group"
|
|
@click="emit('click')"
|
|
>
|
|
<div class="flex items-start gap-4">
|
|
<!-- Image thumbnail -->
|
|
<div v-if="project.image" class="shrink-0 w-20 h-14 rounded overflow-hidden bg-sky-text/5">
|
|
<NuxtImg
|
|
:src="project.image"
|
|
:alt="project.title"
|
|
format="webp"
|
|
width="80"
|
|
height="56"
|
|
class="w-full h-full object-cover"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
<div class="flex-1 min-w-0">
|
|
<h4 class="font-ui font-medium text-sky-text group-hover:text-sky-accent transition-colors truncate">
|
|
{{ project.title }}
|
|
</h4>
|
|
<p class="text-sm text-sky-text/60 line-clamp-2 mt-1">
|
|
{{ project.short_description }}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Level progress -->
|
|
<div class="shrink-0 text-right">
|
|
<div class="text-xs text-sky-text/40 font-ui">{{ $t('skills.level') }}</div>
|
|
<div class="flex items-center gap-1 mt-1">
|
|
<span class="text-sky-text text-sm">{{ project.level_before }}</span>
|
|
<span class="text-sky-accent">→</span>
|
|
<span class="text-sky-accent font-semibold text-sm">{{ project.level_after }}</span>
|
|
</div>
|
|
<div class="text-xs text-sky-accent/80 font-medium">
|
|
({{ levelProgress }})
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Level description if available -->
|
|
<p v-if="project.level_description" class="mt-3 text-xs text-sky-text/50 italic border-t border-sky-text/10 pt-3">
|
|
{{ project.level_description }}
|
|
</p>
|
|
</NuxtLink>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { SkillProject } from '~/composables/useFetchSkillProjects'
|
|
|
|
const props = defineProps<{
|
|
project: SkillProject
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
click: []
|
|
}>()
|
|
|
|
const localePath = useLocalePath()
|
|
|
|
const levelProgress = computed(() => {
|
|
const diff = props.project.level_after - props.project.level_before
|
|
return diff > 0 ? `+${diff}` : diff.toString()
|
|
})
|
|
</script>
|