six-degrees 0.3.0 → 0.4.0-beta.1
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 +34 -6
- package/.next/standalone/.next/routes-manifest.json +6 -0
- 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 +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +10 -10
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +9 -9
- 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.nft.json +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.nft.json +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.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.nft.json +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.nft.json +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.nft.json +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.nft.json +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.nft.json +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 +12 -12
- package/.next/standalone/.next/server/app/import.segments/_full.segment.rsc +12 -12
- 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 +11 -11
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +12 -12
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +11 -11
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +12 -12
- 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 +12 -12
- package/.next/standalone/.next/server/app/launch.segments/_full.segment.rsc +12 -12
- 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 +11 -11
- 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 +12 -12
- package/.next/standalone/.next/server/app/paths.segments/_full.segment.rsc +12 -12
- 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 +11 -11
- 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 +12 -12
- package/.next/standalone/.next/server/app/profile.segments/_full.segment.rsc +12 -12
- 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 +11 -11
- 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 +12 -12
- package/.next/standalone/.next/server/app/queue.segments/_full.segment.rsc +12 -12
- 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 +11 -11
- 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 +12 -12
- package/.next/standalone/.next/server/app/settings.segments/_full.segment.rsc +12 -12
- 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 +11 -11
- 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 +12 -12
- package/.next/standalone/.next/server/app/setup.segments/_full.segment.rsc +12 -12
- 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 +11 -11
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rc27za._.js +8 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1kbl21f._.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 +7 -6
- 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 +3 -2
- package/.next/standalone/.next/server/chunks/lib_rpc_0c9d9ti.js +5 -5
- 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/_0223v9s._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0bcf13g._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0cu6-an._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0wxbq78._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_0z88zcl._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_17gui6a._.js +1 -1
- 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/_next-internal_server_app_scores_page_actions_0x-w58n.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 +4 -4
- 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/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/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/0_yurn4q91vq8.js +16 -0
- package/.next/standalone/.next/static/chunks/0d_mh-06srh2k.js +1 -0
- package/.next/standalone/.next/static/chunks/0x3oadfikvb1c.js +1 -0
- package/.next/standalone/.next/static/chunks/1d5hl1io798n2.js +1 -0
- package/.next/standalone/.next/static/chunks/1qt67cvj9sjwi.js +1 -0
- package/.next/{static/chunks/1chp3nx45meg5.js → standalone/.next/static/chunks/1roznk7hkjes1.js} +1 -1
- package/.next/standalone/.next/static/chunks/2_bf2n4gstad9.js +33 -0
- package/.next/standalone/.next/static/chunks/36gbwek52x_w-.js +2 -0
- package/.next/standalone/.next/static/chunks/3a55suosthntc.js +1 -0
- package/.next/standalone/.next/static/chunks/3fq1zq-6cfwbc.js +1 -0
- package/.next/standalone/.next/static/chunks/3nbnio971zlji.js +2 -0
- package/.next/standalone/.next/static/chunks/3s6awp3qq9s7w.js +1 -0
- package/.next/standalone/CHANGELOG.md +134 -0
- package/.next/standalone/README.md +30 -15
- 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 +17 -1
- package/.next/standalone/app/api/outreach/route.js +107 -44
- package/.next/standalone/app/api/scraper/route.js +69 -8
- 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/components/CompanyScores.js +16 -10
- package/.next/standalone/app/components/LegacyScoresCard.js +1 -1
- package/.next/standalone/app/components/OrbitGraph.js +30 -0
- package/.next/standalone/app/components/PathsAnalyzer.js +1 -1
- package/.next/standalone/app/components/SeparationView.js +96 -14
- package/.next/standalone/app/components/Sidebar.js +258 -239
- package/.next/standalone/app/components/TheirCircle.js +293 -0
- package/.next/standalone/app/components/settings/SectorSection.js +5 -5
- package/.next/standalone/app/components/settings/TierSection.js +104 -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 +1 -3
- package/.next/standalone/app/launch/page.js +4 -1
- package/.next/standalone/app/page.js +55 -51
- package/.next/standalone/app/paths/page.js +17 -12
- package/.next/standalone/app/profile/page.js +3 -3
- package/.next/standalone/app/queue/page.js +38 -50
- package/.next/standalone/app/scores/page.js +72 -0
- package/.next/standalone/app/settings/page.js +15 -3
- package/.next/standalone/db/schema.js +1 -0
- package/.next/standalone/lib/circle.js +169 -0
- package/.next/standalone/lib/companies.js +1 -1
- package/.next/standalone/lib/csv.js +17 -4
- package/.next/standalone/lib/db-client.js +1 -0
- package/.next/standalone/lib/ingest.js +12 -0
- package/.next/standalone/lib/legacy-offer.js +7 -6
- package/.next/standalone/lib/legacy-scores.js +2 -2
- package/.next/standalone/lib/rarity.js +80 -0
- package/.next/standalone/lib/requests-client.js +113 -0
- package/.next/standalone/lib/rpc.js +26 -15
- package/.next/standalone/lib/score-guess.js +60 -0
- package/.next/standalone/lib/scoring.js +1002 -61
- package/.next/standalone/lib/scraper-client.js +208 -17
- package/.next/standalone/lib/sector-directory.js +1 -1
- package/.next/standalone/lib/sector-focus.js +4 -3
- package/.next/standalone/lib/settings-effects.js +19 -1
- package/.next/standalone/lib/settings.js +4 -1
- 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/gen-synthetic.mjs +1 -1
- package/.next/standalone/scripts/scrape.py +62 -0
- package/.next/static/chunks/0_yurn4q91vq8.js +16 -0
- package/.next/static/chunks/0d_mh-06srh2k.js +1 -0
- package/.next/static/chunks/0x3oadfikvb1c.js +1 -0
- package/.next/static/chunks/1d5hl1io798n2.js +1 -0
- package/.next/static/chunks/1qt67cvj9sjwi.js +1 -0
- package/.next/{standalone/.next/static/chunks/1chp3nx45meg5.js → static/chunks/1roznk7hkjes1.js} +1 -1
- package/.next/static/chunks/2_bf2n4gstad9.js +33 -0
- package/.next/static/chunks/36gbwek52x_w-.js +2 -0
- package/.next/static/chunks/3a55suosthntc.js +1 -0
- package/.next/static/chunks/3fq1zq-6cfwbc.js +1 -0
- package/.next/static/chunks/3nbnio971zlji.js +2 -0
- package/.next/static/chunks/3s6awp3qq9s7w.js +1 -0
- package/CHANGELOG.md +134 -0
- package/README.md +30 -15
- package/db/schema.js +1 -0
- package/lib/circle.js +169 -0
- package/lib/companies.js +1 -1
- package/lib/csv.js +17 -4
- package/lib/db-client.js +1 -0
- package/lib/ingest.js +12 -0
- package/lib/legacy-offer.js +7 -6
- package/lib/legacy-scores.js +2 -2
- package/lib/rarity.js +80 -0
- package/lib/requests-client.js +113 -0
- package/lib/rpc.js +26 -15
- package/lib/score-guess.js +60 -0
- package/lib/scoring.js +1002 -61
- package/lib/scraper-client.js +208 -17
- package/lib/sector-directory.js +1 -1
- package/lib/sector-focus.js +4 -3
- package/lib/settings-effects.js +19 -1
- package/lib/settings.js +4 -1
- package/package.json +2 -2
- package/public/demo-data.json +1 -1
- package/scripts/scrape.py +62 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1t3rugi._.js +0 -3
- package/.next/standalone/.next/server/chunks/lib_rpc_0jzisgh.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/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/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/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 → NnWlxbDF24KhrU1eHiEnv}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_ssgManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_buildManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{qqqcxvzBUizkECrT4swyK → NnWlxbDF24KhrU1eHiEnv}/_ssgManifest.js +0 -0
|
@@ -1,80 +1,143 @@
|
|
|
1
1
|
import { db as supabase } from '../../../lib/db';
|
|
2
|
+
import { getDb, decodeRow } from '../../../lib/db-client';
|
|
3
|
+
import { resolveProfile } from '../../../lib/profile';
|
|
4
|
+
|
|
5
|
+
// Requests you've sent, kept per PERSON.
|
|
6
|
+
//
|
|
7
|
+
// A person has one row per bridge whose circle holds them (the table's key is
|
|
8
|
+
// profile + bridge + user), and a request used to mark only the copy that was
|
|
9
|
+
// clicked. The other copies still offered "Add", the Pending count counted
|
|
10
|
+
// copies, and every click awarded XP again. And the card's link wrote a second
|
|
11
|
+
// column (unlock_status) through another route, which Undo never took back.
|
|
12
|
+
// Now a request marks every copy of the person, in both columns, the bridge you
|
|
13
|
+
// asked through is kept (so once they accept, their card and circle say who
|
|
14
|
+
// introduced you: lib/promote.js keeps it), XP comes once per person, and Undo
|
|
15
|
+
// takes back all of it. lib/requests-client.js is the one way the pages call this.
|
|
2
16
|
|
|
3
17
|
// XP rewards by tier
|
|
4
18
|
const XP_SEND = { S: 25, A: 15, B: 10, C: 5, D: 2 };
|
|
5
19
|
const XP_ACCEPT = { S: 100, A: 60, B: 35, C: 15, D: 5 };
|
|
20
|
+
const TIER_ORDER = ['S', 'A', 'B', 'C', 'D'];
|
|
6
21
|
|
|
7
22
|
async function awardXP(userId, amount) {
|
|
8
23
|
if (!userId || !amount) return;
|
|
9
|
-
// Upsert: create if not exists,
|
|
24
|
+
// Upsert: create if not exists, change if exists. Never below zero: an Undo
|
|
25
|
+
// takes back what a send gave, including sends from before sends gave any.
|
|
10
26
|
const { data: existing } = await supabase.from('user_stats').select('xp').eq('id', userId).single();
|
|
11
27
|
if (existing) {
|
|
12
|
-
await supabase.from('user_stats').update({ xp: (existing.xp || 0) + amount }).eq('id', userId);
|
|
13
|
-
} else {
|
|
28
|
+
await supabase.from('user_stats').update({ xp: Math.max(0, (existing.xp || 0) + amount) }).eq('id', userId);
|
|
29
|
+
} else if (amount > 0) {
|
|
14
30
|
await supabase.from('user_stats').insert([{ id: userId, xp: amount, level: 1 }]).catch(() => {});
|
|
15
31
|
}
|
|
16
32
|
}
|
|
17
33
|
|
|
34
|
+
/** The profile a request belongs to: the one sent, else this machine's. */
|
|
35
|
+
function whose(userId) {
|
|
36
|
+
if (userId) return String(userId);
|
|
37
|
+
try { return resolveProfile({ create: false })?.id || null; } catch { return null; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Every copy of one person: by the clicked row's profile URL, or the URL given. */
|
|
41
|
+
function copiesOf(db, { connectionId, profileUrl, userId }) {
|
|
42
|
+
let url = profileUrl ? String(profileUrl) : null;
|
|
43
|
+
let user = userId ? String(userId) : null;
|
|
44
|
+
let clicked = null;
|
|
45
|
+
if (connectionId != null) {
|
|
46
|
+
clicked = db.prepare('SELECT id, profile_url, user_id FROM linkedin_connections WHERE id = ?').get(String(connectionId)) || null;
|
|
47
|
+
if (clicked) {
|
|
48
|
+
url = clicked.profile_url || url;
|
|
49
|
+
user = user || clicked.user_id || null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const cols = 'id, degree, tier, outreach_status, unlock_status, unlocked_from_bridge_id';
|
|
53
|
+
let rows = [];
|
|
54
|
+
if (url) {
|
|
55
|
+
rows = user
|
|
56
|
+
? db.prepare(`SELECT ${cols} FROM linkedin_connections WHERE profile_url = ? AND user_id = ?`).all(url, user)
|
|
57
|
+
: db.prepare(`SELECT ${cols} FROM linkedin_connections WHERE profile_url = ?`).all(url);
|
|
58
|
+
} else if (clicked) {
|
|
59
|
+
rows = db.prepare(`SELECT ${cols} FROM linkedin_connections WHERE id = ?`).all(clicked.id);
|
|
60
|
+
}
|
|
61
|
+
return { url, user, rows };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const inList = (ids) => ids.map(() => '?').join(', ');
|
|
65
|
+
const bestTier = (rows) => TIER_ORDER.find((t) => rows.some((r) => r.tier === t)) || 'D';
|
|
66
|
+
|
|
18
67
|
export async function POST(request) {
|
|
19
68
|
try {
|
|
20
69
|
const body = await request.json();
|
|
21
|
-
const { action, connectionId, profileUrl, userId } = body;
|
|
70
|
+
const { action, connectionId, profileUrl, userId, bridgeId } = body;
|
|
71
|
+
if (!['mark-sent', 'mark-accepted', 'undo'].includes(action)) {
|
|
72
|
+
return Response.json({ error: 'Unknown action' }, { status: 400 });
|
|
73
|
+
}
|
|
74
|
+
const db = getDb();
|
|
75
|
+
const { user, rows } = copiesOf(db, { connectionId, profileUrl, userId });
|
|
76
|
+
// Nobody on file (the sample network lives in the browser): nothing to keep.
|
|
77
|
+
if (!rows.length) return Response.json({ success: true, saved: 0, xp: 0 });
|
|
78
|
+
const ids = rows.map((r) => r.id);
|
|
79
|
+
const now = new Date().toISOString();
|
|
22
80
|
|
|
23
81
|
if (action === 'mark-sent') {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
82
|
+
const already = rows.some((r) => r.outreach_status === 'sent' || r.outreach_status === 'accepted');
|
|
83
|
+
db.prepare(`UPDATE linkedin_connections SET outreach_status = 'sent', updated_at = ?
|
|
84
|
+
WHERE id IN (${inList(ids)}) AND (outreach_status IS NULL OR outreach_status NOT IN ('sent', 'accepted'))`).run(now, ...ids);
|
|
85
|
+
db.prepare(`UPDATE linkedin_connections SET unlock_status = 'pending', updated_at = ?
|
|
86
|
+
WHERE id IN (${inList(ids)}) AND (unlock_status IS NULL OR unlock_status = 'locked')`).run(now, ...ids);
|
|
87
|
+
// Who you asked through: one of your own connections, or nothing.
|
|
88
|
+
if (bridgeId != null) {
|
|
89
|
+
const bridge = db.prepare(
|
|
90
|
+
`SELECT id, name FROM linkedin_connections WHERE id = ? AND degree = 1${user ? ' AND user_id = ?' : ''}`,
|
|
91
|
+
).get(...[String(bridgeId), ...(user ? [user] : [])]);
|
|
92
|
+
if (bridge) {
|
|
93
|
+
db.prepare(`UPDATE linkedin_connections SET unlocked_from_bridge_id = ?, unlocked_from_name = ?, updated_at = ?
|
|
94
|
+
WHERE id IN (${inList(ids)}) AND degree = 2 AND unlocked_from_bridge_id IS NULL`).run(bridge.id, bridge.name, now, ...ids);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const xp = already ? 0 : XP_SEND[bestTier(rows)] || 2;
|
|
98
|
+
await awardXP(whose(user), xp);
|
|
99
|
+
return Response.json({ success: true, saved: ids.length, xp, already });
|
|
40
100
|
}
|
|
41
101
|
|
|
42
102
|
if (action === 'mark-accepted') {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (userId) query = query.eq('user_id', userId);
|
|
46
|
-
const { error } = await query;
|
|
47
|
-
if (error) return Response.json({ error: error.message }, { status: 500 });
|
|
48
|
-
return Response.json({ success: true });
|
|
103
|
+
db.prepare(`UPDATE linkedin_connections SET outreach_status = 'accepted', updated_at = ? WHERE id IN (${inList(ids)})`).run(now, ...ids);
|
|
104
|
+
return Response.json({ success: true, saved: ids.length, xp: 0, accepts: XP_ACCEPT[bestTier(rows)] || 5 });
|
|
49
105
|
}
|
|
50
106
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return Response.json({ error: 'Unknown action' }, { status: 400 });
|
|
107
|
+
// undo
|
|
108
|
+
const wasSent = rows.some((r) => r.outreach_status === 'sent');
|
|
109
|
+
db.prepare(`UPDATE linkedin_connections SET outreach_status = NULL, updated_at = ?
|
|
110
|
+
WHERE id IN (${inList(ids)}) AND outreach_status = 'sent'`).run(now, ...ids);
|
|
111
|
+
db.prepare(`UPDATE linkedin_connections SET unlock_status = 'locked', updated_at = ?
|
|
112
|
+
WHERE id IN (${inList(ids)}) AND unlock_status = 'pending'`).run(now, ...ids);
|
|
113
|
+
const xp = wasSent ? -(XP_SEND[bestTier(rows)] || 2) : 0;
|
|
114
|
+
await awardXP(whose(user), xp);
|
|
115
|
+
return Response.json({ success: true, saved: ids.length, xp });
|
|
61
116
|
} catch (err) {
|
|
62
117
|
console.error('Outreach error:', err);
|
|
63
118
|
return Response.json({ error: err.message }, { status: 500 });
|
|
64
119
|
}
|
|
65
120
|
}
|
|
66
121
|
|
|
122
|
+
/** Everyone with a request out, once each (the newest copy), newest first. */
|
|
67
123
|
export async function GET(request) {
|
|
68
124
|
try {
|
|
69
125
|
const { searchParams } = new URL(request.url);
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
126
|
+
const user = whose(searchParams.get('userId'));
|
|
127
|
+
const rows = getDb().prepare(
|
|
128
|
+
`SELECT * FROM linkedin_connections
|
|
129
|
+
WHERE (outreach_status = 'sent' OR unlock_status = 'pending')${user ? ' AND user_id = ?' : ''}
|
|
130
|
+
ORDER BY created_at DESC`,
|
|
131
|
+
).all(...(user ? [user] : []));
|
|
132
|
+
const seen = new Set();
|
|
133
|
+
const pending = [];
|
|
134
|
+
for (const row of rows) {
|
|
135
|
+
const key = row.profile_url || `id:${row.id}`;
|
|
136
|
+
if (seen.has(key)) continue;
|
|
137
|
+
seen.add(key);
|
|
138
|
+
pending.push(decodeRow('linkedin_connections', row));
|
|
139
|
+
}
|
|
140
|
+
return Response.json({ pending });
|
|
78
141
|
} catch (err) {
|
|
79
142
|
return Response.json({ error: err.message }, { status: 500 });
|
|
80
143
|
}
|
|
@@ -32,6 +32,8 @@ const MAX_LOG = 500;
|
|
|
32
32
|
const state = registerScanState({
|
|
33
33
|
running: false,
|
|
34
34
|
action: null,
|
|
35
|
+
target: null, // who a scan is of: { id, name } (either may be null), or null for scans of no one
|
|
36
|
+
recent: [], // the last few finished jobs, newest first: how each one ended
|
|
35
37
|
log: [],
|
|
36
38
|
startedAt: null,
|
|
37
39
|
exitCode: null,
|
|
@@ -251,20 +253,66 @@ async function status() {
|
|
|
251
253
|
signedIn: m.signedIn,
|
|
252
254
|
},
|
|
253
255
|
network,
|
|
256
|
+
...job(),
|
|
257
|
+
skips: bridgeSkips(),
|
|
258
|
+
linkedin: linkedinState(dataDir()),
|
|
259
|
+
paused: paused(me?.id),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* The running job alone, from memory: no checks, no database. Every Scan
|
|
265
|
+
* button in the app asks this (lib/scraper-client.js watchScanner), every few
|
|
266
|
+
* seconds, so it must cost nothing.
|
|
267
|
+
*
|
|
268
|
+
* `recent` is how the last few jobs ended. A page following one job can then
|
|
269
|
+
* learn how it ended even when another job started before it looked again;
|
|
270
|
+
* with only the latest job's exit code, that one would never seem to finish.
|
|
271
|
+
*/
|
|
272
|
+
function job() {
|
|
273
|
+
return {
|
|
254
274
|
running: state.running,
|
|
255
275
|
action: state.action,
|
|
276
|
+
target: state.target,
|
|
256
277
|
startedAt: state.startedAt,
|
|
257
278
|
exitCode: state.exitCode,
|
|
258
279
|
failure: state.running ? null : state.failure,
|
|
259
280
|
progress: state.running ? scanProgress(state.log, state.action) : null,
|
|
260
281
|
log: state.log.slice(-120),
|
|
261
|
-
|
|
262
|
-
linkedin: linkedinState(dataDir()),
|
|
263
|
-
paused: paused(me?.id),
|
|
282
|
+
recent: state.recent,
|
|
264
283
|
};
|
|
265
284
|
}
|
|
266
285
|
|
|
267
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Where Resume would carry on with one of your connections, for their profile
|
|
288
|
+
* card: { nextPage, pagesRead, legacy }, or null when there is nothing to carry
|
|
289
|
+
* on with (never mapped, read to the end, hidden). The Paused list's own rules
|
|
290
|
+
* (lib/paused.js), so the card and the Scan page can't disagree.
|
|
291
|
+
*/
|
|
292
|
+
function resumePoint(id) {
|
|
293
|
+
try {
|
|
294
|
+
const me = resolveProfile({ create: false });
|
|
295
|
+
if (!me) return null;
|
|
296
|
+
const db = getDb();
|
|
297
|
+
const row = db.prepare(
|
|
298
|
+
`SELECT id, name, tier, profile_url, connected_date, created_at
|
|
299
|
+
FROM linkedin_connections WHERE id = ? AND user_id = ? AND degree = 1`,
|
|
300
|
+
).get(String(id ?? ''), me.id);
|
|
301
|
+
if (!row) return null;
|
|
302
|
+
const mapped = db.prepare(
|
|
303
|
+
'SELECT 1 FROM linkedin_connections WHERE user_id = ? AND degree = 2 AND source_connection_id = ? LIMIT 1',
|
|
304
|
+
).get(me.id, row.id);
|
|
305
|
+
const [p] = pausedList([row], new Set(mapped ? [row.id] : []), readProgress(dataDir(), me.id), readUnclear(dataDir()));
|
|
306
|
+
return p ? { nextPage: p.nextPage, pagesRead: p.pagesRead, legacy: p.legacy } : null;
|
|
307
|
+
} catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export async function GET(request) {
|
|
313
|
+
const q = new URL(request?.url || 'http://127.0.0.1/api/scraper').searchParams;
|
|
314
|
+
if (q.has('job')) return Response.json(job());
|
|
315
|
+
if (q.has('resume')) return Response.json({ resume: resumePoint(q.get('resume')) });
|
|
268
316
|
return Response.json(await status());
|
|
269
317
|
}
|
|
270
318
|
|
|
@@ -439,16 +487,23 @@ export async function POST(request) {
|
|
|
439
487
|
// Resume sends the connection's id; the URL is looked up here, for this
|
|
440
488
|
// profile, so nothing from the request itself reaches the command line.
|
|
441
489
|
let profileUrl = null;
|
|
490
|
+
let person = null;
|
|
442
491
|
if (spec.needsId) {
|
|
443
492
|
try {
|
|
444
493
|
const me = resolveProfile({ create: false });
|
|
445
|
-
|
|
446
|
-
'SELECT profile_url FROM linkedin_connections WHERE id = ? AND user_id = ? AND degree = 1',
|
|
494
|
+
person = me && getDb().prepare(
|
|
495
|
+
'SELECT name, profile_url FROM linkedin_connections WHERE id = ? AND user_id = ? AND degree = 1',
|
|
447
496
|
).get(String(body.id ?? ''), me.id);
|
|
448
|
-
profileUrl =
|
|
497
|
+
profileUrl = person?.profile_url && cleanProfileUrl(person.profile_url);
|
|
449
498
|
} catch { profileUrl = null; }
|
|
450
499
|
if (!profileUrl) return Response.json({ error: 'That connection could not be found.' }, { status: 400 });
|
|
451
500
|
}
|
|
501
|
+
// Who the job is of, so every Scan button can say whose scan is running and a
|
|
502
|
+
// profile card can show its own person's progress. Only ever shown: the id
|
|
503
|
+
// sent alongside a name never reaches the command line (the name does, as
|
|
504
|
+
// always), and anything not shaped like one of our ids is dropped.
|
|
505
|
+
const hintId = typeof body.id === 'string' && /^[\w-]{1,64}$/.test(body.id) ? body.id : null;
|
|
506
|
+
const target = name || profileUrl ? { id: hintId, name: name || person?.name || null } : null;
|
|
452
507
|
// Nothing that searches LinkedIn starts during a cooldown; the scanner checks
|
|
453
508
|
// too, this just says so before a process is spawned.
|
|
454
509
|
// The 1st-degree scans aren't searches, but they open LinkedIn with automation too.
|
|
@@ -517,6 +572,7 @@ export async function POST(request) {
|
|
|
517
572
|
state.running = true;
|
|
518
573
|
state.stopping = false;
|
|
519
574
|
state.action = action;
|
|
575
|
+
state.target = target;
|
|
520
576
|
state.exitCode = null;
|
|
521
577
|
state.startedAt = Date.now();
|
|
522
578
|
state.log = [spec.label + (name ? ` ${name}…` : '…')];
|
|
@@ -560,6 +616,9 @@ export async function POST(request) {
|
|
|
560
616
|
state.child = null;
|
|
561
617
|
state.abort = null;
|
|
562
618
|
state.exitCode = code;
|
|
619
|
+
state.recent = [{
|
|
620
|
+
action: state.action, target: state.target, startedAt: state.startedAt, exitCode: code, failure: state.failure,
|
|
621
|
+
}, ...state.recent].slice(0, 5);
|
|
563
622
|
// A setup's private folder (the download and what was unpacked from it).
|
|
564
623
|
if (cleanup) {
|
|
565
624
|
try { cleanup(); } catch { /* swept at the next setup (sweepSetupLeftovers) */ }
|
|
@@ -625,5 +684,7 @@ export async function POST(request) {
|
|
|
625
684
|
|
|
626
685
|
runStep(0);
|
|
627
686
|
|
|
628
|
-
|
|
687
|
+
// Which job this is, so the page that started it can tell its end from the
|
|
688
|
+
// end of whatever runs next (lib/scraper-client.js runScrape).
|
|
689
|
+
return Response.json({ ok: true, action, startedAt: state.startedAt });
|
|
629
690
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getDb } from '../../../../lib/db-client';
|
|
2
|
-
import { companyOverrides, readForScoring, scoringRows, sectorFocusOf } from '../../../../lib/rpc';
|
|
2
|
+
import { companyOverrides, readForScoring, scoringRows, sectorFocusOf, tierScaleOf } from '../../../../lib/rpc';
|
|
3
3
|
import { parseSectorFocus, previewSectorFocus } from '../../../../lib/sector-focus';
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Scores → Your sector, before you save: what a sector focus would change,
|
|
6
6
|
// against the one saved now. It reads the network once and scores it twice in
|
|
7
7
|
// memory, with the inputs rescoreAll() uses (readForScoring: each company's
|
|
8
8
|
// industry and its sectors from the directory), and writes nothing. A save
|
|
@@ -31,6 +31,7 @@ export async function POST(request) {
|
|
|
31
31
|
read: readForScoring(rows),
|
|
32
32
|
from: sectorFocusOf(db),
|
|
33
33
|
to,
|
|
34
|
+
tierScale: tierScaleOf(db),
|
|
34
35
|
}));
|
|
35
36
|
} catch (err) {
|
|
36
37
|
return Response.json({ error: err.message }, { status: 500 });
|
|
@@ -2,7 +2,7 @@ import { getDb } from '../../../../lib/db-client';
|
|
|
2
2
|
import { readForScoring, scoringRows } from '../../../../lib/rpc';
|
|
3
3
|
import { suggestSectors } from '../../../../lib/sector-directory';
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Scores → Your sector: "Suggested from your network". The directory's
|
|
6
6
|
// sectors most of your scanned people work in, counted here from the rows
|
|
7
7
|
// with the same read rescoring uses, so a suggestion's companies are the ones
|
|
8
8
|
// picking it would lean. Its own read-only GET, not part of GET
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// The Scores tab's company scores (app/scores): every company in your network, the score it carries into
|
|
4
4
|
// people's power, where that score comes from, and a control to set your own.
|
|
5
5
|
// Setting a score rescores everyone (app/api/company-scores → lib/scoring.js).
|
|
6
6
|
|
|
7
7
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
8
|
-
import Link from 'next/link';
|
|
9
8
|
import LegacyScoresCard from './LegacyScoresCard';
|
|
10
9
|
|
|
11
10
|
const LINE = '1px solid rgba(255,255,255,0.1)';
|
|
12
11
|
const SOURCE = {
|
|
13
12
|
yours: { label: 'your score', color: '#00ff88' },
|
|
14
13
|
known: { label: 'known list', color: '#3498DB' },
|
|
14
|
+
data: { label: 'public data', color: '#1abc9c' },
|
|
15
15
|
network: { label: 'estimated: many of your people', color: '#FF6B35' },
|
|
16
16
|
default: { label: 'unknown company', color: '#778' },
|
|
17
17
|
};
|
|
@@ -59,8 +59,14 @@ export function ScorePicker({ company, onSet, compact }) {
|
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
export default function CompanyScores({ onRescored }) {
|
|
62
|
+
export default function CompanyScores({ onRescored, version = 0 }) {
|
|
63
63
|
const { companies, error, setScore, reload } = useCompanyScores();
|
|
64
|
+
// A save beside the list (your sector or tiers, on the Scores tab) rescores
|
|
65
|
+
// everyone: `version` changes, and the list is read again, keeping what you
|
|
66
|
+
// searched and filtered.
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (version) reload().catch(() => {});
|
|
69
|
+
}, [version, reload]);
|
|
64
70
|
const [query, setQuery] = useState('');
|
|
65
71
|
const [only, setOnly] = useState('all');
|
|
66
72
|
const [limit, setLimit] = useState(150);
|
|
@@ -103,22 +109,22 @@ export default function CompanyScores({ onRescored }) {
|
|
|
103
109
|
<LegacyScoresCard onAnswered={legacyAnswered} />
|
|
104
110
|
<div style={{ padding: 14, borderRadius: 10, border: LINE, background: 'rgba(255,255,255,0.03)', fontSize: 12.5, color: '#cfd8d8', lineHeight: 1.6 }}>
|
|
105
111
|
<b style={{ color: '#fff' }}>How a power score works.</b> power = <b>title</b> × <b>company weight</b> + bonus.
|
|
106
|
-
The title (student 1 … founder or C-suite 10) comes from someone’s
|
|
107
|
-
The company weight runs from 0.
|
|
112
|
+
The title (student 1 … founder or C-suite 10) comes from someone’s roles, with former roles at 70%; an audience of their own counts like a title (1M+ followers like a director).
|
|
113
|
+
The company weight runs from 0.725 for a company we don’t know (or none found), the middle of the scale, to 1.0 (a company scored 10), so seniority counts for more at a bigger company.
|
|
108
114
|
The bonus is at most +1.5, for investor, YC, an audience in the millions or the top award of any field, and counts half at an unknown company.
|
|
109
|
-
A 1st-degree person whose circle is unusually strong gets up to +1
|
|
110
|
-
S ≥ 7.5 · A ≥ 5.5 · B ≥ 4 · C ≥ 2.5.
|
|
115
|
+
A 1st-degree person whose circle is unusually strong gets up to +2 more: up to +1 for a high share of it at A or S, or +1 for every 25 people there.
|
|
116
|
+
Tiers are graded on your network’s curve unless you choose the fixed scale (<a href="#tiers" style={{ color: '#3498DB' }}>Tiers</a>, above): your top 3% are S, the next 12% A, the next 25% B (it only lifts, and never lifts anyone under 4 into S or A). On the fixed scale, S ≥ 7.5 · A ≥ 5.5 · B ≥ 4 · C ≥ 2.5.
|
|
111
117
|
<div style={{ marginTop: 6, color: '#8b9a9a' }}>
|
|
112
|
-
So a VP at a 10 scores 9.0, a founder at an unknown company
|
|
118
|
+
So a VP at a 10 scores 9.0, a founder at an unknown company 7.3, a director at an unknown company 5.4, and an intern at a 10 scores 2.0.
|
|
113
119
|
Set a company’s score below and everyone there is rescored.
|
|
114
|
-
Companies in the sectors you pick
|
|
120
|
+
Companies in the sectors you pick (<a href="#sector" style={{ color: '#3498DB' }}>Your sector</a>, above) get +1 or +2; a score you set is never changed.
|
|
115
121
|
</div>
|
|
116
122
|
</div>
|
|
117
123
|
|
|
118
124
|
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', margin: '14px 0 10px' }}>
|
|
119
125
|
<input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Find a company"
|
|
120
126
|
style={{ padding: '6px 10px', borderRadius: 7, border: LINE, background: 'rgba(255,255,255,0.05)', color: '#fff', fontSize: 13, minWidth: 180 }} />
|
|
121
|
-
{[['all', `All ${companies.length}`], ['known', `Known list ${count('known')}`], ['yours', `Yours ${count('yours')}`], ['network', `Estimated ${count('network')}`], ['default', `Unknown ${count('default')}`]].map(([k, l]) => (
|
|
127
|
+
{[['all', `All ${companies.length}`], ['known', `Known list ${count('known')}`], ['data', `Public data ${count('data')}`], ['yours', `Yours ${count('yours')}`], ['network', `Estimated ${count('network')}`], ['default', `Unknown ${count('default')}`]].map(([k, l]) => (
|
|
122
128
|
<button key={k} onClick={() => setOnly(k)} style={{
|
|
123
129
|
padding: '5px 10px', borderRadius: 7, border: LINE, cursor: 'pointer', fontSize: 11.5, fontWeight: 600,
|
|
124
130
|
background: only === k ? 'rgba(52,152,219,0.25)' : 'transparent', color: only === k ? '#cfe6f7' : '#8b9a9a',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// The Scores tab: the one-time offer to keep the curated list's old scores as
|
|
4
4
|
// your own, after the list was made neutral (lib/legacy-offer.js decides who
|
|
5
5
|
// sees it and what "keep" writes). It sits at the top of the Scores tab until
|
|
6
6
|
// it's answered, then never shows again. Not while a CSV import or the sample
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
20
|
+
import useRequests from './useRequests';
|
|
21
|
+
import { hasRequest } from '../../lib/requests-client';
|
|
20
22
|
import {
|
|
21
23
|
drag,
|
|
22
24
|
forceCollide,
|
|
@@ -71,6 +73,7 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
71
73
|
const zoomKRef = useRef(1);
|
|
72
74
|
const savedTransformRef = useRef(null);
|
|
73
75
|
const applySelectionRef = useRef(() => {});
|
|
76
|
+
const applyRequestsRef = useRef(() => {});
|
|
74
77
|
const focusNodeRef = useRef(() => {});
|
|
75
78
|
const [size, setSize] = useState({ w: 0, h: 0 });
|
|
76
79
|
const isMobile = useIsMobile();
|
|
@@ -122,6 +125,15 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
122
125
|
applySelectionRef.current(selectedId);
|
|
123
126
|
}, [selectedId]);
|
|
124
127
|
|
|
128
|
+
// So do requests you've sent (lib/requests-client.js): a request sent from
|
|
129
|
+
// any view rings that person's dots here at once.
|
|
130
|
+
const requests = useRequests();
|
|
131
|
+
const requestsRef = useRef(requests);
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
requestsRef.current = requests;
|
|
134
|
+
applyRequestsRef.current(requests);
|
|
135
|
+
}, [requests]);
|
|
136
|
+
|
|
125
137
|
useEffect(() => {
|
|
126
138
|
const svgEl = svgRef.current;
|
|
127
139
|
if (!svgEl || size.w < 40 || size.h < 40) return undefined;
|
|
@@ -545,6 +557,23 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
545
557
|
};
|
|
546
558
|
applySelectionRef.current(selectedIdRef.current);
|
|
547
559
|
|
|
560
|
+
// ---- requests you've sent ---------------------------------------------
|
|
561
|
+
// A dashed gold ring on every 2nd-degree dot you've asked, in step with
|
|
562
|
+
// the shared list, without rebuilding the scene.
|
|
563
|
+
applyRequestsRef.current = (state) => {
|
|
564
|
+
nodesLayer.selectAll('circle.og-asked').remove();
|
|
565
|
+
node.filter((d) => d.kind === 'd2' && hasRequest(d.connection, state))
|
|
566
|
+
.append('circle')
|
|
567
|
+
.attr('class', 'og-asked')
|
|
568
|
+
.attr('r', (d) => d.r + 2.2)
|
|
569
|
+
.attr('fill', 'none')
|
|
570
|
+
.attr('stroke', '#FFD700')
|
|
571
|
+
.attr('stroke-width', 1)
|
|
572
|
+
.attr('stroke-dasharray', '2 1.6')
|
|
573
|
+
.style('pointer-events', 'none');
|
|
574
|
+
};
|
|
575
|
+
applyRequestsRef.current(requestsRef.current);
|
|
576
|
+
|
|
548
577
|
// ---- fly-to ----------------------------------------------------------
|
|
549
578
|
const nodeById = new Map(renderNodes.map((n) => [n.id, n]));
|
|
550
579
|
focusNodeRef.current = (id) => {
|
|
@@ -591,6 +620,7 @@ const OrbitGraph = forwardRef(function OrbitGraph(
|
|
|
591
620
|
return () => {
|
|
592
621
|
sim?.stop();
|
|
593
622
|
applySelectionRef.current = () => {};
|
|
623
|
+
applyRequestsRef.current = () => {};
|
|
594
624
|
focusNodeRef.current = () => {};
|
|
595
625
|
svg.on('.zoom', null);
|
|
596
626
|
svg.on('click', null);
|
|
@@ -505,7 +505,7 @@ function Section({ title, children }) {
|
|
|
505
505
|
}
|
|
506
506
|
|
|
507
507
|
// The company's score in people's power, where it comes from, and a control
|
|
508
|
-
// to set it (
|
|
508
|
+
// to set it (the Scores tab has every company).
|
|
509
509
|
function CompanyScoreLine({ name }) {
|
|
510
510
|
const { companies, setScore } = useCompanyScores();
|
|
511
511
|
const c = companies?.find((x) => x.name === name);
|