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