Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 84 additions & 19 deletions app/api/card/route.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { promises as fs } from 'fs';
import path from 'path';
import { classifyAgent, getPowerTier } from '../../../lib/agent-class.js';
import { scoreAll } from '../../../lib/scoring.js';
import { addDeveloperRanks } from '../../../lib/ranking.js';

export const runtime = 'nodejs';

Expand All @@ -12,19 +13,15 @@ const COSMOS_KEY = process.env.COSMOS_KEY;
const DATABASE = process.env.COSMOS_DATABASE || 'devglobe';
const CONTAINER = process.env.COSMOS_CONTAINER || 'developers';

async function getDeveloper(login) {
async function getRankedDevelopers() {
if (COSMOS_ENDPOINT && COSMOS_KEY) {
try {
const client = new CosmosClient({ endpoint: COSMOS_ENDPOINT, key: COSMOS_KEY });
const container = client.database(DATABASE).container(CONTAINER);
const { resources } = await container.items.query({
query: 'SELECT * FROM c WHERE c.login = @login',
parameters: [{ name: '@login', value: login }],
query: 'SELECT c.id, c.login, c.name, c.avatarUrl, c.location, c.followers, c.totalStars, c.totalForks, c.totalWatchers, c.totalCommits, c.topLanguage, c.soReputation, c.soAnswers, c.soAcceptRate, c.soBadges, c.claimed FROM c',
}).fetchAll();
if (resources.length > 0) {
const scored = scoreAll(resources);
return scored[0];
}
if (resources.length > 0) return addDeveloperRanks(scoreAll(resources));
} catch (err) {
console.error('Card: Cosmos error', err.message);
}
Expand All @@ -33,8 +30,12 @@ async function getDeveloper(login) {
const filePath = path.join(process.cwd(), 'data', 'developers-sample.json');
const raw = await fs.readFile(filePath, 'utf-8');
const data = JSON.parse(raw);
const scored = scoreAll(data);
return scored.find(d => d.login === login) || null;
return addDeveloperRanks(scoreAll(data));
}

async function getDeveloper(login) {
const developers = await getRankedDevelopers();
return developers.find(d => d.login.toLowerCase() === login.toLowerCase()) || null;
}

function formatNum(n) {
Expand Down Expand Up @@ -66,12 +67,32 @@ export async function GET(request) {
width: '1200',
height: '630',
display: 'flex',
background: 'linear-gradient(135deg, #0a0e17 0%, #111827 40%, #0f172a 100%)',
background: 'linear-gradient(135deg, #080b10 0%, #111820 52%, #15120f 100%)',
fontFamily: 'Inter, sans-serif',
position: 'relative',
overflow: 'hidden',
}}
>
{/* Top identity bar */}
<div
style={{
position: 'absolute',
top: '28',
left: '40',
right: '40',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<div style={{ display: 'flex', color: '#f8fafc', fontSize: '18', fontWeight: '800', letterSpacing: '1' }}>
DEV<span style={{ color: '#22d3ee' }}>GLOBE</span>
</div>
<div style={{ display: 'flex', color: '#64748b', fontSize: '12', letterSpacing: '2' }}>
OPEN SOURCE IDENTITY · 2026
</div>
</div>

{/* Grid pattern overlay */}
<div
style={{
Expand Down Expand Up @@ -109,7 +130,7 @@ export async function GET(request) {
alignItems: 'center',
justifyContent: 'center',
width: '400',
padding: '40',
padding: '76px 40px 54px',
}}
>
{/* Agent icon */}
Expand Down Expand Up @@ -191,7 +212,7 @@ export async function GET(request) {
flexDirection: 'column',
justifyContent: 'center',
flex: 1,
padding: '40px 60px 40px 20px',
padding: '74px 60px 54px 20px',
}}
>
{/* Agent class header */}
Expand Down Expand Up @@ -241,13 +262,57 @@ export async function GET(request) {
&ldquo;{agent.tagline}&rdquo;
</div>

{/* Global and local rank */}
<div style={{ display: 'flex', gap: '12', marginBottom: '22' }}>
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
width: dev.countryRank ? '255' : '522',
height: '82',
padding: '12px 18px',
background: 'linear-gradient(135deg, rgba(34,211,238,0.15), rgba(34,211,238,0.04))',
border: '1px solid rgba(34,211,238,0.32)',
borderRadius: '8',
}}
>
<div style={{ display: 'flex', alignItems: 'baseline', gap: '7' }}>
<span style={{ color: '#67e8f9', fontSize: '34', fontWeight: '800' }}>#{formatNum(dev.globalRank)}</span>
<span style={{ color: '#64748b', fontSize: '14' }}>of {formatNum(dev.globalTotal)}</span>
</div>
<div style={{ display: 'flex', color: '#a5f3fc', fontSize: '11', fontWeight: '700', letterSpacing: '1.6' }}>GLOBAL RANK</div>
</div>
{dev.countryRank && (
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
width: '255',
height: '82',
padding: '12px 18px',
background: 'linear-gradient(135deg, rgba(251,146,60,0.15), rgba(251,146,60,0.04))',
border: '1px solid rgba(251,146,60,0.32)',
borderRadius: '8',
}}
>
<div style={{ display: 'flex', alignItems: 'baseline', gap: '7' }}>
<span style={{ color: '#fdba74', fontSize: '34', fontWeight: '800' }}>#{formatNum(dev.countryRank)}</span>
<span style={{ color: '#64748b', fontSize: '14' }}>of {formatNum(dev.countryTotal)}</span>
</div>
<div style={{ display: 'flex', color: '#fed7aa', fontSize: '11', fontWeight: '700', letterSpacing: '1.2' }}>IN {dev.country.toUpperCase()}</div>
</div>
)}
</div>

{/* Score + Tier */}
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '16',
marginBottom: '28',
marginBottom: '20',
}}
>
<div
Expand All @@ -260,7 +325,7 @@ export async function GET(request) {
<div
style={{
color: power.color,
fontSize: '64',
fontSize: '48',
fontWeight: '800',
lineHeight: '1',
display: 'flex',
Expand Down Expand Up @@ -308,7 +373,7 @@ export async function GET(request) {
<div
style={{
display: 'flex',
gap: '20',
gap: '10',
flexWrap: 'wrap',
}}
>
Expand All @@ -325,9 +390,9 @@ export async function GET(request) {
flexDirection: 'column',
background: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.08)',
borderRadius: '10',
padding: '12px 20px',
minWidth: '130',
borderRadius: '8',
padding: '10px 14px',
minWidth: '120',
}}
>
<div
Expand All @@ -345,7 +410,7 @@ export async function GET(request) {
<div
style={{
color: stat.color,
fontSize: '24',
fontSize: '21',
fontWeight: '700',
display: 'flex',
}}
Expand Down
42 changes: 29 additions & 13 deletions app/page.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
'use client';

import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import React, { useState, useEffect, useCallback, useRef } from 'react';
import Header from '../components/Header.jsx';
import SearchBar from '../components/SearchBar.jsx';
import Leaderboard from '../components/Leaderboard.jsx';
import DetailPanel from '../components/DetailPanel.jsx';
import ComparePanel from '../components/ComparePanel.jsx';
import LoadingOverlay from '../components/LoadingOverlay.jsx';
import { scoreAll } from '../lib/scoring.js';
import { buildCountryRankings } from '../lib/rankings.js';
import { addDeveloperRanks } from '../lib/ranking.js';
import dynamic from 'next/dynamic';

const Globe = dynamic(() => import('../components/Globe.jsx'), { ssr: false });
Expand All @@ -27,13 +27,9 @@ export default function Home() {
const [claimStatus, setClaimStatus] = useState('unclaimed'); // 'unclaimed' | 'claimed' | 'no_match'
const [claimedLogins, setClaimedLogins] = useState(new Set());
const [sidebarOpen, setSidebarOpen] = useState(false);
const [cardRequest, setCardRequest] = useState(0);
const globeRef = useRef(null);

const countryRankings = useMemo(
() => buildCountryRankings(developers),
[developers]
);

useEffect(() => {
// Mirrors the blocking script in layout.jsx so React state matches the
// theme already applied to <html> before hydration.
Expand Down Expand Up @@ -93,7 +89,7 @@ export default function Home() {
const devRes = await fetch('/api/developers');
if (devRes.ok) {
const raw = await devRes.json();
const scored = scoreAll(raw);
const scored = addDeveloperRanks(scoreAll(raw));
setDevelopers(scored);
setFiltered(scored);
const claimed = new Set(raw.filter(d => d.claimed).map(d => d.login));
Expand Down Expand Up @@ -132,7 +128,7 @@ export default function Home() {
const res = await fetch('/api/developers');
if (!res.ok) throw new Error(`Failed to load data: ${res.status}`);
const raw = await res.json();
const scored = scoreAll(raw);
const scored = addDeveloperRanks(scoreAll(raw));
setDevelopers(scored);
setFiltered(scored);
// Build set of all claimed logins from data
Expand All @@ -148,15 +144,32 @@ export default function Home() {
}, []);

const handleSearch = useCallback((results) => {
const scored = scoreAll(results);
setFiltered(scored);
}, []);
const developerByLogin = new Map(developers.map(developer => [developer.login, developer]));
const rankedResults = results.map(result => ({
...developerByLogin.get(result.login),
...result,
}));
setSelectedDev(null);
setCardRequest(0);
setFiltered(rankedResults);
}, [developers]);

const handleGenerateCard = useCallback((developer) => {
const rankedDeveloper = developers.find(item => item.login === developer.login) || developer;
setSelectedDev(rankedDeveloper);
setCardRequest(request => request + 1);
setSidebarOpen(false);
if (rankedDeveloper.lat != null && rankedDeveloper.lng != null) {
setFlyTarget({ lat: rankedDeveloper.lat, lng: rankedDeveloper.lng });
}
}, [developers]);

const handleResetFilter = useCallback(() => {
setFiltered(developers);
}, [developers]);

const handleSelectDev = useCallback((dev) => {
setCardRequest(0);
setSelectedDev(dev);
setSidebarOpen(false);
if (dev?.lat != null && dev?.lng != null) {
Expand Down Expand Up @@ -197,6 +210,7 @@ export default function Home() {
}, []);

const handleHome = useCallback(() => {
setCardRequest(0);
setSelectedDev(null);
setCompareDevs([]);
setFiltered(developers);
Expand Down Expand Up @@ -226,6 +240,7 @@ export default function Home() {
developers={developers}
onResults={handleSearch}
onReset={handleResetFilter}
onGenerateCard={handleGenerateCard}
/>
<a
className="product-hunt-badge"
Expand Down Expand Up @@ -268,10 +283,11 @@ export default function Home() {
)}
{selectedDev && (
<DetailPanel
key={`${selectedDev.login}-${cardRequest}`}
dev={selectedDev}
onClose={handleCloseDetail}
claimedLogins={claimedLogins}
countryRankings={countryRankings}
openCardOnMount={cardRequest > 0}
/>
)}
{compareDevs.length === 2 && (
Expand Down
Loading
Loading