six-degrees 0.4.11 → 0.5.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/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +9 -9
- 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 +12 -12
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +12 -12
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- 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 +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 +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 +1 -1
- 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 +3 -2
- package/.next/standalone/.next/server/app/api/scraper/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/settings/route.js +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 +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 +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/social/crm/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/messages/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/route.js +3 -3
- package/.next/standalone/.next/server/app/api/social/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 +14 -14
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/import.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/import.segments/import/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- 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 +14 -14
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/launch.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/launch.segments/launch/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/paths.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/paths.segments/paths/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/profile.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/profile.segments/profile/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/queue.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/queue.segments/queue/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/scores/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/scores/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/scores.html +1 -1
- package/.next/standalone/.next/server/app/scores.rsc +14 -14
- package/.next/standalone/.next/server/app/scores.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/scores.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/scores.segments/scores/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +13 -13
- 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 +14 -14
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/setup.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/setup.segments/setup/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/social/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/social.html +1 -1
- package/.next/standalone/.next/server/app/social.rsc +14 -14
- package/.next/standalone/.next/server/app/social.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/social.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/social.segments/social/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/chunks/{[root-of-the-server]__0bat8nx._.js → [root-of-the-server]__13dx4sd._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__15wc9e-._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__20rwfdw._.js +8 -0
- package/.next/standalone/.next/server/chunks/_072jl0y._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1_fynia._.js +7 -7
- 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_0851sk0._.js +8 -0
- package/.next/standalone/.next/server/chunks/lib_1q7p2ro._.js +8 -0
- package/.next/standalone/.next/server/chunks/lib_1r6l86l._.js +8 -0
- package/.next/standalone/.next/server/chunks/lib_db-client_15le7cw.js +8 -8
- package/.next/standalone/.next/server/chunks/lib_rpc_04s8mzr.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_053z7as.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0b_-2te.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0dhbxdm.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0dxlk31.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0f8b-qc.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0pzie7_.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0r76fq_.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_0ut7pjp.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_15zyqaz.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_18zpjtq.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1a8xkiz.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1e_55xo.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1oxfd1_.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_rpc_1s0lod0.js +3 -0
- package/.next/standalone/.next/server/chunks/lib_scoring_1-e4ur0.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_01kacen._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_03l7vge._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{_1ap1tu_._.js → _0gtv9jb._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_0jdkqgi._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0xi-fbs._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0xmwmfv._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1-b9hj7._.js +9 -0
- package/.next/standalone/.next/server/chunks/ssr/_112ewem._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_16ab63q._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1fxm0-s._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_1ra1d3r._.js +9 -0
- package/.next/standalone/.next/server/chunks/ssr/app_141w189._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_components_ui_0pdmz2z.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/app_page_1fazqbf.js +22 -10
- package/.next/standalone/.next/server/chunks/ssr/app_paths_page_1ol90m7.js +2 -2
- 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 +6 -5
- 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 +19 -3
- package/.next/standalone/.next/server/chunks/ssr/app_social_page_0h7zxda.js +1 -2
- package/.next/standalone/.next/server/chunks/ssr/lib_0i8v-s3._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/lib_13rapwj._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/lib_galaxy-lab_108_lu-.js +1 -1
- 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/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- 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/03bziijw5srol.js +1 -0
- package/.next/standalone/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
- package/.next/standalone/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
- package/.next/standalone/.next/static/chunks/0g_fy2034bp0x.js +34 -0
- package/.next/standalone/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
- package/.next/standalone/.next/static/chunks/0w-byiwb2zax8.js +2 -0
- package/.next/standalone/.next/static/chunks/1g7jcovdejc9-.js +1 -0
- package/.next/standalone/.next/static/chunks/1i51yylxdzht1.js +1 -0
- package/.next/standalone/.next/static/chunks/1zf8swaasjf1e.js +43 -0
- package/.next/standalone/.next/static/chunks/2rqtpccruavf2.js +1 -0
- package/.next/standalone/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
- package/.next/standalone/.next/static/chunks/30x3oq_st7a5r.js +1 -0
- package/.next/standalone/.next/static/chunks/355ryio1b7sm1.js +1 -0
- package/.next/standalone/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
- package/.next/standalone/.next/static/chunks/3fci6qi39-1n5.js +1 -0
- package/.next/standalone/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
- package/.next/standalone/CHANGELOG.md +187 -0
- package/.next/standalone/app/api/ingest/route.js +40 -1
- package/.next/standalone/app/api/scraper/route.js +7 -1
- package/.next/standalone/app/api/settings/sector-preview/route.js +2 -1
- package/.next/standalone/app/components/AppTabs.js +59 -0
- package/.next/standalone/app/components/ChainView.js +303 -89
- package/.next/standalone/app/components/CirclePanel.js +162 -0
- package/.next/standalone/app/components/CompanyScores.js +2 -2
- package/.next/standalone/app/components/FieldStep.js +4 -4
- package/.next/standalone/app/components/FilterPanel.js +121 -183
- package/.next/standalone/app/components/ForceGraph.js +41 -3
- package/.next/standalone/app/components/GalaxyLab.js +26 -31
- package/.next/standalone/app/components/LegacyScoresCard.js +2 -2
- package/.next/standalone/app/components/MapControls.js +154 -0
- package/.next/standalone/app/components/NoteDetail.js +118 -0
- package/.next/standalone/app/components/PathsAnalyzer.js +147 -110
- package/.next/standalone/app/components/PeopleMap.js +308 -0
- package/.next/standalone/app/components/ScanRadar.js +129 -0
- package/.next/standalone/app/components/ScanStatusBar.js +71 -27
- package/.next/standalone/app/components/SeparationView.js +626 -118
- package/.next/standalone/app/components/Sidebar.js +28 -2
- package/.next/standalone/app/components/settings/NetworkHealthSection.js +1 -1
- package/.next/standalone/app/components/settings/SectorSection.js +2 -2
- package/.next/standalone/app/components/settings/TierSection.js +1 -1
- package/.next/standalone/app/components/settings/TitleSection.js +149 -0
- package/.next/standalone/app/components/views.js +15 -14
- package/.next/standalone/app/page.js +89 -156
- package/.next/standalone/app/paths/page.js +39 -27
- package/.next/standalone/app/profile/page.js +33 -5
- package/.next/standalone/app/queue/page.js +56 -34
- package/.next/standalone/app/scores/page.js +16 -66
- package/.next/standalone/app/settings/page.js +27 -13
- package/.next/standalone/app/setup/page.js +298 -205
- package/.next/standalone/app/social/SocialHub.js +374 -0
- package/.next/standalone/app/social/page.js +10 -362
- package/.next/standalone/lib/chain-layout.js +105 -35
- package/.next/standalone/lib/degrees.js +0 -6
- package/.next/standalone/lib/dot-rings.js +25 -0
- package/.next/standalone/lib/galaxy-lab.js +65 -6
- package/.next/standalone/lib/ingest.js +2 -0
- package/.next/standalone/lib/island.js +40 -0
- package/.next/standalone/lib/linkedin-limits.js +6 -2
- package/.next/standalone/lib/map-heat.js +20 -0
- package/.next/standalone/lib/network-health.js +1 -1
- package/.next/standalone/lib/notifications.js +121 -0
- package/.next/standalone/lib/people-map.js +82 -0
- package/.next/standalone/lib/rarity.js +45 -3
- package/.next/standalone/lib/reach.js +7 -6
- package/.next/standalone/lib/rpc.js +15 -4
- package/.next/standalone/lib/scan-pace.js +46 -0
- package/.next/standalone/lib/scoring.js +39 -8
- package/.next/standalone/lib/scraper-client.js +19 -6
- package/.next/standalone/lib/sector-focus.js +4 -4
- package/.next/standalone/lib/separation.js +137 -10
- package/.next/standalone/lib/settings-effects.js +20 -11
- package/.next/standalone/lib/settings.js +3 -0
- package/.next/standalone/lib/tier-grid.js +99 -0
- package/.next/standalone/lib/title-ranking.js +83 -0
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/scrape.py +51 -13
- package/.next/static/chunks/03bziijw5srol.js +1 -0
- package/.next/static/chunks/0bgq8wmopl_ig.js +7 -0
- package/.next/static/chunks/0ezus6p2eb-vg.js +1 -0
- package/.next/static/chunks/0g_fy2034bp0x.js +34 -0
- package/.next/static/chunks/0q5dru4q6gxl5.js +22 -0
- package/.next/static/chunks/0w-byiwb2zax8.js +2 -0
- package/.next/static/chunks/1g7jcovdejc9-.js +1 -0
- package/.next/static/chunks/1i51yylxdzht1.js +1 -0
- package/.next/static/chunks/1zf8swaasjf1e.js +43 -0
- package/.next/static/chunks/2rqtpccruavf2.js +1 -0
- package/.next/static/chunks/2wgz9ietkqqe6.js +1 -0
- package/.next/static/chunks/30x3oq_st7a5r.js +1 -0
- package/.next/static/chunks/355ryio1b7sm1.js +1 -0
- package/.next/static/chunks/3ap5hgma7n0o7.js +1 -0
- package/.next/static/chunks/3fci6qi39-1n5.js +1 -0
- package/.next/static/chunks/{24tvdun1cb3g1.js → 3sowbvbaj1s6n.js} +1 -1
- package/CHANGELOG.md +187 -0
- package/lib/chain-layout.js +105 -35
- package/lib/degrees.js +0 -6
- package/lib/dot-rings.js +25 -0
- package/lib/galaxy-lab.js +65 -6
- package/lib/ingest.js +2 -0
- package/lib/island.js +40 -0
- package/lib/linkedin-limits.js +6 -2
- package/lib/map-heat.js +20 -0
- package/lib/network-health.js +1 -1
- package/lib/notifications.js +121 -0
- package/lib/people-map.js +82 -0
- package/lib/rarity.js +45 -3
- package/lib/reach.js +7 -6
- package/lib/rpc.js +15 -4
- package/lib/scan-pace.js +46 -0
- package/lib/scoring.js +39 -8
- package/lib/scraper-client.js +19 -6
- package/lib/sector-focus.js +4 -4
- package/lib/separation.js +137 -10
- package/lib/settings-effects.js +20 -11
- package/lib/settings.js +3 -0
- package/lib/tier-grid.js +99 -0
- package/lib/title-ranking.js +83 -0
- package/package.json +1 -1
- package/scripts/scrape.py +51 -13
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17hjtzl._.js +0 -8
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__20tmem7._.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_03i8xef.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_05gt5ya.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_08fc3rp.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_08r2t_q.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0btpv1t.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +0 -8
- package/.next/standalone/.next/server/chunks/lib_rpc_0cm2ev3.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0iovswz.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0is8flp.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0v8ajtd.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0xgmvvt.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_144mzbf.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1bggi0n.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1dkcu9f.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_1o0jb6w.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0-85h6g._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_08cfj2b._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0c_y09g._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0j4-lc3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0l8cxmo._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0xkur4p._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_14iv_zy._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_15x6q32._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_212xw8q._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_components_19c2d9c._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/app_scores_page_09wepvv.js +0 -3
- package/.next/standalone/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
- package/.next/standalone/.next/static/chunks/15sjx07xhpn6p.js +0 -1
- package/.next/standalone/.next/static/chunks/160wjb73ddwqd.js +0 -6
- package/.next/standalone/.next/static/chunks/1a74dky22dxd6.js +0 -1
- package/.next/standalone/.next/static/chunks/1c_n0973t07l0.js +0 -2
- package/.next/standalone/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
- package/.next/standalone/.next/static/chunks/236f0jtp68j_h.js +0 -1
- package/.next/standalone/.next/static/chunks/297e1z34yi--z.js +0 -1
- package/.next/standalone/.next/static/chunks/2_ak6qjxb6oa5.js +0 -1
- package/.next/standalone/.next/static/chunks/2kfhc58df2xyo.js +0 -1
- package/.next/standalone/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
- package/.next/standalone/.next/static/chunks/3ui4d-uqkqj5d.js +0 -2
- package/.next/standalone/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
- package/.next/standalone/app/components/OrbitGraph.js +0 -737
- package/.next/static/chunks/0a5iadnmt9u9k.js +0 -1
- package/.next/static/chunks/15sjx07xhpn6p.js +0 -1
- package/.next/static/chunks/160wjb73ddwqd.js +0 -6
- package/.next/static/chunks/1a74dky22dxd6.js +0 -1
- package/.next/static/chunks/1c_n0973t07l0.js +0 -2
- package/.next/static/chunks/1e1n0kkz-psdb.js +0 -1
- package/.next/static/chunks/236f0jtp68j_h.js +0 -1
- package/.next/static/chunks/297e1z34yi--z.js +0 -1
- package/.next/static/chunks/2_ak6qjxb6oa5.js +0 -1
- package/.next/static/chunks/2kfhc58df2xyo.js +0 -1
- package/.next/static/chunks/39hr8d-b2ibtw.js +0 -31
- package/.next/static/chunks/3ui4d-uqkqj5d.js +0 -2
- package/.next/static/chunks/3zw8bv7-mc4n6.js +0 -33
- /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
- /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_buildManifest.js +0 -0
- /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{jqE2D3zWqSGz5su948Azf → I-cj79yE2IZ8taX2dZGp3}/_ssgManifest.js +0 -0
|
@@ -7,17 +7,21 @@ import useScanner from './components/useScanner';
|
|
|
7
7
|
import useRequests from './components/useRequests';
|
|
8
8
|
import { requestCount } from '../lib/requests-client';
|
|
9
9
|
import { loadNetwork } from '../lib/network';
|
|
10
|
-
import {
|
|
10
|
+
import { resolveView } from './components/views';
|
|
11
11
|
import AutoScanButton from './components/AutoScanButton';
|
|
12
|
-
import { peopleByDegree
|
|
12
|
+
import { peopleByDegree } from '../lib/degrees';
|
|
13
|
+
import { NETWORK_GRID, DEGREES_GRID, shows, gridCounts } from '../lib/tier-grid';
|
|
13
14
|
import Sidebar from './components/Sidebar';
|
|
14
15
|
import FilterPanel from './components/FilterPanel';
|
|
16
|
+
import { viewsForMode } from './components/views';
|
|
17
|
+
import { setNotchTabs } from '../lib/island';
|
|
15
18
|
import OnboardingGate from './components/OnboardingGate';
|
|
16
19
|
import EmptyState from './components/EmptyState';
|
|
17
20
|
import { useUser } from './components/UserProvider';
|
|
18
21
|
import { IS_DEMO, loadDemoNetwork } from '../lib/demo';
|
|
19
22
|
import { hasCsvNetwork, loadCsvNetwork, clearCsvNetwork } from '../lib/csv';
|
|
20
23
|
import Link from 'next/link';
|
|
24
|
+
import AppTabs from './components/AppTabs';
|
|
21
25
|
|
|
22
26
|
// One shared empty list, so "no 2nd-degree data" is the same value every render.
|
|
23
27
|
const NO_DEGREE2 = [];
|
|
@@ -72,27 +76,33 @@ function HomeInner() {
|
|
|
72
76
|
const [degree2, setDegree2] = useState([]);
|
|
73
77
|
// People found only by company scans: 3rd degree in Network Circle's filter.
|
|
74
78
|
const [degree3, setDegree3] = useState([]);
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
const [
|
|
79
|
+
// The Filters panel's grid, one for each tab: which tiers show, at which
|
|
80
|
+
// degrees (lib/tier-grid.js). Network Circle starts with your connections
|
|
81
|
+
// alone; Degrees with everything it has.
|
|
82
|
+
const [grids, setGrids] = useState({ network: NETWORK_GRID, degrees: DEGREES_GRID });
|
|
79
83
|
const [selected, setSelected] = useState(null);
|
|
80
84
|
const focusNodeRef = useRef(null);
|
|
81
|
-
const [filter, setFilter] = useState('all');
|
|
82
85
|
// A link to someone's circle in Bridge Chains, /?chain=<id> (the Scan page's
|
|
83
86
|
// "watch it fill in"), opens Degrees on it. The router's search params rather
|
|
84
87
|
// than window.location: on a click from another page the address bar only
|
|
85
88
|
// changes after this page has rendered, and the map opened on the Galaxy.
|
|
86
|
-
const
|
|
89
|
+
const params = useSearchParams();
|
|
90
|
+
const linkedChain = params.get('chain');
|
|
91
|
+
// Another page's tab row links back here with the tab named: /?mode=separation (app/components/AppTabs.js).
|
|
92
|
+
const linkedMode = ['network', 'degrees', 'separation'].includes(params.get('mode')) ? params.get('mode') : null;
|
|
87
93
|
const [chainOpen, setChainOpen] = useState(linkedChain);
|
|
88
|
-
|
|
94
|
+
// Whose circle is open in Bridge Chains right now: the right panel follows it.
|
|
95
|
+
const [chainIn, setChainIn] = useState(null);
|
|
96
|
+
const [mode, setMode] = useState(() => (chainOpen ? 'degrees' : linkedMode || 'network'));
|
|
89
97
|
const [loading, setLoading] = useState(true);
|
|
90
98
|
const [stats, setStats] = useState(null);
|
|
91
99
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
|
|
92
100
|
const [filterPanelCollapsed, setFilterPanelCollapsed] = useState(true);
|
|
93
|
-
const [visualMode, setVisualMode] = useState(() => (chainOpen ? 'chain' : 'galaxy'));
|
|
101
|
+
const [visualMode, setVisualMode] = useState(() => (chainOpen ? 'chain' : linkedMode === 'degrees' ? 'chain' : linkedMode === 'separation' ? 'separation' : 'galaxy'));
|
|
94
102
|
const [notifications, setNotifications] = useState([]);
|
|
95
103
|
const [showNotifs, setShowNotifs] = useState(false);
|
|
104
|
+
// A notification opened in the right panel (app/components/NoteDetail.js).
|
|
105
|
+
const [openNote, setOpenNote] = useState(null);
|
|
96
106
|
const [csvMode, setCsvMode] = useState(false);
|
|
97
107
|
const [csvSource, setCsvSource] = useState('csv');
|
|
98
108
|
// What the scanner noted: hidden lists, and lists read with nothing new in
|
|
@@ -158,7 +168,7 @@ function HomeInner() {
|
|
|
158
168
|
const [separationPreset, setSeparationPreset] = useState(null);
|
|
159
169
|
const showInSeparation = useCallback((preset) => {
|
|
160
170
|
setSeparationPreset({ ...preset, id: `${preset.query}|${preset.tier || ''}|${preset.rarity || ''}|${Math.random()}` });
|
|
161
|
-
setMode('
|
|
171
|
+
setMode('separation');
|
|
162
172
|
setVisualMode('separation');
|
|
163
173
|
}, []);
|
|
164
174
|
// A card's Insights → that person's circle in Bridge Chains.
|
|
@@ -172,7 +182,8 @@ function HomeInner() {
|
|
|
172
182
|
try { window.history.replaceState(null, '', window.location.pathname); } catch { /* the link stays */ }
|
|
173
183
|
}, []);
|
|
174
184
|
|
|
175
|
-
|
|
185
|
+
// Separation is its own tab, built on the same rows as Degrees: your bridges and their circles.
|
|
186
|
+
const isDegreesMode = mode === 'degrees' || mode === 'separation';
|
|
176
187
|
const connections = degree1;
|
|
177
188
|
|
|
178
189
|
// What the views draw. Memoised because the graph views rebuild their whole
|
|
@@ -182,14 +193,13 @@ function HomeInner() {
|
|
|
182
193
|
// Network Circle draws the degrees picked in the Filter panel, each person
|
|
183
194
|
// once, at the nearest degree they're found (lib/degrees.js).
|
|
184
195
|
const byDegree = useMemo(() => peopleByDegree(degree1, degree2, degree3), [degree1, degree2, degree3]);
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
);
|
|
196
|
+
const grid = isDegreesMode ? grids.degrees : grids.network;
|
|
197
|
+
// Degrees draws your connections (the bridges among them) and their circles;
|
|
198
|
+
// Network Circle draws whoever the grid shows, at any degree.
|
|
189
199
|
const filtered = useMemo(() => {
|
|
190
|
-
if (
|
|
191
|
-
return
|
|
192
|
-
}, [isDegreesMode,
|
|
200
|
+
if (isDegreesMode) return connections.filter((c) => shows(grid, c.tier, 1));
|
|
201
|
+
return [1, 2, 3].flatMap((d) => (byDegree[d] || []).filter((c) => shows(grid, c.tier, d)));
|
|
202
|
+
}, [isDegreesMode, connections, byDegree, grid]);
|
|
193
203
|
// Lookups built once per load. Every click re-renders this component, and the
|
|
194
204
|
// three places below used to scan one list inside another — degree1.find per
|
|
195
205
|
// 2nd-degree row, degree2.some per connection — about 16M comparisons, twice
|
|
@@ -199,21 +209,39 @@ function HomeInner() {
|
|
|
199
209
|
() => new Set(degree2.map(d => d.source_connection_id).filter(Boolean)),
|
|
200
210
|
[degree2],
|
|
201
211
|
);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
degree1.forEach(c => { if (bridgeIds.has(c.id)) counts[c.tier] = (counts[c.tier] || 0) + 1; });
|
|
205
|
-
return counts;
|
|
206
|
-
}, [degree1, bridgeIds]);
|
|
212
|
+
// A 2nd-degree row shows when its own tier does at 2nd degree, and the bridge
|
|
213
|
+
// it came through shows at 1st.
|
|
207
214
|
const filteredD2 = useMemo(() => {
|
|
208
215
|
if (!isDegreesMode) return NO_DEGREE2;
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
216
|
+
return degree2.filter((c) => shows(grid, c.tier, 2) && shows(grid, d1ById.get(c.source_connection_id)?.tier, 1));
|
|
217
|
+
}, [isDegreesMode, grid, d1ById, degree2]);
|
|
218
|
+
// How many people stand behind each dot of the grid. In Degrees, 1st degree
|
|
219
|
+
// counts the bridges, since those are the connections it draws.
|
|
220
|
+
const panelCounts = useMemo(() => gridCounts(isDegreesMode
|
|
221
|
+
? { 1: degree1.filter((c) => bridgeIds.has(c.id)), 2: byDegree[2] }
|
|
222
|
+
: byDegree), [isDegreesMode, degree1, bridgeIds, byDegree]);
|
|
212
223
|
const selectHandler = useCallback((node) => {
|
|
213
224
|
setSelected(node);
|
|
214
|
-
if (node) setSidebarCollapsed(false);
|
|
225
|
+
if (node) { setSidebarCollapsed(false); setOpenNote(null); }
|
|
215
226
|
}, []);
|
|
216
227
|
|
|
228
|
+
// Resolved once: the renderer below and the notch's buttons both need it.
|
|
229
|
+
// (Above the loading screen's early return, because the effect is a hook.)
|
|
230
|
+
const view = resolveView(visualMode, mode);
|
|
231
|
+
|
|
232
|
+
// This tab's views live in the notch under the header (app/components/ScanStatusBar.js);
|
|
233
|
+
// the Filters panel only filters.
|
|
234
|
+
const hasNetwork = degree1.length > 0;
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
if (!hasNetwork) return undefined;
|
|
237
|
+
setNotchTabs({
|
|
238
|
+
items: viewsForMode(mode).map((v) => ({ key: v.key, label: v.label, icon: v.icon, title: v.desc })),
|
|
239
|
+
current: view.key,
|
|
240
|
+
onPick: setVisualMode,
|
|
241
|
+
});
|
|
242
|
+
return () => setNotchTabs(null);
|
|
243
|
+
}, [mode, view.key, hasNetwork]);
|
|
244
|
+
|
|
217
245
|
if (loading) {
|
|
218
246
|
return (
|
|
219
247
|
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: '#0a0a1a', color: '#fff' }}>
|
|
@@ -223,9 +251,6 @@ function HomeInner() {
|
|
|
223
251
|
);
|
|
224
252
|
}
|
|
225
253
|
|
|
226
|
-
// Resolved once, because two places care: the renderer below, and the Orbit
|
|
227
|
-
// toggle, which hides over Separation.
|
|
228
|
-
const view = resolveView(visualMode, mode);
|
|
229
254
|
// Scanning and the Scan page belong to your own network, not the sample or a CSV.
|
|
230
255
|
const canScan = !IS_DEMO && !csvMode;
|
|
231
256
|
|
|
@@ -236,28 +261,13 @@ function HomeInner() {
|
|
|
236
261
|
<h1 style={{ fontSize: isMobile ? 16 : 28, fontWeight: 700, margin: 0, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
|
|
237
262
|
Six Degrees
|
|
238
263
|
</h1>
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}}
|
|
247
|
-
>
|
|
248
|
-
{isMobile ? 'Circle' : 'Network Circle'}
|
|
249
|
-
</button>
|
|
250
|
-
<button
|
|
251
|
-
onClick={() => { setMode('degrees'); setSelected(null); setFilter('all'); setVisualMode('chain'); }}
|
|
252
|
-
style={{
|
|
253
|
-
padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600, cursor: 'pointer',
|
|
254
|
-
background: mode === 'degrees' ? 'linear-gradient(135deg, #FFD700, #FF6B35)' : 'rgba(255,255,255,0.12)',
|
|
255
|
-
color: mode === 'degrees' ? '#000' : '#fff',
|
|
256
|
-
}}
|
|
257
|
-
>
|
|
258
|
-
Degrees
|
|
259
|
-
</button>
|
|
260
|
-
|
|
264
|
+
<AppTabs
|
|
265
|
+
active={mode}
|
|
266
|
+
isMobile={isMobile}
|
|
267
|
+
csvMode={csvMode}
|
|
268
|
+
onMode={(m) => { setMode(m); setSelected(null); setVisualMode(m === 'network' ? 'galaxy' : m === 'degrees' ? 'chain' : 'separation'); }}
|
|
269
|
+
after={!IS_DEMO && !csvMode ? <AutoScanButton isMobile={isMobile} /> : null}
|
|
270
|
+
>
|
|
261
271
|
{/* Which network you are looking at, and the way back out of it.
|
|
262
272
|
Loading the sample used to be a one-way door: it lives in
|
|
263
273
|
sessionStorage, and nothing in the UI cleared it. */}
|
|
@@ -288,63 +298,7 @@ function HomeInner() {
|
|
|
288
298
|
</button>
|
|
289
299
|
</div>
|
|
290
300
|
)}
|
|
291
|
-
|
|
292
|
-
href="/paths"
|
|
293
|
-
style={{
|
|
294
|
-
padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
|
|
295
|
-
background: 'rgba(255,255,255,0.06)', color: '#00ff88', textDecoration: 'none',
|
|
296
|
-
display: 'flex', alignItems: 'center', gap: 4,
|
|
297
|
-
}}
|
|
298
|
-
>
|
|
299
|
-
Paths
|
|
300
|
-
</Link>}
|
|
301
|
-
{!IS_DEMO && <Link
|
|
302
|
-
href="/social"
|
|
303
|
-
title="Experimental: what your own LinkedIn data says about your relationships"
|
|
304
|
-
style={{
|
|
305
|
-
padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
|
|
306
|
-
background: 'rgba(255,255,255,0.06)', color: '#FFD700', textDecoration: 'none',
|
|
307
|
-
display: 'flex', alignItems: 'center', gap: 4,
|
|
308
|
-
}}
|
|
309
|
-
>
|
|
310
|
-
Social<span style={{ fontSize: 9, opacity: 0.7, marginLeft: 2 }}>beta</span>
|
|
311
|
-
</Link>}
|
|
312
|
-
{!IS_DEMO && !csvMode && <Link
|
|
313
|
-
href="/scores"
|
|
314
|
-
style={{
|
|
315
|
-
padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
|
|
316
|
-
background: 'rgba(255,255,255,0.06)', color: '#FFD700', textDecoration: 'none',
|
|
317
|
-
display: 'flex', alignItems: 'center', gap: 4,
|
|
318
|
-
}}
|
|
319
|
-
>
|
|
320
|
-
Scores
|
|
321
|
-
</Link>}
|
|
322
|
-
{!IS_DEMO && !csvMode && <Link
|
|
323
|
-
href="/queue"
|
|
324
|
-
style={{
|
|
325
|
-
padding: isMobile ? '6px 10px' : '8px 16px', borderRadius: 6, border: 'none', fontSize: isMobile ? 11 : 13, fontWeight: 600,
|
|
326
|
-
background: 'rgba(255,255,255,0.06)', color: '#FF6B35', textDecoration: 'none',
|
|
327
|
-
display: 'flex', alignItems: 'center', gap: 4,
|
|
328
|
-
}}
|
|
329
|
-
>
|
|
330
|
-
Outlink
|
|
331
|
-
</Link>}
|
|
332
|
-
{/* Named "Scan" everywhere — nav, page title, README and docs. It is
|
|
333
|
-
also the only route to 2nd-degree data, so it stays visible on
|
|
334
|
-
mobile rather than being the one thing a phone user cannot find. */}
|
|
335
|
-
{!IS_DEMO && !csvMode && <Link
|
|
336
|
-
href="/setup"
|
|
337
|
-
style={{
|
|
338
|
-
padding: isMobile ? '8px 12px' : '8px 16px', borderRadius: 6, border: 'none',
|
|
339
|
-
fontSize: 13, fontWeight: 600,
|
|
340
|
-
background: 'rgba(255,255,255,0.06)', color: '#666', textDecoration: 'none',
|
|
341
|
-
display: 'flex', alignItems: 'center', gap: 4,
|
|
342
|
-
}}
|
|
343
|
-
>
|
|
344
|
-
Scan
|
|
345
|
-
</Link>}
|
|
346
|
-
{!IS_DEMO && !csvMode && <AutoScanButton isMobile={isMobile} />}
|
|
347
|
-
</div>
|
|
301
|
+
</AppTabs>
|
|
348
302
|
{/* Settings — also in CSV/sample mode and on phones, so updates stay reachable */}
|
|
349
303
|
{!IS_DEMO && <Link
|
|
350
304
|
href="/settings"
|
|
@@ -402,14 +356,26 @@ function HomeInner() {
|
|
|
402
356
|
{notifications.length === 0 ? (
|
|
403
357
|
<div style={{ padding: 20, textAlign: 'center', color: '#555', fontSize: 12 }}>No notifications yet</div>
|
|
404
358
|
) : notifications.slice(0, 20).map(n => (
|
|
405
|
-
<div key={n.id}
|
|
406
|
-
|
|
359
|
+
<div key={n.id} role="button" tabIndex={0}
|
|
360
|
+
onClick={() => {
|
|
361
|
+
// Open it in the right panel, and count it as read.
|
|
362
|
+
if (!n.seen) {
|
|
363
|
+
fetch('/api/notifications', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'mark-seen', id: n.id }) }).catch(() => {});
|
|
364
|
+
setNotifications(prev => prev.map(x => (x.id === n.id ? { ...x, seen: true } : x)));
|
|
365
|
+
}
|
|
366
|
+
setOpenNote(n); setShowNotifs(false); setSidebarCollapsed(false);
|
|
367
|
+
}}
|
|
368
|
+
onKeyDown={(e) => { if (e.key === 'Enter') e.currentTarget.click(); }}
|
|
369
|
+
onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(255,255,255,0.06)'; }}
|
|
370
|
+
onMouseLeave={(e) => { e.currentTarget.style.background = n.seen ? 'transparent' : 'rgba(255,215,0,0.04)'; }}
|
|
371
|
+
style={{
|
|
372
|
+
padding: '10px 16px', borderBottom: '1px solid rgba(255,255,255,0.04)', cursor: 'pointer',
|
|
407
373
|
background: n.seen ? 'transparent' : 'rgba(255,215,0,0.04)',
|
|
408
374
|
}}>
|
|
409
375
|
<div style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
|
|
410
376
|
<span style={{ fontSize: 16 }}>{n.icon || '📌'}</span>
|
|
411
377
|
<div style={{ flex: 1 }}>
|
|
412
|
-
<div style={{ fontSize: 12, fontWeight: 600, color: n.seen ? '#888' : '#fff' }}>{n.title}
|
|
378
|
+
<div style={{ fontSize: 12, fontWeight: 600, color: n.seen ? '#888' : '#fff' }}>{n.title}<span style={{ color: '#556', marginLeft: 6 }}>›</span></div>
|
|
413
379
|
{n.message && <div style={{ fontSize: 10, color: '#666', marginTop: 2 }}>{n.message}</div>}
|
|
414
380
|
<div style={{ fontSize: 9, color: '#444', marginTop: 3 }}>{new Date(n.created_at).toLocaleDateString()}</div>
|
|
415
381
|
</div>
|
|
@@ -458,18 +424,11 @@ function HomeInner() {
|
|
|
458
424
|
<FilterPanel
|
|
459
425
|
collapsed={filterPanelCollapsed}
|
|
460
426
|
onToggle={() => setFilterPanelCollapsed(!filterPanelCollapsed)}
|
|
461
|
-
mode={
|
|
462
|
-
filter={filter}
|
|
463
|
-
onFilterChange={setFilter}
|
|
427
|
+
mode={isDegreesMode ? 'degrees' : 'network'}
|
|
464
428
|
visualMode={view.key}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
onDegreesChange={setDegrees}
|
|
469
|
-
hiddenTiers={hiddenTiers}
|
|
470
|
-
onHiddenTiersChange={setHiddenTiers}
|
|
471
|
-
degreeCounts={{ 1: byDegree[1].length, 2: byDegree[2].length, 3: byDegree[3].length }}
|
|
472
|
-
bridgeTierCounts={bridgeTierCounts}
|
|
429
|
+
grid={grid}
|
|
430
|
+
gridCounts={panelCounts}
|
|
431
|
+
onGridChange={(next) => setGrids((g) => ({ ...g, [isDegreesMode ? 'degrees' : 'network']: next }))}
|
|
473
432
|
/>
|
|
474
433
|
{/* Visualization — switches based on visualMode */}
|
|
475
434
|
{(() => {
|
|
@@ -525,6 +484,7 @@ function HomeInner() {
|
|
|
525
484
|
canScan,
|
|
526
485
|
chainOpen,
|
|
527
486
|
onChainOpened: chainOpened,
|
|
487
|
+
onCircle: setChainIn,
|
|
528
488
|
preset: separationPreset,
|
|
529
489
|
};
|
|
530
490
|
return <View {...viewProps} />;
|
|
@@ -535,12 +495,14 @@ function HomeInner() {
|
|
|
535
495
|
tierColors={TIER_COLORS}
|
|
536
496
|
connections={degree1}
|
|
537
497
|
degree2={degree2}
|
|
538
|
-
mode={
|
|
498
|
+
mode={isDegreesMode ? 'degrees' : 'network'}
|
|
539
499
|
collapsed={sidebarCollapsed}
|
|
540
|
-
filter={filter}
|
|
541
500
|
onToggle={() => setSidebarCollapsed(!sidebarCollapsed)}
|
|
542
501
|
onSelect={(node) => { setSelected(node || null); }}
|
|
543
|
-
onSwitchMode={(newMode) => { setMode(newMode);
|
|
502
|
+
onSwitchMode={(newMode) => { setMode(newMode); }}
|
|
503
|
+
note={openNote}
|
|
504
|
+
onCloseNote={() => setOpenNote(null)}
|
|
505
|
+
circle={view.key === 'chain' ? chainIn : null}
|
|
544
506
|
onOpenCircle={openCircle}
|
|
545
507
|
onShowInSeparation={showInSeparation}
|
|
546
508
|
onFocusNode={(nodeId) => { if (focusNodeRef.current) focusNodeRef.current(nodeId); }}
|
|
@@ -550,35 +512,6 @@ function HomeInner() {
|
|
|
550
512
|
/>
|
|
551
513
|
{canScan && <NetworkRefresh onChange={reload} live={view.key === 'chain'} />}
|
|
552
514
|
|
|
553
|
-
{/* Orbit, one tap from Bridge Chains — bottom right, Degrees mode only.
|
|
554
|
-
It used to say "Galaxy" and show Orbit: the Galaxy is a Network
|
|
555
|
-
Circle view, so asking for it here fell back to Orbit. Hidden over
|
|
556
|
-
Separation, where on a phone it sat on top of the score column. */}
|
|
557
|
-
{isDegreesMode && view.key !== 'separation' && (
|
|
558
|
-
<div style={{
|
|
559
|
-
position: 'absolute', bottom: 20, right: 20, zIndex: 20,
|
|
560
|
-
display: 'flex', alignItems: 'center', gap: 8,
|
|
561
|
-
background: 'rgba(0,0,0,0.7)', borderRadius: 20, padding: '6px 14px',
|
|
562
|
-
backdropFilter: 'blur(8px)', border: '1px solid rgba(255,255,255,0.08)',
|
|
563
|
-
}}>
|
|
564
|
-
<span style={{ fontSize: 10, color: '#666' }}>{VIEWS.orbit.icon} {VIEWS.orbit.label}</span>
|
|
565
|
-
<div
|
|
566
|
-
onClick={() => setVisualMode(view.key === 'orbit' ? 'chain' : 'orbit')}
|
|
567
|
-
style={{
|
|
568
|
-
width: 32, height: 18, borderRadius: 9, cursor: 'pointer',
|
|
569
|
-
background: view.key === 'orbit' ? '#FF6B35' : 'rgba(255,255,255,0.15)',
|
|
570
|
-
position: 'relative', transition: 'background 0.2s',
|
|
571
|
-
}}
|
|
572
|
-
>
|
|
573
|
-
<div style={{
|
|
574
|
-
width: 14, height: 14, borderRadius: '50%', background: '#fff',
|
|
575
|
-
position: 'absolute', top: 2,
|
|
576
|
-
left: view.key === 'orbit' ? 16 : 2,
|
|
577
|
-
transition: 'left 0.2s',
|
|
578
|
-
}} />
|
|
579
|
-
</div>
|
|
580
|
-
</div>
|
|
581
|
-
)}
|
|
582
515
|
</div>
|
|
583
516
|
</div>
|
|
584
517
|
);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { Suspense, useEffect, useState } from 'react';
|
|
4
|
+
import AppTabs from '../components/AppTabs';
|
|
5
|
+
import { setNotchTabs } from '../../lib/island';
|
|
4
6
|
import { useRouter, useSearchParams } from 'next/navigation';
|
|
5
7
|
import { runScrape, scraperStatus, notReadyMessage, busyReason } from '../../lib/scraper-client';
|
|
6
8
|
import { loadNetwork } from '../../lib/network';
|
|
@@ -15,7 +17,10 @@ import { companyOf, getSeniority } from '../../lib/companies';
|
|
|
15
17
|
import { localPhoto } from '../../lib/photos';
|
|
16
18
|
|
|
17
19
|
const TIER_COLORS = { S: '#FFD700', A: '#9B59B6', B: '#3498DB', C: '#95A5A6', D: '#BDC3C7' };
|
|
18
|
-
|
|
20
|
+
// Map and Companies (Blake, 2026-10-02: companies are worth more than industries,
|
|
21
|
+
// and an industry's detail opens from a company's card). Whether the map's bubbles
|
|
22
|
+
// are companies or people is a switch in its Filters panel.
|
|
23
|
+
const TABS = [['map', 'Map'], ['companies', 'Companies']];
|
|
19
24
|
|
|
20
25
|
export default function PathsPage() {
|
|
21
26
|
// Suspense because PathsInner reads the address's ?tab= (useSearchParams),
|
|
@@ -32,15 +37,24 @@ function PathsInner() {
|
|
|
32
37
|
const [d1Data, setD1Data] = useState([]);
|
|
33
38
|
const [d2Data, setD2Data] = useState([]);
|
|
34
39
|
const [d3Data, setD3Data] = useState([]);
|
|
35
|
-
// Map
|
|
40
|
+
// Map is the analyzer; Companies is the list it grew from.
|
|
36
41
|
// A link can open a tab (/paths?tab=companies). Company scores have a tab of
|
|
37
|
-
// their own
|
|
42
|
+
// their own (app/scores), and now live in Settings: an old link to /paths?tab=scores goes there.
|
|
38
43
|
// The router's search params rather than window.location: on a click from
|
|
39
44
|
// another page the address bar only changes after this page has rendered.
|
|
45
|
+
// Old links still land: ?tab=people opens the map with people as the bubbles; ?tab=industries the map.
|
|
40
46
|
const asked = useSearchParams().get('tab');
|
|
41
47
|
const [tab, setTab] = useState(() => (TABS.some(([k]) => k === asked) ? asked : 'map'));
|
|
48
|
+
const initialShow = asked === 'people' ? 'people' : 'companies';
|
|
42
49
|
const router = useRouter();
|
|
43
|
-
useEffect(() => { if (asked === 'scores') router.replace('/
|
|
50
|
+
useEffect(() => { if (asked === 'scores') router.replace('/settings#companies'); }, [asked, router]);
|
|
51
|
+
// Map and Companies in the notch under the tabs, as the
|
|
52
|
+
// map page's views are; not while one company is open (its own bar shows).
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (selectedCompany) { setNotchTabs(null); return undefined; }
|
|
55
|
+
setNotchTabs({ items: TABS.map(([key, label]) => ({ key, label })), current: tab, onPick: setTab });
|
|
56
|
+
return () => setNotchTabs(null);
|
|
57
|
+
}, [tab, selectedCompany]);
|
|
44
58
|
// Bumped when company scores change, so the network reloads with new tiers.
|
|
45
59
|
const [reloadKey, setReloadKey] = useState(0);
|
|
46
60
|
// The sample or a CSV import: they live only in this tab, so a company scan could only fail.
|
|
@@ -246,28 +260,25 @@ function PathsInner() {
|
|
|
246
260
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
247
261
|
display: 'flex', flexDirection: 'column', overflow: 'hidden',
|
|
248
262
|
}}>
|
|
249
|
-
|
|
263
|
+
{/* The same header as every page: the app's tabs, Paths lit; Map, People,
|
|
264
|
+
Industries and Companies sit in the notch under it. */}
|
|
265
|
+
<header style={{ padding: '20px 30px', borderBottom: '1px solid rgba(255,255,255,0.1)', flexShrink: 0 }}>
|
|
266
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 }}>
|
|
267
|
+
<Link href="/" style={{ textDecoration: 'none' }}>
|
|
268
|
+
<h1 style={{ fontSize: 28, fontWeight: 700, margin: 0, background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent' }}>
|
|
269
|
+
Six Degrees
|
|
270
|
+
</h1>
|
|
271
|
+
</Link>
|
|
272
|
+
<AppTabs active="paths" csvMode={localOnly} />
|
|
273
|
+
<Link href="/settings" title="Settings" aria-label="Settings" style={{
|
|
274
|
+
width: 32, height: 32, borderRadius: '50%', flexShrink: 0, background: 'rgba(255,255,255,0.06)', color: '#888',
|
|
275
|
+
fontSize: 16, textDecoration: 'none', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
276
|
+
}}>⚙</Link>
|
|
277
|
+
</div>
|
|
278
|
+
</header>
|
|
279
|
+
{selectedCompany && (
|
|
280
|
+
<div style={{ padding: '10px 24px', borderBottom: '1px solid rgba(255,255,255,0.08)', flexShrink: 0 }}>
|
|
250
281
|
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
251
|
-
<Link href="/" style={{
|
|
252
|
-
display: 'flex', alignItems: 'center', gap: 6, color: '#888', textDecoration: 'none',
|
|
253
|
-
fontSize: 13, fontWeight: 600, padding: '6px 14px', borderRadius: 6,
|
|
254
|
-
background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.1)',
|
|
255
|
-
}}><span style={{ fontSize: 16 }}>←</span> Map</Link>
|
|
256
|
-
<h1 style={{
|
|
257
|
-
fontSize: 20, fontWeight: 700, margin: 0,
|
|
258
|
-
background: 'linear-gradient(135deg, #00ff88, #3498DB)',
|
|
259
|
-
WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
|
|
260
|
-
}}>Paths</h1>
|
|
261
|
-
{!selectedCompany && (
|
|
262
|
-
<div style={{ display: 'flex', gap: 4, marginLeft: 8, padding: 3, borderRadius: 8, background: 'rgba(255,255,255,0.05)' }}>
|
|
263
|
-
{TABS.map(([k, label]) => (
|
|
264
|
-
<button key={k} onClick={() => setTab(k)} style={{
|
|
265
|
-
padding: '5px 12px', borderRadius: 6, border: 'none', cursor: 'pointer', fontSize: 12.5, fontWeight: 700,
|
|
266
|
-
background: tab === k ? 'linear-gradient(135deg, #00ff88, #3498DB)' : 'transparent', color: tab === k ? '#000' : '#aab',
|
|
267
|
-
}}>{label}</button>
|
|
268
|
-
))}
|
|
269
|
-
</div>
|
|
270
|
-
)}
|
|
271
282
|
{selectedCompany && (
|
|
272
283
|
<>
|
|
273
284
|
<button onClick={() => { setSelectedCompany(null); setCompanyPeople([]); }}
|
|
@@ -301,10 +312,11 @@ function PathsInner() {
|
|
|
301
312
|
</>
|
|
302
313
|
)}
|
|
303
314
|
</div>
|
|
304
|
-
|
|
315
|
+
</div>
|
|
316
|
+
)}
|
|
305
317
|
|
|
306
318
|
{!selectedCompany && tab !== 'companies' ? (
|
|
307
|
-
<PathsAnalyzer d1={d1Data} d2={d2Data} d3={d3Data}
|
|
319
|
+
<PathsAnalyzer d1={d1Data} d2={d2Data} d3={d3Data} initialShow={initialShow}
|
|
308
320
|
onOpenCompany={(co) => selectCompany({ ...co, sCount: co.S, aCount: co.A })} />
|
|
309
321
|
) : (
|
|
310
322
|
<div style={{ flex: 1, overflow: 'auto', padding: '16px 24px' }}>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
3
|
+
import { Suspense, useEffect, useState } from 'react';
|
|
4
|
+
import { useRouter, useSearchParams } from 'next/navigation';
|
|
4
5
|
import { loadNetwork } from '../../lib/network';
|
|
5
6
|
import MappingProgress from '../components/MappingProgress';
|
|
6
7
|
import { IS_DEMO } from '../../lib/demo';
|
|
@@ -8,6 +9,7 @@ import OnboardingGate from '../components/OnboardingGate';
|
|
|
8
9
|
import { useUser } from '../components/UserProvider';
|
|
9
10
|
import { sectorByKey } from '../../lib/sector-labels';
|
|
10
11
|
import Link from 'next/link';
|
|
12
|
+
import NetworkHealthSection from '../components/settings/NetworkHealthSection';
|
|
11
13
|
|
|
12
14
|
const LEVEL_NAMES = {
|
|
13
15
|
1: 'Observer', 11: 'Connector', 31: 'Networker', 51: 'Strategist', 76: 'Architect',
|
|
@@ -22,18 +24,25 @@ function getLevelName(level) {
|
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export default function ProfilePage() {
|
|
25
|
-
|
|
27
|
+
// useSearchParams (which view) needs a Suspense boundary to build.
|
|
28
|
+
return <Suspense fallback={null}><OnboardingGate><ProfileInner /></OnboardingGate></Suspense>;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
function ProfileInner() {
|
|
29
32
|
const { userId, userName, userProfile } = useUser();
|
|
33
|
+
// Profile, or Insights: how your network holds together (Blake, 2026-10-02:
|
|
34
|
+
// "having the insights button into profile having network health"). A link
|
|
35
|
+
// can open Insights: /profile?view=insights (Settings, the old Scores tab).
|
|
36
|
+
const router = useRouter();
|
|
37
|
+
const view = useSearchParams().get('view') === 'insights' ? 'insights' : 'profile';
|
|
38
|
+
const showView = (v) => router.replace(v === 'insights' ? '/profile?view=insights' : '/profile', { scroll: false });
|
|
30
39
|
const profile = userProfile || { name: userName || 'User', headline: '', sectors: [], goals: [] };
|
|
31
40
|
const [stats, setStats] = useState(null);
|
|
32
41
|
const [loading, setLoading] = useState(true);
|
|
33
42
|
const [notifications, setNotifications] = useState([]);
|
|
34
43
|
const [queueStats, setQueueStats] = useState({ total: 0, sent: 0, accepted: 0 });
|
|
35
44
|
const [mapping, setMapping] = useState({ degree1: [], degree2: [], skips: [] });
|
|
36
|
-
// "Your Sectors" is what you picked in Scores → Your
|
|
45
|
+
// "Your Sectors" is what you picked in Settings → Scores → Your field. (users.sectors,
|
|
37
46
|
// which this card used to show, is never written by anything; the Sidebar
|
|
38
47
|
// and Outlink still read it as free text.) null while it loads; a load that
|
|
39
48
|
// fails says so rather than showing "none picked" (TRAPS §7).
|
|
@@ -163,9 +172,27 @@ function ProfileInner() {
|
|
|
163
172
|
background: 'linear-gradient(135deg, #FFD700, #9B59B6, #3498DB)',
|
|
164
173
|
WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
|
|
165
174
|
}}>Profile</h1>
|
|
175
|
+
{/* Profile, or Insights: network health, built from your own scans */}
|
|
176
|
+
<div role="tablist" aria-label="Profile or Insights" style={{ display: 'flex', gap: 3, marginLeft: 8, padding: 3, borderRadius: 9, background: 'rgba(255,255,255,0.06)' }}>
|
|
177
|
+
{[['profile', 'Profile'], ['insights', 'Insights']].map(([k, label]) => (
|
|
178
|
+
<button key={k} type="button" role="tab" aria-selected={view === k} onClick={() => showView(k)} style={{
|
|
179
|
+
padding: '6px 14px', borderRadius: 7, border: 'none', cursor: 'pointer', fontSize: 13, fontWeight: 700,
|
|
180
|
+
background: view === k ? 'linear-gradient(135deg, #FFD700, #FF6B35)' : 'transparent', color: view === k ? '#000' : '#aab',
|
|
181
|
+
}}>{k === 'insights' ? '✦ ' : ''}{label}</button>
|
|
182
|
+
))}
|
|
183
|
+
</div>
|
|
166
184
|
</header>
|
|
167
185
|
|
|
168
186
|
<div style={{ flex: 1, overflow: 'auto', padding: '30px 20px' }}>
|
|
187
|
+
{view === 'insights' ? (
|
|
188
|
+
<div style={{ maxWidth: 820, margin: '0 auto' }}>
|
|
189
|
+
<div style={{ fontSize: 13, color: '#8a8fa8', lineHeight: 1.6 }}>
|
|
190
|
+
How your network holds together, from the circles you’ve scanned. Only your own numbers, never a
|
|
191
|
+
comparison with anyone else’s.
|
|
192
|
+
</div>
|
|
193
|
+
<NetworkHealthSection />
|
|
194
|
+
</div>
|
|
195
|
+
) : (
|
|
169
196
|
<div style={{ maxWidth: 700, margin: '0 auto' }}>
|
|
170
197
|
|
|
171
198
|
{/* === LEVEL CARD === */}
|
|
@@ -376,8 +403,8 @@ function ProfileInner() {
|
|
|
376
403
|
}}>
|
|
377
404
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
|
|
378
405
|
<h3 style={{ fontSize: 14, fontWeight: 700, margin: 0 }}>Your Sectors</h3>
|
|
379
|
-
<Link href="/
|
|
380
|
-
{sectorFocus?.sectors?.length ? 'Change' : sectorFocus && !sectorFocus.failed ? 'Pick
|
|
406
|
+
<Link href="/settings#sector" style={{ fontSize: 11, color: '#3498DB', textDecoration: 'none', fontWeight: 600 }}>
|
|
407
|
+
{sectorFocus?.sectors?.length ? 'Change' : sectorFocus && !sectorFocus.failed ? 'Pick in Settings' : 'Settings'} →
|
|
381
408
|
</Link>
|
|
382
409
|
</div>
|
|
383
410
|
{!sectorFocus ? (
|
|
@@ -409,6 +436,7 @@ function ProfileInner() {
|
|
|
409
436
|
</div>
|
|
410
437
|
|
|
411
438
|
</div>
|
|
439
|
+
)}
|
|
412
440
|
</div>
|
|
413
441
|
</div>
|
|
414
442
|
);
|