diff --git a/src/components/microsim/PackageVersions.tsx b/src/components/microsim/PackageVersions.tsx new file mode 100644 index 0000000..0172f3f --- /dev/null +++ b/src/components/microsim/PackageVersions.tsx @@ -0,0 +1,81 @@ +import { useState, useEffect } from 'react'; +import { motion } from 'framer-motion'; +import { colors } from '../../designTokens'; +import { fetchPackageVersion } from '../../data/fetchPackageVersion'; + +const PACKAGES = [ + { id: 'us', pypiName: 'policyengine-us', label: 'policyengine-us' }, + { id: 'uk', pypiName: 'policyengine-uk', label: 'policyengine-uk' }, +] as const; + +export default function PackageVersions() { + const [versions, setVersions] = useState>({}); + const [loading, setLoading] = useState(true); + + useEffect(() => { + let cancelled = false; + Promise.all( + PACKAGES.map(async (pkg) => { + const version = await fetchPackageVersion(pkg.pypiName); + return [pkg.id, version] as const; + }), + ).then((results) => { + if (cancelled) return; + setVersions(Object.fromEntries(results)); + setLoading(false); + }); + return () => { + cancelled = true; + }; + }, []); + + return ( +
+

+ Current versions +

+

+ PolicyEngine's country models are open-source Python packages, published + to PyPI with every release. +

+
+ {PACKAGES.map((pkg, i) => ( + { + e.currentTarget.style.borderColor = colors.primary[500]; + e.currentTarget.style.backgroundColor = colors.primary[50]; + }} + onMouseLeave={(e) => { + e.currentTarget.style.borderColor = colors.border.light; + e.currentTarget.style.backgroundColor = colors.white; + }} + > + + {pkg.label} + + + {loading ? '...' : versions[pkg.id] ? `v${versions[pkg.id]}` : '—'} + + + ))} +
+
+ ); +} diff --git a/src/data/fetchPackageVersion.ts b/src/data/fetchPackageVersion.ts new file mode 100644 index 0000000..eff1295 --- /dev/null +++ b/src/data/fetchPackageVersion.ts @@ -0,0 +1,20 @@ +const cache = new Map(); + +export async function fetchPackageVersion( + packageName: string, +): Promise { + if (cache.has(packageName)) return cache.get(packageName)!; + + try { + const res = await fetch(`https://pypi.org/pypi/${packageName}/json`); + if (!res.ok) throw new Error(`PyPI returned ${res.status}`); + const data = await res.json(); + const version: string = data.info?.version; + if (!version) throw new Error('No version in PyPI response'); + cache.set(packageName, version); + return version; + } catch (err) { + console.warn(`Failed to fetch version for ${packageName}:`, err); + return null; + } +} diff --git a/src/pages/OverviewPage.tsx b/src/pages/OverviewPage.tsx index 1423529..c27975c 100644 --- a/src/pages/OverviewPage.tsx +++ b/src/pages/OverviewPage.tsx @@ -1,11 +1,13 @@ import Walkthrough from '../components/microsim/Walkthrough'; import PageHeader from '../components/layout/PageHeader'; +import PackageVersions from '../components/microsim/PackageVersions'; export default function OverviewPage({ country }: { country: string }) { return (
+
); }