You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

68 lines
1.5 KiB

<template>
<div class="project-card">
<NuxtLink :to="`${projectsPath}/${project.id}`" class="project-link">
<div class="project-image">
<img
:src="imageSrc"
:alt="project.title"
loading="lazy"
@error="handleImageError"
class="project-img"
/>
</div>
<div class="project-content">
<h2>{{ project.title }}</h2>
<p>{{ project.description }}</p>
<div class="project-technologies">
<span v-for="tech in project.technologies" :key="tech" class="tech-tag">
{{ tech }}
</span>
</div>
</div>
</NuxtLink>
</div>
</template>
<script setup lang="ts">
import type { Project } from '~/types/project';
import { DEFAULT_IMAGES } from '~/config/paths';
import { navigationItems } from '~/config/navigation';
const props = defineProps<{ project: Project }>();
// Optimisation: précalculer le chemin des projets
const projectsPath = navigationItems.find((item) => item.finder === 'projects')?.path || '/projects';
const imageSrc = ref(props.project.image || DEFAULT_IMAGES.PROJECT);
const handleImageError = () => {
imageSrc.value = DEFAULT_IMAGES.PROJECT;
};
</script>
<style scoped>
@import '~/assets/css/pages/project-detail.css';
.project-img {
width: 100%;
height: auto;
object-fit: cover;
}
.image-placeholder {
background-color: var(--surface-ground);
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
opacity: 0.6;
}
50% {
opacity: 0.8;
}
100% {
opacity: 0.6;
}
}
</style>