feat(01-02): migrate static data files and images to Nuxt structure
- 4 data files created in app/data/ with proper type imports from shared/types - 74 WebP images copied to public/images/ (including flowboard gallery) - All image paths migrated from @/assets/images/ to /images/ - FAQ uses i18n keys instead of direct text
@@ -1,313 +0,0 @@
|
|||||||
<!-- GSD:project-start source:PROJECT.md -->
|
|
||||||
## Project
|
|
||||||
|
|
||||||
**Portfolio Killian Dalcin — Migration Nuxt 4**
|
|
||||||
|
|
||||||
Migration complète d'un portfolio freelance de Vue 3 SPA vers Nuxt 4 avec SSR complet. Le site présente les projets, services et compétences de Killian Dalcin, développeur freelance, avec support bilingue FR/EN. L'objectif est un SEO parfait et un développement rapide via des composants prêts à l'emploi (Nuxt UI v3).
|
|
||||||
|
|
||||||
**Core Value:** Chaque page du portfolio doit être crawlable par les moteurs de recherche sans JavaScript côté client — le SSR est la raison d'être de cette migration.
|
|
||||||
|
|
||||||
### Constraints
|
|
||||||
|
|
||||||
- **Stack**: Nuxt 4 + Nuxt UI v3 + Tailwind v4 — dernières versions stables
|
|
||||||
- **Coût**: Zéro dépendance payante
|
|
||||||
- **Composants**: Nuxt UI v3 en priorité sur le custom (80% suffit)
|
|
||||||
- **TypeScript**: Mode strict partout
|
|
||||||
- **Déploiement**: Docker node:22-alpine, nuxt build (SSR) ou nuxt generate (SSG) selon stratégie
|
|
||||||
- **i18n/Theme**: Persistance cookie uniquement (SSR-safe), pas de localStorage
|
|
||||||
<!-- GSD:project-end -->
|
|
||||||
|
|
||||||
<!-- GSD:stack-start source:codebase/STACK.md -->
|
|
||||||
## Technology Stack
|
|
||||||
|
|
||||||
## Languages
|
|
||||||
- TypeScript ~5.8.0 - Full application development
|
|
||||||
- JavaScript (ES modules) - Frontend runtime
|
|
||||||
- HTML5 - Document structure (in `index.html`)
|
|
||||||
- CSS - Styling with Tailwind CSS
|
|
||||||
- Markdown - Documentation (README.md)
|
|
||||||
- YAML - Configuration (implied through Dockerfile)
|
|
||||||
## Runtime
|
|
||||||
- Node.js 22 - Development and build environment
|
|
||||||
- Browser environment - Vue 3 SFC runtime
|
|
||||||
- npm - Dependency management
|
|
||||||
- Lockfile: `package-lock.json` (present and tracked)
|
|
||||||
## Frameworks
|
|
||||||
- Vue 3.5.13 - Progressive JavaScript framework for UI
|
|
||||||
- Vue Router 4.5.0 - Client-side routing with lazy-loaded pages
|
|
||||||
- Pinia 3.0.1 - State management (minimal usage - currently only `counter.ts`)
|
|
||||||
- Vue I18n 9.14.4 - Internationalization (English and French locale files in `src/locales/`)
|
|
||||||
- Vite 6.2.4 - Build tool and dev server
|
|
||||||
- Vite Plugin Vue DevTools 7.7.2 - Development utilities
|
|
||||||
- @vitejs/plugin-vue 5.2.3 - Vue 3 SFC support
|
|
||||||
- Tailwind CSS 4.1.10 - Utility-first CSS framework
|
|
||||||
- @tailwindcss/postcss 4.1.10 - PostCSS plugin for Tailwind
|
|
||||||
- PostCSS 8.5.6 - CSS transformation pipeline
|
|
||||||
- Autoprefixer 10.4.21 - Vendor prefix handling
|
|
||||||
- Terser 5.43.1 - JavaScript minification
|
|
||||||
- ESLint 9.22.0 - Linting (config: `eslint.config.ts`)
|
|
||||||
- Prettier 3.5.3 - Code formatting (config: `.prettierrc.json`)
|
|
||||||
- vue-tsc 2.2.8 - Vue component type checking
|
|
||||||
- TypeScript compiler with `type-check` npm script
|
|
||||||
- @vueuse/head 2.0.0 - Dynamic document head management for meta tags and SEO
|
|
||||||
## Key Dependencies
|
|
||||||
- vue 3.5.13 - Core framework
|
|
||||||
- vue-router 4.5.0 - SPA routing with code splitting
|
|
||||||
- pinia 3.0.1 - State management store
|
|
||||||
- vue-i18n 9.14.4 - Multi-language support
|
|
||||||
- vite 6.2.4 - Next-gen build tool with HMR
|
|
||||||
- tailwindcss 4.1.10 - Rapid UI development
|
|
||||||
- typescript 5.8.0 - Static typing and compilation
|
|
||||||
- eslint 9.22.0 - Code linting
|
|
||||||
- prettier 3.5.3 - Code formatting
|
|
||||||
- npm-run-all2 7.0.2 - Parallel script execution (used in build process)
|
|
||||||
- @tsconfig/node22 22.0.1 - TSConfig preset for Node 22
|
|
||||||
- @types/node 22.14.0 - Node.js type definitions
|
|
||||||
- jiti 2.4.2 - CommonJS loader for TypeScript modules
|
|
||||||
## Configuration
|
|
||||||
- No `.env` files detected in source
|
|
||||||
- Google Analytics tracking ID hardcoded: `G-CDVVNFY6MV` (in `index.html`)
|
|
||||||
- Umami analytics script loaded from `umami.killiandalcin.fr` (in `index.html`)
|
|
||||||
- Google AdSense client ID hardcoded: `ca-pub-5219367964457248` (in `index.html`)
|
|
||||||
- `vite.config.ts` - Build optimizations:
|
|
||||||
- `tsconfig.json` - References `tsconfig.app.json` and `tsconfig.node.json`
|
|
||||||
- `tsconfig.app.json`:
|
|
||||||
- `eslint.config.ts` - Flat config format:
|
|
||||||
- `.prettierrc.json`:
|
|
||||||
- `postcss.config.js` - Tailwind CSS and Autoprefixer
|
|
||||||
- `tailwind.config.js` - Content scanning for `index.html` and `src/**/*.{vue,js,ts,jsx,tsx}`
|
|
||||||
## Platform Requirements
|
|
||||||
- Node.js 22+ (specified in Dockerfile)
|
|
||||||
- npm 10+ (implied by Node 22)
|
|
||||||
- TypeScript 5.8+
|
|
||||||
- Any Unix-like shell (bash/zsh) or Windows with Node.js
|
|
||||||
- Docker - Multi-stage build with Node 22-alpine and nginx stable-alpine
|
|
||||||
- Web server: nginx (configured in `nginx.conf`)
|
|
||||||
- Deployment target: Static HTML served via nginx
|
|
||||||
- JavaScript enabled (noscript fallback message in `index.html`)
|
|
||||||
- Modern browsers with ES2020+ support (Vite default targets)
|
|
||||||
## Scripts & Commands
|
|
||||||
<!-- GSD:stack-end -->
|
|
||||||
|
|
||||||
<!-- GSD:conventions-start source:CONVENTIONS.md -->
|
|
||||||
## Conventions
|
|
||||||
|
|
||||||
## Naming Patterns
|
|
||||||
- Vue components: PascalCase (e.g., `AppHeader.vue`, `ProjectCard.vue`)
|
|
||||||
- Composables: camelCase with `use` prefix (e.g., `useTheme.ts`, `useProjects.ts`)
|
|
||||||
- Utility/config files: camelCase (e.g., `site.ts`, `techstack.ts`)
|
|
||||||
- Data files: camelCase (e.g., `testimonials.ts`, `faq.ts`)
|
|
||||||
- Type definitions: camelCase in `types/index.ts`
|
|
||||||
- All functions use camelCase (e.g., `toggleTheme`, `openGallery`, `getImageUrl`)
|
|
||||||
- Composables are named with `use` prefix: `useTheme()`, `useGallery()`, `useSeo()`
|
|
||||||
- Getter functions use `get` prefix: `getTheme()`, `getImageUrl()`
|
|
||||||
- Boolean functions/computed use `is`/`has` prefix: `isDark`, `hasNext`, `isOpen`
|
|
||||||
- Handler functions use verb + `Handler`: `toggleTheme`, `openGallery`, `closeGallery`
|
|
||||||
- Refs and computed properties: camelCase (e.g., `isDark`, `currentIndex`, `isOpen`)
|
|
||||||
- Interfaces and types: PascalCase (e.g., `Props`, `SeoOptions`, `Theme`)
|
|
||||||
- Constants: UPPER_SNAKE_CASE for config constants (not extensively used in codebase)
|
|
||||||
- Private/module state: camelCase prefixed with `_` if truly private
|
|
||||||
- Type aliases: PascalCase (e.g., `type Theme = 'light' | 'dark'`)
|
|
||||||
- Interface names: PascalCase (e.g., `interface Props`, `interface SeoOptions`)
|
|
||||||
- Props interfaces: Always named `Props` (e.g., in `<script setup lang="ts">` components)
|
|
||||||
- Generic types from Vue use their original names (e.g., `Ref<boolean>`, `Computed<string>`)
|
|
||||||
## Code Style
|
|
||||||
- Tool: Prettier 3.5.3
|
|
||||||
- Semi-colons: **disabled** (`semi: false`)
|
|
||||||
- Quotes: **single quotes** (`singleQuote: true`)
|
|
||||||
- Print width: **100 characters** (`printWidth: 100`)
|
|
||||||
- Tool: ESLint 9.22.0 with Vue support
|
|
||||||
- Config: `eslint.config.ts` using flat config format
|
|
||||||
- Plugins:
|
|
||||||
## Import Organization
|
|
||||||
- `@/` maps to `./src/` (configured in `tsconfig.app.json`)
|
|
||||||
- Always use `@/` prefix for imports from src directory
|
|
||||||
- Examples:
|
|
||||||
## Error Handling
|
|
||||||
- Try-catch blocks wrap risky operations (e.g., dynamic imports, DOM manipulation)
|
|
||||||
- Fallback values provided when operations fail:
|
|
||||||
- Console warnings for non-critical failures:
|
|
||||||
- Silent failures with fallbacks preferred over throwing errors for UI operations
|
|
||||||
## Logging
|
|
||||||
- `console.warn()` for warnings (missing assets, invalid input)
|
|
||||||
- Logging only in composables for utility functions
|
|
||||||
- No console.log() in production code (only development/debugging)
|
|
||||||
- Error context included: `console.warn('context', error)`
|
|
||||||
## Comments
|
|
||||||
- JSDoc comments for composable functions (exported functions)
|
|
||||||
- Inline comments for non-obvious logic (especially SEO handling in router)
|
|
||||||
- Comments explaining why (not what the code does)
|
|
||||||
- TODO comments for known issues: `// TODO: page 404` in `src/router/index.ts`
|
|
||||||
- Composables include JSDoc for exported functions
|
|
||||||
- Example from `useAssets.ts`:
|
|
||||||
- Not consistently applied across all files; use when function signature isn't obvious
|
|
||||||
## Function Design
|
|
||||||
- Props interfaces always named `Props` in components
|
|
||||||
- Use destructuring in setup: `const { t } = useI18n()`
|
|
||||||
- Optional config objects in composables (e.g., `SeoOptions` with defaults)
|
|
||||||
- Explicit typing on all parameters
|
|
||||||
- Composables return object with all exposed functions and reactive state
|
|
||||||
- Always return computed versions of reactive state when exposing refs:
|
|
||||||
- Functions return early on validation failures with fallbacks
|
|
||||||
## Module Design
|
|
||||||
- Composables export single named function: `export function useTheme() { ... }`
|
|
||||||
- Config files export named constants: `export const siteConfig: SiteConfig = { ... }`
|
|
||||||
- Type definitions export interfaces and types: `export interface Project { ... }`
|
|
||||||
- Data files export arrays or objects: `export const techStack: TechStack = { ... }`
|
|
||||||
- Not extensively used; direct imports preferred
|
|
||||||
- Only `src/types/index.ts` serves as barrel export for type definitions
|
|
||||||
- Components use direct imports: `import AppHeader from '@/components/layout/AppHeader.vue'`
|
|
||||||
- `<script setup lang="ts">` for all components (Vue 3 Composition API)
|
|
||||||
- Props validated with TypeScript interfaces
|
|
||||||
- Composables called at top of setup
|
|
||||||
- Computed properties for derived state
|
|
||||||
- Functions defined after setup calls
|
|
||||||
- `<template>` uses semantic HTML and accessibility attributes
|
|
||||||
- Scoped styles at bottom with `@import` for external stylesheets
|
|
||||||
## Type Safety
|
|
||||||
- Version: ~5.8.0
|
|
||||||
- DOM-focused (`tsconfig.dom.json` from @vue/tsconfig)
|
|
||||||
- Path alias `@/*` points to `./src/*`
|
|
||||||
- Type checking enabled in build: `npm run type-check` runs `vue-tsc --build`
|
|
||||||
- All component Props use interface definitions
|
|
||||||
- Composable return values typed explicitly
|
|
||||||
- Function parameters and return types annotated
|
|
||||||
- Type imports use `import type` syntax
|
|
||||||
- Avoid `any` type; use proper interfaces/generics
|
|
||||||
## Vue 3 Specific
|
|
||||||
- `<script setup>` syntax exclusively used
|
|
||||||
- No Options API in codebase
|
|
||||||
- Composables follow Composition API patterns
|
|
||||||
- `onMounted()` for initialization (theme loading, SEO setup)
|
|
||||||
- `onUnmounted()` for cleanup (removing DOM elements in useSeo)
|
|
||||||
- `watch()` for reactive side effects (theme changes)
|
|
||||||
- `ref()` for primitive state
|
|
||||||
- `computed()` for derived state
|
|
||||||
- Avoid unnecessary reactivity; use constants when possible
|
|
||||||
- Return computed versions of refs from composables
|
|
||||||
<!-- GSD:conventions-end -->
|
|
||||||
|
|
||||||
<!-- GSD:architecture-start source:ARCHITECTURE.md -->
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
## Pattern Overview
|
|
||||||
- Client-side routing with lazy-loaded views for performance optimization
|
|
||||||
- Composition API-based composables for shared logic and state management
|
|
||||||
- Global state managed via Pinia stores
|
|
||||||
- Multi-language support with vue-i18n
|
|
||||||
- Theme switching with localStorage persistence
|
|
||||||
- SEO-optimized with dynamic meta tags and structured data
|
|
||||||
- Google Analytics and GTM integration for tracking
|
|
||||||
## Layers
|
|
||||||
- Purpose: Render UI and handle user interactions
|
|
||||||
- Location: `src/components/`
|
|
||||||
- Contains: Vue Single File Components organized by domain (layout, sections, shared, testimonials, icons)
|
|
||||||
- Depends on: Composables for data access and side effects, Router for navigation
|
|
||||||
- Used by: Views and other components
|
|
||||||
- Purpose: Page-level component assembly and routing targets
|
|
||||||
- Location: `src/views/`
|
|
||||||
- Contains: Full page components (HomePage, ProjectsPage, ContactPage, AboutPage, FiverrPage, FormationPage, ProjectDetailPage)
|
|
||||||
- Depends on: Composables (useSeo, useI18n, useProjects), components, data stores
|
|
||||||
- Used by: Router for navigation
|
|
||||||
- Purpose: Encapsulate reusable logic, data fetching, and side effects
|
|
||||||
- Location: `src/composables/`
|
|
||||||
- Contains: Vue composables for projects, SEO, i18n, themes, galleries, date formatting, assets, site config
|
|
||||||
- Depends on: Types, stores, external libraries (vue-router, vue-i18n)
|
|
||||||
- Used by: Components and views
|
|
||||||
- Purpose: Global state and static data management
|
|
||||||
- Location: `src/stores/`, `src/data/`
|
|
||||||
- Contains: Pinia stores, static project data, testimonials, tech stack, FAQs
|
|
||||||
- Depends on: Types, composables (useI18n for localized data)
|
|
||||||
- Used by: Composables and components
|
|
||||||
- Purpose: Application-wide settings and configuration
|
|
||||||
- Location: `src/config/`, `src/router/`, `src/i18n/`
|
|
||||||
- Contains: Site configuration, router setup, i18n initialization, locale messages
|
|
||||||
- Depends on: Types, data
|
|
||||||
- Used by: Main entry point and throughout app
|
|
||||||
- Purpose: TypeScript interfaces and types
|
|
||||||
- Location: `src/types/index.ts`
|
|
||||||
- Contains: Project, Technology, TechStack, SocialLink, ContactInfo, FiverrService, SiteConfig interfaces
|
|
||||||
## Data Flow
|
|
||||||
- **Global:** Pinia stores (currently minimal - `useCounterStore` exists but unused)
|
|
||||||
- **Composable State:** Reactive refs in composables (theme, locale, gallery state)
|
|
||||||
- **Component State:** Local reactive refs for UI state (menu toggle, form inputs)
|
|
||||||
- **Persistence:** localStorage for theme and locale preferences
|
|
||||||
- **Server-Side Data:** Static JSON-like data in `src/data/` files, not fetched from API
|
|
||||||
## Key Abstractions
|
|
||||||
- Purpose: Unified i18n access with convenience methods
|
|
||||||
- Examples: `src/composables/useI18n.ts`
|
|
||||||
- Pattern: Wraps vue-i18n's `useI18n()`, adds locale switching and computed locale state
|
|
||||||
- Usage: Available in all components via injection
|
|
||||||
- Purpose: Dynamic SEO tag management for SPA
|
|
||||||
- Examples: `src/composables/useSeo.ts`
|
|
||||||
- Pattern: Lifecycle hooks to create/remove meta tags on mount/unmount, prevents tag duplication
|
|
||||||
- Usage: Called in view components with options object for title, description, OG tags, structured data
|
|
||||||
- Purpose: Project data access with localization
|
|
||||||
- Examples: `src/composables/useProjects.ts`
|
|
||||||
- Pattern: Base data stored separately, computed properties merge translations on read
|
|
||||||
- Usage: Returns computed `projects` array that updates when language changes
|
|
||||||
- Purpose: Centralized theme state and persistence
|
|
||||||
- Examples: `src/composables/useTheme.ts`
|
|
||||||
- Pattern: Reactive boolean with computed getter, watch for persistence, DOM manipulation
|
|
||||||
- Usage: Injected globally in App.vue, consumed by ThemeToggle component
|
|
||||||
- Purpose: Typed structure for technology categories
|
|
||||||
- Examples: `src/types/index.ts`
|
|
||||||
- Pattern: Categorized array structure (programming, front, database, devtools, operating_systems, socials)
|
|
||||||
- Usage: Imported in `src/data/techstack.ts` and AboutPage.vue
|
|
||||||
- Purpose: Single source of truth for site-wide settings
|
|
||||||
- Examples: `src/config/site.ts`
|
|
||||||
- Pattern: Exported constant object with typed structure, includes contact info, social links, SEO config
|
|
||||||
- Usage: Imported where needed for links, contact info, performance settings
|
|
||||||
## Entry Points
|
|
||||||
- Location: `index.html`
|
|
||||||
- Triggers: Browser page load
|
|
||||||
- Responsibilities: Define DOM root (`#app`), load analytics/ads scripts, include meta tags, defer main.ts loading
|
|
||||||
- Location: `src/main.ts`
|
|
||||||
- Triggers: After HTML DOM ready
|
|
||||||
- Responsibilities: Create Vue app, install plugins (Pinia, Router, i18n), mount to #app
|
|
||||||
- Location: `src/router/index.ts`
|
|
||||||
- Triggers: App.use(router) in main.ts
|
|
||||||
- Responsibilities: Define route table, implement beforeEach/afterEach hooks for SEO and analytics
|
|
||||||
- Location: `src/App.vue`
|
|
||||||
- Triggers: After Vue app mounts
|
|
||||||
- Responsibilities: Initialize theme, render layout structure (header + router-view + footer), handle route-change scroll behavior
|
|
||||||
- Location: `src/views/*.vue`
|
|
||||||
- Triggers: Router navigation to matching path
|
|
||||||
- Responsibilities: Page-specific SEO setup via `useSeo()`, compose sections and content, manage page-level state
|
|
||||||
## Error Handling
|
|
||||||
- Lazy-loaded routes with no 404 component (TODO comment in router) - currently redirects to HomePage
|
|
||||||
- SEO composable safely creates/finds meta elements before updating
|
|
||||||
- Theme fallback to 'dark' if localStorage empty
|
|
||||||
- Locale fallback to 'en' if not in localStorage
|
|
||||||
- Gallery modal (GalleryModal.vue) handles missing images gracefully
|
|
||||||
- Contact form likely has validation but not visible in read scope
|
|
||||||
## Cross-Cutting Concerns
|
|
||||||
<!-- GSD:architecture-end -->
|
|
||||||
|
|
||||||
<!-- GSD:skills-start source:skills/ -->
|
|
||||||
## Project Skills
|
|
||||||
|
|
||||||
No project skills found. Add skills to any of: `.claude/skills/`, `.agents/skills/`, `.cursor/skills/`, or `.github/skills/` with a `SKILL.md` index file.
|
|
||||||
<!-- GSD:skills-end -->
|
|
||||||
|
|
||||||
<!-- GSD:workflow-start source:GSD defaults -->
|
|
||||||
## GSD Workflow Enforcement
|
|
||||||
|
|
||||||
Before using Edit, Write, or other file-changing tools, start work through a GSD command so planning artifacts and execution context stay in sync.
|
|
||||||
|
|
||||||
Use these entry points:
|
|
||||||
- `/gsd-quick` for small fixes, doc updates, and ad-hoc tasks
|
|
||||||
- `/gsd-debug` for investigation and bug fixing
|
|
||||||
- `/gsd-execute-phase` for planned phase work
|
|
||||||
|
|
||||||
Do not make direct repo edits outside a GSD workflow unless the user explicitly asks to bypass it.
|
|
||||||
<!-- GSD:workflow-end -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- GSD:profile-start -->
|
|
||||||
## Developer Profile
|
|
||||||
|
|
||||||
> Profile not yet configured. Run `/gsd-profile-user` to generate your developer profile.
|
|
||||||
> This section is managed by `generate-claude-profile` -- do not edit manually.
|
|
||||||
<!-- GSD:profile-end -->
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import type { FAQ } from '~~/shared/types'
|
||||||
|
|
||||||
|
export const homeFAQs: FAQ[] = [
|
||||||
|
{
|
||||||
|
questionKey: 'faq.homeFaq.delivery.question',
|
||||||
|
answerKey: 'faq.homeFaq.delivery.answer',
|
||||||
|
featuresKey: 'faq.homeFaq.delivery.features',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionKey: 'faq.homeFaq.maintenance.question',
|
||||||
|
answerKey: 'faq.homeFaq.maintenance.answer',
|
||||||
|
featuresKey: 'faq.homeFaq.maintenance.features',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionKey: 'faq.homeFaq.companies.question',
|
||||||
|
answerKey: 'faq.homeFaq.companies.answer',
|
||||||
|
featuresKey: 'faq.homeFaq.companies.features',
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import type { Project } from '~~/shared/types'
|
||||||
|
|
||||||
|
// Base project data without translations
|
||||||
|
// Titles and descriptions are resolved via i18n keys: projects.${id}.title, projects.${id}.description
|
||||||
|
export const projects: Omit<Project, 'title' | 'description' | 'longDescription'>[] = [
|
||||||
|
{
|
||||||
|
id: 'virtual-tour',
|
||||||
|
image: '/images/virtualtour.webp',
|
||||||
|
technologies: ['Vue.js', 'Three.js', 'WebGL', 'Node.js'],
|
||||||
|
category: 'Web Development',
|
||||||
|
date: '2022',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
title: 'Visit',
|
||||||
|
link: 'https://www.lycee-chabanne16.fr/visites/BACSN/index.htm',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'xinko',
|
||||||
|
image: '/images/xinko.webp',
|
||||||
|
technologies: ['Node.js', 'Discord.js', 'MongoDB', 'Express'],
|
||||||
|
category: 'Bot Development',
|
||||||
|
date: '2023',
|
||||||
|
featured: true,
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
title: 'Invite',
|
||||||
|
link: 'https://discord.com/api/oauth2/authorize?client_id=1035571329866407976&permissions=292288982151&scope=applications.commands%20bot',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'image-manipulation',
|
||||||
|
image: '/images/dig.webp',
|
||||||
|
technologies: ['JavaScript', 'Node.js', 'Canvas', 'npm'],
|
||||||
|
category: 'Open Source',
|
||||||
|
date: '2022',
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'primate-web-admin',
|
||||||
|
image: '/images/primate.webp',
|
||||||
|
technologies: ['React', 'TypeScript', 'Node.js', 'Express'],
|
||||||
|
category: 'Enterprise Software',
|
||||||
|
date: '2023',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'instagram-bot',
|
||||||
|
image: '/images/instagram.webp',
|
||||||
|
technologies: ['JavaScript', 'Node.js', 'Instagram API', 'Canvas'],
|
||||||
|
category: 'Social Media Bot',
|
||||||
|
date: '2022',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
title: 'Repository',
|
||||||
|
link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/instagram-bot',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'crowdin-status-bot',
|
||||||
|
image: '/images/crowdin.webp',
|
||||||
|
technologies: ['Node.js', 'Discord.js', 'Crowdin API', 'Cron'],
|
||||||
|
category: 'Automation',
|
||||||
|
date: '2023',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
title: 'Repository',
|
||||||
|
link: 'https://git.mrkayjaydee.xyz/Mr-KayJayDee/discord-crowdin-status',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'flowboard',
|
||||||
|
image: '/images/flowboard/flowboard_1.webp',
|
||||||
|
technologies: ['Vue.js', 'Node.js', 'TypeScript', 'MongoDB', 'Express'],
|
||||||
|
category: 'Web Development',
|
||||||
|
date: '2024',
|
||||||
|
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: [
|
||||||
|
'/images/flowboard/flowboard_1.webp',
|
||||||
|
'/images/flowboard/flowboard_2.webp',
|
||||||
|
'/images/flowboard/flowboard_3.webp',
|
||||||
|
'/images/flowboard/flowboard_4.webp',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import type { TechStack } from '~~/shared/types'
|
||||||
|
|
||||||
|
export const techStack: TechStack = {
|
||||||
|
programming: [
|
||||||
|
{ name: 'JavaScript', level: 'Advanced', image: '/images/javascript.webp' },
|
||||||
|
{ name: 'TypeScript', level: 'Advanced', image: '/images/typescript.webp' },
|
||||||
|
{ name: 'Node.js', level: 'Advanced', image: '/images/nodejs.webp' },
|
||||||
|
{ name: 'Bash', level: 'Intermediate', image: '/images/bash.webp' },
|
||||||
|
{ name: 'Markdown', level: 'Advanced', image: '/images/markdown.webp' },
|
||||||
|
{ name: 'Ruby', level: 'Intermediate', image: '/images/ruby.webp' },
|
||||||
|
{ name: 'Ruby on Rails', level: 'Intermediate', image: '/images/rubyonrails.webp' },
|
||||||
|
],
|
||||||
|
front: [
|
||||||
|
{ name: 'Vue.js', level: 'Advanced', image: '/images/vuejs.webp' },
|
||||||
|
{ name: 'React', level: 'Intermediate', image: '/images/react.webp' },
|
||||||
|
{ name: 'Angular', level: 'Intermediate', image: '/images/angular.webp' },
|
||||||
|
{ name: 'HTML', level: 'Advanced', image: '/images/html.webp' },
|
||||||
|
{ name: 'CSS', level: 'Advanced', image: '/images/css.webp' },
|
||||||
|
{ name: 'Figma', level: 'Advanced', image: '/images/figma.webp' },
|
||||||
|
{ name: 'WordPress', level: 'Intermediate', image: '/images/wordpress.webp' },
|
||||||
|
],
|
||||||
|
database: [
|
||||||
|
{ name: 'MongoDB', level: 'Advanced', image: '/images/mongodb.webp' },
|
||||||
|
{ name: 'MySQL', level: 'Advanced', image: '/images/mysql.webp' },
|
||||||
|
{ name: 'Redis', level: 'Advanced', image: '/images/redis.webp' },
|
||||||
|
{ name: 'SQLite', level: 'Advanced', image: '/images/sqlite.webp' },
|
||||||
|
{ name: 'PostgreSQL', level: 'Advanced', image: '/images/postgresql.webp' },
|
||||||
|
{ name: 'Bootstrap', level: 'Intermediate', image: '/images/bootstrap.webp' },
|
||||||
|
{ name: 'Tailwind CSS', level: 'Intermediate', image: '/images/tailwindcss.webp' },
|
||||||
|
],
|
||||||
|
devtools: [
|
||||||
|
{ name: 'Git', level: 'Advanced', image: '/images/git.webp' },
|
||||||
|
{ name: 'GitHub', level: 'Advanced', image: '/images/github.webp' },
|
||||||
|
{ name: 'GitLab', level: 'Advanced', image: '/images/gitlab.webp' },
|
||||||
|
{ name: 'GitKraken', level: 'Advanced', image: '/images/gitkraken.webp' },
|
||||||
|
{ name: 'Visual Studio Code', level: 'Advanced', image: '/images/vscode.webp' },
|
||||||
|
{ name: 'Atom', level: 'Advanced', image: '/images/atom.webp' },
|
||||||
|
{ name: 'Docker', level: 'Advanced', image: '/images/docker.webp' },
|
||||||
|
{ name: 'npm', level: 'Advanced', image: '/images/npm.webp' },
|
||||||
|
{ name: 'Postman', level: 'Advanced', image: '/images/postman.webp' },
|
||||||
|
{ name: 'FileZilla', level: 'Advanced', image: '/images/filezilla.webp' },
|
||||||
|
{ name: 'Termius', level: 'Advanced', image: '/images/termius.webp' },
|
||||||
|
{ name: 'HeidiSQL', level: 'Advanced', image: '/images/heidisql.webp' },
|
||||||
|
{ name: 'MySQL Workbench', level: 'Advanced', image: '/images/mysqlworkbench.webp' },
|
||||||
|
{ name: 'Sequel Pro', level: 'Intermediate', image: '/images/sequelpro.webp' },
|
||||||
|
],
|
||||||
|
operating_systems: [
|
||||||
|
{ name: 'Linux', level: 'Advanced', image: '/images/linux.webp' },
|
||||||
|
{ name: 'Ubuntu', level: 'Advanced', image: '/images/ubuntu.webp' },
|
||||||
|
{ name: 'Debian', level: 'Advanced', image: '/images/debian.webp' },
|
||||||
|
{ name: 'Arch Linux', level: 'Intermediate', image: '/images/archlinux.webp' },
|
||||||
|
{ name: 'Kali Linux', level: 'Intermediate', image: '/images/kalilinux.webp' },
|
||||||
|
{ name: 'Deepin', level: 'Intermediate', image: '/images/deepin.webp' },
|
||||||
|
{ name: 'Windows', level: 'Advanced', image: '/images/windows.webp' },
|
||||||
|
{ name: 'macOS', level: 'Advanced', image: '/images/macos.webp' },
|
||||||
|
{ name: 'Android', level: 'Advanced', image: '/images/android.webp' },
|
||||||
|
{ name: 'iOS', level: 'Intermediate', image: '/images/ios.webp' },
|
||||||
|
{ name: 'Wear OS', level: 'Intermediate', image: '/images/wearos.webp' },
|
||||||
|
{ name: 'watchOS', level: 'Intermediate', image: '/images/watchos.webp' },
|
||||||
|
],
|
||||||
|
socials: [
|
||||||
|
{ name: 'Discord', level: 'Advanced', image: '/images/discord.webp' },
|
||||||
|
{ name: 'Instagram', level: 'Advanced', image: '/images/instagram.webp' },
|
||||||
|
{ name: 'LinkedIn', level: 'Advanced', image: '/images/linkedin.webp' },
|
||||||
|
{ name: 'Twitter', level: 'Advanced', image: '/images/twitter.webp' },
|
||||||
|
{ name: 'Reddit', level: 'Advanced', image: '/images/reddit.webp' },
|
||||||
|
{ name: 'Facebook', level: 'Advanced', image: '/images/facebook.webp' },
|
||||||
|
{ name: 'Messenger', level: 'Advanced', image: '/images/messenger.webp' },
|
||||||
|
{ name: 'WhatsApp', level: 'Advanced', image: '/images/whatsapp.webp' },
|
||||||
|
{ name: 'Telegram', level: 'Advanced', image: '/images/telegram.webp' },
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import type { Testimonial, TestimonialsStats } from '~~/shared/types'
|
||||||
|
|
||||||
|
export const testimonials: Testimonial[] = [
|
||||||
|
{
|
||||||
|
name: 'unqlf_',
|
||||||
|
role: 'Client',
|
||||||
|
company: 'France',
|
||||||
|
avatar: 'https://ui-avatars.com/api/?name=U&background=3b82f6&color=ffffff&size=128',
|
||||||
|
rating: 5,
|
||||||
|
content:
|
||||||
|
"Je conseil ce vendeur il écoute clairement les conseils, les informations qu'on lui donne, il mérite clairement son niveau dans le développement et prend en compte chaque erreur.",
|
||||||
|
date: '15/03/2023',
|
||||||
|
platform: 'Fiverr',
|
||||||
|
featured: true,
|
||||||
|
project_type: 'Plugin Minecraft',
|
||||||
|
results: ["Prix: Jusqu'à 50€", 'Durée: 10 jours', 'Écoute client excellente'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'colo263',
|
||||||
|
role: 'Client',
|
||||||
|
company: 'France',
|
||||||
|
avatar: 'https://ui-avatars.com/api/?name=C&background=059669&color=ffffff&size=128',
|
||||||
|
rating: 5,
|
||||||
|
content:
|
||||||
|
"Travail excellent, Communication au top, Disponible en tout temps, réactif et à l'écoute je le recommande vivement et reviendrai vers lui si je dois refaire un projet similaire !",
|
||||||
|
date: '22/04/2023',
|
||||||
|
platform: 'Fiverr',
|
||||||
|
project_type: 'Bot Discord',
|
||||||
|
results: ["Prix: Jusqu'à 50€", 'Durée: 4 jours', 'Communication parfaite'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'aurlienbarbet',
|
||||||
|
role: 'Client',
|
||||||
|
company: 'France',
|
||||||
|
avatar: 'https://ui-avatars.com/api/?name=A&background=dc2626&color=ffffff&size=128',
|
||||||
|
rating: 5,
|
||||||
|
content:
|
||||||
|
"Le prestataire est très professionnel, prêt à faire l'offre la plus juste et à ajuster un prix pour votre commande. Réponds à tout les questions ! une bonne expérience pour ma part",
|
||||||
|
date: '08/06/2023',
|
||||||
|
platform: 'Fiverr',
|
||||||
|
project_type: 'Bot Discord',
|
||||||
|
results: ["Prix: Jusqu'à 50€", 'Durée: 1 jour', 'Prix ajusté sur mesure'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'cobra2',
|
||||||
|
role: 'Client',
|
||||||
|
company: 'France',
|
||||||
|
avatar: 'https://ui-avatars.com/api/?name=C&background=7c3aed&color=ffffff&size=128',
|
||||||
|
rating: 5,
|
||||||
|
content:
|
||||||
|
'Excellent développeur, la commande fut plus rapide que prévu la communication est instantané et le résultat est parfait. Je recommande fortement et reviendrai sûrement pour des mise à jour !',
|
||||||
|
date: '12/11/2022',
|
||||||
|
platform: 'Fiverr',
|
||||||
|
project_type: 'Bot Discord',
|
||||||
|
results: [
|
||||||
|
'Livraison plus rapide que prévu',
|
||||||
|
'Communication instantanée',
|
||||||
|
'Résultat parfait',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'botuhuh',
|
||||||
|
role: 'Client',
|
||||||
|
company: 'France',
|
||||||
|
avatar: 'https://ui-avatars.com/api/?name=B&background=ea580c&color=ffffff&size=128',
|
||||||
|
rating: 5,
|
||||||
|
content: 'awesome guy, I recommend, thanks again !!!!',
|
||||||
|
date: '28/09/2022',
|
||||||
|
platform: 'Fiverr',
|
||||||
|
project_type: 'Bot Discord',
|
||||||
|
results: ['Client international satisfait', 'Recommandation forte', 'Service apprécié'],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
export const testimonialsStats: TestimonialsStats = {
|
||||||
|
totalReviews: 10,
|
||||||
|
averageRating: 5.0,
|
||||||
|
projectsCompleted: 25,
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { globalIgnores } from 'eslint/config'
|
||||||
|
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
|
||||||
|
import pluginVue from 'eslint-plugin-vue'
|
||||||
|
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
|
||||||
|
|
||||||
|
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
|
||||||
|
// import { configureVueProject } from '@vue/eslint-config-typescript'
|
||||||
|
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
|
||||||
|
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
|
||||||
|
|
||||||
|
export default defineConfigWithVueTs(
|
||||||
|
{
|
||||||
|
name: 'app/files-to-lint',
|
||||||
|
files: ['**/*.{ts,mts,tsx,vue}'],
|
||||||
|
},
|
||||||
|
|
||||||
|
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
|
||||||
|
|
||||||
|
pluginVue.configs['flat/essential'],
|
||||||
|
vueTsConfigs.recommended,
|
||||||
|
skipFormatting,
|
||||||
|
)
|
||||||
@@ -4,35 +4,41 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nuxt dev",
|
"dev": "vite",
|
||||||
"build": "nuxt build",
|
"build": "run-p type-check \"build-only {@}\" --",
|
||||||
"generate": "nuxt generate",
|
"preview": "vite preview",
|
||||||
"preview": "nuxt preview",
|
"build-only": "vite build",
|
||||||
"postinstall": "nuxt prepare",
|
"type-check": "vue-tsc --build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint . --fix",
|
||||||
"typecheck": "nuxt typecheck"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nuxt/eslint": "^1.15.2",
|
"@vueuse/head": "^2.0.0",
|
||||||
"@nuxt/image": "^2.0.0",
|
"pinia": "^3.0.1",
|
||||||
"@nuxt/ui": "^3.0.0",
|
"vue": "^3.5.13",
|
||||||
"@nuxtjs/i18n": "^10.2.4",
|
"vue-i18n": "^9.14.4",
|
||||||
"@nuxtjs/sitemap": "^8.0.12",
|
"vue-router": "^4.5.0"
|
||||||
"nuxt": "^4.0.0",
|
|
||||||
"nuxt-gtag": "^4.1.0",
|
|
||||||
"vue": "latest",
|
|
||||||
"vue-router": "latest"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "~5.8.0"
|
"@tailwindcss/postcss": "^4.1.10",
|
||||||
},
|
"@tsconfig/node22": "^22.0.1",
|
||||||
"pnpm": {
|
"@types/node": "^22.14.0",
|
||||||
"onlyBuiltDependencies": [
|
"@vitejs/plugin-vue": "^5.2.3",
|
||||||
"@parcel/watcher",
|
"@vue/eslint-config-prettier": "^10.2.0",
|
||||||
"esbuild",
|
"@vue/eslint-config-typescript": "^14.5.0",
|
||||||
"sharp",
|
"@vue/tsconfig": "^0.7.0",
|
||||||
"unrs-resolver",
|
"autoprefixer": "^10.4.21",
|
||||||
"vue-demi"
|
"eslint": "^9.22.0",
|
||||||
]
|
"eslint-plugin-vue": "~10.0.0",
|
||||||
|
"jiti": "^2.4.2",
|
||||||
|
"npm-run-all2": "^7.0.2",
|
||||||
|
"postcss": "^8.5.6",
|
||||||
|
"prettier": "3.5.3",
|
||||||
|
"tailwindcss": "^4.1.10",
|
||||||
|
"terser": "^5.43.1",
|
||||||
|
"typescript": "~5.8.0",
|
||||||
|
"vite": "^6.2.4",
|
||||||
|
"vite-plugin-vue-devtools": "^7.7.2",
|
||||||
|
"vue-tsc": "^2.2.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 384 B |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 798 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 936 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 16 KiB |