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,7 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useState, useEffect, useMemo } from 'react';
|
|
4
|
-
import {
|
|
3
|
+
import { useState, useEffect, useMemo, useCallback } from 'react';
|
|
4
|
+
import { beginScrape, scraperStatus, notReadyMessage, resumePoint, scansCircleOf, isCircleScan, busyReason } from '../../lib/scraper-client';
|
|
5
|
+
import useScanner from './useScanner';
|
|
6
|
+
import useRequests from './useRequests';
|
|
7
|
+
import TheirCircle from './TheirCircle';
|
|
8
|
+
import { hasRequest, markRequested, undoRequest } from '../../lib/requests-client';
|
|
5
9
|
import { useUser } from './UserProvider';
|
|
6
10
|
import { routeIndex, routesFor } from '../../lib/separation';
|
|
7
11
|
import { topCompanies } from '../../lib/scoring';
|
|
@@ -26,9 +30,13 @@ export default function Sidebar({ selected, stats, tierColors, connections, degr
|
|
|
26
30
|
}, [degree2]);
|
|
27
31
|
// Who reaches whom, from the FULL lists this panel is given — so every route
|
|
28
32
|
// shows even while a tier chip is narrowing the view, and a person picked in
|
|
29
|
-
// Chains
|
|
33
|
+
// Chains or Galaxy gets every route too.
|
|
30
34
|
const routeIdx = useMemo(() => routeIndex(degree2), [degree2]);
|
|
31
35
|
const bridgeById = useMemo(() => new Map(connections.map(c => [c.id, c])), [connections]);
|
|
36
|
+
// Who you've asked, shared with every view: a request sent here shows in
|
|
37
|
+
// Separation, the circle and the Outlink queue at once (lib/requests-client.js).
|
|
38
|
+
const requests = useRequests();
|
|
39
|
+
const isRequested = useCallback((row) => hasRequest(row, requests), [requests]);
|
|
32
40
|
|
|
33
41
|
if (selected) {
|
|
34
42
|
const isBridge = selected.degree === 1 && bridgeMap[selected.id];
|
|
@@ -114,6 +122,16 @@ export default function Sidebar({ selected, stats, tierColors, connections, degr
|
|
|
114
122
|
</div>
|
|
115
123
|
</div>
|
|
116
124
|
|
|
125
|
+
{/* Their circle, out to six degrees: who they know, who you added from
|
|
126
|
+
it, and who those people know (TheirCircle.js). Your connections only:
|
|
127
|
+
a 2nd-degree person's circle isn't yours to scan until they accept. */}
|
|
128
|
+
{selected.degree === 1 && (
|
|
129
|
+
<TheirCircle
|
|
130
|
+
person={selected} connections={connections} degree2={degree2}
|
|
131
|
+
tierColors={tierColors} onSelect={onSelect} isRequested={isRequested}
|
|
132
|
+
/>
|
|
133
|
+
)}
|
|
134
|
+
|
|
117
135
|
{selected.is_catalyst && (
|
|
118
136
|
<div style={{
|
|
119
137
|
background: 'rgba(0,255,136,0.08)', border: '1px solid rgba(0,255,136,0.3)',
|
|
@@ -294,61 +312,65 @@ export default function Sidebar({ selected, stats, tierColors, connections, degr
|
|
|
294
312
|
</button>
|
|
295
313
|
)}
|
|
296
314
|
|
|
297
|
-
{/* Connect to Unlock — for ALL degree-2 connections
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}}>
|
|
303
|
-
<div style={{ fontSize: 10, color: '#FFD700', fontWeight: 700, letterSpacing: 1, marginBottom: 6 }}>
|
|
304
|
-
{selected.unlock_status === 'pending' ? 'CONNECTION PENDING' : 'LOCKED PATH'}
|
|
305
|
-
</div>
|
|
306
|
-
<div style={{ fontSize: 13, color: '#ccc', marginBottom: 12 }}>
|
|
307
|
-
{selected.unlock_status === 'pending'
|
|
308
|
-
? `Waiting for ${selected.name} to accept. Run a scan to check.`
|
|
309
|
-
: `Connect with ${selected.name} to unlock their network and extend your 6 degrees.`
|
|
310
|
-
}
|
|
311
|
-
</div>
|
|
315
|
+
{/* Connect to Unlock — for ALL degree-2 connections. A request sent here
|
|
316
|
+
is the person's, everywhere at once (lib/requests-client.js). */}
|
|
317
|
+
{(selected.degree === 2 || selected.source_connection_id) && selected.degree !== 1 && selected.unlock_status !== 'unlocked' && (() => {
|
|
318
|
+
const asked = isRequested(selected);
|
|
319
|
+
return (
|
|
312
320
|
<div style={{
|
|
313
|
-
|
|
314
|
-
borderRadius:
|
|
315
|
-
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
316
|
-
fontSize: 11, color: '#444', marginBottom: 12,
|
|
321
|
+
background: 'rgba(255,215,0,0.06)', border: `1px ${asked ? 'dashed' : 'solid'} rgba(255,215,0,${asked ? 0.45 : 0.25})`,
|
|
322
|
+
borderRadius: 10, padding: 16, marginTop: 16, textAlign: 'center',
|
|
317
323
|
}}>
|
|
318
|
-
{
|
|
324
|
+
<div style={{ fontSize: 10, color: '#FFD700', fontWeight: 700, letterSpacing: 1, marginBottom: 6 }}>
|
|
325
|
+
{asked ? 'REQUEST SENT' : 'LOCKED PATH'}
|
|
326
|
+
</div>
|
|
327
|
+
<div style={{ fontSize: 13, color: '#ccc', marginBottom: 12 }}>
|
|
328
|
+
{asked
|
|
329
|
+
? `Waiting for ${selected.name} to accept. The next scan of your own connections notices when they do.`
|
|
330
|
+
: `Connect with ${selected.name} to unlock their network and extend your 6 degrees.`
|
|
331
|
+
}
|
|
332
|
+
</div>
|
|
333
|
+
<div style={{
|
|
334
|
+
width: '100%', height: 60, background: 'rgba(255,255,255,0.03)',
|
|
335
|
+
borderRadius: 8, border: '1px dashed rgba(255,255,255,0.1)',
|
|
336
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
337
|
+
fontSize: 11, color: '#444', marginBottom: 12,
|
|
338
|
+
}}>
|
|
339
|
+
{asked ? '[ Cluster Pending... ]' : '[ Hidden Network ]'}
|
|
340
|
+
</div>
|
|
341
|
+
{asked ? (
|
|
342
|
+
<button
|
|
343
|
+
onClick={() => { undoRequest(selected).catch(() => {}); }}
|
|
344
|
+
style={{
|
|
345
|
+
background: 'none', border: 'none', color: '#888', fontSize: 11, fontWeight: 600, cursor: 'pointer',
|
|
346
|
+
}}
|
|
347
|
+
>
|
|
348
|
+
Didn't send it? Undo
|
|
349
|
+
</button>
|
|
350
|
+
) : (
|
|
351
|
+
<a href={selected.profile_url} target="_blank" rel="noopener noreferrer"
|
|
352
|
+
style={{
|
|
353
|
+
display: 'block', padding: '10px 16px',
|
|
354
|
+
background: 'linear-gradient(135deg, #FFD700, #FF6B35)',
|
|
355
|
+
color: '#000', borderRadius: 8, textDecoration: 'none',
|
|
356
|
+
fontWeight: 700, fontSize: 13,
|
|
357
|
+
}}
|
|
358
|
+
onClick={() => {
|
|
359
|
+
// Opens their profile to connect, and marks the request
|
|
360
|
+
// sent, through the bridge whose circle you found them in.
|
|
361
|
+
markRequested(selected, { bridgeId: selected.source_connection_id }).catch(() => {});
|
|
362
|
+
}}
|
|
363
|
+
>
|
|
364
|
+
Connect to Unlock Path
|
|
365
|
+
</a>
|
|
366
|
+
)}
|
|
319
367
|
</div>
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
style={{
|
|
323
|
-
display: 'block', padding: '10px 16px',
|
|
324
|
-
background: 'linear-gradient(135deg, #FFD700, #FF6B35)',
|
|
325
|
-
color: '#000', borderRadius: 8, textDecoration: 'none',
|
|
326
|
-
fontWeight: 700, fontSize: 13,
|
|
327
|
-
}}
|
|
328
|
-
onClick={() => {
|
|
329
|
-
// Mark as pending in unlock system
|
|
330
|
-
fetch('/api/unlock', {
|
|
331
|
-
method: 'POST',
|
|
332
|
-
headers: { 'Content-Type': 'application/json' },
|
|
333
|
-
body: JSON.stringify({ connectionId: selected.id }),
|
|
334
|
-
}).catch(() => {});
|
|
335
|
-
// Sync to outreach/pending system (Outlink)
|
|
336
|
-
fetch('/api/outreach', {
|
|
337
|
-
method: 'POST',
|
|
338
|
-
headers: { 'Content-Type': 'application/json' },
|
|
339
|
-
body: JSON.stringify({ action: 'mark-sent', connectionId: selected.id, profileUrl: selected.profile_url, userId }),
|
|
340
|
-
}).catch(() => {});
|
|
341
|
-
}}
|
|
342
|
-
>
|
|
343
|
-
Connect to Unlock Path
|
|
344
|
-
</a>
|
|
345
|
-
)}
|
|
346
|
-
</div>
|
|
347
|
-
)}
|
|
368
|
+
);
|
|
369
|
+
})()}
|
|
348
370
|
|
|
349
371
|
{/* Create Cluster — for D1 connections OR accepted D2 (promoted, ready to bridge for D3) */}
|
|
350
372
|
{(selected.degree === 1 || selected.outreach_status === 'accepted') && (
|
|
351
|
-
<CreateClusterCard selected={selected} degree2={degree2} />
|
|
373
|
+
<CreateClusterCard key={selected.id} selected={selected} degree2={degree2} />
|
|
352
374
|
)}
|
|
353
375
|
|
|
354
376
|
{selected.profile_url && (
|
|
@@ -916,146 +938,165 @@ function PathBox({ routes, selected, tierColors, onSelect }) {
|
|
|
916
938
|
);
|
|
917
939
|
}
|
|
918
940
|
|
|
941
|
+
// Scan one person's circle, or carry on with it.
|
|
942
|
+
//
|
|
943
|
+
// LinkedIn lists someone else's connections in its own order, with no dates,
|
|
944
|
+
// so anyone new can turn up on any page: Rescan reads the whole list again from
|
|
945
|
+
// page 1. Resume carries on from the page the last read stopped at, for a list
|
|
946
|
+
// that was only partly read. Both show when there is something to resume.
|
|
947
|
+
//
|
|
948
|
+
// Whether a scan is running comes from the scanner (useScanner), not from this
|
|
949
|
+
// card: close the card mid-scan and open it again and the scan is still here,
|
|
950
|
+
// and every other card greys its buttons out until it ends.
|
|
919
951
|
function CreateClusterCard({ selected, degree2 }) {
|
|
920
|
-
const
|
|
921
|
-
const [
|
|
922
|
-
const [
|
|
923
|
-
const
|
|
952
|
+
const scan = useScanner();
|
|
953
|
+
const [checking, setChecking] = useState(false);
|
|
954
|
+
const [problem, setProblem] = useState(null); // why a scan didn't start: { text, offline }
|
|
955
|
+
const [resume, setResume] = useState(undefined); // where Resume carries on; null: nothing to resume
|
|
924
956
|
const clusterCount = degree2?.filter(d => d.source_connection_id === selected.id).length || 0;
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
background: 'rgba(0,255,136,0.12)', color: '#00ff88', fontWeight: 600, fontSize: 11,
|
|
943
|
-
}}
|
|
944
|
-
>
|
|
945
|
-
Rescan Cluster
|
|
946
|
-
</button>
|
|
947
|
-
</div>
|
|
948
|
-
);
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
async function startScrape() {
|
|
952
|
-
setStatus('checking');
|
|
953
|
-
setLog(['Checking the scanner...']);
|
|
957
|
+
const hasCluster = clusterCount > 0;
|
|
958
|
+
|
|
959
|
+
const mine = scan.running && scansCircleOf(scan, selected);
|
|
960
|
+
const busy = scan.running && !mine ? busyReason(scan) : null;
|
|
961
|
+
const last = scan.finished.find((j) => scansCircleOf(j, selected));
|
|
962
|
+
|
|
963
|
+
// Every read moves where Resume would carry on, so ask again whenever one ends.
|
|
964
|
+
const lastEnded = scan.finished[0]?.startedAt ?? null;
|
|
965
|
+
useEffect(() => {
|
|
966
|
+
let live = true;
|
|
967
|
+
resumePoint(selected.id).then((r) => { if (live) setResume(r); }, () => { if (live) setResume(null); });
|
|
968
|
+
return () => { live = false; };
|
|
969
|
+
}, [selected.id, lastEnded]);
|
|
970
|
+
|
|
971
|
+
async function start(action) {
|
|
972
|
+
setProblem(null);
|
|
973
|
+
setChecking(true);
|
|
954
974
|
const blocked = notReadyMessage(await scraperStatus().catch(() => null));
|
|
975
|
+
setChecking(false);
|
|
955
976
|
if (blocked) {
|
|
956
|
-
|
|
957
|
-
setLog([blocked]);
|
|
977
|
+
setProblem({ text: blocked, offline: true });
|
|
958
978
|
return;
|
|
959
979
|
}
|
|
960
|
-
|
|
961
|
-
setStatus('scraping');
|
|
962
|
-
setLog(['Mapping the circle behind ' + selected.name + '...']);
|
|
963
|
-
|
|
964
980
|
try {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
setLog(prev => [...prev, 'Done. Refresh to see the cluster.']);
|
|
969
|
-
} else {
|
|
970
|
-
setStatus('error');
|
|
971
|
-
}
|
|
972
|
-
} catch {
|
|
973
|
-
setStatus('error');
|
|
974
|
-
setLog(['Failed to start the scan']);
|
|
981
|
+
await beginScrape(action, action === 'resume' ? { id: selected.id } : { name: selected.name, id: selected.id });
|
|
982
|
+
} catch (e) {
|
|
983
|
+
setProblem({ text: e.message || 'Could not start the scan.' });
|
|
975
984
|
}
|
|
976
985
|
}
|
|
977
986
|
|
|
987
|
+
const read = hasCluster || Boolean(resume);
|
|
988
|
+
const accent = hasCluster ? '#00ff88' : '#9B59B6';
|
|
989
|
+
const off = Boolean(busy) || checking;
|
|
990
|
+
const button = (primary) => ({
|
|
991
|
+
flex: 1, padding: '11px 8px', borderRadius: 8, fontWeight: 700, fontSize: primary ? 13 : 12,
|
|
992
|
+
cursor: off ? 'not-allowed' : 'pointer', opacity: off ? 0.45 : 1,
|
|
993
|
+
...(primary
|
|
994
|
+
? { border: 'none', background: 'linear-gradient(135deg, #9B59B6, #3498DB)', color: '#fff' }
|
|
995
|
+
: { border: `1px solid ${accent}55`, background: `${accent}14`, color: accent }),
|
|
996
|
+
});
|
|
997
|
+
|
|
978
998
|
return (
|
|
979
|
-
<div style={{
|
|
980
|
-
background: 'rgba(
|
|
999
|
+
<div id="scan-box" style={{
|
|
1000
|
+
background: hasCluster ? 'rgba(0,255,136,0.06)' : 'rgba(155,89,182,0.08)',
|
|
1001
|
+
border: `1px solid ${hasCluster ? 'rgba(0,255,136,0.2)' : 'rgba(155,89,182,0.3)'}`,
|
|
981
1002
|
borderRadius: 10, padding: 16, marginTop: 16,
|
|
982
1003
|
}}>
|
|
983
|
-
<div style={{ fontSize: 10, color:
|
|
984
|
-
CREATE CLUSTER
|
|
1004
|
+
<div style={{ fontSize: 10, color: accent, fontWeight: 700, letterSpacing: 1, marginBottom: 4, textAlign: 'center' }}>
|
|
1005
|
+
{hasCluster ? 'CLUSTER ACTIVE' : 'CREATE CLUSTER'}
|
|
985
1006
|
</div>
|
|
986
|
-
<div style={{ fontSize: 12, color: '#
|
|
987
|
-
|
|
1007
|
+
<div style={{ fontSize: 12, color: '#aaa', marginBottom: 12, textAlign: 'center' }}>
|
|
1008
|
+
{hasCluster
|
|
1009
|
+
? `${clusterCount} connections mapped in 6 Degrees`
|
|
1010
|
+
: <>Scan {selected.name}'s connections to map their network</>}
|
|
988
1011
|
</div>
|
|
989
1012
|
|
|
990
|
-
{
|
|
991
|
-
<
|
|
992
|
-
|
|
993
|
-
style={{
|
|
994
|
-
width: '100%', padding: '12px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
995
|
-
background: 'linear-gradient(135deg, #9B59B6, #3498DB)',
|
|
996
|
-
color: '#fff', fontWeight: 700, fontSize: 14,
|
|
997
|
-
}}
|
|
998
|
-
>
|
|
999
|
-
Scan {selected.name}'s Network
|
|
1000
|
-
</button>
|
|
1001
|
-
)}
|
|
1002
|
-
|
|
1003
|
-
{status === 'offline' && (
|
|
1013
|
+
{mine ? (
|
|
1014
|
+
<ScanLog title={scan.pending ? 'Starting…' : `Scanning ${selected.name}’s circle…`} log={scan.log} />
|
|
1015
|
+
) : problem?.offline ? (
|
|
1004
1016
|
<>
|
|
1005
1017
|
<div style={{ padding: '10px', borderRadius: 8, background: 'rgba(255,80,80,0.1)', border: '1px solid rgba(255,80,80,0.3)', color: '#ff5050', fontSize: 12, marginBottom: 8, textAlign: 'center' }}>
|
|
1006
1018
|
Scanner offline
|
|
1007
1019
|
</div>
|
|
1008
1020
|
<div style={{ fontSize: 10, color: '#888', textAlign: 'center', marginBottom: 8 }}>
|
|
1009
|
-
Open the <strong>Scan</strong> page to finish setting the scanner up
|
|
1021
|
+
{problem.text} Open the <strong>Scan</strong> page to finish setting the scanner up.
|
|
1010
1022
|
</div>
|
|
1011
|
-
<button onClick={() =>
|
|
1023
|
+
<button onClick={() => setProblem(null)} style={{
|
|
1012
1024
|
width: '100%', padding: '10px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
1013
1025
|
background: 'rgba(255,255,255,0.1)', color: '#aaa', fontWeight: 600, fontSize: 12,
|
|
1014
1026
|
}}>
|
|
1015
1027
|
Retry
|
|
1016
1028
|
</button>
|
|
1017
1029
|
</>
|
|
1018
|
-
)
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1030
|
+
) : (
|
|
1031
|
+
<>
|
|
1032
|
+
{last && <LastRead job={last} />}
|
|
1033
|
+
<div style={{ display: 'flex', gap: 8 }}>
|
|
1034
|
+
{resume && (
|
|
1035
|
+
<button onClick={() => start('resume')} disabled={off} style={button(true)}>
|
|
1036
|
+
Resume from page {resume.nextPage}
|
|
1037
|
+
</button>
|
|
1038
|
+
)}
|
|
1039
|
+
<button onClick={() => start('bridge')} disabled={off} style={button(!resume && !hasCluster)}>
|
|
1040
|
+
{read ? 'Rescan from the start' : <>Scan {selected.name}'s Network</>}
|
|
1041
|
+
</button>
|
|
1042
|
+
</div>
|
|
1043
|
+
{resume && (
|
|
1044
|
+
<div style={{ fontSize: 10, color: '#888', marginTop: 8, lineHeight: 1.45, textAlign: 'center' }}>
|
|
1045
|
+
Read to page {resume.pagesRead} so far. Resume carries on from page {resume.nextPage}. LinkedIn
|
|
1046
|
+
lists their connections in its own order, not by date, so Rescan reads them all again from page 1.
|
|
1047
|
+
</div>
|
|
1048
|
+
)}
|
|
1049
|
+
{hasCluster && resume === null && (
|
|
1050
|
+
<div style={{ fontSize: 10, color: '#888', marginTop: 8, lineHeight: 1.45, textAlign: 'center' }}>
|
|
1051
|
+
Their whole list has been read. LinkedIn doesn't date other people's connections, so a
|
|
1052
|
+
rescan reads it all again to find anyone new.
|
|
1029
1053
|
</div>
|
|
1030
|
-
|
|
1031
|
-
|
|
1054
|
+
)}
|
|
1055
|
+
{busy && (
|
|
1056
|
+
<div style={{ fontSize: 10, color: '#bbb', marginTop: 8, textAlign: 'center' }}>
|
|
1057
|
+
{busy}. One scan at a time: this one can start when it finishes.
|
|
1032
1058
|
</div>
|
|
1033
|
-
|
|
1034
|
-
|
|
1059
|
+
)}
|
|
1060
|
+
{problem && (
|
|
1061
|
+
<div style={{ fontSize: 11, color: '#ff8080', marginTop: 8, textAlign: 'center' }}>{problem.text}</div>
|
|
1062
|
+
)}
|
|
1063
|
+
</>
|
|
1035
1064
|
)}
|
|
1065
|
+
</div>
|
|
1066
|
+
);
|
|
1067
|
+
}
|
|
1036
1068
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1069
|
+
/** A running scan's log, newest at the bottom. */
|
|
1070
|
+
function ScanLog({ title, log, small = false }) {
|
|
1071
|
+
return (
|
|
1072
|
+
<div style={{
|
|
1073
|
+
padding: '10px', borderRadius: 8, background: 'rgba(155,89,182,0.1)', border: '1px solid rgba(155,89,182,0.3)',
|
|
1074
|
+
}}>
|
|
1075
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
|
|
1076
|
+
<div style={{ width: small ? 6 : 8, height: small ? 6 : 8, borderRadius: '50%', background: '#9B59B6', animation: 'pulse 1s infinite' }} />
|
|
1077
|
+
<span style={{ fontSize: 11, fontWeight: 600, color: '#9B59B6' }}>{title}</span>
|
|
1078
|
+
</div>
|
|
1079
|
+
<div style={{ fontFamily: 'monospace', fontSize: small ? 9 : 10, color: '#888', maxHeight: small ? 80 : 100, overflow: 'auto' }}>
|
|
1080
|
+
{log.slice(-40).map((l, i) => <div key={i}>{l}</div>)}
|
|
1081
|
+
</div>
|
|
1082
|
+
</div>
|
|
1083
|
+
);
|
|
1084
|
+
}
|
|
1045
1085
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1086
|
+
/** How a scan this page watched ended. */
|
|
1087
|
+
function LastRead({ job, who = null }) {
|
|
1088
|
+
const ok = job.exitCode === 0;
|
|
1089
|
+
const why = !ok && Array.isArray(job.failure) && job.failure.length ? job.failure[job.failure.length - 1] : null;
|
|
1090
|
+
return (
|
|
1091
|
+
<div style={{
|
|
1092
|
+
padding: '8px 10px', borderRadius: 8, marginBottom: 10, fontSize: 11, textAlign: 'center', lineHeight: 1.4,
|
|
1093
|
+
background: ok ? 'rgba(0,255,136,0.08)' : 'rgba(255,80,80,0.08)',
|
|
1094
|
+
border: `1px solid ${ok ? 'rgba(0,255,136,0.25)' : 'rgba(255,80,80,0.25)'}`,
|
|
1095
|
+
color: ok ? '#00ff88' : '#ff8080',
|
|
1096
|
+
}}>
|
|
1097
|
+
{ok
|
|
1098
|
+
? `${who ? `${who}’s circle` : 'The scan'} finished. Refresh the page to see what it found.`
|
|
1099
|
+
: `${who ? `${who}’s scan` : 'The scan'} stopped before the end${why ? `: ${why}` : '.'}`}
|
|
1059
1100
|
</div>
|
|
1060
1101
|
);
|
|
1061
1102
|
}
|
|
@@ -1277,18 +1318,27 @@ function generateInsights(person, user, allConnections, degree2, routes = []) {
|
|
|
1277
1318
|
return insights.slice(0, 5); // Max 5 insights
|
|
1278
1319
|
}
|
|
1279
1320
|
|
|
1321
|
+
// The bridge section's "map the next one" button. Any circle being read shows
|
|
1322
|
+
// here, whoever started it; anything else running greys the button out.
|
|
1280
1323
|
function AutoBridgeButton({ connections, degree2 }) {
|
|
1281
|
-
const
|
|
1282
|
-
const [
|
|
1283
|
-
const [
|
|
1324
|
+
const scan = useScanner();
|
|
1325
|
+
const [checking, setChecking] = useState(false);
|
|
1326
|
+
const [problem, setProblem] = useState(null);
|
|
1284
1327
|
|
|
1328
|
+
// Read since the page loaded, so their circle isn't on screen until a
|
|
1329
|
+
// refresh: never offered as next again meanwhile.
|
|
1330
|
+
const readHere = new Set(scan.finished.filter(isCircleScan).map((j) => j.target?.id).filter(Boolean));
|
|
1285
1331
|
// Find the top S-tier person without a cluster
|
|
1286
1332
|
const bridgedIds = new Set(degree2.map(d => d.source_connection_id).filter(Boolean));
|
|
1287
1333
|
const unbridged = (connections || [])
|
|
1288
|
-
.filter(c => (c.tier === 'S' || c.tier === 'A') && !bridgedIds.has(c.id))
|
|
1334
|
+
.filter(c => (c.tier === 'S' || c.tier === 'A') && !bridgedIds.has(c.id) && !readHere.has(c.id))
|
|
1289
1335
|
.sort((a, b) => (parseFloat(b.power_score) || 0) - (parseFloat(a.power_score) || 0));
|
|
1290
1336
|
|
|
1291
|
-
|
|
1337
|
+
const circle = scan.running && isCircleScan(scan);
|
|
1338
|
+
const busy = scan.running && !circle ? busyReason(scan) : null;
|
|
1339
|
+
const last = scan.finished.find(isCircleScan);
|
|
1340
|
+
|
|
1341
|
+
if (unbridged.length === 0 && !scan.running && !last) {
|
|
1292
1342
|
return (
|
|
1293
1343
|
<div style={{
|
|
1294
1344
|
padding: '10px', borderRadius: 8, marginBottom: 16,
|
|
@@ -1303,95 +1353,64 @@ function AutoBridgeButton({ connections, degree2 }) {
|
|
|
1303
1353
|
async function startAutoBridge() {
|
|
1304
1354
|
const next = unbridged[0];
|
|
1305
1355
|
if (!next) return;
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
setLog([`Scanning ${next.name}'s network...`]);
|
|
1309
|
-
|
|
1356
|
+
setProblem(null);
|
|
1357
|
+
setChecking(true);
|
|
1310
1358
|
const blocked = notReadyMessage(await scraperStatus().catch(() => null));
|
|
1359
|
+
setChecking(false);
|
|
1311
1360
|
if (blocked) {
|
|
1312
|
-
|
|
1313
|
-
setLog([blocked]);
|
|
1361
|
+
setProblem(blocked);
|
|
1314
1362
|
return;
|
|
1315
1363
|
}
|
|
1316
|
-
|
|
1317
|
-
setStatus('scraping');
|
|
1318
1364
|
try {
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
setStatus('error');
|
|
1365
|
+
await beginScrape('bridge', { name: next.name, id: next.id });
|
|
1366
|
+
} catch (e) {
|
|
1367
|
+
setProblem(e.message || 'Could not start the scan.');
|
|
1323
1368
|
}
|
|
1324
1369
|
}
|
|
1325
1370
|
|
|
1371
|
+
const off = Boolean(busy) || checking;
|
|
1326
1372
|
return (
|
|
1327
1373
|
<div style={{
|
|
1328
1374
|
background: 'rgba(155,89,182,0.08)', border: '1px solid rgba(155,89,182,0.2)',
|
|
1329
1375
|
borderRadius: 10, padding: 14, marginBottom: 16,
|
|
1330
1376
|
}}>
|
|
1331
|
-
{
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
{unbridged.length} bridges unmapped
|
|
1335
|
-
</div>
|
|
1336
|
-
<div style={{ fontSize: 11, color: '#aaa', marginBottom: 8 }}>
|
|
1337
|
-
Next: <strong>{unbridged[0]?.name}</strong> ({unbridged[0]?.tier}-tier, {parseFloat(unbridged[0]?.power_score).toFixed(1)})
|
|
1338
|
-
</div>
|
|
1339
|
-
<button onClick={startAutoBridge} style={{
|
|
1340
|
-
width: '100%', padding: '10px', borderRadius: 8, border: 'none', cursor: 'pointer',
|
|
1341
|
-
background: 'linear-gradient(135deg, #9B59B6, #FF6B35)',
|
|
1342
|
-
color: '#fff', fontWeight: 700, fontSize: 13,
|
|
1343
|
-
}}>
|
|
1344
|
-
Auto-Bridge Next
|
|
1345
|
-
</button>
|
|
1346
|
-
</>
|
|
1347
|
-
)}
|
|
1348
|
-
|
|
1349
|
-
{status === 'offline' && (
|
|
1377
|
+
{circle ? (
|
|
1378
|
+
<ScanLog small title={scan.pending ? 'Starting…' : `Scanning ${scan.target?.name || 'a circle'}…`} log={scan.log} />
|
|
1379
|
+
) : (
|
|
1350
1380
|
<>
|
|
1351
|
-
<
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
background: '
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
)}
|
|
1385
|
-
|
|
1386
|
-
{status === 'error' && (
|
|
1387
|
-
<>
|
|
1388
|
-
<div style={{ padding: '8px', borderRadius: 6, background: 'rgba(255,80,80,0.1)', color: '#ff5050', fontSize: 11, marginBottom: 6, textAlign: 'center' }}>
|
|
1389
|
-
Failed — connections may be private
|
|
1390
|
-
</div>
|
|
1391
|
-
<button onClick={() => { setStatus('idle'); setTarget(null); setLog([]); }} style={{
|
|
1392
|
-
width: '100%', padding: '8px', borderRadius: 6, border: 'none', cursor: 'pointer',
|
|
1393
|
-
background: 'rgba(255,255,255,0.1)', color: '#aaa', fontSize: 11,
|
|
1394
|
-
}}>Try Next</button>
|
|
1381
|
+
{last && <LastRead job={last} who={last.target?.name} />}
|
|
1382
|
+
{unbridged.length > 0 ? (
|
|
1383
|
+
<>
|
|
1384
|
+
<div style={{ fontSize: 11, color: '#9B59B6', fontWeight: 700, marginBottom: 6 }}>
|
|
1385
|
+
{unbridged.length} bridges unmapped
|
|
1386
|
+
</div>
|
|
1387
|
+
<div style={{ fontSize: 11, color: '#aaa', marginBottom: 8 }}>
|
|
1388
|
+
Next: <strong>{unbridged[0].name}</strong> ({unbridged[0].tier}-tier, {parseFloat(unbridged[0].power_score).toFixed(1)})
|
|
1389
|
+
</div>
|
|
1390
|
+
<button onClick={startAutoBridge} disabled={off} style={{
|
|
1391
|
+
width: '100%', padding: '10px', borderRadius: 8, border: 'none',
|
|
1392
|
+
cursor: off ? 'not-allowed' : 'pointer', opacity: off ? 0.45 : 1,
|
|
1393
|
+
background: 'linear-gradient(135deg, #9B59B6, #FF6B35)',
|
|
1394
|
+
color: '#fff', fontWeight: 700, fontSize: 13,
|
|
1395
|
+
}}>
|
|
1396
|
+
Auto-Bridge Next
|
|
1397
|
+
</button>
|
|
1398
|
+
</>
|
|
1399
|
+
) : (
|
|
1400
|
+
<div style={{ color: '#00ff88', fontSize: 11, fontWeight: 600, textAlign: 'center' }}>
|
|
1401
|
+
All S/A-tier bridges mapped
|
|
1402
|
+
</div>
|
|
1403
|
+
)}
|
|
1404
|
+
{busy && (
|
|
1405
|
+
<div style={{ fontSize: 10, color: '#bbb', marginTop: 8, textAlign: 'center' }}>
|
|
1406
|
+
{busy}. One scan at a time: this one can start when it finishes.
|
|
1407
|
+
</div>
|
|
1408
|
+
)}
|
|
1409
|
+
{problem && (
|
|
1410
|
+
<div style={{ padding: '8px', borderRadius: 6, background: 'rgba(255,80,80,0.1)', color: '#ff5050', fontSize: 11, marginTop: 8, textAlign: 'center' }}>
|
|
1411
|
+
{problem}
|
|
1412
|
+
</div>
|
|
1413
|
+
)}
|
|
1395
1414
|
</>
|
|
1396
1415
|
)}
|
|
1397
1416
|
</div>
|