From a8cea0f62f43af1682059e8aed5a05ea3389c252 Mon Sep 17 00:00:00 2001 From: Akshay Date: Wed, 12 Aug 2026 21:39:21 -0600 Subject: [PATCH 1/4] Akshay fix Skills Overview data mismatch on HGN Questionnaire Dashboard From 19ef09cfd2bc3bc1d1b541f970e4ec4675c97606 Mon Sep 17 00:00:00 2001 From: Akshay Date: Wed, 12 Aug 2026 22:46:40 -0600 Subject: [PATCH 2/4] Fetch skills survey data on Skills Overview page The radar chart on /hgnhelp/skills-overview read state.userProfile, which holds the HGN account profile and never contains skillInfo. The survey data lives in the userSkills slice and was only ever fetched by the /hgn/profile/skills page, so the overview always fell back to the 'Complete the skills survey' empty state. Fetch the profile from ENDPOINTS.SKILLS_PROFILE on mount instead, so the page works on direct navigation and refresh. Users without survey data (isPlaceholder) keep the existing inline message and are not redirected. --- .../SkillsOverviewPage.jsx | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/src/components/HGNHelpSkillsDashboard/SkillsOverviewPage.jsx b/src/components/HGNHelpSkillsDashboard/SkillsOverviewPage.jsx index 191480bc0d2..e6e3cc341a2 100644 --- a/src/components/HGNHelpSkillsDashboard/SkillsOverviewPage.jsx +++ b/src/components/HGNHelpSkillsDashboard/SkillsOverviewPage.jsx @@ -1,5 +1,9 @@ -import { useState } from 'react'; +import jwtDecode from 'jwt-decode'; +import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; +import config from '~/config.json'; +import httpService from '~/services/httpService'; +import { ENDPOINTS } from '~/utils/URL'; import RadarChart from '../HGNSkillsDashboard/SkillsProfilePage/components/RadarChart'; import Accordion from './Accordion'; import { PreferenceFilterButtons, SkillFilterButtons } from './FilterButtons'; @@ -12,11 +16,48 @@ function SkillsOverviewPage() { const [selectedPreferences, setSelectedPreferences] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const darkMode = useSelector(state => state.theme.darkMode); - const userProfile = useSelector(state => state.userProfile); + const [profileData, setProfileData] = useState(null); + const [loadingProfile, setLoadingProfile] = useState(true); + + // The skills survey lives behind its own endpoint and is not part of state.userProfile, + // so this page has to fetch it itself rather than read it from the store. + useEffect(() => { + const token = localStorage.getItem(config.tokenKey); + let userId; + try { + userId = token ? jwtDecode(token)?.userid : undefined; + } catch (_) { + userId = undefined; + } + + if (!userId) { + setLoadingProfile(false); + return; + } + + httpService.setjwt(token); + httpService + .get(ENDPOINTS.SKILLS_PROFILE(userId)) + // isPlaceholder means the user has not filled in the survey yet + .then(({ data }) => setProfileData(data?.isPlaceholder ? null : data)) + .catch(() => setProfileData(null)) + .finally(() => setLoadingProfile(false)); + }, []); const hasFilters = selectedSkills.length > 0 || selectedPreferences.length > 0 || searchQuery.trim().length > 0; + let radarContent; + if (loadingProfile) { + radarContent =

Loading your skills radar...

; + } else if (profileData?.skillInfo) { + radarContent = ; + } else { + radarContent = ( +

Complete the skills survey to view your radar chart.

+ ); + } + return (

Skills Overview

@@ -24,13 +65,7 @@ function SkillsOverviewPage() { {/* Radar Chart for logged-in user */}

Your Skills Radar

-
- {userProfile?.skillInfo ? ( - - ) : ( -

Complete the skills survey to view your radar chart.

- )} -
+
{radarContent}
{/* Search Bar */} From 2459e63f037dc625d2c8784246b9c42da22a3a7a Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 26 Sep 2026 07:53:45 -0600 Subject: [PATCH 3/4] Fix radar chart label overlap and tick visibility inconsistency Both /hgn/profile/skills and /hgnhelp/skills-overview render the shared RadarChart with 28 skill dimensions, so the wrap-around labels at the top (Markdown & Graphs / Leadership Experience / Leadership Skills) sit within a ~26deg arc and collide regardless of chart size. Always use the abbreviated label instead of gating it behind `compact`, add the two labels missing an abbreviation, and enable Chart.js centerPointLabels to ease crowding at the seam. Also stop gating radial scale tick-number visibility behind `compact`: /hgn/profile/skills defaults compact=true and hid them while skills-overview (compact=false) showed them, for no reason tied to available space. Ticks are now always shown on both pages. --- .../components/RadarChart.jsx | 12 +++- .../components/__tests__/RadarChart.test.jsx | 55 +++++++++++++++++++ 2 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/components/HGNSkillsDashboard/SkillsProfilePage/components/__tests__/RadarChart.test.jsx diff --git a/src/components/HGNSkillsDashboard/SkillsProfilePage/components/RadarChart.jsx b/src/components/HGNSkillsDashboard/SkillsProfilePage/components/RadarChart.jsx index 29ad3f0b503..d2b3248c67d 100644 --- a/src/components/HGNSkillsDashboard/SkillsProfilePage/components/RadarChart.jsx +++ b/src/components/HGNSkillsDashboard/SkillsProfilePage/components/RadarChart.jsx @@ -24,6 +24,7 @@ const SKILL_MAPPINGS = [ }, { label: 'Leadership Skills', + shortLabel: 'Leadership Skl', value: (general, frontend, backend) => general?.leadership_skills || 0, description: 'Skills in team leadership and management', }, @@ -153,6 +154,7 @@ const SKILL_MAPPINGS = [ }, { label: 'Markdown & Graphs', + shortLabel: 'Markdown/Graphs', value: (general, frontend, backend) => frontend?.Documentation || general?.markdown_graphs || 0, description: 'Markdown writing and data visualization', }, @@ -234,7 +236,10 @@ function RadarChart({ profileData, compact = true, onSkillsDataReady }) { const { general = {}, frontend = {}, backend = {} } = profileData?.skillInfo || {}; const chartData = { - labels: skillsData.map(skill => (compact ? skill.shortLabel || skill.label : skill.label)), + // Always prefer the abbreviated label: with 28 dimensions, the wrap-around + // labels at the top (last <-> first) sit within a ~26deg arc and collide + // regardless of chart size, so shortening text is what actually buys room. + labels: skillsData.map(skill => skill.shortLabel || skill.label), datasets: [ { label: 'Skills', @@ -300,6 +305,7 @@ function RadarChart({ profileData, compact = true, onSkillsDataReady }) { }, pointLabels: { + centerPointLabels: true, font: { size: function(context) { const w = context.chart.width; @@ -321,7 +327,9 @@ function RadarChart({ profileData, compact = true, onSkillsDataReady }) { }, ticks: { stepSize: 2, - display: compact ? false : true, + // Always shown: the radial scale numbers are information, not chrome, + // so (like labels above) they shouldn't be gated behind compact. + display: true, color: darkMode ? '#dcdcdc' : '#666', font: { size: 10 }, backdropColor: darkMode ? '#1f1f1f' : '#ffffff', diff --git a/src/components/HGNSkillsDashboard/SkillsProfilePage/components/__tests__/RadarChart.test.jsx b/src/components/HGNSkillsDashboard/SkillsProfilePage/components/__tests__/RadarChart.test.jsx new file mode 100644 index 00000000000..8a05d2d18ed --- /dev/null +++ b/src/components/HGNSkillsDashboard/SkillsProfilePage/components/__tests__/RadarChart.test.jsx @@ -0,0 +1,55 @@ +import { render } from '@testing-library/react'; +import { Provider } from 'react-redux'; +import configureMockStore from 'redux-mock-store'; +import RadarChart from '../RadarChart'; + +// react-chartjs-2 renders to a real , which jsdom doesn't implement. +// Capture the props chart.js would receive instead of rendering the chart. +let lastRadarProps; +vi.mock('react-chartjs-2', () => ({ + Radar: props => { + lastRadarProps = props; + return null; + }, +})); + +const mockStore = configureMockStore([]); + +const profileData = { + skillInfo: { + general: { leadership_experience: 5, leadership_skills: 6, markdown_graphs: 7 }, + frontend: {}, + backend: {}, + }, +}; + +const renderChart = compact => { + const store = mockStore({ theme: { darkMode: false } }); + render( + + + , + ); +}; + +describe('RadarChart', () => { + it('uses abbreviated labels even when compact is false, to avoid overlap at high dimension density', () => { + renderChart(false); + expect(lastRadarProps.data.labels).toContain('Leadership Skl'); + expect(lastRadarProps.data.labels).toContain('Markdown/Graphs'); + expect(lastRadarProps.data.labels).not.toContain('Leadership Skills'); + }); + + it('centers point labels between grid lines to reduce crowding near the wrap-around seam', () => { + renderChart(true); + expect(lastRadarProps.options.scales.r.pointLabels.centerPointLabels).toBe(true); + }); + + it('shows the radial scale numbers regardless of compact, so /hgn/profile/skills matches skills-overview', () => { + renderChart(true); + expect(lastRadarProps.options.scales.r.ticks.display).toBe(true); + + renderChart(false); + expect(lastRadarProps.options.scales.r.ticks.display).toBe(true); + }); +}); From ac119dc76e2fff4fc91b6071c1eec5f7a8611d0d Mon Sep 17 00:00:00 2001 From: Akshay Date: Sat, 26 Sep 2026 08:08:17 -0600 Subject: [PATCH 4/4] chore: retrigger Netlify deploy preview