diff --git a/assets/data/education.json b/assets/data/education.json index d6c43c9..63d2a17 100644 --- a/assets/data/education.json +++ b/assets/data/education.json @@ -1,16 +1,18 @@ -{ - "id": "developer-training", - "title": "DEVELOPER_TRAINING.EXE", - "classification": "Concepteur Développeur d'Applications (RNCP Level BAC+4)", - "objective": "Intensive full-stack development program focusing on modern web technologies including TypeScript, Node.js, and contemporary development frameworks. Building comprehensive skills in both frontend and backend development.", - "timeline": "École O'clock | January 2025 - October 2025", - "techStack": [ - "TYPESCRIPT", - "NODE.JS", - "FULL_STACK", - "WEB_FRAMEWORKS" - ], - "status": "current", - "statusLabel": "ACTIVE | IN_PROGRESS", - "isCurrent": true -} + + { + "id": "developer-training", + "title": "DEVELOPER_TRAINING.EXE", + "classification": "Concepteur Développeur d'Applications (RNCP Level BAC+4)", + "objective": "Intensive full-stack development program focusing on modern web technologies including TypeScript, Node.js, and contemporary development frameworks. Building comprehensive skills in both frontend and backend development.", + "timeline": "École O'clock | January 2025 - October 2025", + "techStack": [ + "TYPESCRIPT", + "NODE.JS", + "FULL_STACK", + "WEB_FRAMEWORKS" + ], + "status": "current", + "statusLabel": "ACTIVE | IN_PROGRESS", + "isCurrent": true + } + diff --git a/src/app/pages/experience/operative-history/column-education-info/column-education-info.component.html b/src/app/pages/experience/operative-history/column-education-info/column-education-info.component.html index 97ba39e..c7bc9b2 100644 --- a/src/app/pages/experience/operative-history/column-education-info/column-education-info.component.html +++ b/src/app/pages/experience/operative-history/column-education-info/column-education-info.component.html @@ -2,22 +2,29 @@

[EDUCATION_PROTOCOL]

