chore: remove outdated planning documents from codebase
- Deleted several planning documents including ARCHITECTURE.md, CONCERNS.md, CONVENTIONS.md, INTEGRATIONS.md, STACK.md, STRUCTURE.md, and TESTING.md. - These files were no longer relevant to the current project structure and development practices.
@@ -1,197 +0,0 @@
|
|||||||
# Architecture
|
|
||||||
|
|
||||||
**Analysis Date:** 2026-04-07
|
|
||||||
|
|
||||||
## Pattern Overview
|
|
||||||
|
|
||||||
**Overall:** Vue 3 SPA (Single Page Application) with component-based architecture and SSR-friendly design patterns
|
|
||||||
|
|
||||||
**Key Characteristics:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Presentation Layer (Components):**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**View Layer (Pages):**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Business Logic Layer (Composables):**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Data/State Layer (Stores & Data):**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Configuration Layer:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Type Definitions:**
|
|
||||||
- Purpose: TypeScript interfaces and types
|
|
||||||
- Location: `src/types/index.ts`
|
|
||||||
- Contains: Project, Technology, TechStack, SocialLink, ContactInfo, FiverrService, SiteConfig interfaces
|
|
||||||
|
|
||||||
## Data Flow
|
|
||||||
|
|
||||||
**1. Initial Page Load:**
|
|
||||||
1. `index.html` loads with embedded Google Analytics and Google AdSense scripts
|
|
||||||
2. `src/main.ts` initializes Vue app, Pinia store, router, and i18n
|
|
||||||
3. `src/App.vue` applies theme from localStorage and renders AppHeader + RouterView + AppFooter
|
|
||||||
4. Router initializes and loads HomePage or requested route
|
|
||||||
5. `useTheme()` applies saved theme class to document
|
|
||||||
6. `useI18n()` loads saved locale from localStorage
|
|
||||||
|
|
||||||
**2. Route Navigation:**
|
|
||||||
1. User clicks link or navigates directly
|
|
||||||
2. Router's `beforeEach` hook updates document title and meta description from route.meta
|
|
||||||
3. Router's `afterEach` hook triggers scroll to top and Google Analytics page view tracking
|
|
||||||
4. Target view component mounts and runs `useSeo()` for SEO metadata
|
|
||||||
5. View renders child components with fetched data
|
|
||||||
6. Components subscribe to composables for reactive data
|
|
||||||
|
|
||||||
**3. Data Access Pattern (Example: Projects):**
|
|
||||||
1. Component imports `useProjects()` composable
|
|
||||||
2. Composable accesses base project data from `src/data/` or static store
|
|
||||||
3. Composable uses `useI18n()` to localize strings
|
|
||||||
4. Component receives computed reactive `projects` array
|
|
||||||
5. Component renders with v-for or passes data to child components
|
|
||||||
|
|
||||||
**4. Theme Switching:**
|
|
||||||
1. `ThemeToggle` component toggles `isDark` ref in `useTheme()`
|
|
||||||
2. Watch handler applies class to document.documentElement
|
|
||||||
3. Watch handler saves to localStorage
|
|
||||||
4. Browser CSS respects `dark` class on document
|
|
||||||
|
|
||||||
**5. Language Switching:**
|
|
||||||
1. `LanguageSwitcher` component calls `switchLocale()` from `useI18n()`
|
|
||||||
2. vue-i18n locale updates and all `{{ t() }}` expressions re-evaluate
|
|
||||||
3. New locale saved to localStorage
|
|
||||||
4. Computed properties like `homeFAQs` in HomePage.vue re-evaluate with new translations
|
|
||||||
|
|
||||||
**State Management:**
|
|
||||||
- **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
|
|
||||||
|
|
||||||
**useI18n() Composable:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**useSeo() Composable:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**useProjects() Composable:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**useTheme() Composable:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**TechStack Interface:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**SiteConfig:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**HTML Entry Point:**
|
|
||||||
- Location: `index.html`
|
|
||||||
- Triggers: Browser page load
|
|
||||||
- Responsibilities: Define DOM root (`#app`), load analytics/ads scripts, include meta tags, defer main.ts loading
|
|
||||||
|
|
||||||
**Application Entry Point:**
|
|
||||||
- Location: `src/main.ts`
|
|
||||||
- Triggers: After HTML DOM ready
|
|
||||||
- Responsibilities: Create Vue app, install plugins (Pinia, Router, i18n), mount to #app
|
|
||||||
|
|
||||||
**Router Entry Point:**
|
|
||||||
- Location: `src/router/index.ts`
|
|
||||||
- Triggers: App.use(router) in main.ts
|
|
||||||
- Responsibilities: Define route table, implement beforeEach/afterEach hooks for SEO and analytics
|
|
||||||
|
|
||||||
**Root Component:**
|
|
||||||
- Location: `src/App.vue`
|
|
||||||
- Triggers: After Vue app mounts
|
|
||||||
- Responsibilities: Initialize theme, render layout structure (header + router-view + footer), handle route-change scroll behavior
|
|
||||||
|
|
||||||
**View Components:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Strategy:** Graceful degradation with fallback content; no explicit error boundaries detected
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Logging:** Console-based or via external services - no custom logger detected; development uses Vue DevTools plugin
|
|
||||||
|
|
||||||
**Validation:** Form validation in components (ContactPage uses validation likely); no centralized validation layer
|
|
||||||
|
|
||||||
**Authentication:** No built-in auth system - portfolio is public facing; new auth stores/views added (LoginView, RegisterView, VerifyEmailView, DashboardView, ForgotPasswordView, guards.ts) but not integrated into main router
|
|
||||||
|
|
||||||
**SEO:** Centralized via `useSeo()` composable and router hooks; dynamic meta tags, Open Graph, Twitter cards, structured data; Google Analytics via gtag in router afterEach; Umami analytics via deferred script tag in index.html
|
|
||||||
|
|
||||||
**Performance:** Code splitting via lazy routes, vendor chunk separation in vite.config.ts, CSS code splitting enabled, Terser minification, webp image support configured, lazy image loading configurable in siteConfig
|
|
||||||
|
|
||||||
**Accessibility:** ARIA labels on interactive elements (AppHeader navigation, buttons); semantic HTML (header, nav, main, section roles); focus styles defined in App.vue
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Architecture analysis: 2026-04-07*
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
# Codebase Concerns
|
|
||||||
|
|
||||||
**Analysis Date:** 2026-04-07
|
|
||||||
|
|
||||||
## Tech Debt
|
|
||||||
|
|
||||||
**Missing 404 Page Implementation:**
|
|
||||||
- Issue: 404 catch-all route currently redirects to HomePage instead of a dedicated 404 page
|
|
||||||
- Files: `src/router/index.ts` (line 51: TODO comment)
|
|
||||||
- Impact: Users encountering invalid routes see the homepage instead of a proper error page, creating confusion and poor UX. SEO also treats all invalid URLs as the homepage.
|
|
||||||
- Fix approach: Create a new `NotFoundPage.vue` component in `src/views/` with proper 404 messaging, then update the route in `src/router/index.ts` to point to this component.
|
|
||||||
|
|
||||||
**Hardcoded GA Tracking ID:**
|
|
||||||
- Issue: Google Analytics tracking ID `G-CDVVNFY6MV` is hardcoded in multiple places
|
|
||||||
- Files: `index.html` (line 9), `src/router/index.ts` (line 109)
|
|
||||||
- Impact: Cannot change analytics without code updates. Difficult to manage different GA IDs for development vs production environments.
|
|
||||||
- Fix approach: Move tracking ID to environment variables (`.env`), access via `import.meta.env.VITE_GA_ID`.
|
|
||||||
|
|
||||||
**Hardcoded Site URL and Analytics:**
|
|
||||||
- Issue: URLs and site identifiers hardcoded throughout the codebase
|
|
||||||
- Files: `src/composables/useSeo.ts` (lines 103, 113, 143, 149), `src/config/site.ts` (line 69), `src/router/index.ts` (line 109)
|
|
||||||
- Impact: Difficult to deploy to different environments (staging vs production). Requires code changes for different domains.
|
|
||||||
- Fix approach: Move to environment configuration. Create environment-based config: `VITE_SITE_URL`, `VITE_SITE_DOMAIN`, etc.
|
|
||||||
|
|
||||||
**External Image Asset Dependency on Placeholder Service:**
|
|
||||||
- Issue: Missing images fallback to external placeholder.com service without reliability guarantee
|
|
||||||
- Files: `src/composables/useAssets.ts` (lines 18, 42)
|
|
||||||
- Impact: If placeholder.com goes down or is rate-limited, missing images break visually. External dependency increases load time.
|
|
||||||
- Fix approach: Use local SVG or base64-encoded placeholder image instead of external URL.
|
|
||||||
|
|
||||||
## Known Bugs
|
|
||||||
|
|
||||||
**Scroll Position Duplication:**
|
|
||||||
- Symptoms: Multiple scroll handlers (in router and App.vue) could cause double-scrolling or jank
|
|
||||||
- Files: `src/App.vue` (lines 14-18), `src/router/index.ts` (lines 62-82, 118-125)
|
|
||||||
- Trigger: Navigation between routes
|
|
||||||
- Workaround: Currently works, but behavior is fragile due to redundancy
|
|
||||||
|
|
||||||
**FormationPage Billing Toggle State Issue:**
|
|
||||||
- Symptoms: Billing toggle (monthly/annual) uses `isAnnual` state but HTML shows `billingType` variable
|
|
||||||
- Files: `src/views/FormationPage.vue` (lines 12-20, 41)
|
|
||||||
- Trigger: When switching between monthly and annual billing
|
|
||||||
- Workaround: Component probably has missing reactive computed for `billingType`
|
|
||||||
|
|
||||||
## Security Considerations
|
|
||||||
|
|
||||||
**v-html Usage in FAQ Component:**
|
|
||||||
- Risk: XSS vulnerability if FAQ answers contain user-generated content or external data
|
|
||||||
- Files: `src/components/ServiceFAQ.vue` (line 22: `<p v-html="faq.answer"></p>`)
|
|
||||||
- Current mitigation: FAQ content is hardcoded in component props (trusted source), but pattern is risky if content source changes
|
|
||||||
- Recommendations: Replace with v-text or plain text content. If HTML is needed, use a sanitization library like `DOMPurify`.
|
|
||||||
|
|
||||||
**Personal Information Exposed in Configuration:**
|
|
||||||
- Risk: Email, phone number, and social profile IDs hardcoded in public config
|
|
||||||
- Files: `src/config/site.ts` (lines 71-100), `index.html` (lines 88, 239-240)
|
|
||||||
- Current mitigation: This is intentional (portfolio/contact site), but increases spam/scraping risk
|
|
||||||
- Recommendations: Consider using form submission instead of direct email/phone links on sensitive pages. Monitor contact form for abuse.
|
|
||||||
|
|
||||||
**Hardcoded Analytics and AdSense IDs:**
|
|
||||||
- Risk: Analytics and AdSense IDs in HTML source reveal account information
|
|
||||||
- Files: `index.html` (lines 9, 19)
|
|
||||||
- Current mitigation: None - IDs are public by design (Google Analytics is meant to be public)
|
|
||||||
- Recommendations: Ensure AdSense account is properly secured with password/2FA. Monitor for unauthorized modifications.
|
|
||||||
|
|
||||||
**Discord User ID Exposed:**
|
|
||||||
- Risk: Discord user ID `370940770225618954` is hardcoded and publicly visible
|
|
||||||
- Files: `src/config/site.ts` (line 92)
|
|
||||||
- Current mitigation: Discord doesn't allow impersonation via ID alone, but enables targeted attacks
|
|
||||||
- Recommendations: Keep Discord contact via link only, without exposing raw user ID. Use Discord username instead.
|
|
||||||
|
|
||||||
## Performance Bottlenecks
|
|
||||||
|
|
||||||
**Eager Image Loading with import.meta.glob:**
|
|
||||||
- Problem: All images under `src/assets/images/**` are eagerly loaded into memory at startup
|
|
||||||
- Files: `src/composables/useAssets.ts` (line 6: `eager: true`)
|
|
||||||
- Cause: `eager: true` loads all matching modules immediately instead of lazy-loading on-demand
|
|
||||||
- Improvement path: Change to lazy loading and implement on-demand imports, or at minimum separate critical images from lazy ones.
|
|
||||||
|
|
||||||
**External Script Dependencies Block Rendering:**
|
|
||||||
- Problem: Google Analytics, Google AdSense, and Umami scripts are loaded synchronously
|
|
||||||
- Files: `index.html` (lines 9, 19, 239-240)
|
|
||||||
- Cause: Multiple external scripts without proper `async` loading strategy
|
|
||||||
- Improvement path: Ensure all external scripts use `async` or `defer` attributes. Currently Umami and GTM use `defer` (good), but ensure they don't block critical rendering path.
|
|
||||||
|
|
||||||
**No Lazy Loading on Images:**
|
|
||||||
- Problem: No lazy-loading attributes on images, causing initial page load to fetch all images
|
|
||||||
- Files: Multiple components (`src/components/layout/AppFooter.vue` line 35, `src/components/layout/AppHeader.vue` line 33)
|
|
||||||
- Cause: `loading="eager"` and `loading="lazy"` not used strategically
|
|
||||||
- Improvement path: Set `loading="lazy"` on all below-fold images. Keep only above-fold images with `loading="eager"`.
|
|
||||||
|
|
||||||
## Fragile Areas
|
|
||||||
|
|
||||||
**GalleryModal Event Listener Management:**
|
|
||||||
- Files: `src/components/GalleryModal.vue` (lines 44-50)
|
|
||||||
- Why fragile: Global document keydown listener added/removed on mount/unmount. If component unmounts unexpectedly, listener could remain attached or fail to attach.
|
|
||||||
- Safe modification: Add try-catch around removeEventListener. Consider using a ref-based approach or delegated events.
|
|
||||||
- Test coverage: No test coverage visible for keyboard navigation. Recommend adding unit tests for keyboard shortcuts.
|
|
||||||
|
|
||||||
**Image URL Resolution Fallback Chain:**
|
|
||||||
- Files: `src/composables/useAssets.ts` (lines 13-44)
|
|
||||||
- Why fragile: Multiple fallback layers (module lookup → URL construction → placeholder) make debugging hard. If one fallback path fails, error is silently logged.
|
|
||||||
- Safe modification: Add explicit logging for each fallback step. Document expected path formats clearly.
|
|
||||||
- Test coverage: Recommend unit tests for edge cases (empty path, missing extensions, malformed paths).
|
|
||||||
|
|
||||||
**SEO Composable DOM Manipulation:**
|
|
||||||
- Files: `src/composables/useSeo.ts` (lines 35-70)
|
|
||||||
- Why fragile: Directly manipulates DOM with `document.querySelector`, `createElement`, `appendChild`. No error handling if DOM structure changes.
|
|
||||||
- Safe modification: Use Vue's ref system or a dedicated SEO library (e.g., `@unhead/vue`). Add error boundaries.
|
|
||||||
- Test coverage: No test coverage for SSR compatibility or DOM cleanup on route changes.
|
|
||||||
|
|
||||||
**Route-based SEO Title Dependency:**
|
|
||||||
- Files: `src/composables/useSeo.ts` (lines 75-76)
|
|
||||||
- Why fragile: Relies on route being available in useRoute() hook. If used in wrong context (non-routed component), will fail silently.
|
|
||||||
- Safe modification: Add null checks. Consider creating a composable specifically for page-level SEO that enforces route dependency.
|
|
||||||
- Test coverage: Recommend tests for components used in and outside routing context.
|
|
||||||
|
|
||||||
## Scaling Limits
|
|
||||||
|
|
||||||
**Single-Page History State:**
|
|
||||||
- Current capacity: Router handles typical portfolio page count (6-8 pages)
|
|
||||||
- Limit: If projects list grows beyond 50-100 items, ProjectsPage.vue performance degrades (all projects loaded at once)
|
|
||||||
- Scaling path: Implement pagination or virtual scrolling in ProjectsPage. Use server-side filtering if projects become dynamic.
|
|
||||||
|
|
||||||
**Inline Image Modules:**
|
|
||||||
- Current capacity: ~15-20 images loaded eagerly in memory
|
|
||||||
- Limit: If image count exceeds 100+, startup time and memory usage increase significantly
|
|
||||||
- Scaling path: Migrate to lazy-loading strategy. Consider CDN for image serving in production.
|
|
||||||
|
|
||||||
**Localization Key Lookups:**
|
|
||||||
- Current capacity: 500+ localization keys across en.ts and fr.ts
|
|
||||||
- Limit: If keys exceed 1000+, lookup performance and bundle size become concerns
|
|
||||||
- Scaling path: Implement lazy-loaded locale files (load only active language). Consider JSON-based locale format for better optimization.
|
|
||||||
|
|
||||||
## Dependencies at Risk
|
|
||||||
|
|
||||||
**No Testing Framework:**
|
|
||||||
- Risk: Zero test coverage visible in codebase. Refactoring breaks are undetectable.
|
|
||||||
- Impact: Security fixes, performance optimizations, and feature additions are risky.
|
|
||||||
- Migration plan: Add Jest + Vue Test Utils. Start with critical paths (router guards, composables, SEO).
|
|
||||||
|
|
||||||
**Hardcoded Translation Keys:**
|
|
||||||
- Risk: If translation key structure changes, UI silently breaks (missing translations show key names)
|
|
||||||
- Impact: Refactoring translations is error-prone and breaks are not caught in CI
|
|
||||||
- Migration plan: Add TypeScript strict typing for i18n keys using type-safe i18n library.
|
|
||||||
|
|
||||||
**External Analytics Dependency:**
|
|
||||||
- Risk: If Google Analytics changes API or service, tracking breaks
|
|
||||||
- Impact: Loss of analytics data, no visibility into user behavior
|
|
||||||
- Migration plan: Already using Umami (self-hosted alternative). Consider making analytics provider pluggable.
|
|
||||||
|
|
||||||
## Missing Critical Features
|
|
||||||
|
|
||||||
**No Error Boundary:**
|
|
||||||
- Problem: No Vue error boundary or fallback component for runtime errors
|
|
||||||
- Blocks: Cannot gracefully handle component errors or show error UI
|
|
||||||
- Fix approach: Create an error boundary component using Vue 3 error handling hooks (errorCaptured), wrap main app with it.
|
|
||||||
|
|
||||||
**No Offline Support:**
|
|
||||||
- Problem: No service worker or offline fallback
|
|
||||||
- Blocks: Portfolio becomes completely unavailable if user loses connection
|
|
||||||
- Fix approach: Implement service worker with offline fallback. Cache critical assets (HTML, CSS, JS).
|
|
||||||
|
|
||||||
**No Loading States:**
|
|
||||||
- Problem: No skeleton loaders or loading indicators for async operations
|
|
||||||
- Blocks: Users don't know if page is loading or broken. Especially impacts image loading.
|
|
||||||
- Fix approach: Add skeleton screens for ProjectDetailPage. Add loading indicators for gallery modal.
|
|
||||||
|
|
||||||
**No Proper 404 Page:**
|
|
||||||
- Problem: 404 redirects to homepage (mentioned in Tech Debt)
|
|
||||||
- Blocks: Users cannot identify when they've hit an invalid URL
|
|
||||||
- Fix approach: Create NotFoundPage.vue with suggestions for navigation.
|
|
||||||
|
|
||||||
**No Analytics Event Tracking:**
|
|
||||||
- Problem: Only page views tracked, no event analytics (clicks, form submissions, etc.)
|
|
||||||
- Blocks: Cannot understand user behavior beyond page traffic
|
|
||||||
- Fix approach: Add event tracking for CTA clicks, social link clicks, gallery interactions.
|
|
||||||
|
|
||||||
## Test Coverage Gaps
|
|
||||||
|
|
||||||
**No Unit Tests:**
|
|
||||||
- What's not tested: Composables (useSeo, useAssets, useProjects, useI18n), utility functions
|
|
||||||
- Files: All of `src/composables/` and `src/data/`
|
|
||||||
- Risk: Refactoring introduces subtle bugs. Type safety is partial (TypeScript, but no runtime checks).
|
|
||||||
- Priority: High - composables are core to the app and impact SEO/styling
|
|
||||||
|
|
||||||
**No Component Tests:**
|
|
||||||
- What's not tested: Interactive components (GalleryModal, ServiceFAQ, language switcher, theme toggle)
|
|
||||||
- Files: `src/components/GalleryModal.vue`, `src/components/ServiceFAQ.vue`, `src/components/ThemeToggle.vue`, `src/components/LanguageSwitcher.vue`
|
|
||||||
- Risk: UI behavior breaks silently. Accessibility features (keyboard nav, ARIA) may regress.
|
|
||||||
- Priority: High - GalleryModal keyboard navigation and language switching are user-facing
|
|
||||||
|
|
||||||
**No Integration Tests:**
|
|
||||||
- What's not tested: Router navigation, SEO meta tag updates, i18n language switching across pages
|
|
||||||
- Files: `src/router/index.ts`, cross-file composable interactions
|
|
||||||
- Risk: Multi-step user flows break. SEO meta tags may not update correctly on navigation.
|
|
||||||
- Priority: Medium - can catch cross-cutting issues
|
|
||||||
|
|
||||||
**No E2E Tests:**
|
|
||||||
- What's not tested: Full user journeys (landing → project detail → gallery → contact)
|
|
||||||
- Framework: None (not using Cypress, Playwright, etc.)
|
|
||||||
- Risk: Visual regressions, layout issues, navigation bugs in real browser contexts
|
|
||||||
- Priority: Medium - would catch integration issues and performance regressions
|
|
||||||
|
|
||||||
**No Accessibility Tests:**
|
|
||||||
- What's not tested: Keyboard navigation, screen reader compatibility, color contrast, focus management
|
|
||||||
- Files: All components with interactive elements
|
|
||||||
- Risk: Accessibility fails silently. Users with disabilities cannot navigate.
|
|
||||||
- Priority: High - portfolio should be accessible to all users
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Concerns audit: 2026-04-07*
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
# Coding Conventions
|
|
||||||
|
|
||||||
**Analysis Date:** 2026-04-07
|
|
||||||
|
|
||||||
## Naming Patterns
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- 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`
|
|
||||||
|
|
||||||
**Functions:**
|
|
||||||
- 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`
|
|
||||||
|
|
||||||
**Variables:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Types:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Formatting:**
|
|
||||||
- Tool: Prettier 3.5.3
|
|
||||||
- Semi-colons: **disabled** (`semi: false`)
|
|
||||||
- Quotes: **single quotes** (`singleQuote: true`)
|
|
||||||
- Print width: **100 characters** (`printWidth: 100`)
|
|
||||||
|
|
||||||
**Linting:**
|
|
||||||
- Tool: ESLint 9.22.0 with Vue support
|
|
||||||
- Config: `eslint.config.ts` using flat config format
|
|
||||||
- Plugins:
|
|
||||||
- `@vue/eslint-config-typescript` - TypeScript support
|
|
||||||
- `eslint-plugin-vue` v10.0.0 - Vue 3 rules
|
|
||||||
- `@vue/eslint-config-prettier/skip-formatting` - Prettier integration (skip-formatting enabled)
|
|
||||||
|
|
||||||
## Import Organization
|
|
||||||
|
|
||||||
**Order:**
|
|
||||||
1. Vue and framework imports (`vue`, `vue-router`, `pinia`, `vue-i18n`)
|
|
||||||
2. Type imports (use `import type` for TypeScript types)
|
|
||||||
3. Local components (`@/components/...`)
|
|
||||||
4. Composables (`@/composables/...`)
|
|
||||||
5. Utilities and helpers
|
|
||||||
6. Data and configuration files
|
|
||||||
7. Styles (scoped CSS imported at end of `<style>`)
|
|
||||||
|
|
||||||
**Path Aliases:**
|
|
||||||
- `@/` maps to `./src/` (configured in `tsconfig.app.json`)
|
|
||||||
- Always use `@/` prefix for imports from src directory
|
|
||||||
- Examples:
|
|
||||||
- `import AppHeader from '@/components/layout/AppHeader.vue'`
|
|
||||||
- `import { useTheme } from '@/composables/useTheme'`
|
|
||||||
- `import type { Project } from '@/types'`
|
|
||||||
- `import { techStack } from '@/data/techstack'`
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- Try-catch blocks wrap risky operations (e.g., dynamic imports, DOM manipulation)
|
|
||||||
- Fallback values provided when operations fail:
|
|
||||||
- In `useAssets()`: returns placeholder image URL if asset fails to load
|
|
||||||
- In `useSeo()`: gracefully handles missing meta elements by creating them
|
|
||||||
- Console warnings for non-critical failures:
|
|
||||||
- `console.warn('message')` for warnings during execution
|
|
||||||
- Error objects logged with context: `console.warn('Failed to load image: ${path}', error)`
|
|
||||||
- Silent failures with fallbacks preferred over throwing errors for UI operations
|
|
||||||
|
|
||||||
**Examples from codebase:**
|
|
||||||
```typescript
|
|
||||||
// In useAssets.ts - graceful fallback
|
|
||||||
if (!path || path.trim() === '') {
|
|
||||||
console.warn('getImageUrl called with empty or undefined path')
|
|
||||||
return `https://via.placeholder.com/400x300/f3f4f6/9ca3af?text=${encodeURIComponent('No image')}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// In useSeo.ts - create if missing
|
|
||||||
let meta = document.querySelector(`meta[${property ? 'property' : 'name'}="${name}"]`)
|
|
||||||
if (!meta) {
|
|
||||||
meta = document.createElement('meta')
|
|
||||||
// ... setup ...
|
|
||||||
document.head.appendChild(meta)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
**Framework:** `console` object (no dedicated logging library)
|
|
||||||
|
|
||||||
**Patterns:**
|
|
||||||
- `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
|
|
||||||
|
|
||||||
**When to Comment:**
|
|
||||||
- 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`
|
|
||||||
|
|
||||||
**JSDoc/TSDoc:**
|
|
||||||
- Composables include JSDoc for exported functions
|
|
||||||
- Example from `useAssets.ts`:
|
|
||||||
```typescript
|
|
||||||
/**
|
|
||||||
* Get image URL from assets folder
|
|
||||||
* @param path - Path like '@/assets/images/filename.webp' or 'filename.webp'
|
|
||||||
* @returns string - The image URL
|
|
||||||
*/
|
|
||||||
const getImageUrl = (path: string | undefined): string => { ... }
|
|
||||||
```
|
|
||||||
- Not consistently applied across all files; use when function signature isn't obvious
|
|
||||||
|
|
||||||
## Function Design
|
|
||||||
|
|
||||||
**Size:** Composables are typically 50-100 lines; keep focused on single responsibility
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Return Values:**
|
|
||||||
- Composables return object with all exposed functions and reactive state
|
|
||||||
- Always return computed versions of reactive state when exposing refs:
|
|
||||||
```typescript
|
|
||||||
return {
|
|
||||||
isOpen, // reactive ref
|
|
||||||
currentImage, // computed from ref
|
|
||||||
openGallery, // function
|
|
||||||
closeGallery // function
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- Functions return early on validation failures with fallbacks
|
|
||||||
|
|
||||||
## Module Design
|
|
||||||
|
|
||||||
**Exports:**
|
|
||||||
- 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 = { ... }`
|
|
||||||
|
|
||||||
**Barrel Files:**
|
|
||||||
- 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'`
|
|
||||||
|
|
||||||
**Component Structure (Vue SFC):**
|
|
||||||
- `<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
|
|
||||||
|
|
||||||
**Example pattern from `AppHeader.vue`:**
|
|
||||||
```typescript
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
// Composables first
|
|
||||||
const { getImageUrl } = useAssets()
|
|
||||||
const { t } = useI18n()
|
|
||||||
// State
|
|
||||||
const isMenuOpen = ref(false)
|
|
||||||
// Computed
|
|
||||||
const navigation = computed(() => [ ... ])
|
|
||||||
// Functions
|
|
||||||
const toggleMenu = () => { ... }
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Type Safety
|
|
||||||
|
|
||||||
**TypeScript Configuration:**
|
|
||||||
- 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`
|
|
||||||
|
|
||||||
**Type Usage Patterns:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Composition API:**
|
|
||||||
- `<script setup>` syntax exclusively used
|
|
||||||
- No Options API in codebase
|
|
||||||
- Composables follow Composition API patterns
|
|
||||||
|
|
||||||
**Lifecycle Hooks:**
|
|
||||||
- `onMounted()` for initialization (theme loading, SEO setup)
|
|
||||||
- `onUnmounted()` for cleanup (removing DOM elements in useSeo)
|
|
||||||
- `watch()` for reactive side effects (theme changes)
|
|
||||||
|
|
||||||
**Reactivity:**
|
|
||||||
- `ref()` for primitive state
|
|
||||||
- `computed()` for derived state
|
|
||||||
- Avoid unnecessary reactivity; use constants when possible
|
|
||||||
- Return computed versions of refs from composables
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Convention analysis: 2026-04-07*
|
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
# External Integrations
|
|
||||||
|
|
||||||
**Analysis Date:** 2026-04-07
|
|
||||||
|
|
||||||
## APIs & External Services
|
|
||||||
|
|
||||||
**Analytics & Tracking:**
|
|
||||||
- Google Analytics (GTM)
|
|
||||||
- Measurement ID: `G-CDVVNFY6MV`
|
|
||||||
- Script: Injected in `index.html` lines 8-16
|
|
||||||
- Implementation: Inline gtag.js initialization with window.dataLayer
|
|
||||||
- Page tracking: Configured in `src/router/index.ts` lines 105-136 via `trackPageView()` function
|
|
||||||
- Tracks: Page path, title, and location on route changes
|
|
||||||
|
|
||||||
- Umami Analytics
|
|
||||||
- Website ID: `83631152-9b6b-4724-aad1-828459ff36dc`
|
|
||||||
- Hosted at: `umami.killiandalcin.fr`
|
|
||||||
- Script tag: `index.html` line 239
|
|
||||||
- Implementation: Self-hosted privacy-focused alternative to Google Analytics
|
|
||||||
|
|
||||||
**Advertising:**
|
|
||||||
- Google AdSense
|
|
||||||
- Client ID: `ca-pub-5219367964457248`
|
|
||||||
- Script: Async loaded in `index.html` lines 18-20
|
|
||||||
- Purpose: Display contextual ads on portfolio pages
|
|
||||||
|
|
||||||
**Social Integration:**
|
|
||||||
Social media links configured in `src/config/site.ts` (no direct API integration):
|
|
||||||
- GitHub/Gitea: `https://gitea.kamisama.ovh/kayjaydee`
|
|
||||||
- LinkedIn: `https://linkedin.com/in/killian-dal-cin`
|
|
||||||
- Discord: `https://discord.com/users/370940770225618954`
|
|
||||||
- Fiverr: `https://www.fiverr.com/users/mr_kayjaydee`
|
|
||||||
- Twitter: `@killiandalcin`
|
|
||||||
|
|
||||||
**Third-Party Services Referenced (Portfolio Content, Not Integrated):**
|
|
||||||
- Instagram API - Referenced in `src/components/TechBadge.vue` and `src/composables/useProjects.ts` as portfolio technology (instagram-bot project)
|
|
||||||
- Crowdin API - Referenced in `src/composables/useProjects.ts` as portfolio technology (crowdin status bot)
|
|
||||||
- Discord.js - Referenced in `src/composables/useProjects.ts` as portfolio technology (Discord bot development)
|
|
||||||
- NPM Package Registry - discord-image-generation published at `https://www.npmjs.com/package/discord-image-generation`
|
|
||||||
|
|
||||||
## Data Storage
|
|
||||||
|
|
||||||
**Databases:**
|
|
||||||
- None detected - Portfolio is static content
|
|
||||||
- Technologies showcased (not used in this app):
|
|
||||||
- MongoDB - Referenced in `src/data/techstack.ts`
|
|
||||||
- MySQL - Referenced in `src/data/techstack.ts`
|
|
||||||
- PostgreSQL - Referenced in `src/data/techstack.ts`
|
|
||||||
- Redis - Referenced in `src/data/techstack.ts`
|
|
||||||
- SQLite - Referenced in `src/data/techstack.ts`
|
|
||||||
|
|
||||||
**File Storage:**
|
|
||||||
- Local filesystem only
|
|
||||||
- Images: `src/assets/images/` directory
|
|
||||||
- Compiled assets: `dist/` directory (generated on build)
|
|
||||||
- Public assets: `public/` directory (favicon, manifest, logos, etc.)
|
|
||||||
|
|
||||||
**Caching:**
|
|
||||||
- Browser caching via content hash in filenames:
|
|
||||||
- Pattern: `assets/[ext]/[name]-[hash].[ext]` (configured in `vite.config.ts` lines 40-42)
|
|
||||||
- CSS: `assets/css/[name]-[hash].css`
|
|
||||||
- JS: `assets/js/[name]-[hash].js`
|
|
||||||
- No server-side caching layer detected
|
|
||||||
|
|
||||||
## Authentication & Identity
|
|
||||||
|
|
||||||
**Auth Provider:**
|
|
||||||
- None - Portfolio is fully public
|
|
||||||
- No authentication system implemented
|
|
||||||
- Fiverr links redirect to external Fiverr service for user authentication
|
|
||||||
|
|
||||||
## Monitoring & Observability
|
|
||||||
|
|
||||||
**Error Tracking:**
|
|
||||||
- None detected
|
|
||||||
- Errors not sent to external service
|
|
||||||
- Console errors only (JavaScript errors in browser dev tools)
|
|
||||||
|
|
||||||
**Logs:**
|
|
||||||
- Browser console logging only
|
|
||||||
- No server-side logging or aggregation
|
|
||||||
- Analytics events sent to Google Analytics and Umami for page views
|
|
||||||
|
|
||||||
**Performance Monitoring:**
|
|
||||||
- Google Analytics provides basic performance metrics
|
|
||||||
- Umami provides engagement metrics
|
|
||||||
- No dedicated APM (Application Performance Monitoring) service
|
|
||||||
|
|
||||||
## CI/CD & Deployment
|
|
||||||
|
|
||||||
**Hosting:**
|
|
||||||
- Static hosting environment (implied)
|
|
||||||
- Docker containerization available:
|
|
||||||
- Build image: `node:22-alpine` (lines 2-17 in `Dockerfile`)
|
|
||||||
- Runtime image: `nginx:stable-alpine` (lines 20-32 in `Dockerfile`)
|
|
||||||
- Port: 80
|
|
||||||
|
|
||||||
**CI Pipeline:**
|
|
||||||
- None detected in repository
|
|
||||||
- Build commands available:
|
|
||||||
- `npm run build` - Full build with type checking
|
|
||||||
- `npm run build-only` - Build only without type checking
|
|
||||||
|
|
||||||
**Deployment Configuration:**
|
|
||||||
- `Dockerfile` - Multi-stage Docker build:
|
|
||||||
1. Build stage: Node 22-alpine with npm install + build
|
|
||||||
2. Production stage: nginx serving built files
|
|
||||||
3. Custom nginx config: `nginx.conf`
|
|
||||||
- `nginx.conf` - SPA routing configuration:
|
|
||||||
- Listens on port 80 (IPv4 and IPv6)
|
|
||||||
- Document root: `/usr/share/nginx/html`
|
|
||||||
- Fallback: All non-file requests route to `/index.html` (SPA requirement)
|
|
||||||
|
|
||||||
## Environment Configuration
|
|
||||||
|
|
||||||
**Required for Development:**
|
|
||||||
- Node.js 22+
|
|
||||||
- npm 10+
|
|
||||||
|
|
||||||
**Required at Runtime:**
|
|
||||||
- No environment variables required (analytics IDs hardcoded in HTML)
|
|
||||||
- Optional (for containerization):
|
|
||||||
- Docker
|
|
||||||
- Docker Compose
|
|
||||||
|
|
||||||
**Hardcoded Configuration:**
|
|
||||||
- Google Analytics: `G-CDVVNFY6MV` - in `index.html` line 9
|
|
||||||
- Google AdSense: `ca-pub-5219367964457248` - in `index.html` line 19
|
|
||||||
- Umami Site ID: `83631152-9b6b-4724-aad1-828459ff36dc` - in `index.html` line 240
|
|
||||||
- Umami URL: `umami.killiandalcin.fr` - in `index.html` line 239
|
|
||||||
- Base URL: `https://killiandalcin.fr` - in multiple config files (`src/config/site.ts`, `index.html`)
|
|
||||||
|
|
||||||
**Secrets Location:**
|
|
||||||
- No secrets management system
|
|
||||||
- All credentials are public analytics/advertising IDs (not sensitive)
|
|
||||||
- No API keys, database passwords, or private credentials in codebase
|
|
||||||
|
|
||||||
## Webhooks & Callbacks
|
|
||||||
|
|
||||||
**Incoming:**
|
|
||||||
- None - Portfolio is read-only
|
|
||||||
|
|
||||||
**Outgoing:**
|
|
||||||
- Google Analytics pageview tracking:
|
|
||||||
- Method: GET requests to Google Analytics endpoint
|
|
||||||
- Triggered: On route navigation
|
|
||||||
- Data: Page path, title, URL
|
|
||||||
- Implementation: `src/router/index.ts` `trackPageView()` function
|
|
||||||
|
|
||||||
- Umami analytics events:
|
|
||||||
- Method: Beacon API (automatic page tracking)
|
|
||||||
- Triggered: Page load and navigation
|
|
||||||
- Data: Standard web vitals
|
|
||||||
|
|
||||||
## CDN & External Resources
|
|
||||||
|
|
||||||
**Fonts:**
|
|
||||||
- Preconnected in `index.html`:
|
|
||||||
- `https://fonts.googleapis.com`
|
|
||||||
- `https://fonts.gstatic.com`
|
|
||||||
- Actual fonts: Not loaded (preconnect only, fonts not referenced in CSS)
|
|
||||||
|
|
||||||
**Images & Media:**
|
|
||||||
- UI Avatar API:
|
|
||||||
- Service: `https://ui-avatars.com/api/`
|
|
||||||
- Usage: Testimonial avatars in `src/data/testimonials.ts` (4 instances)
|
|
||||||
- Pattern: Query-based avatar generation with initials and colors
|
|
||||||
|
|
||||||
- Placeholder Images:
|
|
||||||
- Service: `https://via.placeholder.com/`
|
|
||||||
- Usage: Fallback images in `src/composables/useAssets.ts`
|
|
||||||
- Pattern: 400x300 gray placeholders
|
|
||||||
|
|
||||||
- Portfolio Preview Image:
|
|
||||||
- Hosted at: `https://killiandalcin.fr/portfolio-preview.webp`
|
|
||||||
- Usage: Open Graph and Twitter meta tags (lines 42, 55 in `index.html`)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Integration audit: 2026-04-07*
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
# Technology Stack
|
|
||||||
|
|
||||||
**Analysis Date:** 2026-04-07
|
|
||||||
|
|
||||||
## Languages
|
|
||||||
|
|
||||||
**Primary:**
|
|
||||||
- TypeScript ~5.8.0 - Full application development
|
|
||||||
- JavaScript (ES modules) - Frontend runtime
|
|
||||||
|
|
||||||
**Secondary:**
|
|
||||||
- HTML5 - Document structure (in `index.html`)
|
|
||||||
- CSS - Styling with Tailwind CSS
|
|
||||||
- Markdown - Documentation (README.md)
|
|
||||||
- YAML - Configuration (implied through Dockerfile)
|
|
||||||
|
|
||||||
## Runtime
|
|
||||||
|
|
||||||
**Environment:**
|
|
||||||
- Node.js 22 - Development and build environment
|
|
||||||
- Browser environment - Vue 3 SFC runtime
|
|
||||||
|
|
||||||
**Package Manager:**
|
|
||||||
- npm - Dependency management
|
|
||||||
- Lockfile: `package-lock.json` (present and tracked)
|
|
||||||
|
|
||||||
## Frameworks
|
|
||||||
|
|
||||||
**Core Frontend:**
|
|
||||||
- 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/`)
|
|
||||||
|
|
||||||
**Build & Dev:**
|
|
||||||
- Vite 6.2.4 - Build tool and dev server
|
|
||||||
- Config: `vite.config.ts` with Vue plugin, DevTools plugin, chunk splitting optimization
|
|
||||||
- Build output: `dist/` with CSS code splitting, Terser minification
|
|
||||||
- Vite Plugin Vue DevTools 7.7.2 - Development utilities
|
|
||||||
- @vitejs/plugin-vue 5.2.3 - Vue 3 SFC support
|
|
||||||
|
|
||||||
**Styling:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Code Quality:**
|
|
||||||
- ESLint 9.22.0 - Linting (config: `eslint.config.ts`)
|
|
||||||
- @vue/eslint-config-typescript 14.5.0
|
|
||||||
- @vue/eslint-config-prettier 10.2.0 - Prettier integration
|
|
||||||
- eslint-plugin-vue ~10.0.0
|
|
||||||
- Prettier 3.5.3 - Code formatting (config: `.prettierrc.json`)
|
|
||||||
- Format settings: `semi: false`, `singleQuote: true`, `printWidth: 100`
|
|
||||||
|
|
||||||
**Type Checking:**
|
|
||||||
- vue-tsc 2.2.8 - Vue component type checking
|
|
||||||
- TypeScript compiler with `type-check` npm script
|
|
||||||
|
|
||||||
**Head Management:**
|
|
||||||
- @vueuse/head 2.0.0 - Dynamic document head management for meta tags and SEO
|
|
||||||
|
|
||||||
## Key Dependencies
|
|
||||||
|
|
||||||
**Critical:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Infrastructure & Build:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Developer Tools:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Environment:**
|
|
||||||
- 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`)
|
|
||||||
|
|
||||||
**Build Configuration:**
|
|
||||||
- `vite.config.ts` - Build optimizations:
|
|
||||||
- Path alias: `@/` → `./src/`
|
|
||||||
- CSS code splitting enabled
|
|
||||||
- Terser minification with console/debugger removal
|
|
||||||
- Manual chunk splitting: `vue-vendor` and `ui-components`
|
|
||||||
- Content hash in chunk filenames for cache busting
|
|
||||||
- Source maps disabled
|
|
||||||
- Chunk size warning limit: 1000 KB
|
|
||||||
|
|
||||||
**Type Configuration:**
|
|
||||||
- `tsconfig.json` - References `tsconfig.app.json` and `tsconfig.node.json`
|
|
||||||
- `tsconfig.app.json`:
|
|
||||||
- Extends `@vue/tsconfig/dom.json`
|
|
||||||
- Includes `src/**/*` and `*.vue` files
|
|
||||||
- Path alias: `@/*` → `./src/*`
|
|
||||||
- Excludes `src/**/__tests__/*`
|
|
||||||
|
|
||||||
**Linting Configuration:**
|
|
||||||
- `eslint.config.ts` - Flat config format:
|
|
||||||
- Files: `**/*.{ts,mts,tsx,vue}`
|
|
||||||
- Rules: Vue essential, TypeScript recommended
|
|
||||||
- Skips Prettier formatting enforcement
|
|
||||||
|
|
||||||
**Formatting Configuration:**
|
|
||||||
- `.prettierrc.json`:
|
|
||||||
- No semicolons
|
|
||||||
- Single quotes for strings
|
|
||||||
- 100 character line width
|
|
||||||
|
|
||||||
**PostCSS Configuration:**
|
|
||||||
- `postcss.config.js` - Tailwind CSS and Autoprefixer
|
|
||||||
|
|
||||||
**Tailwind Configuration:**
|
|
||||||
- `tailwind.config.js` - Content scanning for `index.html` and `src/**/*.{vue,js,ts,jsx,tsx}`
|
|
||||||
|
|
||||||
## Platform Requirements
|
|
||||||
|
|
||||||
**Development:**
|
|
||||||
- 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
|
|
||||||
|
|
||||||
**Production:**
|
|
||||||
- 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
|
|
||||||
- Base image: `nginx:stable-alpine`
|
|
||||||
- Document root: `/usr/share/nginx/html`
|
|
||||||
- Port: 80
|
|
||||||
- SPA fallback: All requests route to `/index.html`
|
|
||||||
|
|
||||||
**Browser Support:**
|
|
||||||
- JavaScript enabled (noscript fallback message in `index.html`)
|
|
||||||
- Modern browsers with ES2020+ support (Vite default targets)
|
|
||||||
|
|
||||||
## Scripts & Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev # Start Vite dev server with HMR
|
|
||||||
npm run build # Type check + build (parallel with npm-run-all2)
|
|
||||||
npm run type-check # Run vue-tsc type checking
|
|
||||||
npm run build-only # Build without type checking
|
|
||||||
npm run preview # Preview production build
|
|
||||||
npm run lint # Run ESLint with --fix
|
|
||||||
npm run format # Format src/ with Prettier
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Stack analysis: 2026-04-07*
|
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
# Codebase Structure
|
|
||||||
|
|
||||||
**Analysis Date:** 2026-04-07
|
|
||||||
|
|
||||||
## Directory Layout
|
|
||||||
|
|
||||||
```
|
|
||||||
portfolio/
|
|
||||||
├── .claude/ # Claude editor configuration
|
|
||||||
├── .planning/ # GSD planning documents
|
|
||||||
│ └── codebase/ # Architecture and analysis docs
|
|
||||||
├── .vscode/ # VS Code workspace settings
|
|
||||||
├── dist/ # Vite production build output
|
|
||||||
├── docs/ # Documentation files
|
|
||||||
├── node_modules/ # Dependencies (git-ignored)
|
|
||||||
├── old/ # Archived or deprecated code
|
|
||||||
├── public/ # Static assets served at root
|
|
||||||
│ └── images/ # Public static images
|
|
||||||
├── src/ # Application source code
|
|
||||||
│ ├── assets/ # Static assets imported in code
|
|
||||||
│ │ └── images/ # Project and UI images (webp format)
|
|
||||||
│ ├── components/ # Vue component library
|
|
||||||
│ │ ├── icons/ # Icon SVG components
|
|
||||||
│ │ ├── layout/ # Layout components (Header, Footer)
|
|
||||||
│ │ ├── sections/ # Page section components
|
|
||||||
│ │ ├── shared/ # Reusable UI components
|
|
||||||
│ │ ├── styles/ # Component-scoped CSS files
|
|
||||||
│ │ └── testimonials/ # Testimonial-related components
|
|
||||||
│ ├── composables/ # Vue composables (reusable logic)
|
|
||||||
│ ├── config/ # Application configuration
|
|
||||||
│ ├── data/ # Static data files (projects, testimonials, FAQ)
|
|
||||||
│ ├── i18n/ # Internationalization setup
|
|
||||||
│ ├── locales/ # Translation files (en.ts, fr.ts)
|
|
||||||
│ ├── plugins/ # Vue plugins
|
|
||||||
│ ├── router/ # Vue Router configuration
|
|
||||||
│ ├── stores/ # Pinia state stores
|
|
||||||
│ ├── types/ # TypeScript type definitions
|
|
||||||
│ ├── views/ # Page components (route targets)
|
|
||||||
│ │ └── styles/ # Page-level CSS files
|
|
||||||
│ ├── App.vue # Root component
|
|
||||||
│ ├── main.ts # Application entry point
|
|
||||||
│ └── style.css # Global stylesheet
|
|
||||||
├── .env* # Environment variables (git-ignored)
|
|
||||||
├── .eslintrc.ts # ESLint configuration
|
|
||||||
├── .gitignore # Git ignore rules
|
|
||||||
├── .prettierrc.json # Prettier code formatter config
|
|
||||||
├── eslint.config.ts # ESLint flat config
|
|
||||||
├── index.html # HTML entry point
|
|
||||||
├── package-lock.json # Dependency lock file
|
|
||||||
├── package.json # Project metadata and scripts
|
|
||||||
├── postcss.config.js # PostCSS configuration (Tailwind)
|
|
||||||
├── tailwind.config.js # Tailwind CSS configuration
|
|
||||||
├── tsconfig.app.json # TypeScript config for app code
|
|
||||||
├── tsconfig.json # Base TypeScript config
|
|
||||||
├── tsconfig.node.json # TypeScript config for build tools
|
|
||||||
├── vite.config.ts # Vite build configuration
|
|
||||||
└── [formation.md] # Formation page documentation (uncommitted)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Directory Purposes
|
|
||||||
|
|
||||||
**src/components/**
|
|
||||||
- Purpose: Vue Single File Components for UI building blocks
|
|
||||||
- Contains: Presentational components organized by domain
|
|
||||||
- Key files: `AppHeader.vue`, `ProjectCard.vue`, `HeroSection.vue`
|
|
||||||
|
|
||||||
**src/components/layout/**
|
|
||||||
- Purpose: Layout wrapper components used across pages
|
|
||||||
- Contains: `AppHeader.vue`, `AppFooter.vue`
|
|
||||||
- Key files: Header navigation, footer with social links
|
|
||||||
|
|
||||||
**src/components/sections/**
|
|
||||||
- Purpose: Full-width page section components
|
|
||||||
- Contains: `HeroSection.vue`, `FeaturedProjectsSection.vue`, `ServicesSection.vue`, `CTASection.vue`
|
|
||||||
- Key files: Large reusable page sections with styling
|
|
||||||
|
|
||||||
**src/components/shared/**
|
|
||||||
- Purpose: Shared utility components
|
|
||||||
- Contains: `CTAButtons.vue`, `SectionCTA.vue`
|
|
||||||
- Key files: Reusable button groups and CTA patterns
|
|
||||||
|
|
||||||
**src/components/testimonials/**
|
|
||||||
- Purpose: Testimonial display components
|
|
||||||
- Contains: `TestimonialCard.vue`, `TestimonialsCTA.vue`, `TestimonialsStats.vue`
|
|
||||||
- Key files: Fiverr review display and stats
|
|
||||||
|
|
||||||
**src/composables/**
|
|
||||||
- Purpose: Vue 3 Composition API utilities for reusable logic
|
|
||||||
- Contains: Custom hooks for i18n, SEO, theme, projects, gallery, date formatting, assets, site config
|
|
||||||
- Key files: `useI18n.ts`, `useSeo.ts`, `useTheme.ts`, `useProjects.ts`
|
|
||||||
|
|
||||||
**src/stores/**
|
|
||||||
- Purpose: Pinia state management
|
|
||||||
- Contains: Global reactive state stores
|
|
||||||
- Key files: `counter.ts` (minimal unused example), `auth.ts` (new, for authentication)
|
|
||||||
|
|
||||||
**src/views/**
|
|
||||||
- Purpose: Page-level Vue components matching routes
|
|
||||||
- Contains: `HomePage.vue`, `ProjectsPage.vue`, `ProjectDetailPage.vue`, `AboutPage.vue`, `ContactPage.vue`, `FiverrPage.vue`, `FormationPage.vue`
|
|
||||||
- Key files: Route target components, each handles own SEO and data fetching
|
|
||||||
|
|
||||||
**src/router/**
|
|
||||||
- Purpose: Vue Router configuration and navigation logic
|
|
||||||
- Contains: Route definitions, navigation guards, analytics tracking
|
|
||||||
- Key files: `index.ts` (main router), `guards.ts` (new, for route guards)
|
|
||||||
|
|
||||||
**src/types/**
|
|
||||||
- Purpose: TypeScript interface definitions
|
|
||||||
- Contains: Project, Technology, TechStack, SocialLink, ContactInfo, FiverrService, SiteConfig interfaces
|
|
||||||
- Key files: `index.ts`
|
|
||||||
|
|
||||||
**src/data/**
|
|
||||||
- Purpose: Static data files (non-API)
|
|
||||||
- Contains: Project definitions, testimonials, tech stack, FAQ data
|
|
||||||
- Key files: `techstack.ts`, `testimonials.ts`, `faq.ts`
|
|
||||||
|
|
||||||
**src/config/**
|
|
||||||
- Purpose: Application-wide configuration constants
|
|
||||||
- Contains: Site configuration with contact info, social links, SEO settings, performance flags
|
|
||||||
- Key files: `site.ts` (siteConfig export)
|
|
||||||
|
|
||||||
**src/locales/**
|
|
||||||
- Purpose: Translation message files
|
|
||||||
- Contains: English and French translation objects
|
|
||||||
- Key files: `en.ts`, `fr.ts`
|
|
||||||
|
|
||||||
**src/i18n/**
|
|
||||||
- Purpose: vue-i18n setup and initialization
|
|
||||||
- Contains: i18n instance creation and locale loading
|
|
||||||
- Key files: `index.ts`
|
|
||||||
|
|
||||||
**src/assets/images/**
|
|
||||||
- Purpose: Images imported in code (processed by Vite)
|
|
||||||
- Contains: Tech stack icons, project images, app images in webp format
|
|
||||||
- Subdirs: `fiverr/`, `flowboard/` for project-specific images
|
|
||||||
|
|
||||||
**public/images/**
|
|
||||||
- Purpose: Static images served at root URL without processing
|
|
||||||
- Contains: Logos, favicons, og:image preview images
|
|
||||||
|
|
||||||
**dist/**
|
|
||||||
- Purpose: Vite production build output
|
|
||||||
- Contains: Optimized HTML, JS chunks, CSS, images
|
|
||||||
- Generated: Automatically by `npm run build`
|
|
||||||
|
|
||||||
## Key File Locations
|
|
||||||
|
|
||||||
**Entry Points:**
|
|
||||||
- `index.html` - HTML entry point with Google Analytics, AdSense, structured data schemas
|
|
||||||
- `src/main.ts` - Vue app initialization, plugin registration (Pinia, Router, i18n)
|
|
||||||
- `src/router/index.ts` - Route table and navigation hooks
|
|
||||||
|
|
||||||
**Configuration:**
|
|
||||||
- `vite.config.ts` - Build optimization, chunk splitting, alias resolution (@)
|
|
||||||
- `tsconfig.json` - Base TypeScript settings with references to app and node configs
|
|
||||||
- `tailwind.config.js` - Tailwind CSS customization
|
|
||||||
- `postcss.config.js` - PostCSS with Tailwind
|
|
||||||
- `.eslintrc.ts` - ESLint rules and Vue plugin
|
|
||||||
- `.prettierrc.json` - Code formatting rules
|
|
||||||
|
|
||||||
**Core Logic:**
|
|
||||||
- `src/App.vue` - Root component with theme init, layout structure, scroll on route change
|
|
||||||
- `src/composables/useI18n.ts` - i18n convenience wrapper with locale switching
|
|
||||||
- `src/composables/useSeo.ts` - Dynamic meta tag management for SPA
|
|
||||||
- `src/composables/useTheme.ts` - Theme state and persistence
|
|
||||||
- `src/config/site.ts` - Centralized site configuration and constants
|
|
||||||
|
|
||||||
**Testing:**
|
|
||||||
- No test files detected in committed code (*.test.ts, *.spec.ts not found)
|
|
||||||
- Test setup tools not configured (Jest/Vitest not in package.json)
|
|
||||||
|
|
||||||
## Naming Conventions
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Components: PascalCase.vue (e.g., `AppHeader.vue`, `ProjectCard.vue`)
|
|
||||||
- Composables: camelCase prefixed with 'use' (e.g., `useProjects.ts`, `useSeo.ts`)
|
|
||||||
- Data/Config: camelCase or lowercase (e.g., `techstack.ts`, `site.ts`)
|
|
||||||
- Pages/Views: PascalCase with 'Page' suffix (e.g., `HomePage.vue`, `ProjectsPage.vue`)
|
|
||||||
- CSS: Matches component name or function (e.g., `AppHeader.css`, `HomePage.css`)
|
|
||||||
- Types: camelCase in index.ts (e.g., `Project`, `Technology`, `SiteConfig`)
|
|
||||||
|
|
||||||
**Directories:**
|
|
||||||
- Feature directories: lowercase plural (e.g., `components/`, `composables/`, `views/`)
|
|
||||||
- Subdirectories: lowercase descriptive names (e.g., `layout/`, `sections/`, `shared/`)
|
|
||||||
- Asset subdirectories: descriptive lowercase (e.g., `images/`, `fiverr/`, `flowboard/`)
|
|
||||||
|
|
||||||
**Vue Components:**
|
|
||||||
- Props: camelCase in script, kebab-case in template (Vue standard)
|
|
||||||
- Methods: camelCase (e.g., `toggleTheme()`, `setMetaTag()`)
|
|
||||||
- Computed: camelCase (e.g., `isDark`, `currentLocale`)
|
|
||||||
- Refs: camelCase (e.g., `isMenuOpen`, `galleryIndex`)
|
|
||||||
- CSS classes: kebab-case (e.g., `.hero-title`, `.nav-link`, `.btn-primary`)
|
|
||||||
|
|
||||||
**Constants:**
|
|
||||||
- Global config exports: camelCase (e.g., `siteConfig`)
|
|
||||||
- Array constants in data files: camelCase plural (e.g., `testimonials`, `baseProjects`)
|
|
||||||
- Type/Interface names: PascalCase (e.g., `Project`, `Testimonial`, `Technology`)
|
|
||||||
|
|
||||||
## Where to Add New Code
|
|
||||||
|
|
||||||
**New Feature (e.g., New Page):**
|
|
||||||
- Primary code: `src/views/FeaturePage.vue`
|
|
||||||
- Add route: `src/router/index.ts` (add route object to routes array)
|
|
||||||
- Add SEO data: Route meta object with title/description
|
|
||||||
- Translations: Add keys to `src/locales/en.ts` and `src/locales/fr.ts`
|
|
||||||
- Data files: Create in `src/data/feature.ts` if needed
|
|
||||||
- Tests: Would go in `src/views/__tests__/FeaturePage.spec.ts` (not yet configured)
|
|
||||||
|
|
||||||
**New Component/Module:**
|
|
||||||
- Reusable component: `src/components/FeatureName.vue`
|
|
||||||
- Layout component: `src/components/layout/ComponentName.vue`
|
|
||||||
- Section component: `src/components/sections/SectionName.vue`
|
|
||||||
- Shared/utility component: `src/components/shared/UtilityName.vue`
|
|
||||||
- Component CSS: `src/components/styles/ComponentName.css` (imported in component)
|
|
||||||
|
|
||||||
**New Composable:**
|
|
||||||
- Implementation: `src/composables/useFeatureName.ts`
|
|
||||||
- Return: Object with reactive state and methods
|
|
||||||
- Pattern: Use `onMounted`/`onUnmounted` for lifecycle, return refs/computed/methods
|
|
||||||
- Export: Named export of function (not default)
|
|
||||||
|
|
||||||
**Utilities/Services:**
|
|
||||||
- Shared helpers: `src/composables/useUtilityName.ts` (if stateful) or create `src/utils/utilityName.ts` (if stateless)
|
|
||||||
- Type definitions: Add to `src/types/index.ts`
|
|
||||||
- Config constants: Add to `src/config/site.ts` or create new `src/config/featureName.ts`
|
|
||||||
|
|
||||||
**Styling:**
|
|
||||||
- Global styles: `src/style.css` (imported in main.ts)
|
|
||||||
- Component scoped: `<style scoped>` in .vue file or separate `src/components/styles/ComponentName.css`
|
|
||||||
- Page styles: `src/views/styles/PageName.css`
|
|
||||||
- Tailwind classes: Use directly in templates (no separate CSS needed for basic styling)
|
|
||||||
|
|
||||||
**Translations:**
|
|
||||||
- English messages: `src/locales/en.ts` (export default object with nested structure)
|
|
||||||
- French messages: `src/locales/fr.ts` (same structure as English)
|
|
||||||
- Usage in components: `const { t } = useI18n()` then `{{ t('section.key') }}`
|
|
||||||
|
|
||||||
**Data/State:**
|
|
||||||
- Static data: `src/data/featureName.ts` (export arrays/objects)
|
|
||||||
- Global state: `src/stores/featureName.ts` (defineStore with Pinia)
|
|
||||||
- Site config: Update `src/config/site.ts` with new configuration
|
|
||||||
|
|
||||||
## Special Directories
|
|
||||||
|
|
||||||
**dist/:**
|
|
||||||
- Purpose: Production build output
|
|
||||||
- Generated: Yes (by Vite during `npm run build`)
|
|
||||||
- Committed: No (in .gitignore)
|
|
||||||
- Content: Optimized HTML, JS chunks with hashes, CSS, images
|
|
||||||
|
|
||||||
**node_modules/:**
|
|
||||||
- Purpose: Installed npm dependencies
|
|
||||||
- Generated: Yes (by npm install)
|
|
||||||
- Committed: No (in .gitignore)
|
|
||||||
- Content: Third-party packages
|
|
||||||
|
|
||||||
**public/:**
|
|
||||||
- Purpose: Static files served at root during dev and prod
|
|
||||||
- Generated: No (manually maintained)
|
|
||||||
- Committed: Yes
|
|
||||||
- Content: favicon.ico, favicon.webp, site.webmanifest, static images
|
|
||||||
|
|
||||||
**.git/:**
|
|
||||||
- Purpose: Git version control metadata
|
|
||||||
- Generated: Yes (by git init)
|
|
||||||
- Committed: No (in .gitignore)
|
|
||||||
- Content: Commit history, branches, objects
|
|
||||||
|
|
||||||
**old/:**
|
|
||||||
- Purpose: Archived or deprecated code
|
|
||||||
- Generated: No (manually maintained)
|
|
||||||
- Committed: Yes
|
|
||||||
- Content: Previous versions of components or features
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Structure analysis: 2026-04-07*
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
# Testing Patterns
|
|
||||||
|
|
||||||
**Analysis Date:** 2026-04-07
|
|
||||||
|
|
||||||
## Test Framework
|
|
||||||
|
|
||||||
**Status:** NOT IMPLEMENTED
|
|
||||||
|
|
||||||
No testing framework is currently configured in this project. There are:
|
|
||||||
- No test files (no `.test.ts`, `.spec.ts`, `.test.vue`, or `.spec.vue` files found)
|
|
||||||
- No test runner configured (Jest, Vitest, Cypress, Playwright, etc.)
|
|
||||||
- No test configuration files in project root
|
|
||||||
|
|
||||||
**Recommendations for Implementation:**
|
|
||||||
|
|
||||||
Given this is a Vue 3 + TypeScript portfolio, recommended testing setup would be:
|
|
||||||
|
|
||||||
1. **Unit Testing:** Vitest (modern, Vue 3 native, fast)
|
|
||||||
- Lightweight alternative to Jest
|
|
||||||
- Built-in TypeScript support
|
|
||||||
- Fast HMR for test-driven development
|
|
||||||
|
|
||||||
2. **Component Testing:** Vitest + `@vue/test-utils`
|
|
||||||
- Test Vue components in isolation
|
|
||||||
- Mock composables and routing
|
|
||||||
|
|
||||||
3. **E2E Testing:** Playwright or Cypress
|
|
||||||
- Full user journey testing
|
|
||||||
- SEO/routing validation
|
|
||||||
- Analytics tracking verification
|
|
||||||
|
|
||||||
## Current Development Practices
|
|
||||||
|
|
||||||
**Build Pipeline:**
|
|
||||||
```bash
|
|
||||||
npm run type-check # Vue TypeScript compilation check
|
|
||||||
npm run build # Production build with type checking
|
|
||||||
npm run lint # ESLint with --fix flag
|
|
||||||
npm run format # Prettier formatting on src/
|
|
||||||
npm run dev # Vite dev server
|
|
||||||
```
|
|
||||||
|
|
||||||
**Type Safety as Testing:**
|
|
||||||
- Type-checking replaces some unit test coverage
|
|
||||||
- `npm run type-check` validates all TypeScript during build
|
|
||||||
- ESLint prevents common errors with Vue and TypeScript plugins
|
|
||||||
|
|
||||||
## What Should Be Tested (If Framework Were Added)
|
|
||||||
|
|
||||||
### Composables (`src/composables/`)
|
|
||||||
|
|
||||||
**`useTheme.ts` - Unit Tests Needed:**
|
|
||||||
- `toggleTheme()` flips isDark state
|
|
||||||
- `setTheme('dark')` / `setTheme('light')` correctly sets theme
|
|
||||||
- `getTheme()` returns current theme string
|
|
||||||
- `applyTheme()` sets correct class on `document.documentElement`
|
|
||||||
- `saveTheme()` persists to localStorage
|
|
||||||
- `loadTheme()` reads from localStorage and defaults to 'dark'
|
|
||||||
- Watch effect triggers applyTheme and saveTheme on isDark change
|
|
||||||
- onMounted initialization sequence
|
|
||||||
|
|
||||||
**`useGallery.ts` - Unit Tests Needed:**
|
|
||||||
- `openGallery(images, index)` sets state correctly
|
|
||||||
- `closeGallery()` resets all state
|
|
||||||
- `nextImage()` increments index when available
|
|
||||||
- `previousImage()` decrements index when available
|
|
||||||
- `goToImage(index)` validates index bounds
|
|
||||||
- Computed properties (`currentImage`, `hasNext`, `hasPrevious`) reflect correct values
|
|
||||||
- Body scroll overflow is managed correctly
|
|
||||||
|
|
||||||
**`useSeo.ts` - Unit Tests Needed:**
|
|
||||||
- Meta tags are created and updated correctly
|
|
||||||
- `setTitle()` updates document.title and og:title
|
|
||||||
- `setMetaTag()` creates new tags if missing
|
|
||||||
- `setLinkTag()` manages canonical links
|
|
||||||
- `setStructuredData()` adds JSON-LD scripts
|
|
||||||
- onUnmounted cleanup removes all added elements (no memory leaks)
|
|
||||||
- Structured breadcrumb data is generated for non-home routes
|
|
||||||
- Title suffix appended only when needed
|
|
||||||
|
|
||||||
**`useI18n.ts` - Unit Tests Needed:**
|
|
||||||
- `switchLocale()` changes locale and saves to localStorage
|
|
||||||
- `toggleLocale()` switches between en/fr
|
|
||||||
- Computed properties reflect current locale
|
|
||||||
- Invalid locales rejected
|
|
||||||
|
|
||||||
**`useAssets.ts` - Unit Tests Needed:**
|
|
||||||
- `getImageUrl()` resolves asset paths correctly
|
|
||||||
- Fallback placeholder returned for missing images
|
|
||||||
- Handles both `@/assets/images/` and plain filename formats
|
|
||||||
- Warns on console for missing/empty paths
|
|
||||||
- Graceful error handling with placeholder fallback
|
|
||||||
|
|
||||||
**`useProjects.ts` - Unit Tests Needed:**
|
|
||||||
- Projects computed array returns correct structure
|
|
||||||
- Translations applied to titles, descriptions, buttons
|
|
||||||
- Project count matches expected baseline (7 projects)
|
|
||||||
- Featured flag correctly identifies featured projects
|
|
||||||
|
|
||||||
**`useDateFormat.ts` - Unit Tests Needed:**
|
|
||||||
- `formatRelativeTime()` returns correct French/English strings
|
|
||||||
- Year boundaries handled correctly (1 year = "1 year ago", 2+ = "X years ago")
|
|
||||||
- Month, day granularity works in both locales
|
|
||||||
- Date parsing from DD/MM/YYYY format works correctly
|
|
||||||
|
|
||||||
### Router (`src/router/index.ts`)
|
|
||||||
|
|
||||||
**Navigation Tests:**
|
|
||||||
- All routes load their components (lazy-loaded pages)
|
|
||||||
- ScrollBehavior resets to top on normal navigation
|
|
||||||
- ScrollBehavior restores position on back/forward
|
|
||||||
- ScrollBehavior smooth-scrolls to hash anchors
|
|
||||||
- Meta tags (title, description) updated on route change
|
|
||||||
|
|
||||||
**TODO:** 404 page implementation and testing needed (see comment on line 51)
|
|
||||||
|
|
||||||
### Components (if component testing added)
|
|
||||||
|
|
||||||
**High-value components to test:**
|
|
||||||
- `AppHeader.vue` - Navigation links active state, mobile menu toggle
|
|
||||||
- `ProjectCard.vue` - Image loading, translated content, button visibility
|
|
||||||
- `ContactMethod.vue` - Props validation, conditional link component rendering
|
|
||||||
- `ServiceFAQ.vue` - Q&A toggle state, feature list rendering
|
|
||||||
|
|
||||||
## No Mocking Currently Used
|
|
||||||
|
|
||||||
Since there are no tests, no mocking framework is configured. When tests are added:
|
|
||||||
|
|
||||||
**What to Mock:**
|
|
||||||
- Vue Router (`useRouter`, `useRoute`) - use `@vue/test-utils` mocking
|
|
||||||
- localStorage - mock in test setup
|
|
||||||
- Window/Document APIs - mock in unit tests
|
|
||||||
- Dynamic image imports - mock in `useAssets` tests
|
|
||||||
- Translation (`useI18n`) - provide test translations
|
|
||||||
|
|
||||||
**What NOT to Mock:**
|
|
||||||
- Composable logic itself - test composables directly
|
|
||||||
- Type validation - let TypeScript handle it
|
|
||||||
- Vue reactivity - test against real ref/computed behavior
|
|
||||||
- Business logic in utility functions
|
|
||||||
|
|
||||||
## Missing Infrastructure
|
|
||||||
|
|
||||||
### Configuration Files Needed:
|
|
||||||
|
|
||||||
1. **Vitest config** (`vitest.config.ts`):
|
|
||||||
```typescript
|
|
||||||
import { defineConfig } from 'vitest/config'
|
|
||||||
import vue from '@vitejs/plugin-vue'
|
|
||||||
// ... rest of config
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Test utilities setup** (`src/__tests__/setup.ts`):
|
|
||||||
- Global test configuration
|
|
||||||
- Mock setup for localStorage, window
|
|
||||||
- Test utilities/helpers
|
|
||||||
|
|
||||||
3. **Component test examples** structure:
|
|
||||||
- `src/__tests__/unit/` for unit tests
|
|
||||||
- `src/__tests__/components/` for component tests
|
|
||||||
- Matching file structure to src/
|
|
||||||
|
|
||||||
### Package Dependencies Needed:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"devDependencies": {
|
|
||||||
"vitest": "^1.x",
|
|
||||||
"@vue/test-utils": "^2.x",
|
|
||||||
"@testing-library/vue": "^8.x",
|
|
||||||
"happy-dom": "^12.x",
|
|
||||||
"playwright": "^1.x"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Coverage Targets (If Implemented)
|
|
||||||
|
|
||||||
**Recommended targets:**
|
|
||||||
- Composables: 85%+ coverage (critical for reliability)
|
|
||||||
- Router: 90%+ coverage (navigation is critical)
|
|
||||||
- Components: 70%+ coverage (UI changes less frequently)
|
|
||||||
- Overall: 75%+ coverage
|
|
||||||
|
|
||||||
**High-risk areas needing coverage:**
|
|
||||||
- SEO meta tag manipulation (`useSeo`)
|
|
||||||
- Theme persistence and DOM manipulation (`useTheme`)
|
|
||||||
- Image asset loading with fallbacks (`useAssets`)
|
|
||||||
- Locale switching and persistence (`useI18n`)
|
|
||||||
|
|
||||||
## Development Testing Approach (Current)
|
|
||||||
|
|
||||||
Without automated tests, verification is manual:
|
|
||||||
|
|
||||||
1. **Type checking:** `npm run type-check` validates types
|
|
||||||
2. **Linting:** `npm run lint` catches code style issues
|
|
||||||
3. **Manual testing:** `npm run dev` starts dev server for browser testing
|
|
||||||
4. **Build validation:** `npm run build` ensures code compiles
|
|
||||||
|
|
||||||
This is appropriate for a portfolio site but would need proper testing for production applications or team projects.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Testing analysis: 2026-04-07*
|
|
||||||
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |