> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/RicardoAlejandroSantillan/dev-showcase/llms.txt
> Use this file to discover all available pages before exploring further.

# Charts Integration

> Chart.js integration for vocational and skills data visualization

## Overview

The Dev Showcase project uses Chart.js to visualize vocational assessment data and skill proficiency metrics. The charts manager handles multiple chart types including radar charts for RIASEC profiles, cognitive assessments, and horizontal bar charts for various skill categories.

## Chart Types

<CardGroup cols={3}>
  <Card title="Radar Charts" icon="chart-radar">
    RIASEC vocational profiles and cognitive assessments
  </Card>

  <Card title="Bar Charts" icon="chart-bar">
    Skill proficiency, interests, and career recommendations
  </Card>

  <Card title="Dynamic Data" icon="arrows-rotate">
    Real-time updates based on language selection
  </Card>
</CardGroup>

## RIASEC Vocational Chart

### Overview

The RIASEC (Holland Codes) chart visualizes six vocational personality types:

* **Realistic**: Practical, hands-on work
* **Investigative**: Analytical, scientific thinking
* **Artistic**: Creative, expressive activities
* **Social**: Helping, teaching, counseling
* **Enterprising**: Leading, persuading, business
* **Conventional**: Organizing, data management

### Color Configuration

```javascript charts-manager.js (lines 11-18) theme={null}
const RIASEC_COLORS = {
    realistic: { border: '#c2185b', fill: 'rgba(194,24,91,0.18)', point: '#c2185b' },
    investigative: { border: '#e91e63', fill: 'rgba(233,30,99,0.18)', point: '#e91e63' },
    artistic: { border: '#ffb74d', fill: 'rgba(255,183,77,0.18)', point: '#ffb74d' },
    social: { border: '#ff9800', fill: 'rgba(255,152,0,0.18)', point: '#ff9800' },
    enterprising: { border: '#ff7043', fill: 'rgba(255,112,67,0.22)', point: '#ff7043' },
    conventional: { border: '#ec407a', fill: 'rgba(236,64,122,0.15)', point: '#ec407a' }
};
```

### Data Structure

The radar chart builds datasets from translation files:

```javascript charts-manager.js (lines 28-55) theme={null}
const buildRadarData = () => {
    const voc = getVoc();
    const nums = getChartData()?.riasec ?? {};

    const makeDs = (key, col) => ({
        label: voc.datasets?.[key] ?? key,
        data: nums[key] ?? [],
        backgroundColor: col.fill,
        borderColor: col.border,
        borderWidth: 1.5,
        pointBackgroundColor: col.point,
        pointBorderColor: '#fff',
        pointRadius: 3,
        pointHoverRadius: 5
    });

    return {
        labels: voc.radarLabels ?? [],
        datasets: [
            makeDs('realistic', RIASEC_COLORS.realistic),
            makeDs('investigative', RIASEC_COLORS.investigative),
            makeDs('artistic', RIASEC_COLORS.artistic),
            makeDs('social', RIASEC_COLORS.social),
            makeDs('enterprising', RIASEC_COLORS.enterprising),
            makeDs('conventional', RIASEC_COLORS.conventional)
        ]
    };
};
```

### Chart Options

```javascript charts-manager.js (lines 115-136) theme={null}
const radarOptions = {
    responsive: true, 
    maintainAspectRatio: false,
    plugins: {
        legend: { display: false },
        tooltip: {
            backgroundColor: 'rgba(10,3,47,0.92)', 
            titleColor: '#fff',
            bodyColor: 'rgba(255,255,255,0.85)', 
            borderColor: 'rgba(255,255,255,0.15)',
            borderWidth: 1, 
            padding: 10, 
            cornerRadius: 8,
            callbacks: { 
                label: ctx => `${ctx.dataset.label}: ${ctx.parsed.r}%` 
            }
        }
    },
    scales: {
        r: {
            beginAtZero: true, 
            max: 100,
            ticks: { 
                stepSize: 20, 
                color: 'rgba(0,0,0,0)', 
                backdropColor: 'transparent', 
                font: { size: 9 } 
            },
            grid: { 
                color: 'rgba(255,255,255,0.15)', 
                circular: true 
            },
            pointLabels: { 
                color: 'rgb(160,161,163)', 
                font: { size: 12, weight: '600' }, 
                padding: 12 
            },
            angleLines: { 
                color: 'rgba(255,255,255,0.12)' 
            }
        }
    },
    animation: { 
        duration: 800, 
        easing: 'easeInOutQuart' 
    }
};
```

