retest
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
20498d1ad5
commit
771982bbad
@ -1,119 +0,0 @@
|
|||||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
|
||||||
import { useRoute } from 'vue-router';
|
|
||||||
import { projects } from '~/config/projects';
|
|
||||||
import type { Project } from '~/types/project';
|
|
||||||
|
|
||||||
export const useProjectDetail = () => {
|
|
||||||
const route = useRoute();
|
|
||||||
const showLightbox = ref(false);
|
|
||||||
const currentImageIndex = ref(0);
|
|
||||||
const processedImages = ref<string[]>([]);
|
|
||||||
const processedResources = ref<Array<{
|
|
||||||
name: string;
|
|
||||||
icon: string;
|
|
||||||
url: string;
|
|
||||||
}>>([]);
|
|
||||||
|
|
||||||
const project = computed<Project | undefined>(() => {
|
|
||||||
const projectId = Number(route.params.id);
|
|
||||||
return projects.find(p => p.id === projectId);
|
|
||||||
});
|
|
||||||
|
|
||||||
const processProjectResources = async () => {
|
|
||||||
if (!project.value) return;
|
|
||||||
|
|
||||||
// Process main project image
|
|
||||||
if (project.value.image) {
|
|
||||||
processedImages.value = [project.value.image];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process resources
|
|
||||||
const resourcePromises = Object.entries(project.value).map(async ([key, value]) => {
|
|
||||||
if (['github', 'demo', 'documentation', 'youtube'].includes(key) && value) {
|
|
||||||
return {
|
|
||||||
name: key,
|
|
||||||
icon: `/images/${key}-icon.png`,
|
|
||||||
url: value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
processedResources.value = (await Promise.all(resourcePromises)).filter((resource): resource is { name: string; icon: string; url: string } => resource !== null);
|
|
||||||
};
|
|
||||||
|
|
||||||
const openLightbox = (index: number): void => {
|
|
||||||
currentImageIndex.value = index;
|
|
||||||
showLightbox.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const closeLightbox = (): void => {
|
|
||||||
showLightbox.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const nextImage = (): void => {
|
|
||||||
if (!processedImages.value.length) return;
|
|
||||||
currentImageIndex.value = (currentImageIndex.value + 1) % processedImages.value.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevImage = (): void => {
|
|
||||||
if (!processedImages.value.length) return;
|
|
||||||
currentImageIndex.value = (currentImageIndex.value - 1 + processedImages.value.length) % processedImages.value.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleImageError = (event: Event): void => {
|
|
||||||
const target = event.target as HTMLImageElement;
|
|
||||||
target.src = '/images/default-project.png';
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleIconError = (event: Event): void => {
|
|
||||||
const target = event.target as HTMLImageElement;
|
|
||||||
target.src = '/images/default-icon.png';
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleLightboxImageError = (event: Event): void => {
|
|
||||||
const target = event.target as HTMLImageElement;
|
|
||||||
target.src = '/images/default-project.png';
|
|
||||||
closeLightbox();
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyDown = (event: KeyboardEvent): void => {
|
|
||||||
if (!showLightbox.value) return;
|
|
||||||
|
|
||||||
switch (event.key) {
|
|
||||||
case 'Escape':
|
|
||||||
closeLightbox();
|
|
||||||
break;
|
|
||||||
case 'ArrowLeft':
|
|
||||||
prevImage();
|
|
||||||
break;
|
|
||||||
case 'ArrowRight':
|
|
||||||
nextImage();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
|
||||||
await processProjectResources();
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
window.removeEventListener('keydown', handleKeyDown);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
project,
|
|
||||||
showLightbox,
|
|
||||||
currentImageIndex,
|
|
||||||
processedImages,
|
|
||||||
processedResources,
|
|
||||||
openLightbox,
|
|
||||||
closeLightbox,
|
|
||||||
nextImage,
|
|
||||||
prevImage,
|
|
||||||
handleImageError,
|
|
||||||
handleIconError,
|
|
||||||
handleLightboxImageError
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,54 +0,0 @@
|
|||||||
import { projects } from '~/config/projects';
|
|
||||||
import type { Project, ProcessedResources } from '~/types/project';
|
|
||||||
import { PATHS, DEFAULT_IMAGES } from '~/config/paths';
|
|
||||||
|
|
||||||
export const truncateDescription = (description: string): string => {
|
|
||||||
const maxLength = 150;
|
|
||||||
if (description.length <= maxLength) return description;
|
|
||||||
return description.substring(0, maxLength) + '...';
|
|
||||||
};
|
|
||||||
|
|
||||||
export const processProjectImage = async (project: Project): Promise<string> => {
|
|
||||||
try {
|
|
||||||
const response = await fetch(project.image);
|
|
||||||
if (!response.ok) {
|
|
||||||
return DEFAULT_IMAGES.PROJECT;
|
|
||||||
}
|
|
||||||
return project.image;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading project image:', error);
|
|
||||||
return DEFAULT_IMAGES.PROJECT;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const processProjectResources = async (project: Project): Promise<ProcessedResources> => {
|
|
||||||
const resources: ProcessedResources = {};
|
|
||||||
|
|
||||||
if (project.github) {
|
|
||||||
resources.github = {
|
|
||||||
url: project.github,
|
|
||||||
icon: PATHS.ICONS.GITHUB
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (project.demo) {
|
|
||||||
resources.demo = {
|
|
||||||
url: project.demo,
|
|
||||||
icon: PATHS.ICONS.DEMO
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return resources;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleImageError = (event: Event): void => {
|
|
||||||
const target = event.target as HTMLImageElement;
|
|
||||||
target.src = DEFAULT_IMAGES.PROJECT;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleIconError = (event: Event): void => {
|
|
||||||
const target = event.target as HTMLImageElement;
|
|
||||||
target.src = DEFAULT_IMAGES.ICON;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { projects };
|
|
@ -1,90 +0,0 @@
|
|||||||
import { ref, onMounted, computed } from 'vue';
|
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
import type { Project } from '~/types/project';
|
|
||||||
import {
|
|
||||||
projects,
|
|
||||||
truncateDescription,
|
|
||||||
processProjectImage,
|
|
||||||
processProjectResources,
|
|
||||||
handleImageError,
|
|
||||||
handleIconError,
|
|
||||||
} from "~/assets/ts/pages/projects";
|
|
||||||
import { TEXTS } from '~/config/content';
|
|
||||||
import { DEFAULT_IMAGES } from '~/config/paths';
|
|
||||||
|
|
||||||
interface ProjectResource {
|
|
||||||
url: string;
|
|
||||||
icon: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProcessedResources {
|
|
||||||
github?: ProjectResource;
|
|
||||||
demo?: ProjectResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useProjects = () => {
|
|
||||||
const router = useRouter();
|
|
||||||
const projectImages = ref<Record<number, string>>({});
|
|
||||||
const projectResources = ref<Record<number, ProcessedResources>>({});
|
|
||||||
const isLoading = ref(true);
|
|
||||||
const error = ref<string | null>(null);
|
|
||||||
|
|
||||||
const filteredProjects = computed(() => {
|
|
||||||
return projects;
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadProjectResources = async () => {
|
|
||||||
try {
|
|
||||||
const loadPromises = projects.map(async (project: Project) => {
|
|
||||||
try {
|
|
||||||
const [image, resources] = await Promise.all([
|
|
||||||
processProjectImage(project),
|
|
||||||
processProjectResources(project)
|
|
||||||
]);
|
|
||||||
|
|
||||||
projectImages.value[project.id] = image;
|
|
||||||
projectResources.value[project.id] = resources;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading resources for project ${project.id}:', error);
|
|
||||||
projectImages.value[project.id] = DEFAULT_IMAGES.PROJECT;
|
|
||||||
projectResources.value[project.id] = {};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(loadPromises);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error loading project resources:', error);
|
|
||||||
} finally {
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getProjectStatus = (status: Project['status']) => {
|
|
||||||
switch (status) {
|
|
||||||
case 'completed':
|
|
||||||
return TEXTS.PROJECTS.STATUS.COMPLETED;
|
|
||||||
case 'in-progress':
|
|
||||||
return TEXTS.PROJECTS.STATUS.IN_PROGRESS;
|
|
||||||
case 'planned':
|
|
||||||
return TEXTS.PROJECTS.STATUS.PLANNED;
|
|
||||||
default:
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
loadProjectResources();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
projects: filteredProjects,
|
|
||||||
projectImages,
|
|
||||||
projectResources,
|
|
||||||
isLoading,
|
|
||||||
error,
|
|
||||||
truncateDescription,
|
|
||||||
handleImageError,
|
|
||||||
handleIconError,
|
|
||||||
getProjectStatus
|
|
||||||
};
|
|
||||||
};
|
|
Loading…
Reference in new issue