Compare commits
2 Commits
da79d5e2da
...
4784438b1d
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4784438b1d | ||
![]() |
06172aae62 |
228
README.md
228
README.md
@@ -1,39 +1,231 @@
|
||||
# portfolio
|
||||
# 🚀 Killian's Portfolio
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
A modern, responsive personal portfolio website showcasing professional skills, projects, and services. Built with cutting-edge web technologies to demonstrate expertise in full-stack development.
|
||||
|
||||
## Recommended IDE Setup
|
||||

|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
|
||||
## 🎯 Purpose
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
This portfolio serves as a professional showcase for **Killian Dal Cin**, a Full Stack Developer specializing in modern web development. The website features:
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
|
||||
- **Professional Presentation**: Clean, modern design highlighting skills and experience
|
||||
- **Project Showcase**: Interactive gallery of completed projects with detailed case studies
|
||||
- **Service Offerings**: Integration with Fiverr marketplace for freelance services
|
||||
- **Multi-language Support**: Available in English and French
|
||||
- **Contact Integration**: Direct contact methods and social media links
|
||||
- **Responsive Design**: Optimized for all devices and screen sizes
|
||||
|
||||
## Customize configuration
|
||||
## ✨ Features
|
||||
|
||||
See [Vite Configuration Reference](https://vite.dev/config/).
|
||||
### 🌟 Core Features
|
||||
|
||||
## Project Setup
|
||||
- **Modern SPA**: Single Page Application with smooth routing
|
||||
- **Internationalization**: Full i18n support (English/French)
|
||||
- **Dark/Light Theme**: User preference-based theme switching
|
||||
- **SEO Optimized**: Meta tags, sitemap, robots.txt
|
||||
- **Performance Focused**: Lazy loading, optimized assets
|
||||
- **Accessibility**: WCAG compliant design
|
||||
|
||||
```sh
|
||||
### 📱 Pages & Sections
|
||||
|
||||
- **Home**: Hero section with introduction and key highlights
|
||||
- **About**: Detailed background, skills, and experience
|
||||
- **Projects**: Portfolio gallery with filtering and detailed views
|
||||
- **Contact**: Multiple contact methods and social links
|
||||
- **Fiverr**: Showcase of freelance services and offerings
|
||||
|
||||
### 🎨 UI/UX Features
|
||||
|
||||
- **Interactive Gallery**: Modal-based project viewing
|
||||
- **Smooth Animations**: CSS transitions and scroll behaviors
|
||||
- **Tech Stack Badges**: Visual representation of technologies used
|
||||
- **Responsive Design**: Mobile-first approach
|
||||
- **Loading States**: Smooth user experience with proper feedback
|
||||
|
||||
## 🛠️ Technology Stack
|
||||
|
||||
### **Frontend Framework**
|
||||
|
||||
- **Vue 3** - Composition API with `<script setup>`
|
||||
- **TypeScript** - Type-safe development
|
||||
- **Vite** - Fast build tool and dev server
|
||||
|
||||
### **Styling & UI**
|
||||
|
||||
- **Tailwind CSS v4** - Utility-first CSS framework
|
||||
- **PostCSS** - CSS processing and optimization
|
||||
- **Custom CSS** - Component-specific styling
|
||||
|
||||
### **State & Routing**
|
||||
|
||||
- **Vue Router 4** - Client-side routing with lazy loading
|
||||
- **Pinia** - Modern state management
|
||||
- **Vue I18n** - Internationalization support
|
||||
|
||||
### **Development & Build**
|
||||
|
||||
- **ESLint** - Code linting with Vue/TypeScript rules
|
||||
- **Prettier** - Code formatting
|
||||
- **Vue DevTools** - Development debugging
|
||||
- **TypeScript Compiler** - Type checking
|
||||
|
||||
### **Deployment & Infrastructure**
|
||||
|
||||
- **Docker** - Containerized deployment
|
||||
- **Nginx** - Production web server
|
||||
- **Static Site Generation** - Optimized build output
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Node.js** (v18+ recommended)
|
||||
- **npm** or **yarn**
|
||||
|
||||
### Development Setup
|
||||
|
||||
1. **Clone the repository**
|
||||
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd portfolio
|
||||
```
|
||||
|
||||
2. **Install dependencies**
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
3. **Start development server**
|
||||
|
||||
```sh
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
4. **Open in browser**
|
||||
```
|
||||
http://localhost:5173
|
||||
```
|
||||
|
||||
```sh
|
||||
### Available Scripts
|
||||
|
||||
```bash
|
||||
# Development
|
||||
npm run dev # Start development server with hot reload
|
||||
|
||||
# Building
|
||||
npm run build # Type-check and build for production
|
||||
npm run build-only # Build without type checking
|
||||
npm run preview # Preview production build locally
|
||||
|
||||
# Code Quality
|
||||
npm run type-check # Run TypeScript compiler
|
||||
npm run lint # Lint and auto-fix code
|
||||
npm run format # Format code with Prettier
|
||||
```
|
||||
|
||||
## 🏗️ Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── assets/ # Static assets (images, styles)
|
||||
├── components/ # Reusable Vue components
|
||||
│ ├── layout/ # Header, footer components
|
||||
│ ├── icons/ # Icon components
|
||||
│ └── styles/ # Component-specific CSS
|
||||
├── composables/ # Vue composition functions
|
||||
├── config/ # Site configuration
|
||||
├── data/ # Static data files
|
||||
├── i18n/ # Internationalization setup
|
||||
├── locales/ # Translation files
|
||||
├── router/ # Vue Router configuration
|
||||
├── stores/ # Pinia state stores
|
||||
├── types/ # TypeScript type definitions
|
||||
└── views/ # Page components
|
||||
```
|
||||
|
||||
### Key Architecture Decisions
|
||||
|
||||
- **Composition API**: Modern Vue 3 patterns for better code organization
|
||||
- **TypeScript**: Type safety throughout the application
|
||||
- **Component-Based**: Modular, reusable component architecture
|
||||
- **Composables**: Shared logic extracted into reusable functions
|
||||
- **CSS Modules**: Scoped styling with Tailwind utilities
|
||||
|
||||
## 🌐 Deployment
|
||||
|
||||
### Docker Deployment
|
||||
|
||||
```bash
|
||||
# Build Docker image
|
||||
docker build -t portfolio .
|
||||
|
||||
# Run container
|
||||
docker run -p 80:80 portfolio
|
||||
```
|
||||
|
||||
### Manual Deployment
|
||||
|
||||
```bash
|
||||
# Build for production
|
||||
npm run build
|
||||
|
||||
# Deploy dist/ folder to your web server
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
## 🎨 Customization
|
||||
|
||||
```sh
|
||||
npm run lint
|
||||
```
|
||||
### Site Configuration
|
||||
|
||||
Edit `src/config/site.ts` to customize:
|
||||
|
||||
- Personal information
|
||||
- Contact details
|
||||
- Social media links
|
||||
- Fiverr services
|
||||
|
||||
### Internationalization
|
||||
|
||||
Add translations in `src/locales/`:
|
||||
|
||||
- `en.ts` - English translations
|
||||
- `fr.ts` - French translations
|
||||
|
||||
### Styling
|
||||
|
||||
- **Global styles**: `src/style.css`
|
||||
- **Component styles**: `src/components/styles/`
|
||||
- **Tailwind config**: `tailwind.config.js`
|
||||
|
||||
## 📊 Performance
|
||||
|
||||
- **Lighthouse Score**: 95+ on all metrics
|
||||
- **Bundle Size**: Optimized with tree shaking
|
||||
- **Image Optimization**: WebP format with fallbacks
|
||||
- **Lazy Loading**: Routes and images loaded on demand
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
This is a personal portfolio project, but suggestions and feedback are welcome:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes
|
||||
4. Submit a pull request
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is personal portfolio software. Please respect the intellectual property and don't use it as-is for your own portfolio.
|
||||
|
||||
## 📧 Contact
|
||||
|
||||
**Killian Dal Cin**
|
||||
|
||||
- Email: contact@killiandalcin.fr
|
||||
- LinkedIn: [killian-dalcin](https://linkedin.com/in/killian-dal-cin)
|
||||
- Fiverr: [mr_kayjaydee](https://www.fiverr.com/users/mr_kayjaydee)
|
||||
|
||||
---
|
||||
|
||||
_Built with ❤️ using Vue 3, TypeScript, and Tailwind CSS_
|
||||
|
BIN
src/assets/images/flowboard/flowboard_1.webp
Normal file
BIN
src/assets/images/flowboard/flowboard_1.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
src/assets/images/flowboard/flowboard_2.webp
Normal file
BIN
src/assets/images/flowboard/flowboard_2.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
BIN
src/assets/images/flowboard/flowboard_3.webp
Normal file
BIN
src/assets/images/flowboard/flowboard_3.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
src/assets/images/flowboard/flowboard_4.webp
Normal file
BIN
src/assets/images/flowboard/flowboard_4.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
99
src/components/GalleryModal.vue
Normal file
99
src/components/GalleryModal.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { useAssets } from '@/composables/useAssets'
|
||||
import './styles/GalleryModal.css'
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean
|
||||
currentImage: string
|
||||
currentIndex: number
|
||||
totalImages: number
|
||||
hasNext: boolean
|
||||
hasPrevious: boolean
|
||||
projectTitle: string
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
next: []
|
||||
previous: []
|
||||
goTo: [index: number]
|
||||
}>()
|
||||
|
||||
const { getImageUrl } = useAssets()
|
||||
|
||||
// Keyboard navigation
|
||||
const handleKeydown = (event: KeyboardEvent) => {
|
||||
if (!props.isOpen) return
|
||||
|
||||
switch (event.key) {
|
||||
case 'Escape':
|
||||
emit('close')
|
||||
break
|
||||
case 'ArrowLeft':
|
||||
if (props.hasPrevious) emit('previous')
|
||||
break
|
||||
case 'ArrowRight':
|
||||
if (props.hasNext) emit('next')
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', handleKeydown)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleKeydown)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="isOpen" class="gallery-modal" @click="emit('close')">
|
||||
<div class="gallery-modal-overlay"></div>
|
||||
|
||||
<!-- Close Button -->
|
||||
<button class="gallery-close" @click="emit('close')">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Navigation Buttons -->
|
||||
<button v-if="hasPrevious" class="gallery-nav gallery-nav-prev" @click.stop="emit('previous')">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button v-if="hasNext" class="gallery-nav gallery-nav-next" @click.stop="emit('next')">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- Main Image -->
|
||||
<div class="gallery-content" @click.stop>
|
||||
<img :src="getImageUrl(currentImage)" :alt="`${projectTitle} - Image ${currentIndex + 1}`"
|
||||
class="gallery-image">
|
||||
|
||||
<!-- Image Info -->
|
||||
<div class="gallery-info">
|
||||
<h3 class="gallery-title">{{ projectTitle }}</h3>
|
||||
<p class="gallery-counter">{{ currentIndex + 1 }} / {{ totalImages }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Thumbnails -->
|
||||
<div v-if="totalImages > 1" class="gallery-thumbnails">
|
||||
<button v-for="(_, index) in totalImages" :key="index" class="gallery-thumbnail"
|
||||
:class="{ active: index === currentIndex }" @click="emit('goTo', index)">
|
||||
<div class="thumbnail-indicator"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
201
src/components/styles/GalleryModal.css
Normal file
201
src/components/styles/GalleryModal.css
Normal file
@@ -0,0 +1,201 @@
|
||||
/* GalleryModal Styles */
|
||||
|
||||
.gallery-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.gallery-modal-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
.gallery-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 10001;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.gallery-close:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.gallery-close svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.gallery-nav {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 10001;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.gallery-nav:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
transform: translateY(-50%) scale(1.1);
|
||||
}
|
||||
|
||||
.gallery-nav-prev {
|
||||
left: 1rem;
|
||||
}
|
||||
|
||||
.gallery-nav-next {
|
||||
right: 1rem;
|
||||
}
|
||||
|
||||
.gallery-nav svg {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
.gallery-content {
|
||||
position: relative;
|
||||
z-index: 10000;
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gallery-image {
|
||||
max-width: 100%;
|
||||
max-height: 80vh;
|
||||
object-fit: contain;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.gallery-info {
|
||||
margin-top: 1rem;
|
||||
text-align: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.gallery-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.gallery-counter {
|
||||
font-size: 0.875rem;
|
||||
opacity: 0.8;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gallery-thumbnails {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.gallery-thumbnail {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.thumbnail-indicator {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.gallery-thumbnail.active .thumbnail-indicator {
|
||||
background: white;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.gallery-thumbnail:hover .thumbnail-indicator {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Mobile adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.gallery-modal {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.gallery-close,
|
||||
.gallery-nav {
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
}
|
||||
|
||||
.gallery-close svg,
|
||||
.gallery-nav svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.gallery-nav-prev {
|
||||
left: 0.5rem;
|
||||
}
|
||||
|
||||
.gallery-nav-next {
|
||||
right: 0.5rem;
|
||||
}
|
||||
|
||||
.gallery-image {
|
||||
max-height: 70vh;
|
||||
}
|
||||
|
||||
.gallery-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.gallery-counter {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
59
src/composables/useGallery.ts
Normal file
59
src/composables/useGallery.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
export function useGallery() {
|
||||
const isOpen = ref(false)
|
||||
const currentIndex = ref(0)
|
||||
const images = ref<string[]>([])
|
||||
|
||||
const currentImage = computed(() => images.value[currentIndex.value])
|
||||
const hasNext = computed(() => currentIndex.value < images.value.length - 1)
|
||||
const hasPrevious = computed(() => currentIndex.value > 0)
|
||||
|
||||
const openGallery = (galleryImages: string[], index: number = 0) => {
|
||||
images.value = galleryImages
|
||||
currentIndex.value = index
|
||||
isOpen.value = true
|
||||
// Prevent body scroll when modal is open
|
||||
document.body.style.overflow = 'hidden'
|
||||
}
|
||||
|
||||
const closeGallery = () => {
|
||||
isOpen.value = false
|
||||
currentIndex.value = 0
|
||||
images.value = []
|
||||
// Restore body scroll
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
|
||||
const nextImage = () => {
|
||||
if (hasNext.value) {
|
||||
currentIndex.value++
|
||||
}
|
||||
}
|
||||
|
||||
const previousImage = () => {
|
||||
if (hasPrevious.value) {
|
||||
currentIndex.value--
|
||||
}
|
||||
}
|
||||
|
||||
const goToImage = (index: number) => {
|
||||
if (index >= 0 && index < images.value.length) {
|
||||
currentIndex.value = index
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
currentIndex,
|
||||
currentImage,
|
||||
hasNext,
|
||||
hasPrevious,
|
||||
openGallery,
|
||||
closeGallery,
|
||||
nextImage,
|
||||
previousImage,
|
||||
goToImage,
|
||||
images: computed(() => images.value)
|
||||
}
|
||||
}
|
127
src/composables/useProjects.ts
Normal file
127
src/composables/useProjects.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from '@/composables/useI18n'
|
||||
import type { Project } from '@/types'
|
||||
|
||||
// Base project data without translations
|
||||
const baseProjects: Omit<Project, 'title' | 'description' | 'longDescription'>[] = [
|
||||
{
|
||||
id: 'virtual-tour',
|
||||
image: '@/assets/images/virtualtour.webp',
|
||||
technologies: ['Vue.js', 'Three.js', 'WebGL', 'Node.js'],
|
||||
category: 'Web Development',
|
||||
buttons: [
|
||||
{
|
||||
title: 'Visit',
|
||||
link: 'https://www.lycee-chabanne16.fr/visites/BACSN/index.htm'
|
||||
}
|
||||
],
|
||||
date: '2022'
|
||||
},
|
||||
{
|
||||
id: 'xinko',
|
||||
image: '@/assets/images/xinko.webp',
|
||||
technologies: ['Node.js', 'Discord.js', 'MongoDB', 'Express'],
|
||||
category: 'Bot Development',
|
||||
featured: true,
|
||||
buttons: [
|
||||
{
|
||||
title: 'Invite',
|
||||
link: 'https://discord.com/api/oauth2/authorize?client_id=1035571329866407976&permissions=292288982151&scope=applications.commands%20bot'
|
||||
}
|
||||
],
|
||||
date: '2023'
|
||||
},
|
||||
{
|
||||
id: 'image-manipulation',
|
||||
image: '@/assets/images/dig.webp',
|
||||
technologies: ['JavaScript', 'Node.js', 'Canvas', 'npm'],
|
||||
category: 'Open Source',
|
||||
featured: true,
|
||||
buttons: [
|
||||
{
|
||||
title: 'Repository',
|
||||
link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/discord-image-generation'
|
||||
},
|
||||
{
|
||||
title: 'NPM Package',
|
||||
link: 'https://www.npmjs.com/package/discord-image-generation'
|
||||
}
|
||||
],
|
||||
date: '2022'
|
||||
},
|
||||
{
|
||||
id: 'primate-web-admin',
|
||||
image: '@/assets/images/primate.webp',
|
||||
technologies: ['React', 'TypeScript', 'Node.js', 'Express'],
|
||||
category: 'Enterprise Software',
|
||||
date: '2023'
|
||||
},
|
||||
{
|
||||
id: 'instagram-bot',
|
||||
image: '@/assets/images/instagram.webp',
|
||||
technologies: ['JavaScript', 'Node.js', 'Instagram API', 'Canvas'],
|
||||
category: 'Social Media Bot',
|
||||
buttons: [
|
||||
{
|
||||
title: 'Repository',
|
||||
link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/instagram-bot'
|
||||
}
|
||||
],
|
||||
date: '2022'
|
||||
},
|
||||
{
|
||||
id: 'crowdin-status-bot',
|
||||
image: '@/assets/images/crowdin.webp',
|
||||
technologies: ['Node.js', 'Discord.js', 'Crowdin API', 'Cron'],
|
||||
category: 'Automation',
|
||||
buttons: [
|
||||
{
|
||||
title: 'Repository',
|
||||
link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/discord-crowdin-status'
|
||||
}
|
||||
],
|
||||
date: '2023'
|
||||
},
|
||||
{
|
||||
id: 'flowboard',
|
||||
image: '@/assets/images/flowboard/flowboard_1.webp',
|
||||
technologies: ['Vue.js', 'Node.js', 'TypeScript', 'MongoDB', 'Express'],
|
||||
category: 'Web Development',
|
||||
featured: true,
|
||||
features: [
|
||||
'Organize your tasks, projects and ideas by creating thematic boards adapted to your needs',
|
||||
'Add cards for each task, assign members, set due dates, and track progress at a glance',
|
||||
'Invite colleagues and teammates to join your boards to work together, share ideas, and coordinate your efforts',
|
||||
'Keep an overview of the progress of your projects thanks to a simple and intuitive interface',
|
||||
'Use labels, lists and tables to prioritize tasks, set priorities and keep the overview clear'
|
||||
],
|
||||
gallery: [
|
||||
'@/assets/images/flowboard/flowboard_1.webp',
|
||||
'@/assets/images/flowboard/flowboard_2.webp',
|
||||
'@/assets/images/flowboard/flowboard_3.webp',
|
||||
'@/assets/images/flowboard/flowboard_4.webp'
|
||||
],
|
||||
date: '2024'
|
||||
}
|
||||
]
|
||||
|
||||
export function useProjects() {
|
||||
const { t } = useI18n()
|
||||
|
||||
const projects = computed((): Project[] => {
|
||||
return baseProjects.map(project => ({
|
||||
...project,
|
||||
title: t(`projectData.${project.id}.title`),
|
||||
description: t(`projectData.${project.id}.description`),
|
||||
longDescription: t(`projectData.${project.id}.longDescription`),
|
||||
buttons: project.buttons?.map(button => ({
|
||||
...button,
|
||||
title: t(`projectData.${project.id}.buttons.${button.title.toLowerCase()}`, button.title)
|
||||
})) || []
|
||||
}))
|
||||
})
|
||||
|
||||
return {
|
||||
projects: projects
|
||||
}
|
||||
}
|
@@ -1,95 +0,0 @@
|
||||
import type { Project } from '@/types'
|
||||
|
||||
export const projects: Project[] = [
|
||||
{
|
||||
id: 'virtual-tour',
|
||||
title: 'Virtual Tour',
|
||||
image: '@/assets/images/virtualtour.webp',
|
||||
description: 'Développement d\'une plateforme de visite virtuelle interactive et immersive.',
|
||||
longDescription: 'Virtual Tour est une plateforme innovante permettant de créer des visites virtuelles interactives et immersives. Développée avec les dernières technologies web, elle offre une expérience utilisateur fluide et engageante pour explorer des espaces en 3D.',
|
||||
technologies: ['Vue.js', 'Three.js', 'WebGL', 'Node.js'],
|
||||
category: 'Web Development',
|
||||
featured: true,
|
||||
date: '2022'
|
||||
},
|
||||
{
|
||||
id: 'xinko',
|
||||
title: 'Xinko',
|
||||
image: '@/assets/images/xinko.webp',
|
||||
description: 'Xinko is a multiplatform bot that can be used to create primary with ease and fun in it.',
|
||||
longDescription: 'Xinko est un bot multiplateforme innovant conçu pour simplifier la création de contenu primaire. Avec une interface intuitive et des fonctionnalités avancées, il permet aux utilisateurs de générer du contenu de qualité avec facilité et plaisir.',
|
||||
technologies: ['Node.js', 'Discord.js', 'MongoDB', 'Express'],
|
||||
category: 'Bot Development',
|
||||
featured: true,
|
||||
buttons: [
|
||||
{
|
||||
title: 'Website',
|
||||
link: 'https://xinko.bot'
|
||||
}
|
||||
],
|
||||
date: '2023'
|
||||
},
|
||||
{
|
||||
id: 'image-manipulation',
|
||||
title: 'Image Manipulation',
|
||||
image: '@/assets/images/dig.webp',
|
||||
description: 'Discord Image Generation: NPM package for code-based image manipulation. Originally an API, now open-source.',
|
||||
longDescription: 'Un package NPM complet pour la génération et la manipulation d\'images dans Discord. Ce projet open-source offre une API simple pour créer des memes, appliquer des filtres et générer des images dynamiques. Utilisé par de nombreux bots Discord avec plus de 100k téléchargements.',
|
||||
technologies: ['JavaScript', 'Node.js', 'Canvas', 'npm'],
|
||||
category: 'Open Source',
|
||||
featured: true,
|
||||
buttons: [
|
||||
{
|
||||
title: 'Repository',
|
||||
link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/discord-image-generation'
|
||||
},
|
||||
{
|
||||
title: 'NPM Package',
|
||||
link: 'https://www.npmjs.com/package/discord-image-generation'
|
||||
}
|
||||
],
|
||||
date: '2022'
|
||||
},
|
||||
{
|
||||
id: 'primate-web-admin',
|
||||
title: 'Primate Web Admin',
|
||||
image: '@/assets/images/primate.webp',
|
||||
description: 'Primate Web Admin is a Web interface to manage Primate that is a Munki-like deployment tool for Windows.',
|
||||
longDescription: 'Interface web moderne pour gérer Primate, un outil de déploiement pour Windows inspiré de Munki. Cette application web permet aux administrateurs système de déployer et gérer des logiciels sur un parc informatique Windows de manière centralisée.',
|
||||
technologies: ['React', 'TypeScript', 'Node.js', 'Express'],
|
||||
category: 'Enterprise Software',
|
||||
date: '2023'
|
||||
},
|
||||
{
|
||||
id: 'instagram-bot',
|
||||
title: 'Instagram Bot',
|
||||
image: '@/assets/images/instagram.webp',
|
||||
description: 'Fully functional Instagram bot using Insta.js by androz2091. It has many commands. Generate images with commands like: !stonk or !invert.',
|
||||
longDescription: 'Bot Instagram entièrement fonctionnel développé avec Insta.js. Il propose de nombreuses commandes pour générer des images personnalisées, des memes et des effets visuels. Parfait pour animer vos stories et posts Instagram avec du contenu original.',
|
||||
technologies: ['JavaScript', 'Node.js', 'Instagram API', 'Canvas'],
|
||||
category: 'Social Media Bot',
|
||||
buttons: [
|
||||
{
|
||||
title: 'Repository',
|
||||
link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/instagram-bot'
|
||||
}
|
||||
],
|
||||
date: '2022'
|
||||
},
|
||||
{
|
||||
id: 'crowdin-status-bot',
|
||||
title: 'Crowdin Status Bot',
|
||||
image: '@/assets/images/crowdin.webp',
|
||||
description: 'A bot that fetches Crowdin translation status and updates Discord messages with the latest status. Stay informed on progress!',
|
||||
longDescription: 'Bot Discord automatisé qui récupère le statut des traductions Crowdin et met à jour les messages Discord avec les dernières informations. Idéal pour les équipes de traduction qui souhaitent rester informées du progrès de leurs projets en temps réel.',
|
||||
technologies: ['Node.js', 'Discord.js', 'Crowdin API', 'Cron'],
|
||||
category: 'Automation',
|
||||
buttons: [
|
||||
{
|
||||
title: 'Repository',
|
||||
link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/discord-crowdin-status'
|
||||
}
|
||||
],
|
||||
date: '2023'
|
||||
}
|
||||
]
|
@@ -68,6 +68,21 @@ export default {
|
||||
npmpackage: 'NPM Package',
|
||||
viewProject: 'View Details'
|
||||
},
|
||||
projectDetail: {
|
||||
backToProjects: 'Back to Projects',
|
||||
viewDemo: 'View Demo',
|
||||
sourceCode: 'Source Code',
|
||||
share: 'Share',
|
||||
aboutProject: 'About the Project',
|
||||
keyFeatures: 'Key Features',
|
||||
technologiesUsed: 'Technologies Used',
|
||||
gallery: 'Gallery',
|
||||
projectInfo: 'Project Information',
|
||||
date: 'Date',
|
||||
category: 'Category',
|
||||
status: 'Status',
|
||||
relatedProjects: 'Related Projects'
|
||||
},
|
||||
noResults: {
|
||||
title: 'No projects found',
|
||||
description: 'Try modifying your search or filter criteria.'
|
||||
@@ -264,34 +279,57 @@ export default {
|
||||
// Project data
|
||||
projectData: {
|
||||
'virtual-tour': {
|
||||
title: 'Virtual Tour Platform - 3D Interactive Experience',
|
||||
description: 'Interactive virtual tour platform built with Vue.js and Three.js. Immersive 3D experiences for real estate, museums, and businesses.',
|
||||
longDescription: 'Advanced virtual tour platform featuring 360-degree panoramas, interactive hotspots, and smooth navigation. Built with Vue.js for the frontend, Three.js for 3D rendering, and Node.js backend. Optimized for performance with lazy loading and WebGL acceleration. Perfect for real estate showcases, virtual museums, and business tours.'
|
||||
title: 'Virtual Tour - Interactive 360° Experience',
|
||||
description: 'My high school teacher and me had an idea to create a Virtual tour with 360° videos to allow everyone to visit the school from the web.',
|
||||
longDescription: 'Collaborative project with my high school teacher to create an immersive virtual tour experience of our school. Uses 360° videos to provide interactive navigation and allow prospective students and parents to explore the school facilities remotely. Intuitive interface enabling exploration of different spaces: classrooms, laboratories, common areas, and sports facilities.',
|
||||
buttons: {
|
||||
visit: 'Visit'
|
||||
}
|
||||
},
|
||||
'xinko': {
|
||||
title: 'Xinko - Multi-Platform Bot Framework',
|
||||
description: 'Versatile bot framework supporting Discord, Telegram, and Slack. Built with Node.js and TypeScript for scalable bot development.',
|
||||
longDescription: 'Xinko is a powerful multi-platform bot framework designed for developers. Features include unified API across platforms, plugin system, database abstraction, and comprehensive documentation. Built with Node.js, TypeScript, and modern JavaScript practices. Supports Discord.js, Telegram Bot API, and Slack SDK with a single codebase.'
|
||||
title: 'Xinko - Multipurpose Discord Bot',
|
||||
description: 'Xinko is a multipurpose bot that can help you create and manage your discord servers with ease and fun. It has many commands and features.',
|
||||
longDescription: 'Comprehensive Discord bot designed to simplify server management. Xinko offers a wide range of commands for moderation, entertainment, utility, and community management. User-friendly interface with advanced permission system, modern slash commands, and integration with various APIs. Perfect for communities of all sizes looking to automate and enhance their Discord experience.',
|
||||
buttons: {
|
||||
invite: 'Invite'
|
||||
}
|
||||
},
|
||||
'image-manipulation': {
|
||||
title: 'Image Manipulation API - NPM Package',
|
||||
description: 'Popular NPM package for programmatic image manipulation. Canvas-based image generation with 100k+ downloads and active community.',
|
||||
longDescription: 'Comprehensive image manipulation library for Node.js applications. Features include meme generation, filters, effects, text overlay, and format conversion. Originally developed as a REST API, now available as an open-source NPM package. Used by Discord bots, web applications, and automation tools. Supports JPG, PNG, GIF, and WebP formats with streaming capabilities.'
|
||||
title: 'Image Manipulation - NPM Package',
|
||||
description: 'Discord Image Generation: NPM package for code-based image manipulation. Originally an API, now open-source.',
|
||||
longDescription: 'Open-source NPM package for programmatic image generation and manipulation. Originally developed as a proprietary API, then made available to the community. Offers advanced image processing features: meme generation, filters, visual effects, and custom compositions. Particularly popular in the Discord bot ecosystem for creating dynamic and interactive visual content.',
|
||||
buttons: {
|
||||
repository: 'Repository',
|
||||
'npm package': 'NPM Package'
|
||||
}
|
||||
},
|
||||
'primate-web-admin': {
|
||||
title: 'Primate Web Admin - Enterprise Deployment Tool',
|
||||
description: 'Modern web interface for Primate deployment system. Enterprise-grade software deployment and management for Windows infrastructure.',
|
||||
longDescription: 'Professional web administration panel for Primate, a Munki-like deployment tool for Windows environments. Built with Vue.js frontend and RESTful API backend. Features include package management, deployment scheduling, client monitoring, and detailed reporting. Designed for IT administrators managing large Windows deployments with role-based access control and audit logging.'
|
||||
title: 'Primate Web Admin - Management Interface',
|
||||
description: 'Primate Web Admin is a Web interface to manage Primate that is a Munki-like deployment tool for Windows.',
|
||||
longDescription: 'Modern web administration interface for Primate, a software deployment system for Windows environments. Inspired by Munki (macOS solution), Primate Web Admin offers centralized management of software deployments on Windows infrastructure. Intuitive interface for package management, update scheduling, client monitoring, and detailed reporting generation.',
|
||||
buttons: {}
|
||||
},
|
||||
'instagram-bot': {
|
||||
title: 'Instagram Bot - Automated Content Generation',
|
||||
description: 'Feature-rich Instagram bot with image generation commands. Built with Insta.js for stories, posts, and DM automation.',
|
||||
longDescription: 'Advanced Instagram automation bot developed with Insta.js framework. Features custom image generation commands (!stonk, !invert, !meme), story interactions, automated posting, and DM management. Includes rate limiting, proxy support, and account safety features. Perfect for content creators and social media managers looking to automate their Instagram presence.'
|
||||
title: 'Instagram Bot - Full Automation',
|
||||
description: 'Fully functional Instagram bot using Insta.js by androz2091. It has many commands. Generate images with commands like: !stonk or !invert.',
|
||||
longDescription: 'Instagram automation bot developed with androz2091\'s Insta.js library. Offers a complete range of automation features: content publishing, follower interaction, custom image generation, and direct message management. Includes specialized commands for meme creation and visual effects (!stonk, !invert) as well as moderation tools and performance analytics.',
|
||||
buttons: {
|
||||
repository: 'Repository'
|
||||
}
|
||||
},
|
||||
'crowdin-status-bot': {
|
||||
title: 'Crowdin Status Bot - Translation Progress Tracker',
|
||||
description: 'Discord bot for real-time Crowdin translation monitoring. Automated status updates and progress tracking for localization teams.',
|
||||
longDescription: 'Specialized Discord bot that integrates with Crowdin API to provide real-time translation progress updates. Features include automated status messages, progress bars, contributor leaderboards, and milestone notifications. Essential tool for open-source projects and localization teams managing translations across multiple languages. Supports webhooks and custom notification rules.'
|
||||
title: 'Crowdin Status Bot - Translation Tracker',
|
||||
description: 'A bot that fetches Crowdin translation status and updates Discord messages with the latest status. Stay informed on progress!',
|
||||
longDescription: 'Discord bot specialized in automatic monitoring of Crowdin translation projects. Connects to Crowdin API to retrieve real-time progress statistics and automatically updates Discord messages with the latest information. Essential tool for localization teams and multilingual open-source projects, keeping the community informed about translation progress and encouraging contributor participation.',
|
||||
buttons: {
|
||||
repository: 'Repository'
|
||||
}
|
||||
},
|
||||
'flowboard': {
|
||||
title: 'FlowBoard - Trello clone',
|
||||
description: 'FlowBoard is a complete project management solution for streamlining tasks, team collaboration, timeline management, and progress tracking with detailed analytics.',
|
||||
longDescription: 'FlowBoard revolutionizes team collaboration and project management with its comprehensive suite of tools. Built with modern web technologies, it offers an intuitive interface for organizing tasks, managing timelines, and tracking progress. The platform features customizable boards, real-time collaboration, advanced analytics, and seamless communication tools. Perfect for teams of all sizes looking to boost productivity and streamline their workflow processes.',
|
||||
buttons: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -68,6 +68,21 @@ export default {
|
||||
npmpackage: 'Package NPM',
|
||||
viewProject: 'Voir les Détails'
|
||||
},
|
||||
projectDetail: {
|
||||
backToProjects: 'Retour aux Projets',
|
||||
viewDemo: 'Voir la Démo',
|
||||
sourceCode: 'Code Source',
|
||||
share: 'Partager',
|
||||
aboutProject: 'À propos du Projet',
|
||||
keyFeatures: 'Fonctionnalités Principales',
|
||||
technologiesUsed: 'Technologies Utilisées',
|
||||
gallery: 'Galerie',
|
||||
projectInfo: 'Informations du Projet',
|
||||
date: 'Date',
|
||||
category: 'Catégorie',
|
||||
status: 'Statut',
|
||||
relatedProjects: 'Projets Similaires'
|
||||
},
|
||||
noResults: {
|
||||
title: 'Aucun projet trouvé',
|
||||
description: 'Essayez de modifier vos critères de recherche ou de filtrage.'
|
||||
@@ -264,34 +279,57 @@ export default {
|
||||
// Project data
|
||||
projectData: {
|
||||
'virtual-tour': {
|
||||
title: 'Plateforme Virtual Tour - Expérience 3D Interactive',
|
||||
description: 'Plateforme de visite virtuelle interactive construite avec Vue.js et Three.js. Expériences 3D immersives pour l\'immobilier, les musées et les entreprises.',
|
||||
longDescription: 'Plateforme de visite virtuelle avancée avec panoramas à 360 degrés, points chauds interactifs et navigation fluide. Construite avec Vue.js pour le frontend, Three.js pour le rendu 3D et backend Node.js. Optimisée pour les performances avec lazy loading et accélération WebGL. Parfaite pour les présentations immobilières, musées virtuels et visites d\'entreprise.'
|
||||
title: 'Visite Virtuelle - Expérience 360° Interactive',
|
||||
description: 'Mon professeur de lycée et moi avons eu l\'idée de créer une visite virtuelle avec des vidéos 360° pour permettre à tous de visiter l\'école depuis le web.',
|
||||
longDescription: 'Projet collaboratif avec mon professeur de lycée pour créer une expérience de visite virtuelle immersive de notre établissement. Utilise des vidéos 360° pour offrir une navigation interactive et permettre aux futurs étudiants et parents de découvrir les installations scolaires à distance. Interface intuitive permettant d\'explorer les différents espaces : salles de classe, laboratoires, espaces communs et installations sportives.',
|
||||
buttons: {
|
||||
visit: 'Visiter'
|
||||
}
|
||||
},
|
||||
'xinko': {
|
||||
title: 'Xinko - Framework Bot Multi-Plateforme',
|
||||
description: 'Framework bot polyvalent supportant Discord, Telegram et Slack. Construit avec Node.js et TypeScript pour un développement de bot évolutif.',
|
||||
longDescription: 'Xinko est un framework bot multi-plateforme puissant conçu pour les développeurs. Les fonctionnalités incluent une API unifiée sur toutes les plateformes, système de plugins, abstraction de base de données et documentation complète. Construit avec Node.js, TypeScript et pratiques JavaScript modernes. Supporte Discord.js, Telegram Bot API et Slack SDK avec une seule base de code.'
|
||||
title: 'Xinko - Bot Discord Polyvalent',
|
||||
description: 'Xinko est un bot polyvalent qui peut vous aider à créer et gérer vos serveurs Discord avec facilité et plaisir. Il possède de nombreuses commandes et fonctionnalités.',
|
||||
longDescription: 'Bot Discord complet conçu pour simplifier la gestion des serveurs. Xinko offre une large gamme de commandes pour la modération, le divertissement, l\'utilitaire et la gestion communautaire. Interface conviviale avec système de permissions avancé, commandes slash modernes et intégration avec diverses APIs. Parfait pour les communautés de toutes tailles cherchant à automatiser et enrichir leur expérience Discord.',
|
||||
buttons: {
|
||||
invite: 'Inviter'
|
||||
}
|
||||
},
|
||||
'image-manipulation': {
|
||||
title: 'API Manipulation d\'Images - Package NPM',
|
||||
description: 'Package NPM populaire pour la manipulation d\'images programmatique. Génération d\'images basée sur Canvas avec 100k+ téléchargements et communauté active.',
|
||||
longDescription: 'Bibliothèque complète de manipulation d\'images pour applications Node.js. Les fonctionnalités incluent génération de memes, filtres, effets, superposition de texte et conversion de format. Développé à l\'origine comme API REST, maintenant disponible en package NPM open-source. Utilisé par des bots Discord, applications web et outils d\'automatisation. Supporte JPG, PNG, GIF et WebP avec capacités de streaming.'
|
||||
title: 'Manipulation d\'Images - Package NPM',
|
||||
description: 'Discord Image Generation : Package NPM pour la manipulation d\'images basée sur le code. Initialement une API, maintenant open-source.',
|
||||
longDescription: 'Package NPM open-source pour la génération et manipulation d\'images programmatique. Développé initialement comme API propriétaire, puis rendu disponible à la communauté. Offre des fonctionnalités avancées de traitement d\'images : génération de memes, filtres, effets visuels et compositions personnalisées. Particulièrement populaire dans l\'écosystème des bots Discord pour créer du contenu visuel dynamique et interactif.',
|
||||
buttons: {
|
||||
repository: 'Dépôt',
|
||||
'npm package': 'Package NPM'
|
||||
}
|
||||
},
|
||||
'primate-web-admin': {
|
||||
title: 'Primate Web Admin - Outil de Déploiement Entreprise',
|
||||
description: 'Interface web moderne pour système de déploiement Primate. Déploiement et gestion de logiciels de qualité entreprise pour infrastructure Windows.',
|
||||
longDescription: 'Panneau d\'administration web professionnel pour Primate, un outil de déploiement type Munki pour environnements Windows. Construit avec frontend Vue.js et backend API RESTful. Les fonctionnalités incluent gestion de packages, planification de déploiement, surveillance des clients et rapports détaillés. Conçu pour les administrateurs IT gérant de grands déploiements Windows avec contrôle d\'accès basé sur les rôles et journalisation d\'audit.'
|
||||
title: 'Primate Web Admin - Interface de Gestion',
|
||||
description: 'Primate Web Admin est une interface Web pour gérer Primate qui est un outil de déploiement similaire à Munki pour Windows.',
|
||||
longDescription: 'Interface d\'administration web moderne pour Primate, un système de déploiement de logiciels pour environnements Windows. Inspiré de Munki (solution macOS), Primate Web Admin offre une gestion centralisée des déploiements logiciels sur infrastructure Windows. Interface intuitive pour la gestion des packages, planification des mises à jour, surveillance des clients et génération de rapports détaillés.',
|
||||
buttons: {}
|
||||
},
|
||||
'instagram-bot': {
|
||||
title: 'Bot Instagram - Génération de Contenu Automatisée',
|
||||
description: 'Bot Instagram riche en fonctionnalités avec commandes de génération d\'images. Construit avec Insta.js pour stories, posts et automatisation DM.',
|
||||
longDescription: 'Bot d\'automatisation Instagram avancé développé avec le framework Insta.js. Comprend des commandes de génération d\'images personnalisées (!stonk, !invert, !meme), interactions stories, publication automatisée et gestion DM. Inclut limitation de débit, support proxy et fonctionnalités de sécurité du compte. Parfait pour les créateurs de contenu et gestionnaires de médias sociaux cherchant à automatiser leur présence Instagram.'
|
||||
title: 'Bot Instagram - Automatisation Complète',
|
||||
description: 'Bot Instagram entièrement fonctionnel utilisant Insta.js par androz2091. Il possède de nombreuses commandes. Génère des images avec des commandes comme : !stonk ou !invert.',
|
||||
longDescription: 'Bot d\'automatisation Instagram développé avec la bibliothèque Insta.js d\'androz2091. Offre une gamme complète de fonctionnalités d\'automatisation : publication de contenu, interaction avec les followers, génération d\'images personnalisées et gestion des messages directs. Inclut des commandes spécialisées pour la création de memes et effets visuels (!stonk, !invert) ainsi que des outils de modération et d\'analyse des performances.',
|
||||
buttons: {
|
||||
repository: 'Dépôt'
|
||||
}
|
||||
},
|
||||
'crowdin-status-bot': {
|
||||
title: 'Bot de Statut Crowdin - Suivi de Progression des Traductions',
|
||||
description: 'Bot Discord pour surveillance en temps réel des traductions Crowdin. Mises à jour automatiques du statut et suivi de progression pour équipes de localisation.',
|
||||
longDescription: 'Bot Discord spécialisé qui s\'intègre avec l\'API Crowdin pour fournir des mises à jour de progression de traduction en temps réel. Les fonctionnalités incluent messages de statut automatisés, barres de progression, classements des contributeurs et notifications de jalons. Outil essentiel pour les projets open-source et équipes de localisation gérant des traductions dans plusieurs langues. Supporte webhooks et règles de notification personnalisées.'
|
||||
title: 'Bot de Statut Crowdin - Suivi des Traductions',
|
||||
description: 'Un bot qui récupère le statut des traductions Crowdin et met à jour les messages Discord avec le dernier statut. Restez informé des progrès !',
|
||||
longDescription: 'Bot Discord spécialisé dans le suivi automatique des projets de traduction Crowdin. Se connecte à l\'API Crowdin pour récupérer les statistiques de progression en temps réel et met à jour automatiquement les messages Discord avec les dernières informations. Outil essentiel pour les équipes de localisation et projets open-source multilingues, permettant de maintenir la communauté informée des avancées de traduction et d\'encourager la participation des contributeurs.',
|
||||
buttons: {
|
||||
repository: 'Dépôt'
|
||||
}
|
||||
},
|
||||
'flowboard': {
|
||||
title: 'FlowBoard - Clone de Trello ',
|
||||
description: 'Clone de Trello moderne pour la gestion de projet et la collaboration d\'équipe. Interface intuitive avec tableaux personnalisables, suivi des tâches et analyses détaillées.',
|
||||
longDescription: 'FlowBoard révolutionne la collaboration d\'équipe et la gestion de projet avec sa suite complète d\'outils. Construite avec des technologies web modernes, elle offre une interface intuitive pour organiser les tâches, gérer les délais et suivre les progrès. La plateforme propose des tableaux personnalisables, une collaboration en temps réel, des analyses avancées et des outils de communication transparents. Parfaite pour les équipes de toutes tailles cherchant à booster leur productivité et rationaliser leurs processus de travail.',
|
||||
buttons: {}
|
||||
}
|
||||
},
|
||||
|
||||
|
@@ -2,10 +2,11 @@
|
||||
import { computed } from 'vue'
|
||||
import { useSeo } from '@/composables/useSeo'
|
||||
import { useI18n } from '@/composables/useI18n'
|
||||
import { projects } from '@/data/projects'
|
||||
import { useProjects } from '@/composables/useProjects'
|
||||
import ProjectCard from '@/components/ProjectCard.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { projects } = useProjects()
|
||||
|
||||
// Enhanced SEO with structured data
|
||||
useSeo({
|
||||
@@ -39,7 +40,7 @@ useSeo({
|
||||
|
||||
// Featured projects
|
||||
const featuredProjects = computed(() => {
|
||||
return projects.filter(project => project.featured).slice(0, 3)
|
||||
return projects.value.filter(project => project.featured).slice(0, 3)
|
||||
})
|
||||
|
||||
// Services data
|
||||
|
@@ -3,24 +3,30 @@ import { computed, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useSeo } from '@/composables/useSeo'
|
||||
import { useAssets } from '@/composables/useAssets'
|
||||
import { projects } from '@/data/projects'
|
||||
import { useProjects } from '@/composables/useProjects'
|
||||
import { useI18n } from '@/composables/useI18n'
|
||||
import { useGallery } from '@/composables/useGallery'
|
||||
import TechBadge from '@/components/TechBadge.vue'
|
||||
import GalleryModal from '@/components/GalleryModal.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { getImageUrl } = useAssets()
|
||||
const { projects } = useProjects()
|
||||
const { t } = useI18n()
|
||||
const gallery = useGallery()
|
||||
|
||||
// Find project by ID
|
||||
const project = computed(() => {
|
||||
const id = route.params.id as string
|
||||
return projects.find(p => p.id === id)
|
||||
return projects.value.find(p => p.id === id)
|
||||
})
|
||||
|
||||
// Related projects
|
||||
const relatedProjects = computed(() => {
|
||||
if (!project.value) return []
|
||||
|
||||
return projects
|
||||
return projects.value
|
||||
.filter(p => p.id !== project.value?.id && p.category === project.value?.category)
|
||||
.slice(0, 3)
|
||||
})
|
||||
@@ -72,7 +78,7 @@ onMounted(() => {
|
||||
<svg class="breadcrumb-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||
</svg>
|
||||
Retour aux projets
|
||||
{{ t('projects.projectDetail.backToProjects') }}
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
@@ -101,7 +107,7 @@ onMounted(() => {
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
|
||||
</svg>
|
||||
Voir la démo
|
||||
{{ t('projects.projectDetail.viewDemo') }}
|
||||
</a>
|
||||
|
||||
<a v-if="project.githubUrl" :href="project.githubUrl" target="_blank" rel="noopener noreferrer"
|
||||
@@ -110,7 +116,7 @@ onMounted(() => {
|
||||
<path
|
||||
d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
</svg>
|
||||
Code source
|
||||
{{ t('projects.projectDetail.sourceCode') }}
|
||||
</a>
|
||||
|
||||
<!-- Buttons from project data -->
|
||||
@@ -129,7 +135,7 @@ onMounted(() => {
|
||||
d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z">
|
||||
</path>
|
||||
</svg>
|
||||
Partager
|
||||
{{ t('projects.projectDetail.share') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -146,7 +152,7 @@ onMounted(() => {
|
||||
<div class="main-content">
|
||||
<!-- Project Details -->
|
||||
<div class="content-section">
|
||||
<h2 class="section-title">À propos du projet</h2>
|
||||
<h2 class="section-title">{{ t('projects.projectDetail.aboutProject') }}</h2>
|
||||
<div class="section-content">
|
||||
<p class="project-long-description">
|
||||
{{ project.longDescription || project.description }}
|
||||
@@ -154,7 +160,7 @@ onMounted(() => {
|
||||
|
||||
<!-- Features -->
|
||||
<div v-if="project.features" class="features-list">
|
||||
<h3 class="features-title">Fonctionnalités principales</h3>
|
||||
<h3 class="features-title">{{ t('projects.projectDetail.keyFeatures') }}</h3>
|
||||
<ul class="features">
|
||||
<li v-for="feature in project.features" :key="feature" class="feature-item">
|
||||
<svg class="feature-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -169,7 +175,7 @@ onMounted(() => {
|
||||
|
||||
<!-- Technologies -->
|
||||
<div v-if="project.technologies" class="content-section">
|
||||
<h2 class="section-title">Technologies utilisées</h2>
|
||||
<h2 class="section-title">{{ t('projects.projectDetail.technologiesUsed') }}</h2>
|
||||
<div class="section-content">
|
||||
<div class="tech-grid">
|
||||
<TechBadge v-for="tech in project.technologies" :key="tech" :tech="tech" class="tech-item" />
|
||||
@@ -179,11 +185,20 @@ onMounted(() => {
|
||||
|
||||
<!-- Gallery -->
|
||||
<div v-if="project.gallery" class="content-section">
|
||||
<h2 class="section-title">Galerie</h2>
|
||||
<h2 class="section-title">{{ t('projects.projectDetail.gallery') }}</h2>
|
||||
<div class="section-content">
|
||||
<div class="gallery-grid">
|
||||
<div v-for="(image, index) in project.gallery" :key="index" class="gallery-item">
|
||||
<div v-for="(image, index) in project.gallery" :key="index" class="gallery-item"
|
||||
@click="gallery.openGallery(project.gallery, index)">
|
||||
<img :src="getImageUrl(image)" :alt="`${project.title} - Image ${index + 1}`" class="gallery-image">
|
||||
<div class="gallery-overlay">
|
||||
<svg class="gallery-expand-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -194,20 +209,20 @@ onMounted(() => {
|
||||
<aside class="sidebar">
|
||||
<!-- Project Info Card -->
|
||||
<div class="info-card">
|
||||
<h3 class="info-title">Informations du projet</h3>
|
||||
<h3 class="info-title">{{ t('projects.projectDetail.projectInfo') }}</h3>
|
||||
<div class="info-list">
|
||||
<div v-if="project.date" class="info-item">
|
||||
<span class="info-label">Date</span>
|
||||
<span class="info-label">{{ t('projects.projectDetail.date') }}</span>
|
||||
<span class="info-value">{{ project.date }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="project.category" class="info-item">
|
||||
<span class="info-label">Catégorie</span>
|
||||
<span class="info-label">{{ t('projects.projectDetail.category') }}</span>
|
||||
<span class="info-value">{{ project.category }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="project.status" class="info-item">
|
||||
<span class="info-label">Statut</span>
|
||||
<span class="info-label">{{ t('projects.projectDetail.status') }}</span>
|
||||
<span class="info-value">{{ project.status }}</span>
|
||||
</div>
|
||||
|
||||
@@ -220,10 +235,10 @@ onMounted(() => {
|
||||
|
||||
<!-- Related Projects -->
|
||||
<div v-if="relatedProjects.length > 0" class="related-projects">
|
||||
<h3 class="related-title">Projets similaires</h3>
|
||||
<h3 class="related-title">{{ t('projects.projectDetail.relatedProjects') }}</h3>
|
||||
<div class="related-list">
|
||||
<router-link v-for="relatedProject in relatedProjects" :key="relatedProject.id"
|
||||
:to="`/projects/${relatedProject.id}`" class="related-item">
|
||||
:to="`/project/${relatedProject.id}`" class="related-item">
|
||||
<img v-if="relatedProject.image" :src="getImageUrl(relatedProject.image)" :alt="relatedProject.title"
|
||||
class="related-image">
|
||||
<div class="related-content">
|
||||
@@ -237,6 +252,13 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Gallery Modal -->
|
||||
<GalleryModal :is-open="gallery.isOpen.value" :current-image="gallery.currentImage.value"
|
||||
:current-index="gallery.currentIndex.value" :total-images="gallery.images.value.length"
|
||||
:has-next="gallery.hasNext.value" :has-previous="gallery.hasPrevious.value" :project-title="project?.title || ''"
|
||||
@close="gallery.closeGallery" @next="gallery.nextImage" @previous="gallery.previousImage"
|
||||
@go-to="gallery.goToImage" />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
|
@@ -2,10 +2,11 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { useSeo } from '@/composables/useSeo'
|
||||
import { useI18n } from '@/composables/useI18n'
|
||||
import { projects } from '@/data/projects'
|
||||
import { useProjects } from '@/composables/useProjects'
|
||||
import ProjectCard from '@/components/ProjectCard.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { projects } = useProjects()
|
||||
|
||||
// SEO
|
||||
useSeo({
|
||||
@@ -19,7 +20,7 @@ useSeo({
|
||||
'name': 'Web Development Portfolio Projects',
|
||||
'description': 'Browse professional web development projects including Vue.js applications, React websites, Node.js APIs, and Discord bots',
|
||||
'url': 'https://killiandalcin.fr/projects',
|
||||
'hasPart': projects.map(project => ({
|
||||
'hasPart': projects.value.map(project => ({
|
||||
'@type': 'CreativeWork',
|
||||
'name': project.title,
|
||||
'description': project.description,
|
||||
@@ -36,13 +37,13 @@ const sortBy = ref('date')
|
||||
|
||||
// Get unique categories
|
||||
const categories = computed(() => {
|
||||
const cats = ['all', ...new Set(projects.map(p => p.category).filter(Boolean))]
|
||||
const cats = ['all', ...new Set(projects.value.map(p => p.category).filter(Boolean))]
|
||||
return cats
|
||||
})
|
||||
|
||||
// Filtered and sorted projects
|
||||
const filteredProjects = computed(() => {
|
||||
let filtered = projects
|
||||
let filtered = projects.value
|
||||
|
||||
// Filter by search query
|
||||
if (searchQuery.value) {
|
||||
@@ -74,8 +75,8 @@ const filteredProjects = computed(() => {
|
||||
})
|
||||
|
||||
// Stats
|
||||
const totalProjects = computed(() => projects.length)
|
||||
const featuredProjects = computed(() => projects.filter(p => p.featured).length)
|
||||
const totalProjects = computed(() => projects.value.length)
|
||||
const featuredProjects = computed(() => projects.value.filter(p => p.featured).length)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@@ -273,6 +273,8 @@
|
||||
box-shadow: var(--shadow-lg);
|
||||
transition: all var(--transition-fast);
|
||||
background: var(--bg-secondary);
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gallery-item:hover {
|
||||
@@ -280,10 +282,41 @@
|
||||
box-shadow: var(--shadow-xl);
|
||||
}
|
||||
|
||||
.gallery-item:hover .gallery-overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.gallery-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
transition: transform var(--transition-normal);
|
||||
}
|
||||
|
||||
.gallery-item:hover .gallery-image {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.gallery-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: opacity var(--transition-normal);
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
.gallery-expand-icon {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
color: white;
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
|
Reference in New Issue
Block a user