## Cognitive Assessment Chart

### Purpose

The cognitive chart compares personal scores against general averages across multiple cognitive dimensions.

### Color Scheme

```javascript charts-manager.js (lines 20-23) theme={null}
const COGNITIVE_COLORS = {
    mine: { border: '#4fc3f7', fill: 'rgba(79,195,247,0.22)', point: '#4fc3f7' },
    avg: { border: '#f06292', fill: 'rgba(240,98,146,0.15)', point: '#f06292' }
};
```

### Data Builder

```javascript charts-manager.js (lines 57-82) theme={null}
const buildCognitiveData = () => {
    const voc = getVoc();
    const nums = getChartData()?.cognitive ?? {};

    const makeDs = (key, label, col, extra = {}) => ({
        label,
        data: nums[key] ?? [],
        backgroundColor: col.fill,
        borderColor: col.border,
        borderWidth: 2,
        pointBackgroundColor: col.point,
        pointBorderColor: '#ffffff',
        pointRadius: 5,
        pointHoverRadius: 7,
        pointBorderWidth: 2,
        ...extra
    });

    return {
        labels: voc.cognitiveLabels ?? [],
        datasets: [
            makeDs('mine', voc.cognitiveMe ?? 'My Score', COGNITIVE_COLORS.mine),
            makeDs('avg', voc.cognitiveAvg ?? 'General Average', COGNITIVE_COLORS.avg, { 
                borderDash: [6, 3], 
                pointRadius: 4, 
                pointHoverRadius: 6 
            })
        ]
    };
};
```

<Note>
  The average dataset uses a dashed border (`borderDash: [6, 3]`) to distinguish it visually from the personal score.
</Note>

## Bar Charts

### Bar Chart Categories

The charts manager handles seven different bar chart types:

1. **Top Skills** - Highest proficiency areas
2. **Top Interests** - Primary interest areas
3. **Top Careers** - Best career matches
4. **Low Skills** - Areas for development
5. **Low Interests** - Lower interest areas
6. **Low Careers** - Less suitable careers
7. **Occupational Values** - Work value preferences

### Color Scheme

```javascript charts-manager.js (lines 25-26) theme={null}
const BAR_TOP = { bg: 'rgba(79,195,247,0.75)', border: 'rgba(79,195,247,1)' };
const BAR_LOW = { bg: 'rgba(236,64,122,0.70)', border: 'rgba(236,64,122,1)' };
```

* **Blue** (`BAR_TOP`): Positive metrics (top skills, interests, careers)
* **Pink** (`BAR_LOW`): Areas for improvement (low skills, interests)

### Data Structure

```javascript charts-manager.js (lines 94-113) theme={null}
const buildBarData = () => {
    const voc = getVoc();
    const labels = voc.barLabels ?? {};
    const nums = getChartData()?.bars ?? {};

    const make = (key, color) => ({
        labels: labels[key] ?? [],
        datasets: makeBarDataset(nums[key] ?? [], color)
    });

    return {
        topSkills: make('topSkills', BAR_TOP),
        topInterests: make('topInterests', BAR_TOP),
        topCareers: make('topCareers', BAR_TOP),
        lowSkills: make('lowSkills', BAR_LOW),
        lowInterests: make('lowInterests', BAR_LOW),
        lowCareers: make('lowCareers', BAR_LOW),
        valoresOcupacionales: make('valoresOcupacionales', BAR_TOP)
    };
};
```

