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,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
// Settings → Your sector. Up to three sectors you work in: one of the twelve
|
|
3
|
+
// Scores → Your sector (app/scores; it was Settings → Your sector). Up to three sectors you work in: one of the twelve
|
|
4
4
|
// broad industries, or a narrower sector from the app's directory (Dental,
|
|
5
5
|
// Real Estate, Software & SaaS…). Companies in them count for more when your
|
|
6
6
|
// scanned network is scored, like setting their scores by hand on Paths →
|
|
@@ -10,17 +10,19 @@
|
|
|
10
10
|
// Before you save, the page asks the app what would change (a dry run that
|
|
11
11
|
// writes nothing). Saving stores the choice with your network and rescores
|
|
12
12
|
// everyone, the way a company score change does. Suggestions come from the
|
|
13
|
-
// people you've scanned (/api/settings/sector-suggestions).
|
|
13
|
+
// people you've scanned (/api/settings/sector-suggestions). The picks
|
|
14
|
+
// themselves are SectorPicker.js, which the Scan page's question about your
|
|
15
|
+
// field, before the first scan, shows too.
|
|
14
16
|
|
|
15
|
-
import { useEffect,
|
|
16
|
-
import Link from 'next/link';
|
|
17
|
+
import { useEffect, useRef, useState } from 'react';
|
|
17
18
|
import { Section, Body, Status, Btn, LINE } from '../ui';
|
|
18
|
-
import
|
|
19
|
-
import {
|
|
20
|
-
import { DIRECTORY,
|
|
19
|
+
import SectorPicker from './SectorPicker';
|
|
20
|
+
import { sameFocus, focusFingerprint } from '../../../lib/sector-focus';
|
|
21
|
+
import { DIRECTORY, sectorByKey } from '../../../lib/sector-directory';
|
|
21
22
|
import { SECTOR_BONUS } from '../../../lib/scoring';
|
|
22
23
|
import { TIER_COLORS_CLASSIC } from '../../../lib/tiers';
|
|
23
24
|
import { csvNetworkSource } from '../../../lib/csv';
|
|
25
|
+
import { saveSettings } from '../../../lib/settings-client';
|
|
24
26
|
|
|
25
27
|
const STRENGTH_LABEL = { lean: 'Lean', strong: 'Strong' };
|
|
26
28
|
// What says the reader has taken over the page (see the jump to #sector).
|
|
@@ -28,33 +30,10 @@ const READER_INPUT = ['wheel', 'touchstart', 'keydown', 'pointerdown'];
|
|
|
28
30
|
const DEGREE = { 1: '1st', 2: '2nd', 3: '3rd' };
|
|
29
31
|
const plural = (n, one, many = `${one}s`) => `${n.toLocaleString()} ${n === 1 ? one : many}`;
|
|
30
32
|
|
|
31
|
-
//
|
|
32
|
-
const SECTORS_OF = new Map(INDUSTRIES.map((g) => [g.key, DIRECTORY.filter((s) => s.group === g.key)]));
|
|
33
|
-
const GROUP_OF = new Map(DIRECTORY.map((s) => [s.key, s.group]));
|
|
34
|
-
// What the search box looks through: each sector's name, its words, jobs and
|
|
35
|
-
// companies, as they read (without the list's "(s)" and "$" marks), so
|
|
36
|
-
// "recruiter" finds HR & Recruiting.
|
|
37
|
-
const SEARCHABLE = DIRECTORY.map((s) => ({
|
|
38
|
-
sector: s,
|
|
39
|
-
label: s.label.toLowerCase(),
|
|
40
|
-
words: [...s.words, ...(s.roles || []), ...(s.names || []), ...s.companies].map((w) => ` ${w.replace(/\(s\)$|\$$/, '').toLowerCase()}`),
|
|
41
|
-
}));
|
|
42
|
-
// A sector of work every company has (HR & Recruiting, Legal…) counts firms in
|
|
43
|
-
// that line, not every company that employs someone doing it.
|
|
44
|
-
const FUNCTION_TITLE = 'Counts firms in this line of work, not every company with someone in the job';
|
|
33
|
+
// The sectors that are work every kind of company has (HR & Recruiting, Legal…), for the notes below.
|
|
45
34
|
const FUNCTIONS = DIRECTORY.filter((s) => s.kind === 'function').map((s) => s.label);
|
|
46
35
|
const listed = (labels) => (labels.length > 1 ? `${labels.slice(0, -1).join(', ')} and ${labels.at(-1)}` : labels.join(''));
|
|
47
36
|
|
|
48
|
-
/** Sectors (and whole industries) whose name has the query, or a word or company that starts with it. */
|
|
49
|
-
function searchSectors(query) {
|
|
50
|
-
const q = query.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
51
|
-
if (!q) return null;
|
|
52
|
-
return {
|
|
53
|
-
industries: INDUSTRIES.filter((g) => g.label.toLowerCase().includes(q)),
|
|
54
|
-
sectors: SEARCHABLE.filter((s) => s.label.includes(q) || s.words.some((w) => w.includes(` ${q}`))).map((s) => s.sector),
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
37
|
/** "120 people move up a tier and 4 move down." (or "moved", after a save) */
|
|
59
38
|
function tierLine(up, down, past) {
|
|
60
39
|
const [goes, go] = past ? ['moved', 'moved'] : ['moves', 'move'];
|
|
@@ -64,7 +43,7 @@ function tierLine(up, down, past) {
|
|
|
64
43
|
return up ? `${plural(up, 'person', 'people')} ${verb(up)} up a tier.` : `${plural(down, 'person', 'people')} ${verb(down)} down a tier.`;
|
|
65
44
|
}
|
|
66
45
|
|
|
67
|
-
export default function SectorSection() {
|
|
46
|
+
export default function SectorSection({ onSaved } = {}) {
|
|
68
47
|
const [saved, setSaved] = useState(null);
|
|
69
48
|
const [draft, setDraft] = useState(null);
|
|
70
49
|
const [preview, setPreview] = useState(null);
|
|
@@ -72,9 +51,6 @@ export default function SectorSection() {
|
|
|
72
51
|
const [result, setResult] = useState(null);
|
|
73
52
|
const [loadError, setLoadError] = useState(null);
|
|
74
53
|
const [suggest, setSuggest] = useState({ loading: true });
|
|
75
|
-
const [query, setQuery] = useState('');
|
|
76
|
-
// Industries shown open; those holding a saved pick start open.
|
|
77
|
-
const [open, setOpen] = useState(() => new Set());
|
|
78
54
|
// A CSV import or the sample network held in this window. This section only
|
|
79
55
|
// renders in the browser (the page waits in OnboardingGate), so reading
|
|
80
56
|
// sessionStorage here can't disagree with a server render.
|
|
@@ -92,7 +68,6 @@ export default function SectorSection() {
|
|
|
92
68
|
const focus = d.settings?.sectorFocus || { sectors: [], strength: 'lean' };
|
|
93
69
|
setSaved(focus);
|
|
94
70
|
setDraft(focus);
|
|
95
|
-
setOpen(new Set(focus.sectors.map((k) => GROUP_OF.get(k) || k)));
|
|
96
71
|
})
|
|
97
72
|
.catch((e) => { if (!off) setLoadError(e.message); });
|
|
98
73
|
// Suggestions read every scanned row, so they load on their own and never hold up the rest.
|
|
@@ -103,7 +78,7 @@ export default function SectorSection() {
|
|
|
103
78
|
return () => { off = true; clearTimeout(timer.current); };
|
|
104
79
|
}, []);
|
|
105
80
|
|
|
106
|
-
// Links to /
|
|
81
|
+
// Links to /scores#sector (the profile's card, the old /settings#sector) arrive
|
|
107
82
|
// before this section has loaded, so the browser's own jump to it finds
|
|
108
83
|
// nothing. Jump once it's here, then keep it in place while what loads above
|
|
109
84
|
// it (Updates) pushes it down: the browser only holds steady what's already
|
|
@@ -128,8 +103,6 @@ export default function SectorSection() {
|
|
|
128
103
|
return stop;
|
|
129
104
|
}, [draft, loadError]);
|
|
130
105
|
|
|
131
|
-
const results = useMemo(() => searchSectors(query), [query]);
|
|
132
|
-
|
|
133
106
|
// Every change asks for a fresh dry run, a moment after the last click.
|
|
134
107
|
// Only the newest answer is kept.
|
|
135
108
|
function change(next) {
|
|
@@ -154,55 +127,20 @@ export default function SectorSection() {
|
|
|
154
127
|
}, 250);
|
|
155
128
|
}
|
|
156
129
|
|
|
157
|
-
function toggle(key) {
|
|
158
|
-
const on = draft.sectors.includes(key);
|
|
159
|
-
if (!on && draft.sectors.length >= MAX_SECTORS) return;
|
|
160
|
-
const sectors = on ? draft.sectors.filter((k) => k !== key) : SECTOR_KEYS.filter((k) => k === key || draft.sectors.includes(k));
|
|
161
|
-
// A sector picked from the suggestions or a search shows up in its industry too.
|
|
162
|
-
if (!on && GROUP_OF.has(key)) setOpen((was) => new Set(was).add(GROUP_OF.get(key)));
|
|
163
|
-
change({ ...draft, sectors });
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function flip(group) {
|
|
167
|
-
setOpen((was) => {
|
|
168
|
-
const next = new Set(was);
|
|
169
|
-
if (next.has(group)) next.delete(group); else next.add(group);
|
|
170
|
-
return next;
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
130
|
async function save() {
|
|
175
131
|
setSaving(true);
|
|
176
132
|
setResult(null);
|
|
177
133
|
clearTimeout(timer.current);
|
|
178
134
|
ticket.current++;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (!r.ok) { setResult({ tone: 'bad', text: d.error || 'Could not save.' }); return; }
|
|
186
|
-
setResult({ tone: 'ok', text: savedLine(d.settings.sectorFocus, d.effects?.sectorFocus) });
|
|
187
|
-
} catch {
|
|
188
|
-
setResult({ tone: 'bad', text: 'Could not reach the app. Reload this page to see what is saved.' });
|
|
189
|
-
} finally {
|
|
190
|
-
setSaving(false);
|
|
191
|
-
}
|
|
135
|
+
const d = await saveSettings({ sectorFocus: draft });
|
|
136
|
+
setSaving(false);
|
|
137
|
+
if (d.settings) { setSaved(d.settings.sectorFocus); setDraft(d.settings.sectorFocus); setPreview(null); }
|
|
138
|
+
if (d.error) { setResult({ tone: 'bad', text: d.error }); return; }
|
|
139
|
+
setResult({ tone: 'ok', text: savedLine(d.settings.sectorFocus, d.effects?.sectorFocus) });
|
|
140
|
+
onSaved?.();
|
|
192
141
|
}
|
|
193
142
|
|
|
194
143
|
const changed = draft && saved && !sameFocus(draft, saved);
|
|
195
|
-
const full = draft && draft.sectors.length >= MAX_SECTORS;
|
|
196
|
-
// One chip for any pick: an industry or a sector, wherever it's shown.
|
|
197
|
-
const chip = (key, label, { note, title } = {}) => {
|
|
198
|
-
const on = draft.sectors.includes(key);
|
|
199
|
-
return (
|
|
200
|
-
<Chip key={key} color={sectorByKey(key).color} on={on} blocked={!on && full} disabled={saving} title={title}
|
|
201
|
-
onClick={() => toggle(key)}>
|
|
202
|
-
{label}{note && <span style={{ fontWeight: 400, color: on ? '#dfe7e7' : '#8b9a9a' }}>{note}</span>}
|
|
203
|
-
</Chip>
|
|
204
|
-
);
|
|
205
|
-
};
|
|
206
144
|
|
|
207
145
|
return (
|
|
208
146
|
<Section id="sector" title="Your sector"
|
|
@@ -212,82 +150,8 @@ export default function SectorSection() {
|
|
|
212
150
|
|
|
213
151
|
{draft && (
|
|
214
152
|
<>
|
|
215
|
-
<
|
|
216
|
-
|
|
217
|
-
{!draft.sectors.length && <span style={{ fontSize: 13, color: '#667' }}>none yet</span>}
|
|
218
|
-
{draft.sectors.map((k) => {
|
|
219
|
-
const s = sectorByKey(k);
|
|
220
|
-
return (
|
|
221
|
-
<button key={k} type="button" disabled={saving} onClick={() => toggle(k)} aria-label={`Remove ${s.label}`}
|
|
222
|
-
style={{
|
|
223
|
-
display: 'flex', alignItems: 'center', gap: 6, padding: '4px 10px', borderRadius: 999, fontSize: 12.5,
|
|
224
|
-
fontWeight: 600, color: '#fff', background: `${s.color}33`, border: `1px solid ${s.color}`, cursor: 'pointer',
|
|
225
|
-
}}>
|
|
226
|
-
{s.label} <span aria-hidden="true" style={{ color: '#cfd8d8' }}>×</span>
|
|
227
|
-
</button>
|
|
228
|
-
);
|
|
229
|
-
})}
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
<Suggestions state={suggest} chip={chip} />
|
|
233
|
-
|
|
234
|
-
<label htmlFor="sector-search" style={{ display: 'block', fontSize: 13, color: '#8b9a9a', margin: '16px 0 6px' }}>
|
|
235
|
-
Find a sector
|
|
236
|
-
</label>
|
|
237
|
-
<div style={{ display: 'flex', gap: 8 }}>
|
|
238
|
-
<input id="sector-search" type="search" value={query} onChange={(e) => setQuery(e.target.value)}
|
|
239
|
-
placeholder="Try dentist, bakery or Stripe"
|
|
240
|
-
autoComplete="off" spellCheck={false}
|
|
241
|
-
style={{
|
|
242
|
-
flex: 1, minWidth: 0, padding: '8px 12px', borderRadius: 7, fontSize: 14, color: '#fff',
|
|
243
|
-
background: 'rgba(255,255,255,0.05)', border: LINE, outline: 'none',
|
|
244
|
-
}} />
|
|
245
|
-
{query && <Btn onClick={() => setQuery('')}>Clear</Btn>}
|
|
246
|
-
</div>
|
|
247
|
-
|
|
248
|
-
{results ? (
|
|
249
|
-
<div style={{ marginTop: 10 }} aria-live="polite">
|
|
250
|
-
{results.industries.length + results.sectors.length === 0 ? (
|
|
251
|
-
<Body style={{ fontSize: 12.5 }}>
|
|
252
|
-
Nothing in the list matches “{query.trim()}”. Try a job title, a kind of business or a company, or clear
|
|
253
|
-
the search and pick one of the twelve broad industries.
|
|
254
|
-
</Body>
|
|
255
|
-
) : (
|
|
256
|
-
<div role="group" aria-label="Sectors that match your search" style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
|
|
257
|
-
{results.industries.map((g) => chip(g.key, g.label, { note: ' · the whole industry' }))}
|
|
258
|
-
{results.sectors.map((s) => chip(s.key, s.label, { title: s.kind === 'function' ? FUNCTION_TITLE : `In ${sectorByKey(s.group).label}` }))}
|
|
259
|
-
</div>
|
|
260
|
-
)}
|
|
261
|
-
</div>
|
|
262
|
-
) : (
|
|
263
|
-
<div style={{ marginTop: 10 }}>
|
|
264
|
-
{INDUSTRIES.map((g) => {
|
|
265
|
-
const sectors = SECTORS_OF.get(g.key);
|
|
266
|
-
const isOpen = open.has(g.key);
|
|
267
|
-
const inside = sectors.filter((s) => draft.sectors.includes(s.key)).length;
|
|
268
|
-
return (
|
|
269
|
-
<div key={g.key} style={{ borderTop: LINE, padding: '8px 0' }}>
|
|
270
|
-
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
|
|
271
|
-
{chip(g.key, g.label, { title: 'The whole industry: companies its sectors place, or whose name or the well-known list says so' })}
|
|
272
|
-
<button type="button" aria-expanded={isOpen} aria-controls={`sectors-${g.key}`} onClick={() => flip(g.key)}
|
|
273
|
-
style={{
|
|
274
|
-
padding: '4px 6px', fontSize: 12.5, color: inside ? '#9fd3ff' : '#8b9a9a', background: 'none', border: 'none',
|
|
275
|
-
cursor: 'pointer',
|
|
276
|
-
}}>
|
|
277
|
-
{isOpen ? '▾' : '▸'} {plural(sectors.length, 'sector')}{inside ? ` · ${inside} picked` : ''}
|
|
278
|
-
</button>
|
|
279
|
-
</div>
|
|
280
|
-
{isOpen && (
|
|
281
|
-
<div id={`sectors-${g.key}`} role="group" aria-label={`Sectors in ${g.label}`}
|
|
282
|
-
style={{ display: 'flex', flexWrap: 'wrap', gap: 8, margin: '8px 0 2px 14px' }}>
|
|
283
|
-
{sectors.map((s) => chip(s.key, s.label, s.kind === 'function' ? { title: FUNCTION_TITLE } : undefined))}
|
|
284
|
-
</div>
|
|
285
|
-
)}
|
|
286
|
-
</div>
|
|
287
|
-
);
|
|
288
|
-
})}
|
|
289
|
-
</div>
|
|
290
|
-
)}
|
|
153
|
+
<SectorPicker sectors={draft.sectors} onChange={(sectors) => change({ ...draft, sectors })} disabled={saving}
|
|
154
|
+
suggest={suggest} />
|
|
291
155
|
|
|
292
156
|
<div style={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 8, marginTop: 14 }}>
|
|
293
157
|
<span style={{ fontSize: 13, color: '#8b9a9a' }}>How much:</span>
|
|
@@ -321,7 +185,7 @@ export default function SectorSection() {
|
|
|
321
185
|
|
|
322
186
|
<ul style={{ margin: '16px 0 0', paddingLeft: 18, fontSize: 12.5, color: '#778', lineHeight: 1.7 }}>
|
|
323
187
|
<li>Tiers rank how reachable someone is through your network, not the people themselves.</li>
|
|
324
|
-
<li>A company score you set on <
|
|
188
|
+
<li>A company score you set on <a href="#companies" style={{ color: '#3498DB' }}>Company scores</a>, below, always wins. Your sector never changes it.</li>
|
|
325
189
|
<li>If you scan highest tier first, people in your sectors come up sooner. Scanning newest first, the default, doesn’t go by tier.</li>
|
|
326
190
|
<li>
|
|
327
191
|
An industry includes its sectors. It counts a company in any of its sectors (Healthcare & Biotech counts a
|
|
@@ -352,44 +216,6 @@ export default function SectorSection() {
|
|
|
352
216
|
);
|
|
353
217
|
}
|
|
354
218
|
|
|
355
|
-
/** A pick you can turn on and off: an industry or a sector, with its industry's colour. */
|
|
356
|
-
function Chip({ color, on, blocked, disabled, title, onClick, children }) {
|
|
357
|
-
return (
|
|
358
|
-
<button type="button" aria-pressed={on} disabled={blocked || disabled} onClick={onClick}
|
|
359
|
-
title={blocked ? `Up to ${MAX_SECTORS} sectors. Turn one off first.` : title}
|
|
360
|
-
style={{
|
|
361
|
-
display: 'flex', alignItems: 'center', gap: 7, padding: '6px 12px', borderRadius: 999, maxWidth: '100%',
|
|
362
|
-
fontSize: 12.5, fontWeight: 600, textAlign: 'left', cursor: blocked ? 'not-allowed' : 'pointer',
|
|
363
|
-
color: on ? '#fff' : blocked ? '#556' : '#b8c4c4',
|
|
364
|
-
background: on ? `${color}33` : 'rgba(255,255,255,0.04)',
|
|
365
|
-
border: on ? `1px solid ${color}` : LINE,
|
|
366
|
-
}}>
|
|
367
|
-
<span style={{ width: 8, height: 8, borderRadius: '50%', background: color, opacity: blocked ? 0.4 : 1, flexShrink: 0 }} />
|
|
368
|
-
<span>{children}</span>
|
|
369
|
-
</button>
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/** "Suggested from your network": the directory's sectors most of your scanned people work in. */
|
|
374
|
-
function Suggestions({ state, chip }) {
|
|
375
|
-
const note = { fontSize: 12.5, marginTop: 10 };
|
|
376
|
-
if (state.loading) return <Body style={note}>Looking through your network for suggestions…</Body>;
|
|
377
|
-
if (state.error) return <Body style={note}>Couldn’t load suggestions ({state.error}). You can still pick below.</Body>;
|
|
378
|
-
if (!state.scored) return <Body style={note}>Suggestions appear here once you’ve scanned your network.</Body>;
|
|
379
|
-
if (!state.list.length) return <Body style={note}>None of the list’s sectors stands out among the people you’ve scanned yet.</Body>;
|
|
380
|
-
return (
|
|
381
|
-
<div style={{ marginTop: 14 }}>
|
|
382
|
-
<div style={{ fontSize: 13, color: '#8b9a9a', marginBottom: 6 }}>Suggested from your network</div>
|
|
383
|
-
<div role="group" aria-label="Suggested from your network" style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
|
|
384
|
-
{state.list.map((s) => chip(s.key, s.label, {
|
|
385
|
-
note: `: ${plural(s.people, 'person', 'people')} at ${plural(s.companies, 'company', 'companies')}`,
|
|
386
|
-
title: `In ${sectorByKey(s.group).label}`,
|
|
387
|
-
}))}
|
|
388
|
-
</div>
|
|
389
|
-
</div>
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
219
|
/** "Saved: …. Rescored your network (N people). …" */
|
|
394
220
|
function savedLine(focus, effect) {
|
|
395
221
|
// The labels have commas of their own ("Tech, Software & AI"), so a dot
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// Scores → Tiers (app/scores; it was Settings → Tiers). How the S to D rings are drawn: on your network's curve
|
|
4
|
+
// (your top 3% are S, and so on down), or on the fixed scale, the same lines
|
|
5
|
+
// for everyone. The curve only lifts, so a network full of companies the
|
|
6
|
+
// curated list knows keeps its tiers. The model is lib/scoring.js (CURVE,
|
|
7
|
+
// curvedTier); saving rescores everyone, as a new sector focus does, and says
|
|
8
|
+
// how many people changed tier (lib/settings-effects.js).
|
|
9
|
+
|
|
10
|
+
import { useEffect, useState } from 'react';
|
|
11
|
+
import { Section, Body, Status, LINE } from '../ui';
|
|
12
|
+
import { CURVE, CURVE_FLOOR } from '../../../lib/scoring';
|
|
13
|
+
import { csvNetworkSource } from '../../../lib/csv';
|
|
14
|
+
import { saveSettings } from '../../../lib/settings-client';
|
|
15
|
+
|
|
16
|
+
const pct = (x) => `${Math.round(x * 100)}%`;
|
|
17
|
+
const [S, A, B] = CURVE.map(([, share]) => share);
|
|
18
|
+
const plural = (n, one, many) => `${n.toLocaleString()} ${n === 1 ? one : many}`;
|
|
19
|
+
|
|
20
|
+
const OPTIONS = [
|
|
21
|
+
{
|
|
22
|
+
key: 'curve',
|
|
23
|
+
label: "On your network's curve",
|
|
24
|
+
note: `Your top ${pct(S)} are S, the next ${pct(A - S)} A and the next ${pct(B - A)} B, so any network has a top, even one full of companies the app doesn't know. It only lifts: nobody drops a tier because their network is strong, and nobody under ${CURVE_FLOOR} is lifted into S or A.`,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
key: 'fixed',
|
|
28
|
+
label: 'On the fixed scale',
|
|
29
|
+
note: 'The same lines for everyone: S from 7.5, A from 5.5, B from 4, C from 2.5. Easy to compare from one network to another, but a network outside well-known companies has almost nobody in S or A.',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
export default function TierSection({ onSaved } = {}) {
|
|
34
|
+
const [saved, setSaved] = useState(null);
|
|
35
|
+
const [saving, setSaving] = useState(false);
|
|
36
|
+
const [result, setResult] = useState(null);
|
|
37
|
+
const [loadError, setLoadError] = useState(null);
|
|
38
|
+
// A CSV import or the sample network held in this window: neither is changed
|
|
39
|
+
// by this (a CSV is always graded on its own curve; the sample keeps its
|
|
40
|
+
// tiers). This section only renders in the browser (OnboardingGate), so
|
|
41
|
+
// reading sessionStorage here can't disagree with a server render.
|
|
42
|
+
const [onScreen] = useState(() => (typeof window === 'undefined' ? null : csvNetworkSource()));
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
let off = false;
|
|
46
|
+
fetch('/api/settings')
|
|
47
|
+
.then((r) => r.json().then((d) => (r.ok ? d : Promise.reject(new Error(d.error || 'Could not load how tiers are graded.')))))
|
|
48
|
+
.then((d) => { if (!off) setSaved(d.settings?.tierScale || 'curve'); })
|
|
49
|
+
.catch((e) => { if (!off) setLoadError(e.message); });
|
|
50
|
+
return () => { off = true; };
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
async function choose(key) {
|
|
54
|
+
if (saving || key === saved) return;
|
|
55
|
+
setSaving(true);
|
|
56
|
+
setResult(null);
|
|
57
|
+
const { settings, effects, error } = await saveSettings({ tierScale: key });
|
|
58
|
+
if (settings) setSaved(settings.tierScale);
|
|
59
|
+
if (error) setResult({ tone: 'bad', text: error });
|
|
60
|
+
else {
|
|
61
|
+
const e = effects?.tierScale;
|
|
62
|
+
setResult({ tone: 'ok', text: e ? `Saved. ${plural(e.moved, 'person', 'people')} changed tier.` : 'Saved.' });
|
|
63
|
+
onSaved?.();
|
|
64
|
+
}
|
|
65
|
+
setSaving(false);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Section id="tiers" title="Tiers" intro="How the S to D rings are drawn. A tier says where someone sits in your network, not what they're worth.">
|
|
70
|
+
{loadError && <Status tone="bad">{loadError}</Status>}
|
|
71
|
+
{saved && (
|
|
72
|
+
<div role="radiogroup" aria-label="How tiers are graded" style={{ display: 'grid', gap: 8 }}>
|
|
73
|
+
{OPTIONS.map((o) => {
|
|
74
|
+
const on = saved === o.key;
|
|
75
|
+
return (
|
|
76
|
+
<button key={o.key} type="button" role="radio" aria-checked={on} disabled={saving} onClick={() => choose(o.key)} style={{
|
|
77
|
+
textAlign: 'left', padding: '12px 14px', borderRadius: 8, cursor: saving ? 'wait' : on ? 'default' : 'pointer', color: '#fff',
|
|
78
|
+
background: on ? 'rgba(155,89,182,0.14)' : 'rgba(255,255,255,0.03)',
|
|
79
|
+
border: on ? '1px solid rgba(155,89,182,0.6)' : LINE,
|
|
80
|
+
}}>
|
|
81
|
+
<div style={{ fontSize: 14, fontWeight: 650 }}>{on ? '● ' : '○ '}{o.label}{o.key === 'curve' ? ' (recommended)' : ''}</div>
|
|
82
|
+
<Body style={{ marginTop: 4 }}>{o.note}</Body>
|
|
83
|
+
</button>
|
|
84
|
+
);
|
|
85
|
+
})}
|
|
86
|
+
</div>
|
|
87
|
+
)}
|
|
88
|
+
{onScreen && (
|
|
89
|
+
<Body style={{ color: '#FFD700', marginTop: 10 }}>
|
|
90
|
+
{onScreen === 'sample'
|
|
91
|
+
? 'The sample network open in this window keeps its own tiers (they’re the same either way).'
|
|
92
|
+
: 'The CSV import open in this window is always graded on its own curve.'}{' '}
|
|
93
|
+
This applies to networks you’ve scanned.
|
|
94
|
+
</Body>
|
|
95
|
+
)}
|
|
96
|
+
{result && <Status tone={result.tone}>{result.text}</Status>}
|
|
97
|
+
</Section>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useSyncExternalStore } from 'react';
|
|
4
|
+
import { watchRequests, requestsNow, REQUESTS_UNKNOWN } from '../../lib/requests-client';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Who you've sent a request to, shared by every view (lib/requests-client.js).
|
|
8
|
+
* Pass the answer to hasRequest(row, state) and requestCount(state).
|
|
9
|
+
*/
|
|
10
|
+
export default function useRequests() {
|
|
11
|
+
return useSyncExternalStore(watchRequests, requestsNow, () => REQUESTS_UNKNOWN);
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useSyncExternalStore } from 'react';
|
|
4
|
+
import { watchScanner, scannerNow, SCANNER_UNKNOWN } from '../../lib/scraper-client';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* What the scanner is running, shared by every Scan button on the page: one
|
|
8
|
+
* answer, asked once (lib/scraper-client.js). Any button that starts a scan
|
|
9
|
+
* greys out while `running`, and says why with busyReason().
|
|
10
|
+
*/
|
|
11
|
+
export default function useScanner() {
|
|
12
|
+
return useSyncExternalStore(watchScanner, scannerNow, () => SCANNER_UNKNOWN);
|
|
13
|
+
}
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
import ForceGraph from './ForceGraph';
|
|
14
14
|
import OrbitGraph from './OrbitGraph';
|
|
15
15
|
import ChainView from './ChainView';
|
|
16
|
-
import BridgeRing from './BridgeRing';
|
|
17
16
|
import GridView from './GridView';
|
|
18
17
|
import ListView from './ListView';
|
|
19
18
|
import RingsView from './RingsView';
|
|
@@ -31,18 +30,17 @@ export const VIEWS = {
|
|
|
31
30
|
orbit: { component: OrbitGraph, modes: ['network', 'degrees'], label: 'Orbit', icon: '🪐', desc: 'Tier orbits + circle dots; in Degrees, every mapped circle fanned out', allDegree2: true },
|
|
32
31
|
separation: { component: SeparationView, modes: ['degrees'], label: 'Separation', icon: '🏆', desc: 'Every 2nd degree, ranked · every way in' },
|
|
33
32
|
chain: { component: ChainView, modes: ['degrees'], label: 'Bridge Chains', icon: '🔗', desc: 'Degree paths' },
|
|
34
|
-
revolver: { component: BridgeRing, modes: ['degrees'], label: 'Revolver', icon: '🎯', desc: 'Rotary dial · spin to switch' },
|
|
35
33
|
rings: { component: RingsView, modes: ['network', 'degrees'], label: 'Pyramid', icon: '🔺', desc: 'Tier hierarchy' },
|
|
36
34
|
list: { component: ListView, modes: ['network', 'degrees'], label: 'List', icon: '☰', desc: 'Ranked power list' },
|
|
37
35
|
grid: { component: GridView, modes: ['network', 'degrees'], label: 'Grid', icon: '▦', desc: 'Cards' },
|
|
38
36
|
};
|
|
39
37
|
|
|
40
38
|
/**
|
|
41
|
-
* The order they appear in the menu.
|
|
42
|
-
*
|
|
39
|
+
* The order they appear in the menu. Orbit is the first Degrees view, so it is
|
|
40
|
+
* also where resolveView lands when the chosen view isn't a Degrees one
|
|
43
41
|
* (Galaxy, say, carried over from Network Circle).
|
|
44
42
|
*/
|
|
45
|
-
const ORDER = ['galaxy', 'orbit', 'separation', 'chain', '
|
|
43
|
+
const ORDER = ['galaxy', 'orbit', 'separation', 'chain', 'rings', 'list'];
|
|
46
44
|
|
|
47
45
|
export function viewsForMode(mode) {
|
|
48
46
|
return ORDER
|
|
@@ -19,6 +19,13 @@ body:has(> div > [data-map]) {
|
|
|
19
19
|
overflow: hidden;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
/* The Scan page's field question holds Continue and Skip for now at the foot
|
|
23
|
+
of the window (app/components/FieldStep.js). Without this, a pick reached
|
|
24
|
+
with Tab can stop behind them. */
|
|
25
|
+
html:has([data-field-question]) {
|
|
26
|
+
scroll-padding-bottom: 80px;
|
|
27
|
+
}
|
|
28
|
+
|
|
22
29
|
::-webkit-scrollbar {
|
|
23
30
|
width: 6px;
|
|
24
31
|
}
|
|
@@ -125,7 +125,7 @@ function HeroSection() {
|
|
|
125
125
|
transition: 'all 1s cubic-bezier(0.16, 1, 0.3, 1) 0.15s',
|
|
126
126
|
letterSpacing: '-0.03em',
|
|
127
127
|
}}>
|
|
128
|
-
|
|
128
|
+
Six Degrees of<br />Separation
|
|
129
129
|
</h1>
|
|
130
130
|
|
|
131
131
|
{/* Subtitle */}
|
|
@@ -315,7 +315,7 @@ function GalaxySection() {
|
|
|
315
315
|
}
|
|
316
316
|
|
|
317
317
|
// ═══════════════════════════════════════════════════════════════
|
|
318
|
-
// BRIDGE CHAINS —
|
|
318
|
+
// BRIDGE CHAINS — six degrees visualization
|
|
319
319
|
// ═══════════════════════════════════════════════════════════════
|
|
320
320
|
function ChainSection() {
|
|
321
321
|
const [ref, inView] = useInView();
|
|
@@ -474,6 +474,9 @@ function ScoringSection() {
|
|
|
474
474
|
</div>
|
|
475
475
|
))}
|
|
476
476
|
</div>
|
|
477
|
+
<p style={{ textAlign: 'center', fontSize: 12, color: '#555', marginTop: 24 }}>
|
|
478
|
+
These are the fixed scale’s lines. By default tiers are graded on your own network’s curve: your top 3% are S, the next 12% A.
|
|
479
|
+
</p>
|
|
477
480
|
</div>
|
|
478
481
|
</section>
|
|
479
482
|
);
|
|
@@ -615,7 +618,7 @@ function TechSection() {
|
|
|
615
618
|
// The model in lib/scoring.js: a product, so a title counts for more at a bigger company.
|
|
616
619
|
{ sym: 'T', label: 'Title', desc: 'C-suite 10, VP 9, Director 7.5, Manager 6.5', note: 'Multiplied by the company', color: TIER.S },
|
|
617
620
|
{ sym: 'C', label: 'Company', desc: 'Well-known 7–10, else estimated from your network', note: 'Weight 0.5 to 1.0', color: TIER.A },
|
|
618
|
-
{ sym: 'R + B', label: 'Reach & bridge', desc: 'Investor, YC, an audience in the millions; a strong circle', note: 'Up to +1.5 and +
|
|
621
|
+
{ sym: 'R + B', label: 'Reach & bridge', desc: 'Investor, YC, an audience in the millions; a strong circle', note: 'Up to +1.5 and +2', color: TIER.B },
|
|
619
622
|
].map((item, i) => (
|
|
620
623
|
<div key={i} style={{
|
|
621
624
|
padding: 16, borderRadius: 12, background: `${item.color}08`,
|