diff --git a/src/components/discover/DiscoverApp.tsx b/src/components/discover/DiscoverApp.tsx index c89e6c0..14d5bd8 100644 --- a/src/components/discover/DiscoverApp.tsx +++ b/src/components/discover/DiscoverApp.tsx @@ -2,7 +2,6 @@ import { useState, useCallback, useEffect, useMemo } from 'react'; import { trpc } from '@/lib/trpc/client'; -import Link from 'next/link'; import { TextButton } from '@/components/ui/text-button'; import { NetworkGraph, type NetworkUser, type EdgeFilter } from '@/components/admin/NetworkGraph'; import { SemanticMap, type SemanticMapPoint } from '@/components/discover/SemanticMap'; @@ -30,23 +29,6 @@ export default function DiscoverApp({ standalone = false }: DiscoverAppProps = { } type DiscoverMode = 'network' | 'explore'; -type ViewMode = 'table' | 'graph' | 'map'; - -function ViewToggle({ viewMode, setViewMode }: { viewMode: ViewMode; setViewMode: (v: ViewMode) => void }) { - return ( -
- setViewMode('table')} active={viewMode === 'table'} className="pb-1 text-xs font-semibold tracking-[1px] uppercase"> - Table - - setViewMode('graph')} active={viewMode === 'graph'} className="pb-1 text-xs font-semibold tracking-[1px] uppercase"> - Graph - - setViewMode('map')} active={viewMode === 'map'} className="pb-1 text-xs font-semibold tracking-[1px] uppercase"> - Map - -
- ); -} function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { const hint = useSessionHint(); @@ -60,11 +42,11 @@ function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { const [seedUsername, setSeedUsername] = useState(initialSeed); const [activeUsername, setActiveUsername] = useState(initialSeed); const [selectedUsers, setSelectedUsers] = useState>(new Set()); - const [viewMode, setViewMode] = useState('graph'); - // Default to 'explore' so signed-out visitors land on the public map of all - // analyzed devs instead of an empty network prompt. Signed-in users with a - // GitHub username get auto-switched to 'network' by the effect below, and - // ?seed= in the URL also forces network mode. + // Default to 'explore' so every visitor — signed in or not — lands on the + // semantic map of every analyzed dev first. The map IS the value prop; + // showing it before any input keeps the funnel value-first. `?seed=` + // still forces network mode (the "Find similar" CTA round-trips through + // OAuth and lands here). const [discoverMode, setDiscoverMode] = useState(initialSeed ? 'network' : 'explore'); const [edgeFilter, setEdgeFilter] = useState({ following: true, followers: true, semantic: true }); const trpcUtils = trpc.useUtils(); @@ -125,11 +107,12 @@ function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { } }, [seedUsername]); + // Pre-fill the seed input with the signed-in user's GH handle so flipping + // to "My Network" is one click — but DON'T auto-switch modes. Landing must + // stay on the global map; the map is the value-first surface. useEffect(() => { - if (hint?.githubUsername && !activeUsername) { + if (hint?.githubUsername && !seedUsername) { setSeedUsername(hint.githubUsername); - setActiveUsername(hint.githubUsername); - setDiscoverMode('network'); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [hint?.githubUsername]); @@ -144,15 +127,6 @@ function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { [similarData] ); - const allProfileUsers: NetworkUser[] = useMemo(() => - (allProfiles || []).map(u => ({ - username: u.username, avatar: u.avatar, name: null, bio: u.summary, - followers: 0, publicRepos: 0, hasGGProfile: true, - archetype: u.archetype, score: u.score, topSkills: u.topSkills, - })), - [allProfiles] - ); - const currentNetwork = enrichedNetwork && enrichedNetwork.seed.toLowerCase() === activeUsername.toLowerCase() ? enrichedNetwork : null; const isLoading = discoverMode === 'network' ? (!!activeUsername && networkLoading) @@ -160,9 +134,9 @@ function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { const archetypeCounts = useMemo(() => { const counts = new Map(); - for (const u of allProfileUsers) counts.set(u.archetype || 'Unknown', (counts.get(u.archetype || 'Unknown') || 0) + 1); + for (const u of allProfiles || []) counts.set(u.archetype || 'Unknown', (counts.get(u.archetype || 'Unknown') || 0) + 1); return counts; - }, [allProfileUsers]); + }, [allProfiles]); return (
@@ -196,19 +170,13 @@ function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { loading={isLoading} fallback={
-
-
- -
+
} fixture={
-
-
- -
+
{Array.from({ length: 6 }).map((_, i) => ( @@ -228,38 +196,30 @@ function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { {currentNetwork.users.length} connections of @{currentNetwork.seed} -
-
- + + {semanticUsers.length > 0 && ( + - - {semanticUsers.length > 0 && ( - - )} -
- - + )}
- {viewMode === 'graph' ? ( - - ) : ( - - )} + )} @@ -315,34 +275,23 @@ function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { )} -
- {viewMode === 'graph' ? ( - - ) : viewMode === 'map' ? ( - p.x != null && p.y != null) - .map((p): SemanticMapPoint => ({ - username: p.username, - x: p.x as number, - y: p.y as number, - archetype: p.archetype, - confidence: p.confidence, - topSkills: p.topSkills, - avatar: p.avatar, - }))} - height={600} - onClickNode={(username) => router.push(`/hire/${username}`)} - /> - ) : ( - - )} + p.x != null && p.y != null) + .map((p): SemanticMapPoint => ({ + username: p.username, + x: p.x as number, + y: p.y as number, + archetype: p.archetype, + confidence: p.confidence, + topSkills: p.topSkills, + avatar: p.avatar, + }))} + height={600} + onClickNode={(username) => router.push(`/hire/${username}`)} + /> )} @@ -350,122 +299,3 @@ function NetworkExplorer({ showHeading = true }: { showHeading?: boolean }) { ); } -function NetworkTable({ users, semanticUsers }: { users: NetworkUser[]; semanticUsers: NetworkUser[] }) { - return ( - - - - - - - - - - - - {users.map((u) => ( - - - - - - - - ))} - {semanticUsers.length > 0 && ( - <> - - - - {semanticUsers.map((u) => ( - - - - - - - - ))} - - )} - -
DeveloperBioRelationFollowersGG
-
- {u.username} - {u.username} - {u.name && {u.name}} -
-
{u.bio || '\u2014'} - {u.isMutual ? ( - Mutual - ) : u.isFollowing ? ( - Following - ) : ( - Follower - )} - {u.followers} - {u.hasGGProfile ? Yes : } -
- Semantically Similar Developers -
-
- {u.username} - {u.username} -
-
{u.archetype || u.bio || '\u2014'}{u.similarity}% - {u.score != null ? {u.score}/100 : '\u2014'} - Yes
- ); -} - -function ExploreAllTable({ profiles }: { profiles: Array<{ - username: string; - avatar: string; - summary: string | null; - archetype: string | null; - score: number | null; - topSkills: string[]; - updatedAt: string; -}> }) { - return ( - - - - - - - - - - - - {profiles.map((u) => ( - - - - - - - - ))} - -
DeveloperArchetypeSkillsScoreAnalyzed
-
- {u.username} - {u.username} -
-
{u.archetype || '\u2014'} -
- {u.topSkills.slice(0, 3).map(skill => ( - {skill} - ))} -
-
- {u.score != null ? ( - {u.score}/100 - ) : N/A} - - {new Date(u.updatedAt).toLocaleDateString()} -
- ); -}