From 595942f5913c6c5d9c5d384a1a45dcbc4f6a12c6 Mon Sep 17 00:00:00 2001 From: khondokartowsif171 Date: Mon, 25 May 2026 03:41:52 +0600 Subject: [PATCH] feat: fetch service demo/repo links from Dashboard API - mergeServices() maps API data (demoUrl, gitRepo, isActive) onto local static data - sessionStorage cache (5 min TTL) like nav links - GitHub button becomes a link when gitRepo is set - Falls back to PORTFOLIO_DATA.services if API is unavailable Co-Authored-By: Claude Sonnet 4.6 --- src/projects.jsx | 78 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 9 deletions(-) diff --git a/src/projects.jsx b/src/projects.jsx index 0e65e39..54c82e5 100644 --- a/src/projects.jsx +++ b/src/projects.jsx @@ -144,14 +144,25 @@ const ProjectCard = ({ p }) => { }}>{p.impact.secondary}
- + {p.gitRepo ? ( + + ) : ( + + )} {p.demo && ( { return null; }; +// Merge Dashboard API data (demo/git links, active state) into local static data +function mergeServices(local, apiData) { + const byId = {}; + apiData.forEach(s => { byId[s.serviceId] = s; }); + return local + .filter(s => !byId[s.id] || byId[s.id].isActive !== false) + .map(s => { + const api = byId[s.id]; + if (!api) return s; + return { + ...s, + demo: api.demoUrl || s.demo || null, + gitRepo: api.gitRepo || null, + name: api.name || s.name, + nameBn: api.nameBn || s.nameBn, + impact: { + primary: api.impactPrimary || s.impact.primary, + secondary: api.impactSecondary || s.impact.secondary, + }, + }; + }); +} + const Projects = () => { const D = PORTFOLIO_DATA; + const [services, setServices] = React.useState(D.services); + + React.useEffect(() => { + const CACHE_KEY = 'aura_services_v1'; + const CACHE_TTL = 5 * 60 * 1000; + const cached = sessionStorage.getItem(CACHE_KEY); + if (cached) { + try { + const { data, ts } = JSON.parse(cached); + if (Date.now() - ts < CACHE_TTL && data.length > 0) { + setServices(mergeServices(D.services, data)); + return; + } + } catch {} + } + fetch('https://aura.auraajenticai.cloud/api/public/services', { signal: AbortSignal.timeout(3000) }) + .then(r => r.ok ? r.json() : null) + .then(data => { + if (Array.isArray(data) && data.length > 0) { + sessionStorage.setItem(CACHE_KEY, JSON.stringify({ data, ts: Date.now() })); + setServices(mergeServices(D.services, data)); + } + }) + .catch(() => {}); + }, []); + return (
@@ -319,7 +379,7 @@ const Projects = () => { gridTemplateColumns: "repeat(2, 1fr)", gap: 16, }} className="proj-grid"> - {D.services.map(p => )} + {services.map(p => )}