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.

118 lines
5.1 KiB

<template>
<div class="project-detail">
<div class="back-button">
<NuxtLink :to="projectsPath" class="back-link">
<span></span> {{ TEXTS.COMMON.BACK_TO_PROJECTS }}
</NuxtLink>
</div>
<div v-if="currentProject" class="project-content">
<div class="project-header">
<h1>{{ currentProject.title }}</h1>
<div class="project-technologies">
<span v-for="tech in currentProject.technologies" :key="tech" class="tech-tag">
{{ tech }}
</span>
</div>
</div>
<div class="project-image">
<img :src="projectImage" :alt="currentProject.title" loading="lazy" @error="handleImageError" class="project-img" />
</div>
<div class="project-description">
<h2>{{ TEXTS.PROJECTS.DESCRIPTION.TITLE }}</h2>
<p>{{ currentProject.longDescription || currentProject.description }}</p>
</div>
<div class="project-features">
<h2>{{ TEXTS.PROJECTS.DESCRIPTION.FEATURES }}</h2>
<ul>
<li v-for="feature in currentProject.features" :key="feature">
{{ feature }}
</li>
</ul>
</div>
<div class="project-resources">
<h2>{{ TEXTS.PROJECTS.DESCRIPTION.RESOURCES }}</h2>
<div class="resources-grid">
<a v-if="currentProject.github" :href="currentProject.github" target="_blank" rel="noopener noreferrer" class="resource-link">
<img v-if="PATHS.ICONS.GITHUB" :src="PATHS.ICONS.GITHUB" :alt="TEXTS.PROJECTS.RESOURCES.GITHUB" class="resource-icon" @error="handleIconError" loading="lazy" />
<span>{{ TEXTS.PROJECTS.RESOURCES.GITHUB }}</span>
</a>
<a v-if="currentProject.demo" :href="currentProject.demo" target="_blank" rel="noopener noreferrer" class="resource-link" >
<img v-if="PATHS.ICONS.DEMO" :src="PATHS.ICONS.DEMO" :alt="TEXTS.PROJECTS.RESOURCES.DEMO" class="resource-icon" @error="handleIconError" loading="lazy" />
<span>{{ TEXTS.PROJECTS.RESOURCES.DEMO }}</span>
</a>
<NuxtLink :to="`${projectsPath}/${currentProject.id}/docs`" class="resource-link">
<img v-if="PATHS.ICONS.DOCUMENTATION" :src="PATHS.ICONS.DOCUMENTATION" :alt="TEXTS.PROJECTS.RESOURCES.DOCUMENTATION" class="resource-icon" @error="handleIconError" loading="lazy" />
<span>{{ TEXTS.PROJECTS.RESOURCES.DOCUMENTATION }}</span>
</NuxtLink>
<a v-if="currentProject.youtube" :href="currentProject.youtube" target="_blank" rel="noopener noreferrer" class="resource-link" >
<img v-if="PATHS.ICONS.YOUTUBE" :src="PATHS.ICONS.YOUTUBE" :alt="TEXTS.PROJECTS.RESOURCES.YOUTUBE" class="resource-icon" @error="handleIconError" loading="lazy" />
<span>{{ TEXTS.PROJECTS.RESOURCES.YOUTUBE }}</span>
</a>
</div>
</div>
<div v-if="projectGallery && projectGallery.length > 0" class="project-gallery">
<h2>{{ TEXTS.PROJECTS.DESCRIPTION.GALLERY }}</h2>
<div class="gallery-grid">
<div v-for="(image, index) in projectGallery" :key="index" class="gallery-item" @click="openLightbox(index)">
<img :src="image" :alt="`${currentProject.title} gallery image ${index + 1}`" loading="lazy" class="gallery-image" @error="handleImageError" />
</div>
</div>
</div>
</div>
<div v-else class="error">
<h2>{{ TEXTS.COMMON.ERROR.NOT_FOUND }}</h2>
<p>{{ TEXTS.COMMON.ERROR.PROJECT_NOT_FOUND }}</p>
<NuxtLink :to="projectsPath" class="back-link">
{{ TEXTS.COMMON.BACK_TO_PROJECTS }}
</NuxtLink>
</div>
<!-- Lightbox -->
<div v-if="showLightbox" class="lightbox" @click.self="closeLightbox">
<button class="lightbox-close" @click="closeLightbox">&times;</button>
<button class="lightbox-prev" @click="prevImage">&lt;</button>
<img v-if="projectGallery[currentImageIndex]" :src="projectGallery[currentImageIndex]" :alt="`${currentProject?.title} gallery image ${currentImageIndex + 1}`" class="lightbox-image" />
<button class="lightbox-next" @click="nextImage">&gt;</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useProjectDetail } from '~/assets/ts/project/useProjectDetail';
import { TEXTS } from '~/config/content';
import { navigationItems } from '~/config/navigation';
import { DEFAULT_IMAGES, PATHS } from '~/config/paths';
const route = useRoute();
const projectId = Number(route.params.id);
const projectsPath = navigationItems.find((item) => item.finder === 'projects')?.path || '/projects';
const {
currentProject,
handleImageError,
handleIconError,
showLightbox,
currentImageIndex,
openLightbox,
closeLightbox,
nextImage,
prevImage
} = useProjectDetail(projectId);
const projectImage = computed(() => {return currentProject.value?.image || DEFAULT_IMAGES.PROJECT;});
const projectGallery = computed(() => {return currentProject.value?.gallery || [];});
</script>
<style scoped>
@import '~/assets/css/pages/project-detail.css';
.project-img {
width: 100%;
height: auto;
object-fit: cover;
}
</style>