mirror of
https://github.com/adam-benyekkou/my_portfolio.git
synced 2026-01-15 20:20:09 +00:00
Added skilltree from about page to dataService
This commit is contained in:
183
assets/data/skilltree.json
Normal file
183
assets/data/skilltree.json
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
{
|
||||||
|
"id": "workstation",
|
||||||
|
"title": "Workstation/",
|
||||||
|
"isExpanded": true,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 0,
|
||||||
|
"hasChildren": true,
|
||||||
|
"visible": true,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "tools",
|
||||||
|
"title": "Tools/",
|
||||||
|
"isExpanded": true,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 1,
|
||||||
|
"hasChildren": true,
|
||||||
|
"visible": true,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "webstorm",
|
||||||
|
"title": "Webstorm/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 2,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "linux",
|
||||||
|
"title": "Linux_Debian/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 2,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "git",
|
||||||
|
"title": "Git/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 2,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "docker",
|
||||||
|
"title": "Docker/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 2,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dev",
|
||||||
|
"title": "Dev/",
|
||||||
|
"isExpanded": true,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 1,
|
||||||
|
"hasChildren": true,
|
||||||
|
"visible": true,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "front",
|
||||||
|
"title": "Front/",
|
||||||
|
"isExpanded": true,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 2,
|
||||||
|
"hasChildren": true,
|
||||||
|
"visible": true,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "angular",
|
||||||
|
"title": "Angular/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "tailwind",
|
||||||
|
"title": "Tailwind_SASS/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "html-css",
|
||||||
|
"title": "HTML_CSS/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "back",
|
||||||
|
"title": "Back/",
|
||||||
|
"isExpanded": true,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 2,
|
||||||
|
"hasChildren": true,
|
||||||
|
"visible": true,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "nodejs",
|
||||||
|
"title": "NodeJS_Express/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "typescript",
|
||||||
|
"title": "Typescript/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "php",
|
||||||
|
"title": "PHP_Symfony/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "python",
|
||||||
|
"title": "Python_Django/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "data",
|
||||||
|
"title": "Data/",
|
||||||
|
"isExpanded": true,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 2,
|
||||||
|
"hasChildren": true,
|
||||||
|
"visible": true,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "postgresql",
|
||||||
|
"title": "PostgreSQL/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "mongodb",
|
||||||
|
"title": "MongoDB/",
|
||||||
|
"isExpanded": false,
|
||||||
|
"isSelected": false,
|
||||||
|
"level": 3,
|
||||||
|
"hasChildren": false,
|
||||||
|
"visible": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
import { Component, input, signal, computed } from '@angular/core';
|
import {
|
||||||
|
Component,
|
||||||
|
input,
|
||||||
|
signal,
|
||||||
|
computed,
|
||||||
|
inject,
|
||||||
|
effect,
|
||||||
|
} from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { type NeuralProfileNode } from '../../../shared/models/about.model';
|
import { type NeuralProfileNode } from '../../../shared/models/about.model';
|
||||||
|
import { DataService } from '../../../shared/services/data.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-neural-profile-tree',
|
selector: 'app-neural-profile-tree',
|
||||||
@@ -10,203 +18,51 @@ import { type NeuralProfileNode } from '../../../shared/models/about.model';
|
|||||||
styleUrl: './neural-profile-tree.component.css',
|
styleUrl: './neural-profile-tree.component.css',
|
||||||
})
|
})
|
||||||
export class NeuralProfileTreeComponent {
|
export class NeuralProfileTreeComponent {
|
||||||
// Input for external data (optional)
|
private readonly dataService = inject(DataService);
|
||||||
|
|
||||||
|
// Input for external data (optional override)
|
||||||
externalData = input<NeuralProfileNode[]>([]);
|
externalData = input<NeuralProfileNode[]>([]);
|
||||||
|
|
||||||
// Internal tree state and interaction signals
|
// Internal tree state and interaction signals
|
||||||
private treeState = signal<NeuralProfileNode[]>(this.getInitialData());
|
private treeState = signal<NeuralProfileNode[]>([]);
|
||||||
public hoveredNodeId = signal<string | null>(null);
|
public hoveredNodeId = signal<string | null>(null);
|
||||||
public selectedNodeId = signal<string | null>(null);
|
public selectedNodeId = signal<string | null>(null);
|
||||||
|
|
||||||
// Computed flattened tree for rendering
|
// Computed flattened tree for rendering
|
||||||
treeData = computed(() => this.flattenTreeWithVisibility(this.treeState()));
|
treeData = computed(() => this.flattenTreeWithVisibility(this.treeState()));
|
||||||
|
|
||||||
private getInitialData(): NeuralProfileNode[] {
|
// Computed to determine data source
|
||||||
return [
|
private dataSource = computed(() => {
|
||||||
{
|
const external = this.externalData();
|
||||||
id: 'workstation',
|
const fromService = this.dataService.skills();
|
||||||
title: 'Workstation/',
|
|
||||||
isExpanded: true,
|
console.log('External data:', external);
|
||||||
isSelected: false,
|
console.log('Service data:', fromService);
|
||||||
level: 0,
|
console.log(
|
||||||
hasChildren: true,
|
'Service data type:',
|
||||||
visible: true,
|
Array.isArray(fromService) ? 'array' : typeof fromService,
|
||||||
children: [
|
);
|
||||||
{
|
|
||||||
id: 'tools',
|
// Use external data if provided, otherwise use service data
|
||||||
title: 'Tools/',
|
let data = external.length > 0 ? external : fromService;
|
||||||
isExpanded: true,
|
|
||||||
isSelected: false,
|
// Ensure data is an array - if it's a single object, wrap it in an array
|
||||||
level: 1,
|
if (data && !Array.isArray(data)) {
|
||||||
hasChildren: true,
|
console.log('Converting single object to array');
|
||||||
visible: true,
|
data = [data];
|
||||||
children: [
|
}
|
||||||
{
|
|
||||||
id: 'webstorm',
|
// Ensure data is actually an array
|
||||||
title: 'Webstorm/',
|
return Array.isArray(data) ? data : [];
|
||||||
isExpanded: false,
|
});
|
||||||
isSelected: false,
|
|
||||||
level: 2,
|
constructor() {
|
||||||
hasChildren: false,
|
// Effect to update tree state when data source changes
|
||||||
visible: true,
|
effect(() => {
|
||||||
},
|
const data = this.dataSource();
|
||||||
{
|
console.log('Neural profile data updated:', data);
|
||||||
id: 'linux',
|
this.treeState.set(data);
|
||||||
title: 'Linux_Debian/',
|
});
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 2,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'git',
|
|
||||||
title: 'Git/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 2,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'docker',
|
|
||||||
title: 'Docker/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 2,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'dev',
|
|
||||||
title: 'Dev/',
|
|
||||||
isExpanded: true,
|
|
||||||
isSelected: false,
|
|
||||||
level: 1,
|
|
||||||
hasChildren: true,
|
|
||||||
visible: true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 'front',
|
|
||||||
title: 'Front/',
|
|
||||||
isExpanded: true,
|
|
||||||
isSelected: false,
|
|
||||||
level: 2,
|
|
||||||
hasChildren: true,
|
|
||||||
visible: true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 'angular',
|
|
||||||
title: 'Angular/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'tailwind',
|
|
||||||
title: 'Tailwind_SASS/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'html-css',
|
|
||||||
title: 'HTML_CSS/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'back',
|
|
||||||
title: 'Back/',
|
|
||||||
isExpanded: true,
|
|
||||||
isSelected: false,
|
|
||||||
level: 2,
|
|
||||||
hasChildren: true,
|
|
||||||
visible: true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 'nodejs',
|
|
||||||
title: 'NodeJS_Express/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'typescript',
|
|
||||||
title: 'Typescript/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'php',
|
|
||||||
title: 'PHP_Symfony/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'python',
|
|
||||||
title: 'Python_Django/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'data',
|
|
||||||
title: 'Data/',
|
|
||||||
isExpanded: true,
|
|
||||||
isSelected: false,
|
|
||||||
level: 2,
|
|
||||||
hasChildren: true,
|
|
||||||
visible: true,
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
id: 'postgresql',
|
|
||||||
title: 'PostgreSQL/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'mongodb',
|
|
||||||
title: 'MongoDB/',
|
|
||||||
isExpanded: false,
|
|
||||||
isSelected: false,
|
|
||||||
level: 3,
|
|
||||||
hasChildren: false,
|
|
||||||
visible: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private flattenTreeWithVisibility(
|
private flattenTreeWithVisibility(
|
||||||
@@ -269,7 +125,7 @@ export class NeuralProfileTreeComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW METHOD: Handle chip cell hover
|
// Handle chip cell hover
|
||||||
hoverChipChild(childId: string): void {
|
hoverChipChild(childId: string): void {
|
||||||
this.hoveredNodeId.set(childId);
|
this.hoveredNodeId.set(childId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { Injectable, signal, inject } from '@angular/core';
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { type Project } from '../models/project.model';
|
import { type Project } from '../models/project.model';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
import { NeuralProfileNode } from '../models/about.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
@@ -11,9 +12,11 @@ export class DataService {
|
|||||||
|
|
||||||
// Private signals for state management
|
// Private signals for state management
|
||||||
private _projects = signal<Project[]>([]);
|
private _projects = signal<Project[]>([]);
|
||||||
|
private _skills = signal<NeuralProfileNode[]>([]);
|
||||||
|
|
||||||
// Public readonly signals
|
// Public readonly signals
|
||||||
readonly projects = this._projects.asReadonly();
|
readonly projects = this._projects.asReadonly();
|
||||||
|
readonly skills = this._skills.asReadonly();
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
console.log('DataService initialized');
|
console.log('DataService initialized');
|
||||||
@@ -22,52 +25,49 @@ export class DataService {
|
|||||||
|
|
||||||
private async loadInitialData(): Promise<void> {
|
private async loadInitialData(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
console.log('Starting to load projects...');
|
console.log('Starting to load data...');
|
||||||
const projects = await this.fetchProjects();
|
const [projects, skills] = await Promise.all([
|
||||||
|
this.fetchProjects(),
|
||||||
|
this.fetchSkills(),
|
||||||
|
]);
|
||||||
|
|
||||||
console.log('Projects loaded successfully:', projects);
|
console.log('Projects loaded successfully:', projects);
|
||||||
|
console.log('Skills loaded successfully:', skills);
|
||||||
console.log('Number of projects:', projects.length);
|
console.log('Number of projects:', projects.length);
|
||||||
|
console.log('Number of skills:', skills.length);
|
||||||
|
|
||||||
this._projects.set(projects);
|
this._projects.set(projects);
|
||||||
console.log('Projects signal updated');
|
this._skills.set(skills);
|
||||||
|
console.log('All data signals updated');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading data:', error);
|
console.error('Error loading data:', error);
|
||||||
|
|
||||||
// Fallback to hardcoded data for debugging
|
|
||||||
console.log('Loading fallback data...');
|
|
||||||
const fallbackProjects: Project[] = [
|
|
||||||
{
|
|
||||||
id: 'portfolio',
|
|
||||||
title: 'PORTFOLIO_SYSTEM.EXE',
|
|
||||||
status: '[MISSION_ACTIVE]',
|
|
||||||
classification: 'Personal Showcase Platform',
|
|
||||||
objective: 'Professional presentation interface with NieR aesthetic',
|
|
||||||
statusDescription: 'LIVE | CONTINUOUS_UPDATE',
|
|
||||||
techStack: ['ANGULAR', 'TYPESCRIPT', 'TAILWIND', 'VERCEL'],
|
|
||||||
demoUrl: 'https://adambenyekkoudev.vercel.app/',
|
|
||||||
codeUrl: 'https://github.com/adam-benyekkou/angular_portfolio',
|
|
||||||
isRedacted: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'redacted-1',
|
|
||||||
title: 'REDACTED_PROJECT_1',
|
|
||||||
status: '[REDACTED]',
|
|
||||||
classification: '',
|
|
||||||
objective: '',
|
|
||||||
statusDescription: '',
|
|
||||||
techStack: [],
|
|
||||||
isRedacted: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
this._projects.set(fallbackProjects);
|
|
||||||
console.log('Fallback data loaded');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchProjects(): Promise<Project[]> {
|
private async fetchProjects(): Promise<Project[]> {
|
||||||
console.log('Fetching projects from assets/data/projects.json');
|
console.log('Fetching projects from data/projects.json');
|
||||||
const result = await firstValueFrom(
|
const result = await firstValueFrom(
|
||||||
this.http.get<Project[]>('data/projects.json'),
|
this.http.get<Project[]>('data/projects.json'),
|
||||||
);
|
);
|
||||||
console.log('HTTP request completed:', result);
|
console.log('Projects HTTP request completed:', result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async fetchSkills(): Promise<NeuralProfileNode[]> {
|
||||||
|
console.log('Fetching skills from data/skilltree.json');
|
||||||
|
const result = await firstValueFrom(
|
||||||
|
this.http.get<NeuralProfileNode[] | NeuralProfileNode>(
|
||||||
|
'data/skilltree.json',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log('Skills HTTP request completed:', result);
|
||||||
|
|
||||||
|
// If the JSON contains a single object instead of an array, wrap it in an array
|
||||||
|
if (result && !Array.isArray(result)) {
|
||||||
|
console.log('Converting single skill object to array');
|
||||||
|
return [result as NeuralProfileNode];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.isArray(result) ? result : [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user