- @if (currentTraining(); as training) { -
-
-
+ @if (dataService.educations().length === 0) { +
Loading education data...
+ } @else { + @if (currentTraining(); as training) { +
+
+
-
-
{{ training.statusLabel }}
-

{{ training.title }}

-
+
+
{{ training.statusLabel }}
+

{{ training.title }}

+
-
- - - -
-
+
+ + + + +
+
+ } @else { +
No current training found
+ } } diff --git a/src/app/pages/experience/operative-history/column-education-info/column-education-info.component.ts b/src/app/pages/experience/operative-history/column-education-info/column-education-info.component.ts index cdd5453..9139aec 100644 --- a/src/app/pages/experience/operative-history/column-education-info/column-education-info.component.ts +++ b/src/app/pages/experience/operative-history/column-education-info/column-education-info.component.ts @@ -1,8 +1,8 @@ -import { Component, signal } from '@angular/core'; -import { type Experience } from '../../../../shared/models/experience.model'; +import { Component, inject, computed } from '@angular/core'; import { DetailBlockComponent } from '../shared/detail-block/detail-block.component'; import { InfoCardComponent } from '../shared/info-card/info-card.component'; import { TechStackComponent } from '../tech-stack/tech-stack.component'; +import { DataService } from '../../../../shared/services/data.service'; @Component({ selector: 'app-column-education-info', @@ -11,23 +11,22 @@ import { TechStackComponent } from '../tech-stack/tech-stack.component'; styleUrl: './column-education-info.component.css', }) export class ColumnEducationInfoComponent { - private _currentTraining = signal({ - id: 'developer-training', - title: 'DEVELOPER_TRAINING.EXE', - classification: "Concepteur Développeur d'Applications (RNCP Level BAC+4)", - objective: - 'Intensive full-stack development program focusing on modern web technologies including TypeScript, Node.js, and contemporary development frameworks. Building comprehensive skills in both frontend and backend development.', - timeline: "École O'clock | January 2025 - October 2025", - techStack: [ - 'TYPESCRIPT', - 'NODE.JS', - 'FULL_STACK', - 'WEB_FRAMEWORKS', - ] as const, - status: 'current', - statusLabel: 'ACTIVE | IN_PROGRESS', - isCurrent: true, - } as const); + readonly dataService = inject(DataService); - currentTraining = this._currentTraining.asReadonly(); + // Find the current training (status: 'current' or isCurrent: true) + currentTraining = computed(() => { + const educations = this.dataService.educations(); + console.log('Educations:', educations); + + if (educations.length === 0) { + console.log('No educations loaded yet'); + return null; + } + + const current = educations.find( + (edu) => edu.status === 'current' || edu.isCurrent, + ); + console.log('Current training:', current); + return current || null; + }); } diff --git a/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.css b/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.css index 0c2f785..4f2650e 100644 --- a/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.css +++ b/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.css @@ -38,8 +38,6 @@ animation: slideInLeft 0.4s ease-out calc(var(--animation-delay-base) * 7) forwards; } - - /* Personal Info Grid */ .trait-grid { display: grid; @@ -47,32 +45,6 @@ gap: 0.25rem; } -.trait-item { - background: linear-gradient(135deg, var(--color-nier-accent) 0%, var(--color-nier-highlight) 100%); - color: var(--color-nier-text-light); - padding: 0.375rem 0.25rem; - font-size: 0.7rem; - font-family: var(--font-terminal); - text-transform: uppercase; - letter-spacing: 0.05em; - border-radius: 3px; - white-space: nowrap; - transition: var(--transition-smooth); - box-shadow: 0 2px 4px rgba(0,0,0,0.1); - text-align: center; - - /* Initial state for staggered animation */ - opacity: 0; - transform: scale(0.8) translateY(10px); - animation: techItemFadeIn 0.4s ease-out forwards; - animation-delay: calc(var(--animation-delay-base) * 19); -} - -.trait-item:hover { - transform: translateY(-2px) scale(1.05); - box-shadow: 0 6px 12px rgba(0,0,0,0.2); -} - .interest-item { margin-bottom: 0.5rem; display: flex; @@ -187,17 +159,6 @@ } } -@keyframes techItemFadeIn { - from { - opacity: 0; - transform: scale(0.8) translateY(10px); - } - to { - opacity: 1; - transform: scale(1) translateY(0); - } -} - @keyframes fadeIn { from { opacity: 0; @@ -252,18 +213,6 @@ gap: 0.375rem; } - .trait-item { - padding: 0.5rem 0.375rem; - font-size: 0.75rem; - - /* Simplified trait animation */ - animation-name: techItemFadeIn; - animation-duration: 0.2s; - animation-timing-function: ease-out; - animation-delay: 0.7s; - animation-fill-mode: forwards; - } - .interest-item { margin-bottom: 0.75rem; padding: 0.5rem 0.25rem; @@ -323,7 +272,6 @@ /* Remove complex animations on very small screens */ @media (max-width: 360px) { - .trait-item, .interest-item, .philosophy-text, .philosophy-text::before, @@ -346,7 +294,6 @@ .personal-column, .column-title, .info-card, - .trait-item, .interest-item, .philosophy-text, .philosophy-text::before, diff --git a/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.html b/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.html index ac70586..e333f00 100644 --- a/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.html +++ b/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.html @@ -17,14 +17,15 @@ -
- CURIOUS - PERSISTENT - ANALYTICAL - CREATIVE - ADAPTIVE - SELF-LEARNER -
+ @if (dataService.traits().length === 0) { +
Loading traits...
+ } @else { +
+ @for (trait of traits(); track trait) { + + } +
+ }
@@ -35,7 +36,4 @@ {{ personalNote() }}
- - - diff --git a/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.ts b/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.ts index 7d44048..aa4e514 100644 --- a/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.ts +++ b/src/app/pages/experience/operative-history/column-personal-info/column-personal-info.component.ts @@ -1,13 +1,17 @@ -import { Component, signal } from '@angular/core'; +import { Component, signal, inject } from '@angular/core'; import { InfoCardComponent } from '../shared/info-card/info-card.component'; +import { TraitItemComponent } from './trait-item/trait-item.component'; +import { DataService } from '../../../../shared/services/data.service'; @Component({ selector: 'app-column-personal-info', - imports: [InfoCardComponent], + imports: [InfoCardComponent, TraitItemComponent], templateUrl: './column-personal-info.component.html', styleUrl: './column-personal-info.component.css', }) export class ColumnPersonalInfoComponent { + readonly dataService = inject(DataService); + private readonly _personalNote = signal( 'In the spaces between keystrokes and compiler runs, I contemplate the philosophical implications of our digital convergence. When not debugging the matrix, I tend to six small organic beings who remind me that even in our increasingly automated world, care and attention to living creatures remains profoundly important.', ); @@ -20,4 +24,7 @@ export class ColumnPersonalInfoComponent { readonly personalNote = this._personalNote.asReadonly(); readonly philosophyText = this._philosophyText.asReadonly(); + + // Use traits from DataService + traits = this.dataService.traits; } diff --git a/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.css b/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.css new file mode 100644 index 0000000..97c0a41 --- /dev/null +++ b/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.css @@ -0,0 +1,58 @@ +.trait-item { + background: linear-gradient(135deg, var(--color-nier-accent) 0%, var(--color-nier-highlight) 100%); + color: var(--color-nier-text-light); + padding: 0.375rem 0.25rem; + font-size: 0.7rem; + font-family: var(--font-terminal); + text-transform: uppercase; + letter-spacing: 0.05em; + border-radius: 3px; + white-space: nowrap; + transition: all 0.3s ease; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + text-align: center; + display: block; + width: 100%; + + /* Animation */ + opacity: 0; + transform: scale(0.8) translateY(10px); + animation: techItemFadeIn 0.4s ease-out forwards; + animation-delay: calc(var(--animation-delay-base, 0.1s) * 19); +} + +.trait-item:hover { + transform: translateY(-2px) scale(1.05); + box-shadow: 0 6px 12px rgba(0,0,0,0.2); +} + +/* Animation keyframe */ +@keyframes techItemFadeIn { + from { + opacity: 0; + transform: scale(0.8) translateY(10px); + } + to { + opacity: 1; + transform: scale(1) translateY(0); + } +} + +/* Responsive design */ +@media (max-width: 480px) { + .trait-item { + padding: 0.5rem 0.375rem; + font-size: 0.75rem; + animation-delay: 0.7s; + } +} + +/* Reduced motion support */ +@media (prefers-reduced-motion: reduce) { + .trait-item { + animation: none !important; + transition: none !important; + opacity: 1 !important; + transform: none !important; + } +} diff --git a/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.html b/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.html new file mode 100644 index 0000000..0f0192b --- /dev/null +++ b/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.html @@ -0,0 +1,2 @@ + +{{ traitvalue() }} diff --git a/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.ts b/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.ts new file mode 100644 index 0000000..74ce927 --- /dev/null +++ b/src/app/pages/experience/operative-history/column-personal-info/trait-item/trait-item.component.ts @@ -0,0 +1,11 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'app-trait-item', + imports: [], + templateUrl: './trait-item.component.html', + styleUrl: './trait-item.component.css', +}) +export class TraitItemComponent { + traitvalue = input(); +} diff --git a/src/app/pages/experience/operative-history/column-professional-info/column-professional-info.component.html b/src/app/pages/experience/operative-history/column-professional-info/column-professional-info.component.html index 9ab6b6c..0955ea2 100644 --- a/src/app/pages/experience/operative-history/column-professional-info/column-professional-info.component.html +++ b/src/app/pages/experience/operative-history/column-professional-info/column-professional-info.component.html @@ -1,32 +1,36 @@

[PROFESSIONAL_ARCHIVE]

-
- @for (experience of experiences(); track experience.id) { -
-
- @if (experience.status === 'current') { -
- } + @if (dataService.experiences().length === 0) { +
Loading professional experience...
+ } @else { +
+ @for (experience of experiences(); track experience.id) { +
+
+ @if (experience.status === 'current') { +
+ } -
-
- {{ experience.statusLabel }} +
+
+ {{ experience.statusLabel }} +
+

+ {{ experience.title }} +

+
+ +
+ + +
-

- {{ experience.title }} -

-
- -
- - - -
-
- } -
+
+ } +
+ }
diff --git a/src/app/pages/experience/operative-history/column-professional-info/column-professional-info.component.ts b/src/app/pages/experience/operative-history/column-professional-info/column-professional-info.component.ts index 26561dd..7d35099 100644 --- a/src/app/pages/experience/operative-history/column-professional-info/column-professional-info.component.ts +++ b/src/app/pages/experience/operative-history/column-professional-info/column-professional-info.component.ts @@ -1,7 +1,7 @@ -import { Component, signal } from '@angular/core'; -import { Experience } from '../../../../shared/models/experience.model'; +import { Component, inject } from '@angular/core'; import { DetailBlockComponent } from '../shared/detail-block/detail-block.component'; import { TechStackComponent } from '../tech-stack/tech-stack.component'; +import { DataService } from '../../../../shared/services/data.service'; @Component({ selector: 'app-column-professional-info', @@ -10,52 +10,8 @@ import { TechStackComponent } from '../tech-stack/tech-stack.component'; styleUrl: './column-professional-info.component.css', }) export class ColumnProfessionalInfoComponent { - private _experiences = signal([ - { - id: 'senior-specialist', - title: 'PYTHON_API_SPECIALIST.EXE', - classification: 'Python & Django API/RPA Specialist', - objective: - 'Specialized in Python/Django development for API automation and RPA processes. Led technical interventions for complex enterprise HR system integrations, developing robust solutions that bridge business requirements with technical implementation.', - timeline: 'UKG | January 2024 - January 2025', - techStack: ['PYTHON', 'DJANGO', 'API', 'RPA'], - status: 'completed', - statusLabel: 'ARCHIVED | SME_SPECIALIST', - }, - { - id: 'specialist-ii', - title: 'SYSTEMS_INTEGRATION_SME.EXE', - classification: 'Enterprise Systems Integration', - objective: - 'Subject Matter Expert (SME) for enterprise HRSD connectors and system integrations. Specialized in complex technical challenges involving API development, RPA automation, and SFTP protocols. Provided BI support and data visualization solutions.', - timeline: 'UKG | January 2023 - January 2024', - techStack: ['PYTHON', 'DJANGO', 'API', 'RPA'], - status: 'completed', - statusLabel: 'ARCHIVED | SME_SPECIALIST', - }, - { - id: 'specialist', - title: 'SOLUTION_SUPPORT.EXE', - classification: 'B2B Solution Support', - objective: - 'Delivered high-level technical support for VIP business clients using HRSD solutions. Developed expertise in SaaS platforms and enterprise decision-making processes while managing critical client relationships.', - timeline: 'UKG | October 2021 - January 2023', - techStack: ['SAAS', 'HRSD', 'CLIENT_MGMT'], - status: 'completed', - statusLabel: 'ARCHIVED | VIP_TIER', - }, - { - id: 'technician', - title: 'IT_TECHNICIAN.EXE', - classification: 'Information Technology Technician', - objective: - 'Provided VIP end-user support, managed hardware/software deployments, and implemented networking and information security practices in a mission-critical environment.', - timeline: 'Armée de Terre | July 2018 - December 2018', - techStack: ['NETWORKING', 'SECURITY', 'DEPLOYMENT'], - status: 'completed', - statusLabel: 'ARCHIVED | MILITARY', - }, - ]); + readonly dataService = inject(DataService); - experiences = this._experiences; + // Use experiences from DataService + experiences = this.dataService.experiences; } diff --git a/src/app/shared/models/education.model.ts b/src/app/shared/models/education.model.ts new file mode 100644 index 0000000..4f98245 --- /dev/null +++ b/src/app/shared/models/education.model.ts @@ -0,0 +1,13 @@ +type Education = { + id: string; + title: string; + classification: string; + objective: string; + timeline: string; + techStack: string[]; + status: 'current' | 'completed'; + statusLabel: string; + isCurrent?: boolean; +}; + +export { type Education }; diff --git a/src/app/shared/services/data.service.ts b/src/app/shared/services/data.service.ts index b93799a..7c0a3fb 100644 --- a/src/app/shared/services/data.service.ts +++ b/src/app/shared/services/data.service.ts @@ -2,7 +2,9 @@ import { Injectable, signal, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { type Project } from '../models/project.model'; import { firstValueFrom } from 'rxjs'; -import { NeuralProfileNode } from '../models/about.model'; +import { type NeuralProfileNode } from '../models/about.model'; +import { type Education } from '../models/education.model'; +import { type Experience } from '../models/experience.model'; @Injectable({ providedIn: 'root', @@ -13,10 +15,16 @@ export class DataService { // Private signals for state management private _projects = signal([]); private _skills = signal([]); + private _educations = signal([]); + private _experiences = signal([]); + private _traits = signal([]); // Public readonly signals readonly projects = this._projects.asReadonly(); readonly skills = this._skills.asReadonly(); + readonly educations = this._educations.asReadonly(); + readonly experiences = this._experiences.asReadonly(); + readonly traits = this._traits.asReadonly(); constructor() { this.loadInitialData(); @@ -24,27 +32,93 @@ export class DataService { private async loadInitialData(): Promise { try { - const [projects, skills] = await Promise.all([ - this.fetchProjects(), - this.fetchSkills(), - ]); + const [projects, skills, educations, experiences, traits] = + await Promise.all([ + this.fetchProjects(), + this.fetchSkills(), + this.fetchEducations(), + this.fetchExperiences(), + this.fetchTraits(), + ]); + + console.log('All data loaded:', { + projects, + skills, + educations, + experiences, + traits, + }); this._projects.set(projects); this._skills.set(skills); + this._educations.set(educations); + this._experiences.set(experiences); + this._traits.set(traits); } catch (error) { console.error('Error loading data:', error); } } private async fetchProjects(): Promise { - return firstValueFrom(this.http.get('data/projects.json')); + try { + return await firstValueFrom( + this.http.get('data/projects.json'), + ); + } catch (error) { + console.error('Error fetching projects:', error); + return []; + } } private async fetchSkills(): Promise { - const result = await firstValueFrom( - this.http.get('data/skilltree.json'), - ); + try { + const result = await firstValueFrom( + this.http.get('data/skilltree.json'), + ); + console.log('Fetched skills:', result); + return [result]; + } catch (error) { + console.error('Error fetching skills:', error); + return []; + } + } - return [result]; + private async fetchEducations(): Promise { + try { + const result = await firstValueFrom( + this.http.get('data/education.json'), + ); + console.log('Fetched education:', result); + return [result]; + } catch (error) { + console.error('Error fetching education:', error); + return []; + } + } + + private async fetchExperiences(): Promise { + try { + const result = await firstValueFrom( + this.http.get('data/experiences.json'), + ); + console.log('Fetched experiences:', result); + return result; + } catch (error) { + console.error('Error fetching experiences:', error); + return []; + } + } + + private async fetchTraits(): Promise { + try { + const result = await firstValueFrom( + this.http.get('data/traits.json'), + ); + console.log('Fetched traits:', result); + return result; + } catch (error) { + console.error('Error fetching traits:', error); + return []; + } } }