import { useQuery } from '@tanstack/react-query'; import { Link } from 'react-router-dom'; import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import { fetchProjects, fetchLanguages, type ProjectSummary, type Source } from '../api/client'; import { ContributionGraph, AllTimeGraph } from '../components/ContributionGraph'; export function DashPage() { const projectsQ = useQuery({ queryKey: ['projects'], queryFn: fetchProjects, refetchInterval: 60_000, }); const projects = projectsQ.data ?? []; const ranked = rankProjects(projects); return ( <>

i rarely say anything that warrants capital letters. a peek into the projects i'm working on is below.

{projectsQ.isLoading &&

loading...

} {projectsQ.isError && (

error: {(projectsQ.error as Error).message}

)} {ranked.map((p) => ( ))} ); } function ProjectCard({ project: p }: { project: ProjectSummary }) { const langsQ = useQuery({ queryKey: ['languages', p.source, p.host, p.repo], queryFn: () => fetchLanguages(p.source as Source, p.host, p.repo), enabled: p.source === 'github' || p.source === 'gitea', staleTime: 10 * 60_000, }); const langs = langsQ.data; const topLangs = langs ? topLanguages(langs, 3) : null; return (
{p.repo}
{p.source} {topLangs && ` · ${topLangs}`}
{p.commit_count > 0 && {p.commit_count} commits} {p.issue_count > 0 && {p.issue_count} issues} {p.pr_count > 0 && {p.pr_count} prs}
{formatRange(p.first_activity, p.last_activity)}
); } function topLanguages(langs: Record, n: number): string { return Object.entries(langs) .sort(([, a], [, b]) => b - a) .slice(0, n) .map(([lang]) => lang.toLowerCase()) .join(', '); } function formatRange(first: string | null, last: string | null): string { const fmt = (iso: string) => new Date(iso).toLocaleDateString('en-GB', { month: 'short', year: 'numeric' }).toLowerCase(); if (first && last) return `${fmt(first)} — ${fmt(last)}`; if (last) return fmt(last); return ''; } function rankProjects(projects: ProjectSummary[]): ProjectSummary[] { if (projects.length === 0) return []; const now = Date.now(); const maxVolume = Math.max(...projects.map((p) => p.commit_count + p.issue_count + p.pr_count)); const oldest = Math.min( ...projects.map((p) => (p.last_activity ? new Date(p.last_activity).getTime() : 0)), ); const range = now - oldest || 1; return [...projects].sort((a, b) => score(b) - score(a)); function score(p: ProjectSummary): number { const volume = (p.commit_count + p.issue_count + p.pr_count) / (maxVolume || 1); const recency = p.last_activity ? (new Date(p.last_activity).getTime() - oldest) / range : 0; return 0.6 * recency + 0.4 * volume; } }