From 45bda8009fa177e55c2911c78ed047d5c84efe3a Mon Sep 17 00:00:00 2001 From: Shivansh Gupta Date: Wed, 4 Mar 2026 23:29:01 +0530 Subject: [PATCH] feat: implement SkillContext for optimized data fetching --- web-app/src/context/SkillContext.tsx | 76 ++++++++++++++++++++++++++++ web-app/src/main.tsx | 5 +- 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 web-app/src/context/SkillContext.tsx diff --git a/web-app/src/context/SkillContext.tsx b/web-app/src/context/SkillContext.tsx new file mode 100644 index 00000000..3ef74f73 --- /dev/null +++ b/web-app/src/context/SkillContext.tsx @@ -0,0 +1,76 @@ +import React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react'; +import type { Skill, StarMap } from '../types'; +import { supabase } from '../lib/supabase'; + +interface SkillContextType { + skills: Skill[]; + stars: StarMap; + loading: boolean; + refreshSkills: () => Promise; +} + +const SkillContext = createContext(undefined); + +export function SkillProvider({ children }: { children: React.ReactNode }) { + const [skills, setSkills] = useState([]); + const [stars, setStars] = useState({}); + const [loading, setLoading] = useState(true); + + const fetchSkillsAndStars = useCallback(async (silent = false) => { + if (!silent) setLoading(true); + try { + // Fetch skills index + const res = await fetch('/skills.json'); + const data = await res.json(); + setSkills(data); + + // Fetch stars from Supabase if available + if (supabase) { + const { data: starData, error } = await supabase + .from('skill_stars') + .select('skill_id, star_count'); + + if (!error && starData) { + const starMap: StarMap = {}; + starData.forEach((item: { skill_id: string; star_count: number }) => { + starMap[item.skill_id] = item.star_count; + }); + setStars(starMap); + } + } + } catch (err) { + console.error('SkillContext: Failed to load skills', err); + } finally { + if (!silent) setLoading(false); + } + }, []); + + useEffect(() => { + fetchSkillsAndStars(); + }, [fetchSkillsAndStars]); + + const refreshSkills = useCallback(async () => { + await fetchSkillsAndStars(true); + }, [fetchSkillsAndStars]); + + const value = useMemo(() => ({ + skills, + stars, + loading, + refreshSkills + }), [skills, stars, loading, refreshSkills]); + + return ( + + {children} + + ); +} + +export function useSkills() { + const context = useContext(SkillContext); + if (context === undefined) { + throw new Error('useSkills must be used within a SkillProvider'); + } + return context; +} diff --git a/web-app/src/main.tsx b/web-app/src/main.tsx index 6545cc9a..533d286c 100644 --- a/web-app/src/main.tsx +++ b/web-app/src/main.tsx @@ -2,6 +2,7 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import './index.css'; import App from './App'; +import { SkillProvider } from './context/SkillContext'; const rootElement = document.getElementById('root'); if (!rootElement) { @@ -10,6 +11,8 @@ if (!rootElement) { createRoot(rootElement).render( - + + + , );