chore(initial): ajout de la structure de base du projet avec Vite et Vue 3

- Création des fichiers de configuration pour ESLint, Prettier, et Tailwind CSS
- Ajout de la configuration de l'éditeur avec .editorconfig
- Mise en place de la structure de répertoires pour les composants, les pages, et les données
- Intégration de la gestion des langues avec vue-i18n
- Ajout de la configuration de Vite et des dépendances nécessaires
- Création des fichiers de localisation pour l'anglais et le français
- Ajout de la structure de base pour le portfolio avec des exemples de projets
- Mise en place des composants de base pour l'interface utilisateur
This commit is contained in:
Mr¤KayJayDee
2025-06-22 15:00:35 +02:00
commit cc7368b550
122 changed files with 11938 additions and 0 deletions

View File

@@ -0,0 +1,133 @@
<script setup lang="ts">
import { computed } from 'vue'
import { type Technology } from '@/types'
import { useAssets } from '@/composables/useAssets'
import { techStack } from '@/data/techstack'
interface Props {
tech: Technology | string
showLevel?: boolean
showImage?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showLevel: true,
showImage: true
})
const { getImageUrl } = useAssets()
// Get the technology data (handle both string and object)
const techData = computed(() => {
if (typeof props.tech === 'string') {
// Create a mapping for technologies that don't match exactly
const techMapping: Record<string, string> = {
'Three.js': 'JavaScript',
'WebGL': 'JavaScript',
'Discord.js': 'JavaScript',
'Express': 'Node.js',
'Canvas': 'JavaScript',
'Insta.js': 'JavaScript',
'Instagram API': 'JavaScript',
'Crowdin API': 'JavaScript',
'Cron': 'Node.js'
}
// Try to find the exact match first
let foundTech = Object.values(techStack)
.flat()
.find(t => t.name.toLowerCase() === props.tech.toLowerCase())
// If not found, try the mapping
if (!foundTech && techMapping[props.tech]) {
foundTech = Object.values(techStack)
.flat()
.find(t => t.name.toLowerCase() === techMapping[props.tech].toLowerCase())
}
if (foundTech) {
return foundTech
}
// Fallback: create a basic tech object from string
return {
name: props.tech,
image: '', // No image for unknown techs
level: 'Intermediate' as const
}
}
return props.tech
})
// Get the actual image URL
const imageUrl = computed(() => {
if (!techData.value.image) return ''
return getImageUrl(techData.value.image)
})
const getLevelColor = (level: string) => {
switch (level) {
case 'Advanced':
return 'badge-success'
case 'Intermediate':
return 'badge-primary'
case 'Beginner':
return 'badge-secondary'
default:
return 'badge-secondary'
}
}
</script>
<template>
<div class="tech-badge">
<!-- Tech image -->
<img v-if="showImage && imageUrl" :src="imageUrl" :alt="techData.name" class="tech-image" loading="lazy">
<!-- Tech name -->
<span class="tech-name">{{ techData.name }}</span>
<!-- Level indicator -->
<span v-if="showLevel" :class="['badge', getLevelColor(techData.level)]" class="tech-level">
{{ techData.level }}
</span>
</div>
</template>
<style scoped>
.tech-badge {
display: inline-flex;
align-items: center;
gap: var(--space-sm);
padding: var(--space-sm) var(--space-md);
background: var(--bg-primary);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius-lg);
transition: all var(--transition-fast);
white-space: nowrap;
}
.tech-badge:hover {
box-shadow: var(--shadow-md);
transform: translateY(-1px);
}
.tech-image {
width: 20px;
height: 20px;
object-fit: contain;
flex-shrink: 0;
}
.tech-name {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--text-primary);
}
.tech-level {
font-size: var(--font-size-xs);
padding: var(--space-xs) var(--space-sm);
}
</style>