six-degrees 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +35 -7
- package/.next/standalone/.next/required-server-files.json +6 -1
- package/.next/standalone/.next/routes-manifest.json +18 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/admin-delete/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js +1 -1
- package/.next/standalone/.next/server/app/api/admin-update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js +1 -1
- package/.next/standalone/.next/server/app/api/bulk-import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/legacy/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/company-scores/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js +1 -1
- package/.next/standalone/.next/server/app/api/connections/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/restart/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/reveal/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/data/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js +1 -1
- package/.next/standalone/.next/server/app/api/delete-cluster/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js +1 -1
- package/.next/standalone/.next/server/app/api/ingest/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js +1 -1
- package/.next/standalone/.next/server/app/api/network/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/.next/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/outreach/route.js +3 -3
- package/.next/standalone/.next/server/app/api/outreach/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js +1 -1
- package/.next/standalone/.next/server/app/api/queue/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js +1 -1
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-preview/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/sector-suggestions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js +1 -1
- package/.next/standalone/.next/server/app/api/setup-profile/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js +1 -1
- package/.next/standalone/.next/server/app/api/unlock/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js +1 -1
- package/.next/standalone/.next/server/app/api/update-images/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js +1 -1
- package/.next/standalone/.next/server/app/api/users/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/avatars/[file]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/import/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/import.html +1 -1
- package/.next/standalone/.next/server/app/import.rsc +13 -13
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/launch/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/launch.html +2 -2
- package/.next/standalone/.next/server/app/launch.rsc +13 -13
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/paths/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/paths.html +1 -1
- package/.next/standalone/.next/server/app/paths.rsc +13 -13
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/profile/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/profile/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/profile.html +1 -1
- package/.next/standalone/.next/server/app/profile.rsc +13 -13
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/queue/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/queue/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/queue.html +1 -1
- package/.next/standalone/.next/server/app/queue.rsc +13 -13
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/scores/page/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/scores/page/build-manifest.json +20 -0
- package/.next/standalone/.next/server/app/scores/page/next-font-manifest.json +11 -0
- package/.next/standalone/.next/server/app/scores/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/.next/server/app/scores/page/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/scores/page.js +13 -0
- package/.next/standalone/.next/server/app/scores/page.js.map +5 -0
- package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +3 -0
- package/.next/standalone/.next/server/app/scores.html +1 -0
- package/.next/standalone/.next/server/app/scores.meta +26 -0
- package/.next/standalone/.next/server/app/scores.rsc +24 -0
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +24 -0
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +4 -0
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +28 -0
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +13 -13
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup.html +1 -1
- package/.next/standalone/.next/server/app/setup.rsc +13 -13
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +12 -12
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0-rnla2._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__00la_y0._.js +1 -1
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0i5hdu4._.js → [root-of-the-server]__03ydxxh._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__07t47cm._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0bat8nx._.js +8 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0cj5jqc._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0e0fei3._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljcioj._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ljnyho._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o5-tcg._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0qrnous._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0t3v1a1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0uag-k5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0um1uey._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0wmexuc._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0xu34sz._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__133knx1._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__14ou7j5._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__15-17hl._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__16iu0hv._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__19b7mig._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1bbcwub._.js +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1eris29._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1gjo_cq._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1qcm08_._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1rphsdk._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1w25q0m._.js +1 -1
- package/.next/standalone/.next/server/chunks/_02twlca._.js +10 -0
- package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0lw8_i0._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +8 -6
- package/.next/standalone/.next/server/chunks/_1fn30_l._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1itd1o_._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1iwkh8t._.js +1 -1
- package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +13 -8
- package/.next/standalone/.next/server/chunks/{lib_rpc_02ndsg2.js → lib_rpc_03i8xef.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_08q5vp6.js → lib_rpc_08fc3rp.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_00tx2sm.js → lib_rpc_08r2t_q.js} +1 -1
- package/.next/standalone/.next/server/chunks/{lib_rpc_02-qa4d.js → lib_rpc_0btpv1t.js} +1 -1
- package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -5
- package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0an6blh._.js → [root-of-the-server]__0zzcbqg._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1194jps._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1z1j3ek._.js → [root-of-the-server]__19ip1qq._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_01f38ty._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_06w_gl4._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0hw_qnj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_13kzrwv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_15xuwkv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1cyxocj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1gpbbw9._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1hgd5dp._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1vpfyd-._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_20w06i-._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_scores_page_actions_0x-w58n.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_components_settings_SectorPicker_1c-29qw.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_launch_page_19t0dyd.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +11 -9
- package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +4 -0
- package/.next/standalone/.next/server/chunks/ssr/app_profile_page_0u3-l5y.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_queue_page_0c6znd6.js +5 -5
- package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_settings_page_1f8mjj_.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_setup_page_1-282zx.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/lib_1bn38gs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_0x3i8za._.js +131 -0
- package/.next/standalone/.next/server/chunks/ssr/{node_modules_next_dist_1bunoni._.js → node_modules_next_dist_1v8aef8._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/node_modules_next_dist_client_components_server-async-storage_0t1dato.js +3 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware-manifest.json +5 -5
- package/.next/standalone/.next/server/next-font-manifest.js +1 -1
- package/.next/standalone/.next/server/next-font-manifest.json +4 -0
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/prefetch-hints.json +14 -0
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
- package/.next/standalone/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
- package/.next/standalone/.next/static/chunks/0l1568qff0efj.js +1 -0
- package/.next/standalone/.next/static/chunks/0muwt9bykzr-1.js +1 -0
- package/.next/standalone/.next/static/chunks/0w76iraw7ij3k.js +18 -0
- package/.next/standalone/.next/static/chunks/135n87ydxjrra.js +2 -0
- package/.next/standalone/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
- package/.next/standalone/.next/static/chunks/28bu26ausqzmn.js +1 -0
- package/.next/standalone/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
- package/.next/standalone/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
- package/.next/standalone/.next/static/chunks/2xjho0lroiwlq.js +1 -0
- package/.next/standalone/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
- package/.next/standalone/.next/static/chunks/3qh78g2aqhprd.js +2 -0
- package/.next/standalone/CHANGELOG.md +286 -0
- package/.next/standalone/CONTRIBUTING.md +15 -4
- package/.next/standalone/README.md +50 -29
- package/.next/standalone/SECURITY.md +10 -0
- package/.next/standalone/app/api/company-scores/legacy/route.js +1 -1
- package/.next/standalone/app/api/company-scores/route.js +1 -1
- package/.next/standalone/app/api/ingest/route.js +23 -2
- package/.next/standalone/app/api/outreach/route.js +107 -44
- package/.next/standalone/app/api/scraper/route.js +146 -17
- package/.next/standalone/app/api/settings/sector-preview/route.js +3 -2
- package/.next/standalone/app/api/settings/sector-suggestions/route.js +1 -1
- package/.next/standalone/app/api/update-images/route.js +41 -7
- package/.next/standalone/app/avatars/[file]/route.js +6 -1
- package/.next/standalone/app/components/Avatar.js +5 -2
- package/.next/standalone/app/components/ChainView.js +659 -469
- package/.next/standalone/app/components/CompanyScores.js +16 -10
- package/.next/standalone/app/components/EmptyState.js +1 -1
- package/.next/standalone/app/components/FieldStep.js +94 -0
- package/.next/standalone/app/components/ForceGraph.js +183 -768
- package/.next/standalone/app/components/GridView.js +9 -7
- package/.next/standalone/app/components/LegacyScoresCard.js +1 -1
- package/.next/standalone/app/components/LinkedInLimits.js +22 -7
- package/.next/standalone/app/components/ListView.js +6 -4
- package/.next/standalone/app/components/MappingProgress.js +5 -6
- package/.next/standalone/app/components/OnboardingGate.js +1 -1
- package/.next/standalone/app/components/OrbitGraph.js +33 -1
- package/.next/standalone/app/components/OutlinkQuest.js +5 -4
- package/.next/standalone/app/components/PathsAnalyzer.js +1 -1
- package/.next/standalone/app/components/RingsView.js +9 -8
- package/.next/standalone/app/components/SeparationView.js +96 -14
- package/.next/standalone/app/components/Sidebar.js +366 -287
- package/.next/standalone/app/components/TheirCircle.js +300 -0
- package/.next/standalone/app/components/settings/SectorPicker.js +202 -0
- package/.next/standalone/app/components/settings/SectorSection.js +21 -195
- package/.next/standalone/app/components/settings/TierSection.js +99 -0
- package/.next/standalone/app/components/useRequests.js +12 -0
- package/.next/standalone/app/components/useScanner.js +13 -0
- package/.next/standalone/app/components/views.js +3 -5
- package/.next/standalone/app/globals.css +7 -0
- package/.next/standalone/app/launch/page.js +6 -3
- package/.next/standalone/app/page.js +183 -74
- package/.next/standalone/app/paths/page.js +20 -14
- package/.next/standalone/app/profile/page.js +3 -3
- package/.next/standalone/app/queue/page.js +56 -60
- package/.next/standalone/app/scores/page.js +72 -0
- package/.next/standalone/app/settings/page.js +15 -3
- package/.next/standalone/app/setup/page.js +423 -260
- package/.next/standalone/db/schema.js +2 -1
- package/.next/standalone/eslint.config.mjs +3 -3
- package/.next/standalone/install.sh +3 -3
- package/.next/standalone/lib/chain-layout.js +139 -0
- package/.next/standalone/lib/circle.js +169 -0
- package/.next/standalone/lib/companies.js +1 -1
- package/.next/standalone/lib/csv.js +21 -10
- package/.next/standalone/lib/data-export.js +2 -1
- package/.next/standalone/lib/data-folder.js +4 -2
- package/.next/standalone/lib/db-client.js +1 -0
- package/.next/standalone/lib/galaxy.js +12 -0
- package/.next/standalone/lib/ingest.js +27 -8
- package/.next/standalone/lib/legacy-offer.js +7 -6
- package/.next/standalone/lib/legacy-scores.js +2 -2
- package/.next/standalone/lib/linkedin-limits.js +30 -8
- package/.next/standalone/lib/photos.js +47 -0
- package/.next/standalone/lib/quest.js +8 -3
- package/.next/standalone/lib/rarity.js +80 -0
- package/.next/standalone/lib/reach.js +113 -0
- package/.next/standalone/lib/requests-client.js +113 -0
- package/.next/standalone/lib/rpc.js +26 -15
- package/.next/standalone/lib/scan-state.js +1 -0
- package/.next/standalone/lib/scanner-setup.js +20 -1
- package/.next/standalone/lib/score-guess.js +60 -0
- package/.next/standalone/lib/scoring.js +1761 -69
- package/.next/standalone/lib/scraper-client.js +235 -17
- package/.next/standalone/lib/sector-directory.js +1 -1
- package/.next/standalone/lib/sector-focus.js +17 -3
- package/.next/standalone/lib/separation.js +4 -2
- package/.next/standalone/lib/settings-client.js +21 -0
- package/.next/standalone/lib/settings-effects.js +19 -1
- package/.next/standalone/lib/settings.js +7 -2
- package/.next/standalone/next.config.mjs +25 -3
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +2 -2
- package/.next/standalone/public/demo-data.json +1 -1
- package/.next/standalone/scripts/build-app.mjs +11 -2
- package/.next/standalone/scripts/check-node.mjs +1 -1
- package/.next/standalone/scripts/gen-synthetic.mjs +1 -1
- package/.next/standalone/scripts/image_store.py +77 -21
- package/.next/standalone/scripts/scrape.py +305 -29
- package/.next/standalone/server.js +1 -1
- package/.next/static/chunks/0ax_9zzboyv6d.js +44 -0
- package/.next/static/chunks/0cuuo2jwc9h94.js +1 -0
- package/.next/static/chunks/0l1568qff0efj.js +1 -0
- package/.next/static/chunks/0muwt9bykzr-1.js +1 -0
- package/.next/static/chunks/0w76iraw7ij3k.js +18 -0
- package/.next/static/chunks/135n87ydxjrra.js +2 -0
- package/.next/static/chunks/18xu0fbgo_qz-.js +1 -0
- package/.next/static/chunks/28bu26ausqzmn.js +1 -0
- package/.next/static/chunks/{40nvbgfn7-072.css → 2p6o90z0vgcwr.css} +1 -1
- package/.next/static/chunks/2pnbm1z6j7xab.js +1 -0
- package/.next/static/chunks/2xjho0lroiwlq.js +1 -0
- package/.next/static/chunks/3e8zg6ctvvb8p.js +33 -0
- package/.next/static/chunks/3qh78g2aqhprd.js +2 -0
- package/CHANGELOG.md +286 -0
- package/README.md +50 -29
- package/SECURITY.md +10 -0
- package/db/schema.js +2 -1
- package/lib/chain-layout.js +139 -0
- package/lib/circle.js +169 -0
- package/lib/companies.js +1 -1
- package/lib/csv.js +21 -10
- package/lib/data-export.js +2 -1
- package/lib/data-folder.js +4 -2
- package/lib/db-client.js +1 -0
- package/lib/galaxy.js +12 -0
- package/lib/ingest.js +27 -8
- package/lib/legacy-offer.js +7 -6
- package/lib/legacy-scores.js +2 -2
- package/lib/linkedin-limits.js +30 -8
- package/lib/photos.js +47 -0
- package/lib/quest.js +8 -3
- package/lib/rarity.js +80 -0
- package/lib/reach.js +113 -0
- package/lib/requests-client.js +113 -0
- package/lib/rpc.js +26 -15
- package/lib/scan-state.js +1 -0
- package/lib/scanner-setup.js +20 -1
- package/lib/score-guess.js +60 -0
- package/lib/scoring.js +1761 -69
- package/lib/scraper-client.js +235 -17
- package/lib/sector-directory.js +1 -1
- package/lib/sector-focus.js +17 -3
- package/lib/separation.js +4 -2
- package/lib/settings-client.js +21 -0
- package/lib/settings-effects.js +19 -1
- package/lib/settings.js +7 -2
- package/package.json +2 -2
- package/public/demo-data.json +1 -1
- package/scripts/check-node.mjs +1 -1
- package/scripts/image_store.py +77 -21
- package/scripts/scrape.py +305 -29
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1t3rugi._.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0_8i3ft.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0huvw-3.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0ip2vdh.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0jzisgh.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0yi9xvd.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1jyhd0o.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1kn59-i.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1q2jkza.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1vpm1sk.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1x6p_31.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_07u8byv._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_08r_mlf._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0942v8n._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0hl_t7r._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0tb83dw._.js +0 -132
- package/.next/standalone/.next/server/chunks/ssr/_1-87bs1._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_16vay6l._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/node_modules_1kciwn4._.js +0 -3
- package/.next/standalone/.next/static/chunks/06x3jiiz96m5t.js +0 -1
- package/.next/standalone/.next/static/chunks/08k1crid-05dt.js +0 -1
- package/.next/standalone/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
- package/.next/standalone/.next/static/chunks/0q-_khndfxubv.js +0 -2
- package/.next/standalone/.next/static/chunks/1c79veqedvubt.js +0 -1
- package/.next/standalone/.next/static/chunks/1chp3nx45meg5.js +0 -44
- package/.next/standalone/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
- package/.next/standalone/.next/static/chunks/1uadqylrod5_v.js +0 -1
- package/.next/standalone/.next/static/chunks/23u0zclgwpc4d.js +0 -1
- package/.next/standalone/.next/static/chunks/289ktj97di6zg.js +0 -1
- package/.next/standalone/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
- package/.next/standalone/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
- package/.next/standalone/app/components/BridgeRing.js +0 -753
- package/.next/static/chunks/06x3jiiz96m5t.js +0 -1
- package/.next/static/chunks/08k1crid-05dt.js +0 -1
- package/.next/static/chunks/0ngx9s6-zgshe.js +0 -33
- package/.next/static/chunks/0q-_khndfxubv.js +0 -2
- package/.next/static/chunks/1c79veqedvubt.js +0 -1
- package/.next/static/chunks/1chp3nx45meg5.js +0 -44
- package/.next/static/chunks/1irqj0lf-fu3d.js +0 -1
- package/.next/static/chunks/1uadqylrod5_v.js +0 -1
- package/.next/static/chunks/23u0zclgwpc4d.js +0 -1
- package/.next/static/chunks/289ktj97di6zg.js +0 -1
- package/.next/static/chunks/2b3lmf8e6uj8m.js +0 -16
- package/.next/static/chunks/3npq5yd7c4dmn.js +0 -2
- /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_buildManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → WseE_5HuU-1q0ywC9CP3U}/_ssgManifest.js +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
|
4
|
-
import {
|
|
3
|
+
import { Suspense, useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
|
4
|
+
import { useSearchParams } from 'next/navigation';
|
|
5
|
+
import { scraperStatus, beginScrape, notReadyMessage, busyReason, isCircleScan, loadScanNotes, NO_SCAN_NOTES } from '../lib/scraper-client';
|
|
6
|
+
import useScanner from './components/useScanner';
|
|
7
|
+
import useRequests from './components/useRequests';
|
|
8
|
+
import { requestCount } from '../lib/requests-client';
|
|
5
9
|
import { loadNetwork } from '../lib/network';
|
|
6
|
-
import { resolveView } from './components/views';
|
|
10
|
+
import { VIEWS, resolveView } from './components/views';
|
|
7
11
|
import Sidebar from './components/Sidebar';
|
|
8
12
|
import FilterPanel from './components/FilterPanel';
|
|
9
13
|
import OnboardingGate from './components/OnboardingGate';
|
|
@@ -16,6 +20,18 @@ import Link from 'next/link';
|
|
|
16
20
|
// One shared empty list, so "no 2nd-degree data" is the same value every render.
|
|
17
21
|
const NO_DEGREE2 = [];
|
|
18
22
|
|
|
23
|
+
// A job that changes the network. Setting the scanner up and signing in don't.
|
|
24
|
+
const CHANGES_NETWORK = (job) => Boolean(job) && !['install', 'setup', 'login'].includes(job.action);
|
|
25
|
+
|
|
26
|
+
/** Counts for the header and panels, from the two lists. */
|
|
27
|
+
function statsFor(d1, d2) {
|
|
28
|
+
const tierCounts = {};
|
|
29
|
+
d1.forEach(c => { tierCounts[c.tier] = (tierCounts[c.tier] || 0) + 1; });
|
|
30
|
+
const d2TierCounts = {};
|
|
31
|
+
d2.forEach(c => { d2TierCounts[c.tier] = (d2TierCounts[c.tier] || 0) + 1; });
|
|
32
|
+
return { total: d1.length, tiers: tierCounts, d2Total: d2.length, d2Tiers: d2TierCounts };
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
const TIER_COLORS = {
|
|
20
36
|
S: '#FFD700',
|
|
21
37
|
A: '#9B59B6',
|
|
@@ -25,9 +41,13 @@ const TIER_COLORS = {
|
|
|
25
41
|
};
|
|
26
42
|
|
|
27
43
|
export default function Home() {
|
|
44
|
+
// Suspense because HomeInner reads the address's ?chain= (useSearchParams),
|
|
45
|
+
// which Next requires to sit inside one for the page to build.
|
|
28
46
|
return (
|
|
29
47
|
<OnboardingGate>
|
|
30
|
-
<
|
|
48
|
+
<Suspense>
|
|
49
|
+
<HomeInner />
|
|
50
|
+
</Suspense>
|
|
31
51
|
</OnboardingGate>
|
|
32
52
|
);
|
|
33
53
|
}
|
|
@@ -51,17 +71,28 @@ function HomeInner() {
|
|
|
51
71
|
const [selected, setSelected] = useState(null);
|
|
52
72
|
const focusNodeRef = useRef(null);
|
|
53
73
|
const [filter, setFilter] = useState('all');
|
|
54
|
-
|
|
74
|
+
// A link to someone's circle in Bridge Chains, /?chain=<id> (the Scan page's
|
|
75
|
+
// "watch it fill in"), opens Degrees on it. The router's search params rather
|
|
76
|
+
// than window.location: on a click from another page the address bar only
|
|
77
|
+
// changes after this page has rendered, and the map opened on the Galaxy.
|
|
78
|
+
const linkedChain = useSearchParams().get('chain');
|
|
79
|
+
const [chainOpen, setChainOpen] = useState(linkedChain);
|
|
80
|
+
const [mode, setMode] = useState(() => (chainOpen ? 'degrees' : 'network'));
|
|
55
81
|
const [loading, setLoading] = useState(true);
|
|
56
82
|
const [stats, setStats] = useState(null);
|
|
57
83
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
|
|
58
84
|
const [filterPanelCollapsed, setFilterPanelCollapsed] = useState(true);
|
|
59
|
-
const [visualMode, setVisualMode] = useState('galaxy');
|
|
85
|
+
const [visualMode, setVisualMode] = useState(() => (chainOpen ? 'chain' : 'galaxy'));
|
|
60
86
|
const [notifications, setNotifications] = useState([]);
|
|
61
87
|
const [showNotifs, setShowNotifs] = useState(false);
|
|
62
|
-
const [pending, setPending] = useState([]);
|
|
63
88
|
const [csvMode, setCsvMode] = useState(false);
|
|
64
89
|
const [csvSource, setCsvSource] = useState('csv');
|
|
90
|
+
// What the scanner noted: hidden lists, and lists read with nothing new in
|
|
91
|
+
// them. With the network, they say who is ready for a circle scan (lib/reach.js).
|
|
92
|
+
const [scanNotes, setScanNotes] = useState(NO_SCAN_NOTES);
|
|
93
|
+
const shapeRef = useRef('');
|
|
94
|
+
// Everyone with a request out, shared with every view (lib/requests-client.js).
|
|
95
|
+
const pendingCount = requestCount(useRequests());
|
|
65
96
|
|
|
66
97
|
useEffect(() => {
|
|
67
98
|
if (!userId) return;
|
|
@@ -81,23 +112,43 @@ function HomeInner() {
|
|
|
81
112
|
|
|
82
113
|
setDegree1(d1);
|
|
83
114
|
setDegree2(d2);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
d1.forEach(c => { tierCounts[c.tier] = (tierCounts[c.tier] || 0) + 1; });
|
|
87
|
-
const d2TierCounts = {};
|
|
88
|
-
d2.forEach(c => { d2TierCounts[c.tier] = (d2TierCounts[c.tier] || 0) + 1; });
|
|
89
|
-
setStats({ total: d1.length, tiers: tierCounts, d2Total: d2.length, d2Tiers: d2TierCounts });
|
|
115
|
+
shapeRef.current = networkShape(d1, d2);
|
|
116
|
+
setStats(statsFor(d1, d2));
|
|
90
117
|
setLoading(false);
|
|
91
118
|
}
|
|
92
119
|
load();
|
|
93
120
|
if (!IS_DEMO && !hasCsvNetwork()) {
|
|
121
|
+
loadScanNotes().then(setScanNotes);
|
|
94
122
|
// Fetch notifications
|
|
95
123
|
fetch(`/api/notifications?userId=${userId}`).then(r => r.json()).then(d => setNotifications(d.notifications || [])).catch(() => {});
|
|
96
|
-
// Fetch pending outreach
|
|
97
|
-
fetch(`/api/outreach?userId=${userId}`).then(r => r.json()).then(d => setPending(d.pending || [])).catch(() => {});
|
|
98
124
|
}
|
|
99
125
|
}, [userId]);
|
|
100
126
|
|
|
127
|
+
// The network again, after a scan: circles fill in while they're scanned
|
|
128
|
+
// (Blake, 2026-09-28: "the d3 dots as it builds more in"). Only a real change
|
|
129
|
+
// is published, because the graph views rebuild their scene whenever these
|
|
130
|
+
// lists change identity (TRAPS §29).
|
|
131
|
+
const reload = useCallback(async () => {
|
|
132
|
+
if (IS_DEMO || hasCsvNetwork() || !userId) return;
|
|
133
|
+
try {
|
|
134
|
+
const [{ degree1: d1, degree2: d2 }, notes] = await Promise.all([loadNetwork(userId), loadScanNotes()]);
|
|
135
|
+
setScanNotes((prev) => (JSON.stringify(prev) === JSON.stringify(notes) ? prev : notes));
|
|
136
|
+
const shape = networkShape(d1, d2);
|
|
137
|
+
if (shape === shapeRef.current) return;
|
|
138
|
+
shapeRef.current = shape;
|
|
139
|
+
setDegree1(d1);
|
|
140
|
+
setDegree2(d2);
|
|
141
|
+
setStats(statsFor(d1, d2));
|
|
142
|
+
} catch { /* the app restarting, say: keep what's on screen */ }
|
|
143
|
+
}, [userId]);
|
|
144
|
+
|
|
145
|
+
// Followed once: Bridge Chains has opened it, so leaving that view and coming
|
|
146
|
+
// back, or reloading, starts from the overview.
|
|
147
|
+
const chainOpened = useCallback(() => {
|
|
148
|
+
setChainOpen(null);
|
|
149
|
+
try { window.history.replaceState(null, '', window.location.pathname); } catch { /* the link stays */ }
|
|
150
|
+
}, []);
|
|
151
|
+
|
|
101
152
|
const isDegreesMode = mode === 'degrees';
|
|
102
153
|
const connections = degree1;
|
|
103
154
|
|
|
@@ -136,22 +187,24 @@ function HomeInner() {
|
|
|
136
187
|
if (loading) {
|
|
137
188
|
return (
|
|
138
189
|
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: '#0a0a1a', color: '#fff' }}>
|
|
139
|
-
<div style={{ fontSize: 24, fontWeight: 700 }}>Loading
|
|
190
|
+
<div style={{ fontSize: 24, fontWeight: 700 }}>Loading Six Degrees…</div>
|
|
140
191
|
<div style={{ fontSize: 14, color: '#888', marginTop: 8 }}>Mapping your LinkedIn network</div>
|
|
141
192
|
</div>
|
|
142
193
|
);
|
|
143
194
|
}
|
|
144
195
|
|
|
145
|
-
// Resolved once, because two places care: the renderer below, and the
|
|
196
|
+
// Resolved once, because two places care: the renderer below, and the Orbit
|
|
146
197
|
// toggle, which hides over Separation.
|
|
147
198
|
const view = resolveView(visualMode, mode);
|
|
199
|
+
// Scanning and the Scan page belong to your own network, not the sample or a CSV.
|
|
200
|
+
const canScan = !IS_DEMO && !csvMode;
|
|
148
201
|
|
|
149
202
|
return (
|
|
150
203
|
<div data-map style={{ height: '100vh', overflow: 'hidden', background: '#0a0a1a', color: '#fff', fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif' }}>
|
|
151
204
|
<header style={{ padding: isMobile ? '10px 12px' : '20px 30px', borderBottom: '1px solid rgba(255,255,255,0.1)' }}>
|
|
152
205
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: isMobile ? 4 : 8, flexWrap: isMobile ? 'wrap' : 'nowrap', gap: isMobile ? 6 : 0 }}>
|
|
153
206
|
<h1 style={{ fontSize: isMobile ? 16 : 28, fontWeight: 700, margin: 0, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
|
|
154
|
-
|
|
207
|
+
Six Degrees
|
|
155
208
|
</h1>
|
|
156
209
|
<div style={{ display: 'flex', gap: isMobile ? 2 : 4, background: 'rgba(255,255,255,0.08)', borderRadius: 8, padding: isMobile ? 2 : 3, flexWrap: isMobile ? 'wrap' : 'nowrap' }}>
|
|
157
210
|
<button
|
|
@@ -215,6 +268,16 @@ function HomeInner() {
|
|
|
215
268
|
>
|
|
216
269
|
Paths
|
|
217
270
|
</Link>}
|
|
271
|
+
{!IS_DEMO && !csvMode && <Link
|
|
272
|
+
href="/scores"
|
|
273
|
+
style={{
|
|
274
|
+
padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
|
|
275
|
+
background: 'rgba(255,255,255,0.06)', color: '#FFD700', textDecoration: 'none',
|
|
276
|
+
display: 'flex', alignItems: 'center', gap: 4,
|
|
277
|
+
}}
|
|
278
|
+
>
|
|
279
|
+
Scores
|
|
280
|
+
</Link>}
|
|
218
281
|
{!IS_DEMO && !csvMode && <Link
|
|
219
282
|
href="/queue"
|
|
220
283
|
style={{
|
|
@@ -315,29 +378,8 @@ function HomeInner() {
|
|
|
315
378
|
</div>
|
|
316
379
|
)}
|
|
317
380
|
</div>}
|
|
318
|
-
{/* Refresh button —
|
|
319
|
-
{!IS_DEMO && !csvMode && <
|
|
320
|
-
onClick={async () => {
|
|
321
|
-
try {
|
|
322
|
-
const status = await scraperStatus();
|
|
323
|
-
const blocked = notReadyMessage(status);
|
|
324
|
-
if (blocked) { alert(blocked); return; }
|
|
325
|
-
if (status.running) { alert('The scanner is already busy.'); return; }
|
|
326
|
-
await startScrape('refresh');
|
|
327
|
-
alert('Checking for new connections — watch it on the Scan page.');
|
|
328
|
-
} catch (e) {
|
|
329
|
-
alert(e.message);
|
|
330
|
-
}
|
|
331
|
-
}}
|
|
332
|
-
title="Refresh connections + bridges"
|
|
333
|
-
style={{
|
|
334
|
-
width: isMobile ? 28 : 32, height: isMobile ? 28 : 32, borderRadius: '50%', border: 'none', cursor: 'pointer',
|
|
335
|
-
background: 'rgba(255,255,255,0.06)', color: '#888', fontSize: isMobile ? 12 : 14,
|
|
336
|
-
display: isMobile ? 'none' : 'flex', alignItems: 'center', justifyContent: 'center', marginLeft: 8,
|
|
337
|
-
}}
|
|
338
|
-
>
|
|
339
|
-
↻
|
|
340
|
-
</button>}
|
|
381
|
+
{/* Refresh button — Check for new, once you say yes; notifies */}
|
|
382
|
+
{!IS_DEMO && !csvMode && <RefreshButton isMobile={isMobile} />}
|
|
341
383
|
{/* Profile icon — top right */}
|
|
342
384
|
<a href={IS_DEMO ? '/launch' : csvMode ? '/import' : '/profile'} style={{
|
|
343
385
|
width: isMobile ? 30 : 36, height: isMobile ? 30 : 36, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
@@ -356,8 +398,8 @@ function HomeInner() {
|
|
|
356
398
|
</a>
|
|
357
399
|
</div>
|
|
358
400
|
|
|
359
|
-
{/* Pending count badge — in header */}
|
|
360
|
-
{isDegreesMode &&
|
|
401
|
+
{/* Pending count badge — in header: everyone with a request out, once each (lib/requests-client.js) */}
|
|
402
|
+
{isDegreesMode && !csvMode && pendingCount > 0 && (
|
|
361
403
|
<Link href="/queue" style={{
|
|
362
404
|
padding: '5px 12px', borderRadius: 16, fontSize: 12, fontWeight: 600, textDecoration: 'none',
|
|
363
405
|
background: 'rgba(255,107,53,0.12)', color: '#FF6B35',
|
|
@@ -365,7 +407,7 @@ function HomeInner() {
|
|
|
365
407
|
display: 'inline-flex', alignItems: 'center', gap: 5,
|
|
366
408
|
}}>
|
|
367
409
|
<span style={{ fontSize: 10 }}>⏳</span>
|
|
368
|
-
{
|
|
410
|
+
{pendingCount} Pending
|
|
369
411
|
</Link>
|
|
370
412
|
)}
|
|
371
413
|
</header>
|
|
@@ -428,6 +470,14 @@ function HomeInner() {
|
|
|
428
470
|
selectedId: selected?.id ?? null,
|
|
429
471
|
tierColors: TIER_COLORS,
|
|
430
472
|
focusNodeRef,
|
|
473
|
+
// Every row, whatever the filters: rarity counts the ways in across
|
|
474
|
+
// all your scanned circles, and "already connected" needs everyone.
|
|
475
|
+
fullDegree1: degree1,
|
|
476
|
+
fullDegree2: degree2,
|
|
477
|
+
scanNotes,
|
|
478
|
+
canScan,
|
|
479
|
+
chainOpen,
|
|
480
|
+
onChainOpened: chainOpened,
|
|
431
481
|
};
|
|
432
482
|
return <View {...viewProps} />;
|
|
433
483
|
})()}
|
|
@@ -440,38 +490,20 @@ function HomeInner() {
|
|
|
440
490
|
mode={mode}
|
|
441
491
|
collapsed={sidebarCollapsed}
|
|
442
492
|
filter={filter}
|
|
443
|
-
pending={pending}
|
|
444
493
|
onToggle={() => setSidebarCollapsed(!sidebarCollapsed)}
|
|
445
494
|
onSelect={(node) => { setSelected(node || null); }}
|
|
446
495
|
onSwitchMode={(newMode) => { setMode(newMode); setFilter('all'); }}
|
|
447
496
|
onFocusNode={(nodeId) => { if (focusNodeRef.current) focusNodeRef.current(nodeId); }}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const url = person.profile_url;
|
|
452
|
-
await fetch('/api/outreach', {
|
|
453
|
-
method: 'POST',
|
|
454
|
-
headers: { 'Content-Type': 'application/json' },
|
|
455
|
-
body: JSON.stringify({ action: 'mark-sent', connectionId: id, profileUrl: url }),
|
|
456
|
-
});
|
|
457
|
-
setPending(prev => [...prev, person]);
|
|
458
|
-
}}
|
|
459
|
-
onUndoPending={async (person) => {
|
|
460
|
-
if (IS_DEMO) return;
|
|
461
|
-
const id = person.id;
|
|
462
|
-
await fetch('/api/outreach', {
|
|
463
|
-
method: 'POST',
|
|
464
|
-
headers: { 'Content-Type': 'application/json' },
|
|
465
|
-
body: JSON.stringify({ action: 'undo', connectionId: id }),
|
|
466
|
-
});
|
|
467
|
-
setPending(prev => prev.filter(p => p.id !== id));
|
|
468
|
-
}}
|
|
497
|
+
csvSource={IS_DEMO ? 'sample' : csvMode ? csvSource : null}
|
|
498
|
+
scanNotes={scanNotes}
|
|
499
|
+
canScan={canScan}
|
|
469
500
|
/>
|
|
501
|
+
{canScan && <NetworkRefresh onChange={reload} live={view.key === 'chain'} />}
|
|
470
502
|
|
|
471
|
-
{/*
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
phone it sat on top of the score column. */}
|
|
503
|
+
{/* Orbit, one tap from Bridge Chains — bottom right, Degrees mode only.
|
|
504
|
+
It used to say "Galaxy" and show Orbit: the Galaxy is a Network
|
|
505
|
+
Circle view, so asking for it here fell back to Orbit. Hidden over
|
|
506
|
+
Separation, where on a phone it sat on top of the score column. */}
|
|
475
507
|
{isDegreesMode && view.key !== 'separation' && (
|
|
476
508
|
<div style={{
|
|
477
509
|
position: 'absolute', bottom: 20, right: 20, zIndex: 20,
|
|
@@ -479,19 +511,19 @@ function HomeInner() {
|
|
|
479
511
|
background: 'rgba(0,0,0,0.7)', borderRadius: 20, padding: '6px 14px',
|
|
480
512
|
backdropFilter: 'blur(8px)', border: '1px solid rgba(255,255,255,0.08)',
|
|
481
513
|
}}>
|
|
482
|
-
<span style={{ fontSize: 10, color: '#666' }}
|
|
514
|
+
<span style={{ fontSize: 10, color: '#666' }}>{VIEWS.orbit.icon} {VIEWS.orbit.label}</span>
|
|
483
515
|
<div
|
|
484
|
-
onClick={() => setVisualMode(
|
|
516
|
+
onClick={() => setVisualMode(view.key === 'orbit' ? 'chain' : 'orbit')}
|
|
485
517
|
style={{
|
|
486
518
|
width: 32, height: 18, borderRadius: 9, cursor: 'pointer',
|
|
487
|
-
background:
|
|
519
|
+
background: view.key === 'orbit' ? '#FF6B35' : 'rgba(255,255,255,0.15)',
|
|
488
520
|
position: 'relative', transition: 'background 0.2s',
|
|
489
521
|
}}
|
|
490
522
|
>
|
|
491
523
|
<div style={{
|
|
492
524
|
width: 14, height: 14, borderRadius: '50%', background: '#fff',
|
|
493
525
|
position: 'absolute', top: 2,
|
|
494
|
-
left:
|
|
526
|
+
left: view.key === 'orbit' ? 16 : 2,
|
|
495
527
|
transition: 'left 0.2s',
|
|
496
528
|
}} />
|
|
497
529
|
</div>
|
|
@@ -501,3 +533,80 @@ function HomeInner() {
|
|
|
501
533
|
</div>
|
|
502
534
|
);
|
|
503
535
|
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* A cheap fingerprint of the network: who is in it, at which degree and in
|
|
539
|
+
* whose circle, and how they're marked. Equal fingerprints mean nothing a view
|
|
540
|
+
* draws has changed.
|
|
541
|
+
*/
|
|
542
|
+
function networkShape(d1, d2) {
|
|
543
|
+
let h = 0x811c9dc5;
|
|
544
|
+
const mix = (text) => {
|
|
545
|
+
for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 0x01000193);
|
|
546
|
+
};
|
|
547
|
+
for (const r of [...d1, ...d2]) {
|
|
548
|
+
mix(`${r.id}|${r.degree}|${r.source_connection_id ?? ''}|${r.unlocked_from_bridge_id ?? ''}|${r.tier}|${r.power_score}|${r.unlock_status ?? ''}|${r.profile_image_url ?? ''};`);
|
|
549
|
+
}
|
|
550
|
+
return `${d1.length}:${d2.length}:${h >>> 0}`;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Looks at the network again when a scan has changed it: once when any scan
|
|
554
|
+
// ends, and every 20 seconds while a circle is being scanned with Bridge Chains
|
|
555
|
+
// open, since the scanner saves every 10 pages and the circle fills in as it
|
|
556
|
+
// does. Its own component for the same reason as RefreshButton below.
|
|
557
|
+
function NetworkRefresh({ onChange, live }) {
|
|
558
|
+
const scan = useScanner();
|
|
559
|
+
const ended = scan.finished.find(CHANGES_NETWORK)?.startedAt ?? null;
|
|
560
|
+
const filling = live && scan.running && isCircleScan(scan);
|
|
561
|
+
useEffect(() => {
|
|
562
|
+
if (ended != null) onChange();
|
|
563
|
+
}, [ended, onChange]);
|
|
564
|
+
useEffect(() => {
|
|
565
|
+
if (!filling) return undefined;
|
|
566
|
+
const timer = setInterval(onChange, 20000);
|
|
567
|
+
return () => clearInterval(timer);
|
|
568
|
+
}, [filling, onChange]);
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Its own component so the scanner's answer, which changes every second or two
|
|
573
|
+
// while a scan runs, re-renders this button and not the whole map.
|
|
574
|
+
//
|
|
575
|
+
// It asks first, as a company scan does, and says what it will do and what it
|
|
576
|
+
// costs: one click on a small icon used to open a Chrome window on LinkedIn
|
|
577
|
+
// with no word of what it was about to do.
|
|
578
|
+
function RefreshButton({ isMobile }) {
|
|
579
|
+
const scan = useScanner();
|
|
580
|
+
const busy = busyReason(scan);
|
|
581
|
+
return (
|
|
582
|
+
<button
|
|
583
|
+
onClick={async () => {
|
|
584
|
+
try {
|
|
585
|
+
const blocked = notReadyMessage(await scraperStatus());
|
|
586
|
+
if (blocked) { alert(blocked); return; }
|
|
587
|
+
const ok = window.confirm(
|
|
588
|
+
'Check for new connections?\n\nThis opens a Chrome window on your LinkedIn connections ' +
|
|
589
|
+
'list and reads it from the newest, stopping once it reaches people already saved. It ' +
|
|
590
|
+
'usually takes under a minute. It reads your own list, not a search, so it doesn’t ' +
|
|
591
|
+
'use your search budget, but like any scan it is LinkedIn traffic from your account.',
|
|
592
|
+
);
|
|
593
|
+
if (!ok) return;
|
|
594
|
+
await beginScrape('refresh');
|
|
595
|
+
alert('Checking for new connections — watch it on the Scan page.');
|
|
596
|
+
} catch (e) {
|
|
597
|
+
alert(e.message);
|
|
598
|
+
}
|
|
599
|
+
}}
|
|
600
|
+
disabled={Boolean(busy)}
|
|
601
|
+
title={busy ? `${busy}. One scan at a time.` : 'Check for new connections'}
|
|
602
|
+
style={{
|
|
603
|
+
width: isMobile ? 28 : 32, height: isMobile ? 28 : 32, borderRadius: '50%', border: 'none',
|
|
604
|
+
cursor: busy ? 'not-allowed' : 'pointer', opacity: busy ? 0.4 : 1,
|
|
605
|
+
background: 'rgba(255,255,255,0.06)', color: '#888', fontSize: isMobile ? 12 : 14,
|
|
606
|
+
display: isMobile ? 'none' : 'flex', alignItems: 'center', justifyContent: 'center', marginLeft: 8,
|
|
607
|
+
}}
|
|
608
|
+
>
|
|
609
|
+
↻
|
|
610
|
+
</button>
|
|
611
|
+
);
|
|
612
|
+
}
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Suspense, useEffect, useState } from 'react';
|
|
4
|
-
import { useSearchParams } from 'next/navigation';
|
|
5
|
-
import { runScrape, scraperStatus, notReadyMessage } from '../../lib/scraper-client';
|
|
4
|
+
import { useRouter, useSearchParams } from 'next/navigation';
|
|
5
|
+
import { runScrape, scraperStatus, notReadyMessage, busyReason } from '../../lib/scraper-client';
|
|
6
6
|
import { loadNetwork } from '../../lib/network';
|
|
7
7
|
import { IS_DEMO } from '../../lib/demo';
|
|
8
8
|
import { hasCsvNetwork, loadCsvNetwork } from '../../lib/csv';
|
|
9
9
|
import OnboardingGate from '../components/OnboardingGate';
|
|
10
10
|
import PathsAnalyzer from '../components/PathsAnalyzer';
|
|
11
|
-
import CompanyScores from '../components/CompanyScores';
|
|
12
11
|
import { useUser } from '../components/UserProvider';
|
|
12
|
+
import useScanner from '../components/useScanner';
|
|
13
13
|
import Link from 'next/link';
|
|
14
14
|
import { companyOf, getSeniority } from '../../lib/companies';
|
|
15
|
+
import { localPhoto } from '../../lib/photos';
|
|
15
16
|
|
|
16
17
|
const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
17
|
-
const TABS = [['map', 'Map'], ['industries', 'Industries'], ['companies', 'Companies']
|
|
18
|
+
const TABS = [['map', 'Map'], ['industries', 'Industries'], ['companies', 'Companies']];
|
|
18
19
|
|
|
19
20
|
export default function PathsPage() {
|
|
20
21
|
// Suspense because PathsInner reads the address's ?tab= (useSearchParams),
|
|
@@ -32,11 +33,14 @@ function PathsInner() {
|
|
|
32
33
|
const [d2Data, setD2Data] = useState([]);
|
|
33
34
|
const [d3Data, setD3Data] = useState([]);
|
|
34
35
|
// Map and Industries are the analyzer; Companies is the list it grew from.
|
|
35
|
-
// A link can open a tab (/paths?tab=scores
|
|
36
|
+
// A link can open a tab (/paths?tab=companies). Company scores have a tab of
|
|
37
|
+
// their own now (app/scores): an old link to /paths?tab=scores goes there.
|
|
36
38
|
// The router's search params rather than window.location: on a click from
|
|
37
39
|
// another page the address bar only changes after this page has rendered.
|
|
38
40
|
const asked = useSearchParams().get('tab');
|
|
39
41
|
const [tab, setTab] = useState(() => (TABS.some(([k]) => k === asked) ? asked : 'map'));
|
|
42
|
+
const router = useRouter();
|
|
43
|
+
useEffect(() => { if (asked === 'scores') router.replace('/scores#companies'); }, [asked, router]);
|
|
40
44
|
// Bumped when company scores change, so the network reloads with new tiers.
|
|
41
45
|
const [reloadKey, setReloadKey] = useState(0);
|
|
42
46
|
|
|
@@ -92,6 +96,9 @@ function PathsInner() {
|
|
|
92
96
|
|
|
93
97
|
const [scanning, setScanning] = useState(false);
|
|
94
98
|
const [scanLog, setScanLog] = useState([]);
|
|
99
|
+
// Anything else the scanner runs (a profile card's scan, say) greys this out too.
|
|
100
|
+
const scan = useScanner();
|
|
101
|
+
const busy = !scanning && scan.running ? busyReason(scan) : null;
|
|
95
102
|
|
|
96
103
|
function enrichPeople(people) {
|
|
97
104
|
const d1Urls = new Set(d1Data.map(c => c.profile_url));
|
|
@@ -255,13 +262,14 @@ function PathsInner() {
|
|
|
255
262
|
<span style={{ fontSize: 11, color: '#888' }}>
|
|
256
263
|
{connectedCount}/{companyPeople.length} connected
|
|
257
264
|
</span>
|
|
258
|
-
<button onClick={scanFullCompany} disabled={scanning}
|
|
265
|
+
<button onClick={scanFullCompany} disabled={scanning || Boolean(busy)}
|
|
266
|
+
title={busy ? `${busy}. One scan at a time.` : undefined}
|
|
259
267
|
style={{
|
|
260
|
-
marginLeft: 'auto', padding: '5px 12px', borderRadius: 6, border: 'none', cursor: scanning ? 'not-allowed' : 'pointer',
|
|
261
|
-
background: scanning ? '#333' : 'rgba(0,255,136,0.15)', color: scanning ? '#555' : '#00ff88',
|
|
268
|
+
marginLeft: 'auto', padding: '5px 12px', borderRadius: 6, border: 'none', cursor: scanning || busy ? 'not-allowed' : 'pointer',
|
|
269
|
+
background: scanning || busy ? '#333' : 'rgba(0,255,136,0.15)', color: scanning || busy ? '#555' : '#00ff88',
|
|
262
270
|
fontSize: 11, fontWeight: 600,
|
|
263
271
|
}}>
|
|
264
|
-
{scanning ? 'Scanning...' : '+ Scan Full Company'}
|
|
272
|
+
{scanning ? 'Scanning...' : busy ? 'Scanner busy' : '+ Scan Full Company'}
|
|
265
273
|
</button>
|
|
266
274
|
<span
|
|
267
275
|
title="Not yet tested against live LinkedIn. Results may be incomplete."
|
|
@@ -275,9 +283,7 @@ function PathsInner() {
|
|
|
275
283
|
</div>
|
|
276
284
|
</header>
|
|
277
285
|
|
|
278
|
-
{!selectedCompany && tab
|
|
279
|
-
<CompanyScores onRescored={() => setReloadKey((k) => k + 1)} />
|
|
280
|
-
) : !selectedCompany && tab !== 'companies' ? (
|
|
286
|
+
{!selectedCompany && tab !== 'companies' ? (
|
|
281
287
|
<PathsAnalyzer d1={d1Data} d2={d2Data} d3={d3Data} tab={tab}
|
|
282
288
|
onOpenCompany={(co) => selectCompany({ ...co, sCount: co.S, aCount: co.A })} />
|
|
283
289
|
) : (
|
|
@@ -461,8 +467,8 @@ function PathPersonRow({ p, level, companyPeople, d1Data, selectedCompany }) {
|
|
|
461
467
|
}}>{p.connected ? '✓' : '🔒'}</div>
|
|
462
468
|
|
|
463
469
|
{/* Photo */}
|
|
464
|
-
{(p.profile_image_url || p.imageUrl) ? (
|
|
465
|
-
<img src={p.profile_image_url || p.imageUrl} alt="" style={{
|
|
470
|
+
{localPhoto(p.profile_image_url || p.imageUrl) ? (
|
|
471
|
+
<img src={localPhoto(p.profile_image_url || p.imageUrl)} alt="" style={{
|
|
466
472
|
width: 26, height: 26, borderRadius: '50%', objectFit: 'cover', flexShrink: 0,
|
|
467
473
|
}} onError={e => e.target.style.display = 'none'} />
|
|
468
474
|
) : (
|
|
@@ -33,7 +33,7 @@ function ProfileInner() {
|
|
|
33
33
|
const [notifications, setNotifications] = useState([]);
|
|
34
34
|
const [queueStats, setQueueStats] = useState({ total: 0, sent: 0, accepted: 0 });
|
|
35
35
|
const [mapping, setMapping] = useState({ degree1: [], degree2: [], skips: [] });
|
|
36
|
-
// "Your Sectors" is what you picked in
|
|
36
|
+
// "Your Sectors" is what you picked in Scores → Your sector. (users.sectors,
|
|
37
37
|
// which this card used to show, is never written by anything; the Sidebar
|
|
38
38
|
// and Outlink still read it as free text.) null while it loads; a load that
|
|
39
39
|
// fails says so rather than showing "none picked" (TRAPS §7).
|
|
@@ -376,8 +376,8 @@ function ProfileInner() {
|
|
|
376
376
|
}}>
|
|
377
377
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
|
378
378
|
<h3 style={{ fontSize: 14, fontWeight: 700, margin: 0 }}>Your Sectors</h3>
|
|
379
|
-
<Link href="/
|
|
380
|
-
{sectorFocus?.sectors?.length ? 'Change' : sectorFocus && !sectorFocus.failed ? 'Pick
|
|
379
|
+
<Link href="/scores#sector" style={{ fontSize: 11, color: '#3498DB', textDecoration: 'none', fontWeight: 600 }}>
|
|
380
|
+
{sectorFocus?.sectors?.length ? 'Change' : sectorFocus && !sectorFocus.failed ? 'Pick on Scores' : 'Scores'} →
|
|
381
381
|
</Link>
|
|
382
382
|
</div>
|
|
383
383
|
{!sectorFocus ? (
|