### Bar Chart Options

```javascript charts-manager.js (lines 161-186) theme={null}
const barOptions = {
    indexAxis: 'y', // Horizontal bars
    responsive: true, 
    maintainAspectRatio: false,
    plugins: {
        legend: { display: false },
        tooltip: {
            backgroundColor: 'rgba(10,3,47,0.92)', 
            padding: 12, 
            titleColor: '#fff',
            bodyColor: 'rgba(255,255,255,0.85)', 
            borderColor: 'rgba(255,255,255,0.15)',
            borderWidth: 1, 
            cornerRadius: 8,
            callbacks: { 
                label: ctx => '  ' + ctx.parsed.x.toFixed(1) + '%' 
            }
        }
    },
    scales: {
        x: {
            beginAtZero: true, 
            max: 100,
            grid: { 
                color: 'rgba(255,255,255,0.06)', 
                drawBorder: false 
            },
            border: { display: false },
            ticks: { 
                color: 'rgba(255,255,255,0.45)', 
                font: { size: 10 }, 
                callback: v => v + '%', 
                maxTicksLimit: 5 
            }
        },
        y: {
            grid: { display: false }, 
            border: { display: false },
            ticks: { 
                color: 'rgba(255,255,255,0.9)', 
                font: { size: 11, weight: '600' }, 
                padding: 6 
            }
        }
    },
    layout: { padding: { right: 8 } },
    animation: { 
        duration: 700, 
        easing: 'easeInOutQuart' 
    }
};
```

## Chart Lifecycle Management

### Section-Based Chart Display

Charts are created and destroyed based on the active section:

```javascript charts-manager.js (lines 188-193) theme={null}
const SECTION_BAR_CHARTS = {
    profile: ['topSkillsChart', 'topInterestsChart', 'topCareersChart',
        'lowSkillsChart', 'lowInterestsChart', 'lowCareersChart'],
    aptitudes: ['valoresOcupacionalesChart'],
    proyecto: []
};
```

### Creation and Destruction

<Tabs>
  <Tab title="Create Charts">
    ```javascript charts-manager.js (lines 243-265) theme={null}
    const createBarCharts = () => {
        const sectionKey = getActiveSectionKey();
        const allowedIds = SECTION_BAR_CHARTS[sectionKey] ?? [];
        if (!allowedIds.length) return;

        const bar = buildBarData();

        const ALL_PAIRS = [
            ['topSkillsChart', bar.topSkills],
            ['topInterestsChart', bar.topInterests],
            ['topCareersChart', bar.topCareers],
            ['lowSkillsChart', bar.lowSkills],
            ['lowInterestsChart', bar.lowInterests],
            ['lowCareersChart', bar.lowCareers],
            ['valoresOcupacionalesChart', bar.valoresOcupacionales]
        ];

        ALL_PAIRS.forEach(([id, data]) => {
            if (!allowedIds.includes(id)) return;
            const ctx = document.getElementById(id);
            if (ctx) barCharts[id] = new Chart(ctx, { type: 'bar', data, options: barOptions });
        });
    };
    ```
  </Tab>

  <Tab title="Destroy Charts">
    ```javascript charts-manager.js (lines 238-241) theme={null}
    const destroyBarCharts = () => {
        Object.values(barCharts).forEach(chart => chart?.destroy?.());
        barCharts = {};
    };
    ```
  </Tab>
</Tabs>

## Language Support

### Translation Function

```javascript charts-manager.js (lines 7-9) theme={null}
const getT = () => window.currentTranslations ?? {};
const getVoc = () => getT()?.skills?.vocational ?? {};
const getChartData = () => getT()?.chartsData ?? {};
```

### Data from Language Files

Chart labels and data come from JSON translation files:

