Viani's picture
feat: tracked orgs dialog + search suggestion banner
c121e6a
import {
ProviderInfo,
CalendarData,
ModelData,
OrgCandidate,
CandidateSummary,
} from "../types/heatmap";
import { generateCalendarData } from "./calendar";
import { fetchAllProvidersData, fetchAllAuthorsData } from "./authors";
import { assignColor } from "../constants/colors";
import { LEADERBOARD_SIZE } from "../constants/organizations";
export interface RankingBadge {
className: string;
icon: string | null;
}
export const getRankingBadge = (rank: number): RankingBadge => {
if (rank === 1) {
return {
className:
"absolute -top-4 -left-4 w-12 h-12 bg-gradient-to-br from-yellow-400 via-yellow-500 to-yellow-600 text-white rounded-full flex items-center justify-center text-lg font-bold shadow-2xl border-4 border-yellow-300",
icon: "👑",
};
} else if (rank === 2) {
return {
className:
"absolute -top-4 -left-4 w-10 h-10 bg-gradient-to-br from-gray-300 via-gray-400 to-gray-500 text-white rounded-full flex items-center justify-center text-base font-bold shadow-xl border-3 border-gray-200",
icon: "🥈",
};
} else if (rank === 3) {
return {
className:
"absolute -top-4 -left-4 w-10 h-10 bg-gradient-to-br from-amber-600 via-amber-700 to-amber-800 text-white rounded-full flex items-center justify-center text-base font-bold shadow-xl border-3 border-amber-400",
icon: "🥉",
};
} else {
return {
className:
"absolute -top-3 -left-3 w-8 h-8 bg-gradient-to-br from-blue-500 to-blue-600 text-white rounded-full flex items-center justify-center text-sm font-bold shadow-lg",
icon: null,
};
}
};
export const extractUniqueAuthors = (providers: ProviderInfo[]): string[] => {
const allAuthors = providers.flatMap(({ authors }) => authors);
return Array.from(new Set(allAuthors));
};
export const sortProvidersByActivity = (
providers: ProviderInfo[],
calendarData: CalendarData
): ProviderInfo[] => {
return [...providers].sort((a, b) => {
const aCount = (calendarData[a.authors[0]] || []).reduce(
(sum, day) => sum + day.count,
0
);
const bCount = (calendarData[b.authors[0]] || []).reduce(
(sum, day) => sum + day.count,
0
);
return bCount - aCount;
});
};
const candidatesToProviders = (candidates: OrgCandidate[]): ProviderInfo[] =>
candidates.map((c) => ({
color: c.color || "",
authors: c.authors,
}));
export const getProviders = async (candidates: OrgCandidate[]) => {
const allProviders = candidatesToProviders(candidates);
const uniqueAuthors = extractUniqueAuthors(allProviders);
const [authorModelsData, providersWithMetadata] = await Promise.all([
fetchAllAuthorsData(uniqueAuthors),
fetchAllProvidersData(allProviders),
]);
const calendarData = generateCalendarData(
authorModelsData,
providersWithMetadata
);
const sorted = sortProvidersByActivity(providersWithMetadata, calendarData);
const topN = sorted.slice(0, LEADERBOARD_SIZE);
const coloredProviders = topN.map((provider, index) => ({
...provider,
color: provider.color || assignColor(index),
}));
const topKeys = new Set(coloredProviders.map((p) => p.authors[0]));
const filteredCalendar: CalendarData = {};
for (const key of Object.keys(calendarData)) {
if (topKeys.has(key)) {
filteredCalendar[key] = calendarData[key];
}
}
const lastProvider = coloredProviders[coloredProviders.length - 1];
const lastKey = lastProvider?.authors[0];
const minActivityCount = lastKey
? (filteredCalendar[lastKey] || []).reduce((s, d) => s + d.count, 0)
: 0;
const allCandidates: CandidateSummary[] = sorted.map((p) => ({
author: p.authors[0],
fullName: p.fullName || p.authors[0],
avatarUrl: p.avatarUrl || null,
}));
return {
calendarData: filteredCalendar,
providers: coloredProviders,
allCandidates,
minActivityCount,
};
};