```json en.json (example structure) theme={null}
{
  "chartsData": {
    "riasec": {
      "realistic": [75, 82, 68, 90, 78, 85],
      "investigative": [88, 92, 85, 90, 87, 89],
      "artistic": [65, 70, 60, 72, 68, 75],
      "social": [78, 80, 75, 85, 82, 88],
      "enterprising": [70, 68, 72, 75, 73, 80],
      "conventional": [82, 85, 80, 88, 84, 90]
    },
    "cognitive": {
      "mine": [45, 52, 48, 50, 46, 53],
      "avg": [40, 42, 41, 43, 40, 44]
    },
    "bars": {
      "topSkills": [92, 88, 85, 82, 78],
      "topInterests": [90, 87, 84, 80, 76],
      "topCareers": [88, 85, 82, 79, 75]
    }
  },
  "skills": {
    "vocational": {
      "radarLabels": ["Problem Solving", "Creativity", "Leadership", ...],
      "cognitiveLabels": ["Verbal", "Numerical", "Spatial", ...],
      "barLabels": {
        "topSkills": ["Programming", "Data Analysis", "Web Development", ...]
      }
    }
  }
}
```

## Global Refresh Function

Charts are refreshed when language changes:

```javascript charts-manager.js (lines 267-274) theme={null}
window.refreshVocationalCharts = () => {
    const statsContent = document.getElementById('vocational-stats-content');
    if (!statsContent?.classList.contains('active')) return;
    updateRadarLabels();
    updateCognitiveLabels();
    destroyBarCharts();
    createBarCharts();
};
```

## MutationObserver Pattern

Charts are automatically created/destroyed when sections become visible:

```javascript charts-manager.js (lines 296-320) theme={null}
if (vocationalStatsContent) {
    let wasActive = vocationalStatsContent.classList.contains('active');

    const observer = new MutationObserver(() => {
        const isActive = vocationalStatsContent.classList.contains('active');

        if (!wasActive && isActive) {
            setTimeout(() => {
                createRadar();
                destroyBarCharts();
                createBarCharts();
                syncCognitiveChart();
            }, 150);
        } else if (wasActive && !isActive) {
            destroyBarCharts();
            destroyRadar();
            destroyCognitiveChart();
        }

        wasActive = isActive;
    });

    observer.observe(vocationalStatsContent, { 
        attributes: true, 
        attributeFilter: ['class'] 
    });
}
```

<Note>
  The 150ms delay ensures the DOM has fully updated before creating charts.
</Note>

## Performance Considerations

<AccordionGroup>
  <Accordion title="Chart Destruction">
    Charts are properly destroyed before creating new ones to prevent memory leaks:

    ```javascript theme={null}
    Object.values(barCharts).forEach(chart => chart?.destroy?.());
    ```
  </Accordion>

  <Accordion title="Lazy Initialization">
    Charts are only created when their section becomes visible, reducing initial page load:

    ```javascript theme={null}
    if (!radarChart) return; // Skip if already created
    ```
  </Accordion>

  <Accordion title="Conditional Rendering">
    Only relevant charts for each section are rendered:

    ```javascript theme={null}
    const allowedIds = SECTION_BAR_CHARTS[sectionKey] ?? [];
    if (!allowedIds.includes(id)) return;
    ```
  </Accordion>
</AccordionGroup>

## HTML Canvas Elements

Charts render to canvas elements in the HTML:

```html theme={null}
<canvas id="vocationalRadarChart" width="400" height="400"></canvas>
<canvas id="cognitiveRadarChart" width="400" height="400"></canvas>
<canvas id="topSkillsChart" width="600" height="300"></canvas>
```

## Dependencies

The charts manager requires:

* **Chart.js** (v3+)
* Language translation system (`window.currentTranslations`)
* Proper HTML structure with canvas elements

<Warning>
  Ensure Chart.js is loaded before the charts-manager.js script.
</Warning>
