@aeolus-fleet/gateway 0.36.0 → 0.36.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/dist/person-waiters.js +3 -1
- package/dist/person-waiters.js.map +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +3 -3
- package/web/.next/diagnostics/route-bundle-stats.json +40 -40
- package/web/.next/fallback-build-manifest.json +3 -3
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/server/app/(console)/bridge/basket/page/server-reference-manifest.json +7 -7
- package/web/.next/server/app/(console)/bridge/basket/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/bridge/basket/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/bridge/done/page/server-reference-manifest.json +7 -7
- package/web/.next/server/app/(console)/bridge/done/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/bridge/done/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/bridge/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/(console)/bridge/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/bridge/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/bridge/settings/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/(console)/bridge/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/bridge/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/bridge/settings/requests/[requestId]/page/server-reference-manifest.json +2 -2
- package/web/.next/server/app/(console)/bridge/settings/requests/[requestId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/bridge/settings/requests/[requestId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/bridge/settings/routing/page/server-reference-manifest.json +5 -5
- package/web/.next/server/app/(console)/bridge/settings/routing/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/bridge/settings/routing/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/gateway-off/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/(console)/gateway-off/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/gateway-off/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/meetings/[meetingId]/page/server-reference-manifest.json +3 -3
- package/web/.next/server/app/(console)/meetings/[meetingId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/meetings/[meetingId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/outside-parties/[name]/change/page/server-reference-manifest.json +17 -17
- package/web/.next/server/app/(console)/outside-parties/[name]/change/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/outside-parties/[name]/change/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/outside-parties/[name]/page/server-reference-manifest.json +8 -8
- package/web/.next/server/app/(console)/outside-parties/[name]/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/outside-parties/[name]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/outside-parties/[name]/retire/page/server-reference-manifest.json +17 -17
- package/web/.next/server/app/(console)/outside-parties/[name]/retire/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/outside-parties/[name]/retire/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/outside-parties/add/page/server-reference-manifest.json +10 -10
- package/web/.next/server/app/(console)/outside-parties/add/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/outside-parties/add/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/outside-parties/add-a2a-agent/page/server-reference-manifest.json +2 -2
- package/web/.next/server/app/(console)/outside-parties/add-a2a-agent/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/outside-parties/add-a2a-agent/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/outside-parties/add-a2a-client/page/server-reference-manifest.json +2 -2
- package/web/.next/server/app/(console)/outside-parties/add-a2a-client/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/outside-parties/add-a2a-client/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/outside-parties/page/server-reference-manifest.json +2 -2
- package/web/.next/server/app/(console)/outside-parties/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/outside-parties/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/parties/page/server-reference-manifest.json +9 -9
- package/web/.next/server/app/(console)/parties/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/parties/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/sign-in/failed/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/(console)/sign-in/failed/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/sign-in/failed/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/slack/disconnect/page/server-reference-manifest.json +11 -11
- package/web/.next/server/app/(console)/slack/disconnect/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/slack/disconnect/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/slack/expose/page/server-reference-manifest.json +11 -11
- package/web/.next/server/app/(console)/slack/expose/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/slack/expose/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/slack/page/server-reference-manifest.json +11 -11
- package/web/.next/server/app/(console)/slack/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/slack/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(console)/slack/remove/page/server-reference-manifest.json +11 -11
- package/web/.next/server/app/(console)/slack/remove/page.js.nft.json +1 -1
- package/web/.next/server/app/(console)/slack/remove/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +1 -1
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/authorize/failed/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/authorize/failed/page.js.nft.json +1 -1
- package/web/.next/server/app/authorize/failed/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/authorize/pick/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/authorize/pick/page.js.nft.json +1 -1
- package/web/.next/server/app/authorize/pick/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/authorize/waiting/approval/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/authorize/waiting/approval/page.js.nft.json +1 -1
- package/web/.next/server/app/authorize/waiting/approval/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/ssr/01fr_app_(console)_bridge_settings_requests_[requestId]_page_actions_1au-p5lgnv5e-.js +1 -1
- package/web/.next/server/chunks/ssr/01fr_app_(console)_bridge_settings_requests_[requestId]_page_actions_1au-p5lgnv5e-.js.map +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_bridge_settings_routing_page_actions_136lm_jip4d_x.js +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_bridge_settings_routing_page_actions_136lm_jip4d_x.js.map +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_[name]_change_page_actions_04td11fxohsfm.js +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_[name]_change_page_actions_04td11fxohsfm.js.map +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_[name]_page_actions_0u-45itza1ls7.js +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_[name]_page_actions_0u-45itza1ls7.js.map +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_[name]_retire_page_actions_0s7ps6i0ts7v5.js +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_[name]_retire_page_actions_0s7ps6i0ts7v5.js.map +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_add-a2a-client_page_actions_0d_wgiu94c32e.js +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_add-a2a-client_page_actions_0d_wgiu94c32e.js.map +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_add_page_actions_0yfz_dfn-8uo7.js +1 -1
- package/web/.next/server/chunks/ssr/0vkm_server_app_(console)_outside-parties_add_page_actions_0yfz_dfn-8uo7.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__001xx3sm5ur-e._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__001xx3sm5ur-e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__0a6a1yc_ac-qt._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__0a8db18tyk__5._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__0a8db18tyk__5._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__0k3g0p3t9t0c-._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__0k3g0p3t9t0c-._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__10s9e8g5x-ws8._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__10s9e8g5x-ws8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__13yoncr_nx3jc._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__13yoncr_nx3jc._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__18-8taiduk1jn._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__18-8taiduk1jn._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__18v7b5ga58a9n._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__1qpow96uit9t8._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__1qpow96uit9t8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_00hfzlqn1og18._.js +1 -1
- package/web/.next/server/chunks/ssr/_00hfzlqn1og18._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_0a_nsm-ye2r6v._.js +1 -1
- package/web/.next/server/chunks/ssr/_0a_nsm-ye2r6v._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_0ka_9elusb800._.js +1 -1
- package/web/.next/server/chunks/ssr/_0ka_9elusb800._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_0pyd1_y7w8ngp._.js +1 -1
- package/web/.next/server/chunks/ssr/_0pyd1_y7w8ngp._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_0xickgbbju_9-._.js +1 -1
- package/web/.next/server/chunks/ssr/_0xickgbbju_9-._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_10-gn6g9yc63s._.js +3 -0
- package/web/.next/server/chunks/ssr/{_0swrejvekuzfx._.js.map → _10-gn6g9yc63s._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_177fdczus39s7._.js +3 -0
- package/web/.next/server/chunks/ssr/{_1qdpkpsskm5wj._.js.map → _177fdczus39s7._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_1_wav28san6dm._.js +1 -1
- package/web/.next/server/chunks/ssr/_1e2o809f0ekry._.js +1 -1
- package/web/.next/server/chunks/ssr/_1pjtire7rqla0._.js +1 -1
- package/web/.next/server/chunks/ssr/_1pjtire7rqla0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_1qbsmlji34yph._.js +1 -1
- package/web/.next/server/chunks/ssr/_1qbsmlji34yph._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_0wys4l6za8umr._.js +1 -1
- package/web/.next/server/chunks/ssr/web_0wys4l6za8umr._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web__next-internal_server_app_(console)_bridge_basket_page_actions_102xk3g8eko_q.js +1 -1
- package/web/.next/server/chunks/ssr/web__next-internal_server_app_(console)_bridge_basket_page_actions_102xk3g8eko_q.js.map +1 -1
- package/web/.next/server/chunks/ssr/web__next-internal_server_app_(console)_bridge_done_page_actions_0mm90xd2feta2.js +1 -1
- package/web/.next/server/chunks/ssr/web__next-internal_server_app_(console)_bridge_done_page_actions_0mm90xd2feta2.js.map +1 -1
- package/web/.next/server/chunks/ssr/web__next-internal_server_app_(console)_bridge_page_actions_082880qz6xa5k.js +1 -1
- package/web/.next/server/chunks/ssr/web__next-internal_server_app_(console)_bridge_page_actions_082880qz6xa5k.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_16cy3vmcijydk._.js +3 -0
- package/web/.next/server/chunks/ssr/{web_features_1je122n6carea._.js.map → web_features_16cy3vmcijydk._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/web_features_1iiqi6dhrst33._.js +3 -0
- package/web/.next/server/chunks/ssr/{web_features_1nrnskep6p8ci._.js.map → web_features_1iiqi6dhrst33._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/web_features_add-a2a-agent_ui_a2a-agent-form_tsx_0z8t1aalwzyl7._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_add-a2a-agent_ui_a2a-agent-form_tsx_0z8t1aalwzyl7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_add-a2a-client_ui_a2a-client-form_tsx_1anv88g385nwe._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_add-a2a-client_ui_a2a-client-form_tsx_1anv88g385nwe._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_add-server_ui_add-server_tsx_1jnmar5xju49k._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_add-server_ui_add-server_tsx_1jnmar5xju49k._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_hooks_bridge-actions_ts_0h-_xm2rcb-ij._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_state_0354e3wb0dzx1._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_state_0354e3wb0dzx1._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_state_basket-panel_tsx_1qi_kzld0rfgs._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_state_basket-panel_tsx_1qi_kzld0rfgs._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_state_decision-panel_tsx_1mzsixgc8zuxc._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_state_decision-panel_tsx_1mzsixgc8zuxc._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_state_done-panel_tsx_1m844-qb1z9p_._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_bridge_state_done-panel_tsx_1m844-qb1z9p_._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_frame_ui_gateway-header_tsx_0e9tekx4jf7ng._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_frame_ui_gateway-header_tsx_0e9tekx4jf7ng._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_meeting_state_meeting-controls_tsx_12g4kzk2mbvvz._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_meeting_state_meeting-controls_tsx_12g4kzk2mbvvz._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_outside-parties_ui_switch-card_tsx_1_xnu9bm4tc-_._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_outside-parties_ui_switch-card_tsx_1_xnu9bm4tc-_._.js.map +1 -1
- package/web/.next/server/chunks/ssr/web_features_outside-party_actions_ts_102ar5pdowf8s._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_parties_actions_ts_0r-tbfsig2ty-._.js +1 -1
- package/web/.next/server/chunks/ssr/web_features_slack_actions_ts_1l3yg9vl20_vu._.js +1 -1
- package/web/.next/server/middleware-build-manifest.js +3 -3
- package/web/.next/server/pages/500.html +1 -1
- package/web/.next/server/preview-props.json +3 -3
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +50 -50
- package/web/.next/static/chunks/0az9upwm-8_q9.js +1 -0
- package/web/.next/static/chunks/0gbozvd_yh421.js +1 -0
- package/web/.next/static/chunks/{1lyv8ukt_q3ph.js → 0l9_oie6xi0kd.js} +1 -1
- package/web/.next/static/chunks/{3rt603msircrl.js → 0m512dxdcrv-b.js} +1 -1
- package/web/.next/static/chunks/{2799kv0y5g2va.js → 1485slb5zus5p.js} +1 -1
- package/web/.next/static/chunks/{36fnm0ezsx1eh.js → 24olc0btcvn7q.js} +1 -1
- package/web/.next/static/chunks/{1ymfhxs-xqiy6.js → 296w-74ry5lzk.js} +1 -1
- package/web/.next/static/chunks/{40j58bya72xh1.js → 2z2auwx1s2mgl.js} +1 -1
- package/web/.next/static/chunks/{0fu7je2vzzzh7.js → 397zvdr6xr5pq.js} +1 -1
- package/web/.next/static/chunks/{3hvw5n04pbwg8.js → 3db90zxmny2y-.js} +1 -1
- package/web/.next/static/chunks/{322fzslly9wpf.js → 3x0p-z3zdrk_b.js} +1 -1
- package/web/.next/static/chunks/{1ehmzxr697ifa.js → 3yq9jt4p_0uid.js} +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/.next/server/chunks/ssr/_0swrejvekuzfx._.js +0 -3
- package/web/.next/server/chunks/ssr/_1qdpkpsskm5wj._.js +0 -3
- package/web/.next/server/chunks/ssr/web_features_1je122n6carea._.js +0 -3
- package/web/.next/server/chunks/ssr/web_features_1nrnskep6p8ci._.js +0 -3
- package/web/.next/static/chunks/2jr8r6g9kk42a.js +0 -1
- package/web/.next/static/chunks/3f2trjz7_wrzh.js +0 -1
- /package/web/.next/static/{IMWKx2sJwt7EwwpStr8tb → CMppMkysNciqc8k4TWwia}/_buildManifest.js +0 -0
- /package/web/.next/static/{IMWKx2sJwt7EwwpStr8tb → CMppMkysNciqc8k4TWwia}/_clientMiddlewareManifest.js +0 -0
- /package/web/.next/static/{IMWKx2sJwt7EwwpStr8tb → CMppMkysNciqc8k4TWwia}/_ssgManifest.js +0 -0
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
(()=>{"use strict";module.exports=[87952,a=>{var b=a.i(87924),c=a.i(72131),d=a.i(5050);let e=(0,d.createServerReference)("
|
|
1
|
+
(()=>{"use strict";module.exports=[87952,a=>{var b=a.i(87924),c=a.i(72131),d=a.i(5050);let e=(0,d.createServerReference)("40d55047d0fa263dfb22afcf17c36e422f88773b7f",d.callServer,void 0,d.findSourceMapURL,"turnRoute"),f=["slack","chat","web-push","email"],g={decide:{name:"Decide",holds:"Questions from your ships",item:"question",items:"questions"},do:{name:"Do what only you can",holds:"Steps: sign in, create, approve",item:"step",items:"steps"},"stay-aware":{name:"Stay aware",holds:"Alerts and notices",item:"alert or notice",items:"alerts and notices"},"everything-else":{name:"Everything else",holds:"Replies and other messages",item:"message",items:"messages"}},h={slack:"Slack",chat:"A chat","web-push":"Web push",email:"Email"},i="Bridge keeps every item.";var j=a.i(10039);a.s(["RoutingPanel",0,function({fleetId:a,routing:d,isSlackLinked:k}){let[l,m]=(0,c.useTransition)(),[n,o]=(0,c.useState)(),[p,q]=(0,c.useState)();return(0,b.jsxs)("section",{className:"grid gap-2","aria-labelledby":"bridge-routing-heading",children:[(0,b.jsx)("h2",{id:"bridge-routing-heading",className:"text-section font-semibold",children:"Per job"}),(0,b.jsx)("div",{className:"overflow-x-auto rounded-md border border-border bg-card",children:(0,b.jsxs)("table",{className:"w-full text-left",children:[(0,b.jsx)("thead",{children:(0,b.jsxs)("tr",{className:"border-b border-border text-caption text-muted-foreground",children:[(0,b.jsx)("th",{scope:"col",className:"p-3 font-medium",children:"Job"}),(0,b.jsx)("th",{scope:"col",className:"p-3 font-medium",children:"Bridge"}),f.map(a=>(0,b.jsx)("th",{scope:"col",className:"p-3 font-medium",children:h[a]},a))]})}),(0,b.jsx)("tbody",{children:d.jobs.map(({job:c,places:j})=>(0,b.jsxs)("tr",{"data-testid":"bridge-routing-job",className:"border-b border-border last:border-b-0",children:[(0,b.jsx)("th",{scope:"row",className:"p-3 font-normal",children:(0,b.jsxs)("span",{className:"flex flex-col",children:[(0,b.jsx)("span",{children:g[c].name}),(0,b.jsx)("span",{className:"text-caption text-muted-foreground",children:g[c].holds})]})}),(0,b.jsx)("td",{className:"p-3 text-caption text-muted-foreground",children:"Always"}),f.map(f=>{let p=j[f],r=`${c}-${f}`;return(0,b.jsx)("td",{className:"p-3",children:(0,b.jsx)("button",{type:"button","data-testid":`bridge-routing-${r}`,"aria-pressed":p,"aria-label":`${h[f]} for ${g[c].name}`,"aria-busy":l&&n===r,disabled:l||!p&&(!d.offered[f]||"slack"===f&&!k),onClick:()=>{var b;b={job:c,place:f,isOn:!p},q(void 0),o(`${b.job}-${b.place}`),m(async()=>{let c=await e({fleet:a,...b});q(c.isOk?{message:function({job:a,place:b,isOn:c}){let{name:d,item:e,items:f}=g[a],j=`${h[b]} is ${c?"on":"off"} for ${d}`;return c?"chat"===b?`${j}: it lists every ${e} not done when you ask it.`:`${j}: it shows ${f} that arrive from now on. Those already waiting stay in Bridge.`:"everything-else"===a&&"slack"===b?`${j}: only a bot ship's messages and replies to Slack show there. ${i}`:`${j}. ${i}`}(b),isFailure:!1}:{message:c.message,isFailure:!0})})},className:`inline-flex h-7 min-w-12 items-center justify-center rounded-full px-3 text-caption disabled:cursor-not-allowed disabled:opacity-50 ${p?"bg-primary text-primary-foreground":"border border-border bg-card text-muted-foreground hover:bg-accent"}`,children:p?"On":"Off"})},f)})]},c))})]})}),void 0===p?null:(0,b.jsx)(j.f,{message:p.message,isFailure:p.isFailure}),(0,b.jsx)("p",{className:"text-caption text-muted-foreground",children:"While a Bridge page is open in focus, web push and email do not wake you. Slack still posts: you turned it on, and Bridge keeps every item."})]})}],87952)},15970,a=>{var b=a.i(87924),c=a.i(72131),d=a.i(5050);let e=(0,d.createServerReference)("4068e0a012962959eed53cdc1898b8acfd21d81a71",d.callServer,void 0,d.findSourceMapURL,"unlinkSlack");var f=a.i(32043),g=a.i(10039);a.s(["SlackLinkPanel",0,function({fleetId:a,slackLink:d}){let[h,i]=(0,c.useTransition)(),[j,k]=(0,c.useState)(),{workspaceName:l,link:m}=d;return(0,b.jsxs)("section",{className:"grid gap-2","aria-labelledby":"bridge-slack-heading",children:[(0,b.jsx)("h2",{id:"bridge-slack-heading",className:"text-section font-semibold",children:"Slack"}),(0,b.jsxs)("div",{className:"flex flex-wrap items-center gap-3 rounded-md border border-border bg-card p-3",children:[(0,b.jsx)("span",{"data-testid":"bridge-slack-state",className:"min-w-0 flex-1",children:null===m?`Not linked in ${l}`:`Slack linked as @${m.slackName} in ${l}`}),null===m?(0,b.jsx)("a",{href:`/slack/link?${new URLSearchParams({fleet:a}).toString()}`,"data-testid":"bridge-slack-link",className:"inline-flex h-9 items-center rounded-md bg-primary px-4 text-primary-foreground hover:bg-primary-hover",children:"Link Slack"}):(0,b.jsx)(f.f,{"data-testid":"bridge-slack-unlink",isPending:h,pendingLabel:"Unlinking...",onClick:()=>{k(void 0),i(async()=>{let b=await e(a);b.isOk||k(b.message)})},children:"Unlink"})]}),void 0===j?null:(0,b.jsx)(g.f,{message:j,isFailure:!0})]})}],15970)},62887,a=>{var b=a.i(87924),c=a.i(72131),d=a.i(5050);let e=(0,d.createServerReference)("601545266cbcd00188699907a363d2d01cacdd7590",d.callServer,void 0,d.findSourceMapURL,"turnOffWebPush"),f=(0,d.createServerReference)("606869fb42c6cf9c02d918ac32cc0df0df992ce8e8",d.callServer,void 0,d.findSourceMapURL,"turnOnWebPush"),g=[[/Edg\//,"Edge"],[/Firefox\//,"Firefox"],[/Chrome\//,"Chrome"],[/Safari\//,"Safari"]],h=[[/iPhone|iPad/,"iOS"],[/Android/,"Android"],[/Mac OS X/,"macOS"],[/Windows/,"Windows"],[/Linux/,"Linux"]],i=(a,b)=>b.find(([b])=>b.test(a))?.[1];var j=a.i(32043),k=a.i(10039);let l="/wake-worker.js";async function m(){let a=await navigator.serviceWorker.getRegistration(l);return await a?.pushManager.getSubscription()??null}async function n(a){let b;if(await Notification.requestPermission()!=="granted")return{isOk:!1,message:"Your browser did not allow notifications from this site: allow them, then turn web push on again"};let c=await navigator.serviceWorker.register(l);return await navigator.serviceWorker.ready,{isOk:!0,value:await c.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:(b=atob(a.replaceAll("-","+").replaceAll("_","/")),Uint8Array.from(b,a=>a.charCodeAt(0)))})}}a.s(["WebPushPanel",0,function({fleetId:a,webPushKey:d,browsers:l}){let[o,p]=(0,c.useState)(!0),[q,r]=(0,c.useState)(),[s,t]=(0,c.useState)(),[u,v]=(0,c.useState)(),[,w]=(0,c.useTransition)();(0,c.useEffect)(()=>{"serviceWorker"in navigator&&"PushManager"in window&&"Notification"in window?m().then(a=>{r(a?.endpoint)}):p(!1)},[]);let x=void 0!==q&&l.some(({endpoint:a,isGone:b})=>a===q&&!b);return(0,b.jsxs)("div",{className:"grid gap-2","data-testid":"bridge-wake-web-push",children:[l.map(({endpoint:c,browser:d,addedAt:f,isGone:g})=>(0,b.jsxs)("div",{className:"flex items-center gap-3 rounded-md border border-border bg-card p-3",children:[(0,b.jsxs)("span",{className:"flex min-w-0 flex-1 flex-col",children:[(0,b.jsxs)("span",{className:"truncate",children:["Web push in ",d,c===q?" · this browser":""]}),(0,b.jsx)("span",{className:"text-caption text-muted-foreground",children:g?"No longer takes wakes: its push service ended them. Turn web push on again in that browser, or remove it.":`On since ${new Date(f).toLocaleDateString()}: it tells you when something waits in this fleet, with a link here. Never what waits.`})]}),(0,b.jsx)(j.f,{"data-testid":"bridge-web-push-remove",isPending:s===c,pendingLabel:"Removing...",disabled:void 0!==s,onClick:()=>{t(c),v(void 0),w(async()=>{c===q&&(await (await m())?.unsubscribe(),r(void 0));let b=await e(a,c);t(void 0),b.isOk||v(b.message)})},children:"Remove"})]},c)),o?x?null:(0,b.jsx)("div",{children:(0,b.jsx)(j.f,{"data-testid":"bridge-web-push-on",variant:"primary",isPending:"on"===s,pendingLabel:"Turning on...",disabled:void 0!==s,onClick:()=>{t("on"),v(void 0),w(async()=>{try{var b;let c,e,j=await n(d);if(!j.isOk)return void v(j.message);let k=function(a,b){let{endpoint:c,keys:d}=a,e=d?.p256dh,f=d?.auth;return void 0===c||void 0===e||void 0===f?void 0:{endpoint:c,keys:{p256dh:e,auth:f},browser:b}}(j.value.toJSON(),(b=navigator.userAgent,c=i(b,g),e=i(b,h),void 0===c?"A browser":void 0===e?c:`${c} on ${e}`));if(void 0===k)return void v("Your browser gave no push subscription: try again");let l=await f(a,k);l.isOk?r(k.endpoint):v(l.message)}catch{v("Your browser could not turn web push on: try again")}finally{t(void 0)}})},children:"Turn on web push in this browser"})}):(0,b.jsx)("p",{className:"text-caption text-muted-foreground",children:"This browser takes no web push."}),void 0===u?null:(0,b.jsx)(k.f,{message:u,isFailure:!0})]})}],62887)}]})();
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=web_features_bridge_state_0354e3wb0dzx1._.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../web/features/bridge/state/routing-panel.tsx","../../../../../web/features/bridge/hooks/data%3A24f2e5%20%3Ctext/javascript%3E","../../../../../web/features/bridge/services/routing.ts","../../../../../web/features/bridge/state/slack-link-panel.tsx","../../../../../web/features/bridge/hooks/data%3Af76815%20%3Ctext/javascript%3E","../../../../../web/features/bridge/hooks/slack-link-path.ts","../../../../../web/features/bridge/state/web-push-panel.tsx","../../../../../web/features/bridge/hooks/data%3A285584%20%3Ctext/javascript%3E","../../../../../web/features/bridge/hooks/data%3A13e6d4%20%3Ctext/javascript%3E","../../../../../web/features/bridge/services/web-push.ts"],"sourcesContent":["'use client';\n\nimport { useState, useTransition } from 'react';\n\nimport { turnRoute } from '../hooks/bridge-actions';\nimport { JOB_NAMES, PLACE_NAMES, ROUTED_PLACES, turnedMessageOf, type RoutedJob, type RoutedPlace, type RoutingView } from '../services/routing';\nimport { Notice } from '../ui/notice';\n\n/** What a change came to, said by the grid. */\ninterface Said {\n readonly message: string;\n readonly isFailure: boolean;\n}\n\n/**\n * Routing per job (Bridge, section 8; the routing mockup): per job, the\n * places that show it besides Bridge, which always does. A switch turns a\n * place on or off at once; what it then shows is said beside the grid\n * (CC20). A place the gateway does not offer, or Slack before it is linked,\n * cannot be turned on; one on can always be turned off.\n */\nexport function RoutingPanel({ fleetId, routing, isSlackLinked }: { fleetId: string; routing: RoutingView; isSlackLinked: boolean }) {\n const [isTurning, startTransition] = useTransition();\n const [turning, setTurning] = useState<string | undefined>();\n const [said, setSaid] = useState<Said | undefined>();\n const isUsable = (place: RoutedPlace): boolean => routing.offered[place] && (place !== 'slack' || isSlackLinked);\n\n const turn = (change: { job: RoutedJob; place: RoutedPlace; isOn: boolean }) => {\n setSaid(undefined);\n setTurning(`${change.job}-${change.place}`);\n startTransition(async () => {\n const outcome = await turnRoute({ fleet: fleetId, ...change });\n setSaid(outcome.isOk ? { message: turnedMessageOf(change), isFailure: false } : { message: outcome.message, isFailure: true });\n });\n };\n\n return (\n <section className=\"grid gap-2\" aria-labelledby=\"bridge-routing-heading\">\n <h2 id=\"bridge-routing-heading\" className=\"text-section font-semibold\">\n Per job\n </h2>\n <div className=\"overflow-x-auto rounded-md border border-border bg-card\">\n <table className=\"w-full text-left\">\n <thead>\n <tr className=\"border-b border-border text-caption text-muted-foreground\">\n <th scope=\"col\" className=\"p-3 font-medium\">\n Job\n </th>\n <th scope=\"col\" className=\"p-3 font-medium\">\n Bridge\n </th>\n {ROUTED_PLACES.map((place) => (\n <th key={place} scope=\"col\" className=\"p-3 font-medium\">\n {PLACE_NAMES[place]}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {routing.jobs.map(({ job, places }) => (\n <tr key={job} data-testid=\"bridge-routing-job\" className=\"border-b border-border last:border-b-0\">\n <th scope=\"row\" className=\"p-3 font-normal\">\n <span className=\"flex flex-col\">\n <span>{JOB_NAMES[job].name}</span>\n <span className=\"text-caption text-muted-foreground\">{JOB_NAMES[job].holds}</span>\n </span>\n </th>\n <td className=\"p-3 text-caption text-muted-foreground\">Always</td>\n {ROUTED_PLACES.map((place) => {\n const isOn = places[place];\n const key = `${job}-${place}`;\n return (\n <td key={place} className=\"p-3\">\n <button\n type=\"button\"\n data-testid={`bridge-routing-${key}`}\n aria-pressed={isOn}\n aria-label={`${PLACE_NAMES[place]} for ${JOB_NAMES[job].name}`}\n aria-busy={isTurning && turning === key}\n disabled={isTurning || (!isOn && !isUsable(place))}\n onClick={() => {\n turn({ job, place, isOn: !isOn });\n }}\n className={`inline-flex h-7 min-w-12 items-center justify-center rounded-full px-3 text-caption disabled:cursor-not-allowed disabled:opacity-50 ${isOn ? 'bg-primary text-primary-foreground' : 'border border-border bg-card text-muted-foreground hover:bg-accent'}`}\n >\n {isOn ? 'On' : 'Off'}\n </button>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n {said === undefined ? null : <Notice message={said.message} isFailure={said.isFailure} />}\n <p className=\"text-caption text-muted-foreground\">While a Bridge page is open in focus, web push and email do not wake you. Slack still posts: you turned it on, and Bridge keeps every item.</p>\n </section>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"40720a2e7f7a70673a75e3a4c0d8b553caf05d2f9f\":{\"name\":\"turnRoute\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_7=/*#__PURE__*/createServerReference(\"40720a2e7f7a70673a75e3a4c0d8b553caf05d2f9f\",callServer,void 0,findSourceMapURL,\"turnRoute\");export{$$RSC_SERVER_ACTION_7 as turnRoute};","/**\n * Routing per job as Bridge's routing page shows it (Bridge, section 8):\n * the jobs and places by the names the person reads, and what a change\n * says it does (CC20).\n */\nimport type { inferRouterOutputs } from '@trpc/server';\n\nimport type { GatewayRouter } from '../../../../src/adapters/trpc/router.js';\n\n/** Where each job shows besides Bridge, per place, as the API answers it. */\nexport type RoutingView = inferRouterOutputs<GatewayRouter>['people']['routing'];\nexport type RoutedJob = RoutingView['jobs'][number]['job'];\nexport type RoutedPlace = keyof RoutingView['offered'];\n\nexport const ROUTED_PLACES: readonly RoutedPlace[] = ['slack', 'chat', 'web-push', 'email'];\n\nexport const JOB_NAMES: Readonly<Record<RoutedJob, { readonly name: string; readonly holds: string; readonly item: string; readonly items: string }>> = {\n decide: { name: 'Decide', holds: 'Questions from your ships', item: 'question', items: 'questions' },\n do: { name: 'Do what only you can', holds: 'Steps: sign in, create, approve', item: 'step', items: 'steps' },\n 'stay-aware': { name: 'Stay aware', holds: 'Alerts and notices', item: 'alert or notice', items: 'alerts and notices' },\n 'everything-else': { name: 'Everything else', holds: 'Replies and other messages', item: 'message', items: 'messages' },\n};\n\nexport const PLACE_NAMES: Readonly<Record<RoutedPlace, string>> = { slack: 'Slack', chat: 'A chat', 'web-push': 'Web push', email: 'Email' };\n\nconst KEEPS_EVERY_ITEM = 'Bridge keeps every item.';\n\n/** What turning a place on or off for a job does: a chat lists what is not done, a place that pushes only what arrives after. */\nexport function turnedMessageOf({ job, place, isOn }: { job: RoutedJob; place: RoutedPlace; isOn: boolean }): string {\n const { name, item, items } = JOB_NAMES[job];\n const turned = `${PLACE_NAMES[place]} is ${isOn ? 'on' : 'off'} for ${name}`;\n if (!isOn) {\n return job === 'everything-else' && place === 'slack' ? `${turned}: only a bot ship's messages and replies to Slack show there. ${KEEPS_EVERY_ITEM}` : `${turned}. ${KEEPS_EVERY_ITEM}`;\n }\n if (place === 'chat') {\n return `${turned}: it lists every ${item} not done when you ask it.`;\n }\n return `${turned}: it shows ${items} that arrive from now on. Those already waiting stay in Bridge.`;\n}\n","'use client';\n\nimport { useState, useTransition } from 'react';\n\nimport { unlinkSlack } from '../hooks/bridge-actions';\nimport type { SlackLink } from '../hooks/read-bridge';\nimport { SLACK_LINK_PATH } from '../hooks/slack-link-path';\nimport { ActionButton } from '../ui/action-button';\nimport { Notice } from '../ui/notice';\n\n/**\n * The person's Slack link (gateway scopes 9.3, 9.6): \"Slack linked as\n * @mathilde in Acme\" with Unlink, or Link Slack, which signs in with Slack.\n * Linked, the person talks to the fleet's bots in Slack; what else shows\n * there is routed per job, beside it (Bridge, section 8).\n */\nexport function SlackLinkPanel({ fleetId, slackLink }: { fleetId: string; slackLink: SlackLink }) {\n const [isUnlinking, startTransition] = useTransition();\n const [failure, setFailure] = useState<string | undefined>();\n const { workspaceName, link } = slackLink;\n\n const unlink = () => {\n setFailure(undefined);\n startTransition(async () => {\n const outcome = await unlinkSlack(fleetId);\n if (!outcome.isOk) {\n setFailure(outcome.message);\n }\n });\n };\n\n return (\n <section className=\"grid gap-2\" aria-labelledby=\"bridge-slack-heading\">\n <h2 id=\"bridge-slack-heading\" className=\"text-section font-semibold\">\n Slack\n </h2>\n <div className=\"flex flex-wrap items-center gap-3 rounded-md border border-border bg-card p-3\">\n <span data-testid=\"bridge-slack-state\" className=\"min-w-0 flex-1\">\n {link === null ? `Not linked in ${workspaceName}` : `Slack linked as @${link.slackName} in ${workspaceName}`}\n </span>\n {link === null ? (\n <a\n href={`${SLACK_LINK_PATH}?${new URLSearchParams({ fleet: fleetId }).toString()}`}\n data-testid=\"bridge-slack-link\"\n className=\"inline-flex h-9 items-center rounded-md bg-primary px-4 text-primary-foreground hover:bg-primary-hover\"\n >\n Link Slack\n </a>\n ) : (\n <ActionButton data-testid=\"bridge-slack-unlink\" isPending={isUnlinking} pendingLabel=\"Unlinking...\" onClick={unlink}>\n Unlink\n </ActionButton>\n )}\n </div>\n {failure === undefined ? null : <Notice message={failure} isFailure />}\n </section>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"40bc48a9fabe75ea0125a8969f993153e9f0a9f644\":{\"name\":\"unlinkSlack\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_10=/*#__PURE__*/createServerReference(\"40bc48a9fabe75ea0125a8969f993153e9f0a9f644\",callServer,void 0,findSourceMapURL,\"unlinkSlack\");export{$$RSC_SERVER_ACTION_10 as unlinkSlack};","/** Where Link Slack starts (gateway scopes 9.3): the button in Bridge's settings and the button in a bot's answer in Slack (src/core/slack/take-slack-event.ts). */\nexport const SLACK_LINK_PATH = '/slack/link';\n","'use client';\n\nimport { useEffect, useState, useTransition } from 'react';\n\nimport { turnOffWebPush, turnOnWebPush } from '../hooks/bridge-actions';\nimport type { WakeChannels } from '../services/deliveries';\nimport { applicationServerKeyOf, browserNameOf, subscriptionInputOf } from '../services/web-push';\nimport { ActionButton } from '../ui/action-button';\nimport { Notice } from '../ui/notice';\n\n/** The service worker that shows a wake and opens Bridge when it is pressed. */\nconst WORKER_PATH = '/wake-worker.js';\n\n/** Whether this browser takes web push at all. */\nfunction isPushTaken(): boolean {\n return 'serviceWorker' in navigator && 'PushManager' in window && 'Notification' in window;\n}\n\n/** This browser's own push subscription with the gateway's worker, if it has one. */\nasync function ownSubscription(): Promise<PushSubscription | null> {\n const registration = await navigator.serviceWorker.getRegistration(WORKER_PATH);\n return (await registration?.pushManager.getSubscription()) ?? null;\n}\n\n/** Asks the browser's permission, then subscribes it with the gateway's key: the subscription to send, or why there is none. */\nasync function subscribed(webPushKey: string): Promise<{ readonly isOk: true; readonly value: PushSubscription } | { readonly isOk: false; readonly message: string }> {\n if ((await Notification.requestPermission()) !== 'granted') {\n return { isOk: false, message: 'Your browser did not allow notifications from this site: allow them, then turn web push on again' };\n }\n const registration = await navigator.serviceWorker.register(WORKER_PATH);\n await navigator.serviceWorker.ready;\n return { isOk: true, value: await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: applicationServerKeyOf(webPushKey) }) };\n}\n\n/**\n * Web push in the section of wakes of Bridge's settings (wake a person, W8): the\n * person turns it on for this browser, which asks the browser's permission\n * first, sees the browsers they turned it on in, and removes one. Nothing is\n * on until they turn it on. A browser whose push service ended its\n * subscription is said to be gone.\n */\nexport function WebPushPanel({ fleetId, webPushKey, browsers }: { fleetId: string; webPushKey: string; browsers: WakeChannels['webPush']['browsers'] }) {\n const [isTaken, setIsTaken] = useState(true);\n const [ownEndpoint, setOwnEndpoint] = useState<string | undefined>();\n const [working, setWorking] = useState<string | undefined>();\n const [failure, setFailure] = useState<string | undefined>();\n const [, startTransition] = useTransition();\n\n useEffect(() => {\n if (!isPushTaken()) {\n setIsTaken(false);\n return;\n }\n void ownSubscription().then((own) => {\n setOwnEndpoint(own?.endpoint);\n });\n }, []);\n\n const turnOn = () => {\n setWorking('on');\n setFailure(undefined);\n startTransition(async () => {\n try {\n const subscription = await subscribed(webPushKey);\n if (!subscription.isOk) {\n setFailure(subscription.message);\n return;\n }\n const input = subscriptionInputOf(subscription.value.toJSON(), browserNameOf(navigator.userAgent));\n if (input === undefined) {\n setFailure('Your browser gave no push subscription: try again');\n return;\n }\n const outcome = await turnOnWebPush(fleetId, input);\n if (outcome.isOk) {\n setOwnEndpoint(input.endpoint);\n } else {\n setFailure(outcome.message);\n }\n } catch {\n setFailure('Your browser could not turn web push on: try again');\n } finally {\n setWorking(undefined);\n }\n });\n };\n\n const remove = (endpoint: string) => {\n setWorking(endpoint);\n setFailure(undefined);\n startTransition(async () => {\n if (endpoint === ownEndpoint) {\n await (await ownSubscription())?.unsubscribe();\n setOwnEndpoint(undefined);\n }\n const outcome = await turnOffWebPush(fleetId, endpoint);\n setWorking(undefined);\n if (!outcome.isOk) {\n setFailure(outcome.message);\n }\n });\n };\n\n const isOwnOn = ownEndpoint !== undefined && browsers.some(({ endpoint, isGone }) => endpoint === ownEndpoint && !isGone);\n return (\n <div className=\"grid gap-2\" data-testid=\"bridge-wake-web-push\">\n {browsers.map(({ endpoint, browser, addedAt, isGone }) => (\n <div key={endpoint} className=\"flex items-center gap-3 rounded-md border border-border bg-card p-3\">\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate\">\n Web push in {browser}\n {endpoint === ownEndpoint ? ' · this browser' : ''}\n </span>\n <span className=\"text-caption text-muted-foreground\">\n {isGone ? 'No longer takes wakes: its push service ended them. Turn web push on again in that browser, or remove it.' : `On since ${new Date(addedAt).toLocaleDateString()}: it tells you when something waits in this fleet, with a link here. Never what waits.`}\n </span>\n </span>\n <ActionButton data-testid=\"bridge-web-push-remove\" isPending={working === endpoint} pendingLabel=\"Removing...\" disabled={working !== undefined} onClick={() => { remove(endpoint); }}>\n Remove\n </ActionButton>\n </div>\n ))}\n {!isTaken ? (\n <p className=\"text-caption text-muted-foreground\">This browser takes no web push.</p>\n ) : isOwnOn ? null : (\n <div>\n <ActionButton data-testid=\"bridge-web-push-on\" variant=\"primary\" isPending={working === 'on'} pendingLabel=\"Turning on...\" disabled={working !== undefined} onClick={turnOn}>\n Turn on web push in this browser\n </ActionButton>\n </div>\n )}\n {failure === undefined ? null : <Notice message={failure} isFailure />}\n </div>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"60eaa9c806d6593a0208abff2bc4d470472e235af0\":{\"name\":\"turnOffWebPush\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_9=/*#__PURE__*/createServerReference(\"60eaa9c806d6593a0208abff2bc4d470472e235af0\",callServer,void 0,findSourceMapURL,\"turnOffWebPush\");export{$$RSC_SERVER_ACTION_9 as turnOffWebPush};","/* __next_internal_action_entry_do_not_use__ [{\"60c97fa100defe2ce2abac96196c23f10c622fd02d\":{\"name\":\"turnOnWebPush\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_8=/*#__PURE__*/createServerReference(\"60c97fa100defe2ce2abac96196c23f10c622fd02d\",callServer,void 0,findSourceMapURL,\"turnOnWebPush\");export{$$RSC_SERVER_ACTION_8 as turnOnWebPush};","/**\n * What the section of wakes of Bridge's settings works out in the browser (wake a\n * person, W8): the name a browser it turns web push on in is listed by, the\n * gateway's push key as the browser's push manager takes it, and the\n * subscription the browser sends the gateway.\n */\n\n/** A browser's push subscription as the gateway takes it, with the browser's name. */\nexport interface SubscriptionInput {\n readonly endpoint: string;\n readonly keys: { readonly p256dh: string; readonly auth: string };\n readonly browser: string;\n}\n\n/** The browsers and systems a name is made of, the first that matches counting: Edge and Chrome also say Safari, and Chrome says Chrome. */\nconst BROWSERS: readonly (readonly [RegExp, string])[] = [\n [/Edg\\//, 'Edge'],\n [/Firefox\\//, 'Firefox'],\n [/Chrome\\//, 'Chrome'],\n [/Safari\\//, 'Safari'],\n];\nconst SYSTEMS: readonly (readonly [RegExp, string])[] = [\n [/iPhone|iPad/, 'iOS'],\n [/Android/, 'Android'],\n [/Mac OS X/, 'macOS'],\n [/Windows/, 'Windows'],\n [/Linux/, 'Linux'],\n];\n\nconst firstOf = (userAgent: string, names: readonly (readonly [RegExp, string])[]): string | undefined => names.find(([pattern]) => pattern.test(userAgent))?.[1];\n\n/** The name a browser is listed by, such as Firefox on Linux, from its user agent. */\nexport function browserNameOf(userAgent: string): string {\n const browser = firstOf(userAgent, BROWSERS);\n const system = firstOf(userAgent, SYSTEMS);\n if (browser === undefined) {\n return 'A browser';\n }\n return system === undefined ? browser : `${browser} on ${system}`;\n}\n\n/** The bytes a base64url key stands for, as the browser's push manager takes the gateway's key. */\nexport function applicationServerKeyOf(key: string): Uint8Array<ArrayBuffer> {\n const binary = atob(key.replaceAll('-', '+').replaceAll('_', '/'));\n return Uint8Array.from(binary, (character) => character.charCodeAt(0));\n}\n\n/** The subscription to send, from the push manager's own, or none when it gave no endpoint or keys. */\nexport function subscriptionInputOf(subscription: { endpoint?: string; keys?: Record<string, string> }, browser: string): SubscriptionInput | undefined {\n const { endpoint, keys } = subscription;\n const p256dh = keys?.p256dh;\n const auth = keys?.auth;\n return endpoint === undefined || p256dh === undefined || auth === undefined ? undefined : { endpoint, keys: { p256dh, auth }, browser };\n}\n"],"names":["$$RSC_SERVER_ACTION_7","$$RSC_SERVER_ACTION_10","$$RSC_SERVER_ACTION_9"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../web/features/bridge/state/routing-panel.tsx","../../../../../web/features/bridge/hooks/data%3A50db25%20%3Ctext/javascript%3E","../../../../../web/features/bridge/services/routing.ts","../../../../../web/features/bridge/state/slack-link-panel.tsx","../../../../../web/features/bridge/hooks/data%3Ab85c88%20%3Ctext/javascript%3E","../../../../../web/features/bridge/hooks/slack-link-path.ts","../../../../../web/features/bridge/state/web-push-panel.tsx","../../../../../web/features/bridge/hooks/data%3A2e8654%20%3Ctext/javascript%3E","../../../../../web/features/bridge/hooks/data%3A60870e%20%3Ctext/javascript%3E","../../../../../web/features/bridge/services/web-push.ts"],"sourcesContent":["'use client';\n\nimport { useState, useTransition } from 'react';\n\nimport { turnRoute } from '../hooks/bridge-actions';\nimport { JOB_NAMES, PLACE_NAMES, ROUTED_PLACES, turnedMessageOf, type RoutedJob, type RoutedPlace, type RoutingView } from '../services/routing';\nimport { Notice } from '../ui/notice';\n\n/** What a change came to, said by the grid. */\ninterface Said {\n readonly message: string;\n readonly isFailure: boolean;\n}\n\n/**\n * Routing per job (Bridge, section 8; the routing mockup): per job, the\n * places that show it besides Bridge, which always does. A switch turns a\n * place on or off at once; what it then shows is said beside the grid\n * (CC20). A place the gateway does not offer, or Slack before it is linked,\n * cannot be turned on; one on can always be turned off.\n */\nexport function RoutingPanel({ fleetId, routing, isSlackLinked }: { fleetId: string; routing: RoutingView; isSlackLinked: boolean }) {\n const [isTurning, startTransition] = useTransition();\n const [turning, setTurning] = useState<string | undefined>();\n const [said, setSaid] = useState<Said | undefined>();\n const isUsable = (place: RoutedPlace): boolean => routing.offered[place] && (place !== 'slack' || isSlackLinked);\n\n const turn = (change: { job: RoutedJob; place: RoutedPlace; isOn: boolean }) => {\n setSaid(undefined);\n setTurning(`${change.job}-${change.place}`);\n startTransition(async () => {\n const outcome = await turnRoute({ fleet: fleetId, ...change });\n setSaid(outcome.isOk ? { message: turnedMessageOf(change), isFailure: false } : { message: outcome.message, isFailure: true });\n });\n };\n\n return (\n <section className=\"grid gap-2\" aria-labelledby=\"bridge-routing-heading\">\n <h2 id=\"bridge-routing-heading\" className=\"text-section font-semibold\">\n Per job\n </h2>\n <div className=\"overflow-x-auto rounded-md border border-border bg-card\">\n <table className=\"w-full text-left\">\n <thead>\n <tr className=\"border-b border-border text-caption text-muted-foreground\">\n <th scope=\"col\" className=\"p-3 font-medium\">\n Job\n </th>\n <th scope=\"col\" className=\"p-3 font-medium\">\n Bridge\n </th>\n {ROUTED_PLACES.map((place) => (\n <th key={place} scope=\"col\" className=\"p-3 font-medium\">\n {PLACE_NAMES[place]}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {routing.jobs.map(({ job, places }) => (\n <tr key={job} data-testid=\"bridge-routing-job\" className=\"border-b border-border last:border-b-0\">\n <th scope=\"row\" className=\"p-3 font-normal\">\n <span className=\"flex flex-col\">\n <span>{JOB_NAMES[job].name}</span>\n <span className=\"text-caption text-muted-foreground\">{JOB_NAMES[job].holds}</span>\n </span>\n </th>\n <td className=\"p-3 text-caption text-muted-foreground\">Always</td>\n {ROUTED_PLACES.map((place) => {\n const isOn = places[place];\n const key = `${job}-${place}`;\n return (\n <td key={place} className=\"p-3\">\n <button\n type=\"button\"\n data-testid={`bridge-routing-${key}`}\n aria-pressed={isOn}\n aria-label={`${PLACE_NAMES[place]} for ${JOB_NAMES[job].name}`}\n aria-busy={isTurning && turning === key}\n disabled={isTurning || (!isOn && !isUsable(place))}\n onClick={() => {\n turn({ job, place, isOn: !isOn });\n }}\n className={`inline-flex h-7 min-w-12 items-center justify-center rounded-full px-3 text-caption disabled:cursor-not-allowed disabled:opacity-50 ${isOn ? 'bg-primary text-primary-foreground' : 'border border-border bg-card text-muted-foreground hover:bg-accent'}`}\n >\n {isOn ? 'On' : 'Off'}\n </button>\n </td>\n );\n })}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n {said === undefined ? null : <Notice message={said.message} isFailure={said.isFailure} />}\n <p className=\"text-caption text-muted-foreground\">While a Bridge page is open in focus, web push and email do not wake you. Slack still posts: you turned it on, and Bridge keeps every item.</p>\n </section>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"40d55047d0fa263dfb22afcf17c36e422f88773b7f\":{\"name\":\"turnRoute\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_7=/*#__PURE__*/createServerReference(\"40d55047d0fa263dfb22afcf17c36e422f88773b7f\",callServer,void 0,findSourceMapURL,\"turnRoute\");export{$$RSC_SERVER_ACTION_7 as turnRoute};","/**\n * Routing per job as Bridge's routing page shows it (Bridge, section 8):\n * the jobs and places by the names the person reads, and what a change\n * says it does (CC20).\n */\nimport type { inferRouterOutputs } from '@trpc/server';\n\nimport type { GatewayRouter } from '../../../../src/adapters/trpc/router.js';\n\n/** Where each job shows besides Bridge, per place, as the API answers it. */\nexport type RoutingView = inferRouterOutputs<GatewayRouter>['people']['routing'];\nexport type RoutedJob = RoutingView['jobs'][number]['job'];\nexport type RoutedPlace = keyof RoutingView['offered'];\n\nexport const ROUTED_PLACES: readonly RoutedPlace[] = ['slack', 'chat', 'web-push', 'email'];\n\nexport const JOB_NAMES: Readonly<Record<RoutedJob, { readonly name: string; readonly holds: string; readonly item: string; readonly items: string }>> = {\n decide: { name: 'Decide', holds: 'Questions from your ships', item: 'question', items: 'questions' },\n do: { name: 'Do what only you can', holds: 'Steps: sign in, create, approve', item: 'step', items: 'steps' },\n 'stay-aware': { name: 'Stay aware', holds: 'Alerts and notices', item: 'alert or notice', items: 'alerts and notices' },\n 'everything-else': { name: 'Everything else', holds: 'Replies and other messages', item: 'message', items: 'messages' },\n};\n\nexport const PLACE_NAMES: Readonly<Record<RoutedPlace, string>> = { slack: 'Slack', chat: 'A chat', 'web-push': 'Web push', email: 'Email' };\n\nconst KEEPS_EVERY_ITEM = 'Bridge keeps every item.';\n\n/** What turning a place on or off for a job does: a chat lists what is not done, a place that pushes only what arrives after. */\nexport function turnedMessageOf({ job, place, isOn }: { job: RoutedJob; place: RoutedPlace; isOn: boolean }): string {\n const { name, item, items } = JOB_NAMES[job];\n const turned = `${PLACE_NAMES[place]} is ${isOn ? 'on' : 'off'} for ${name}`;\n if (!isOn) {\n return job === 'everything-else' && place === 'slack' ? `${turned}: only a bot ship's messages and replies to Slack show there. ${KEEPS_EVERY_ITEM}` : `${turned}. ${KEEPS_EVERY_ITEM}`;\n }\n if (place === 'chat') {\n return `${turned}: it lists every ${item} not done when you ask it.`;\n }\n return `${turned}: it shows ${items} that arrive from now on. Those already waiting stay in Bridge.`;\n}\n","'use client';\n\nimport { useState, useTransition } from 'react';\n\nimport { unlinkSlack } from '../hooks/bridge-actions';\nimport type { SlackLink } from '../hooks/read-bridge';\nimport { SLACK_LINK_PATH } from '../hooks/slack-link-path';\nimport { ActionButton } from '../ui/action-button';\nimport { Notice } from '../ui/notice';\n\n/**\n * The person's Slack link (gateway scopes 9.3, 9.6): \"Slack linked as\n * @mathilde in Acme\" with Unlink, or Link Slack, which signs in with Slack.\n * Linked, the person talks to the fleet's bots in Slack; what else shows\n * there is routed per job, beside it (Bridge, section 8).\n */\nexport function SlackLinkPanel({ fleetId, slackLink }: { fleetId: string; slackLink: SlackLink }) {\n const [isUnlinking, startTransition] = useTransition();\n const [failure, setFailure] = useState<string | undefined>();\n const { workspaceName, link } = slackLink;\n\n const unlink = () => {\n setFailure(undefined);\n startTransition(async () => {\n const outcome = await unlinkSlack(fleetId);\n if (!outcome.isOk) {\n setFailure(outcome.message);\n }\n });\n };\n\n return (\n <section className=\"grid gap-2\" aria-labelledby=\"bridge-slack-heading\">\n <h2 id=\"bridge-slack-heading\" className=\"text-section font-semibold\">\n Slack\n </h2>\n <div className=\"flex flex-wrap items-center gap-3 rounded-md border border-border bg-card p-3\">\n <span data-testid=\"bridge-slack-state\" className=\"min-w-0 flex-1\">\n {link === null ? `Not linked in ${workspaceName}` : `Slack linked as @${link.slackName} in ${workspaceName}`}\n </span>\n {link === null ? (\n <a\n href={`${SLACK_LINK_PATH}?${new URLSearchParams({ fleet: fleetId }).toString()}`}\n data-testid=\"bridge-slack-link\"\n className=\"inline-flex h-9 items-center rounded-md bg-primary px-4 text-primary-foreground hover:bg-primary-hover\"\n >\n Link Slack\n </a>\n ) : (\n <ActionButton data-testid=\"bridge-slack-unlink\" isPending={isUnlinking} pendingLabel=\"Unlinking...\" onClick={unlink}>\n Unlink\n </ActionButton>\n )}\n </div>\n {failure === undefined ? null : <Notice message={failure} isFailure />}\n </section>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"4068e0a012962959eed53cdc1898b8acfd21d81a71\":{\"name\":\"unlinkSlack\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_10=/*#__PURE__*/createServerReference(\"4068e0a012962959eed53cdc1898b8acfd21d81a71\",callServer,void 0,findSourceMapURL,\"unlinkSlack\");export{$$RSC_SERVER_ACTION_10 as unlinkSlack};","/** Where Link Slack starts (gateway scopes 9.3): the button in Bridge's settings and the button in a bot's answer in Slack (src/core/slack/take-slack-event.ts). */\nexport const SLACK_LINK_PATH = '/slack/link';\n","'use client';\n\nimport { useEffect, useState, useTransition } from 'react';\n\nimport { turnOffWebPush, turnOnWebPush } from '../hooks/bridge-actions';\nimport type { WakeChannels } from '../services/deliveries';\nimport { applicationServerKeyOf, browserNameOf, subscriptionInputOf } from '../services/web-push';\nimport { ActionButton } from '../ui/action-button';\nimport { Notice } from '../ui/notice';\n\n/** The service worker that shows a wake and opens Bridge when it is pressed. */\nconst WORKER_PATH = '/wake-worker.js';\n\n/** Whether this browser takes web push at all. */\nfunction isPushTaken(): boolean {\n return 'serviceWorker' in navigator && 'PushManager' in window && 'Notification' in window;\n}\n\n/** This browser's own push subscription with the gateway's worker, if it has one. */\nasync function ownSubscription(): Promise<PushSubscription | null> {\n const registration = await navigator.serviceWorker.getRegistration(WORKER_PATH);\n return (await registration?.pushManager.getSubscription()) ?? null;\n}\n\n/** Asks the browser's permission, then subscribes it with the gateway's key: the subscription to send, or why there is none. */\nasync function subscribed(webPushKey: string): Promise<{ readonly isOk: true; readonly value: PushSubscription } | { readonly isOk: false; readonly message: string }> {\n if ((await Notification.requestPermission()) !== 'granted') {\n return { isOk: false, message: 'Your browser did not allow notifications from this site: allow them, then turn web push on again' };\n }\n const registration = await navigator.serviceWorker.register(WORKER_PATH);\n await navigator.serviceWorker.ready;\n return { isOk: true, value: await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: applicationServerKeyOf(webPushKey) }) };\n}\n\n/**\n * Web push in the section of wakes of Bridge's settings (wake a person, W8): the\n * person turns it on for this browser, which asks the browser's permission\n * first, sees the browsers they turned it on in, and removes one. Nothing is\n * on until they turn it on. A browser whose push service ended its\n * subscription is said to be gone.\n */\nexport function WebPushPanel({ fleetId, webPushKey, browsers }: { fleetId: string; webPushKey: string; browsers: WakeChannels['webPush']['browsers'] }) {\n const [isTaken, setIsTaken] = useState(true);\n const [ownEndpoint, setOwnEndpoint] = useState<string | undefined>();\n const [working, setWorking] = useState<string | undefined>();\n const [failure, setFailure] = useState<string | undefined>();\n const [, startTransition] = useTransition();\n\n useEffect(() => {\n if (!isPushTaken()) {\n setIsTaken(false);\n return;\n }\n void ownSubscription().then((own) => {\n setOwnEndpoint(own?.endpoint);\n });\n }, []);\n\n const turnOn = () => {\n setWorking('on');\n setFailure(undefined);\n startTransition(async () => {\n try {\n const subscription = await subscribed(webPushKey);\n if (!subscription.isOk) {\n setFailure(subscription.message);\n return;\n }\n const input = subscriptionInputOf(subscription.value.toJSON(), browserNameOf(navigator.userAgent));\n if (input === undefined) {\n setFailure('Your browser gave no push subscription: try again');\n return;\n }\n const outcome = await turnOnWebPush(fleetId, input);\n if (outcome.isOk) {\n setOwnEndpoint(input.endpoint);\n } else {\n setFailure(outcome.message);\n }\n } catch {\n setFailure('Your browser could not turn web push on: try again');\n } finally {\n setWorking(undefined);\n }\n });\n };\n\n const remove = (endpoint: string) => {\n setWorking(endpoint);\n setFailure(undefined);\n startTransition(async () => {\n if (endpoint === ownEndpoint) {\n await (await ownSubscription())?.unsubscribe();\n setOwnEndpoint(undefined);\n }\n const outcome = await turnOffWebPush(fleetId, endpoint);\n setWorking(undefined);\n if (!outcome.isOk) {\n setFailure(outcome.message);\n }\n });\n };\n\n const isOwnOn = ownEndpoint !== undefined && browsers.some(({ endpoint, isGone }) => endpoint === ownEndpoint && !isGone);\n return (\n <div className=\"grid gap-2\" data-testid=\"bridge-wake-web-push\">\n {browsers.map(({ endpoint, browser, addedAt, isGone }) => (\n <div key={endpoint} className=\"flex items-center gap-3 rounded-md border border-border bg-card p-3\">\n <span className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"truncate\">\n Web push in {browser}\n {endpoint === ownEndpoint ? ' · this browser' : ''}\n </span>\n <span className=\"text-caption text-muted-foreground\">\n {isGone ? 'No longer takes wakes: its push service ended them. Turn web push on again in that browser, or remove it.' : `On since ${new Date(addedAt).toLocaleDateString()}: it tells you when something waits in this fleet, with a link here. Never what waits.`}\n </span>\n </span>\n <ActionButton data-testid=\"bridge-web-push-remove\" isPending={working === endpoint} pendingLabel=\"Removing...\" disabled={working !== undefined} onClick={() => { remove(endpoint); }}>\n Remove\n </ActionButton>\n </div>\n ))}\n {!isTaken ? (\n <p className=\"text-caption text-muted-foreground\">This browser takes no web push.</p>\n ) : isOwnOn ? null : (\n <div>\n <ActionButton data-testid=\"bridge-web-push-on\" variant=\"primary\" isPending={working === 'on'} pendingLabel=\"Turning on...\" disabled={working !== undefined} onClick={turnOn}>\n Turn on web push in this browser\n </ActionButton>\n </div>\n )}\n {failure === undefined ? null : <Notice message={failure} isFailure />}\n </div>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"601545266cbcd00188699907a363d2d01cacdd7590\":{\"name\":\"turnOffWebPush\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_9=/*#__PURE__*/createServerReference(\"601545266cbcd00188699907a363d2d01cacdd7590\",callServer,void 0,findSourceMapURL,\"turnOffWebPush\");export{$$RSC_SERVER_ACTION_9 as turnOffWebPush};","/* __next_internal_action_entry_do_not_use__ [{\"606869fb42c6cf9c02d918ac32cc0df0df992ce8e8\":{\"name\":\"turnOnWebPush\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_8=/*#__PURE__*/createServerReference(\"606869fb42c6cf9c02d918ac32cc0df0df992ce8e8\",callServer,void 0,findSourceMapURL,\"turnOnWebPush\");export{$$RSC_SERVER_ACTION_8 as turnOnWebPush};","/**\n * What the section of wakes of Bridge's settings works out in the browser (wake a\n * person, W8): the name a browser it turns web push on in is listed by, the\n * gateway's push key as the browser's push manager takes it, and the\n * subscription the browser sends the gateway.\n */\n\n/** A browser's push subscription as the gateway takes it, with the browser's name. */\nexport interface SubscriptionInput {\n readonly endpoint: string;\n readonly keys: { readonly p256dh: string; readonly auth: string };\n readonly browser: string;\n}\n\n/** The browsers and systems a name is made of, the first that matches counting: Edge and Chrome also say Safari, and Chrome says Chrome. */\nconst BROWSERS: readonly (readonly [RegExp, string])[] = [\n [/Edg\\//, 'Edge'],\n [/Firefox\\//, 'Firefox'],\n [/Chrome\\//, 'Chrome'],\n [/Safari\\//, 'Safari'],\n];\nconst SYSTEMS: readonly (readonly [RegExp, string])[] = [\n [/iPhone|iPad/, 'iOS'],\n [/Android/, 'Android'],\n [/Mac OS X/, 'macOS'],\n [/Windows/, 'Windows'],\n [/Linux/, 'Linux'],\n];\n\nconst firstOf = (userAgent: string, names: readonly (readonly [RegExp, string])[]): string | undefined => names.find(([pattern]) => pattern.test(userAgent))?.[1];\n\n/** The name a browser is listed by, such as Firefox on Linux, from its user agent. */\nexport function browserNameOf(userAgent: string): string {\n const browser = firstOf(userAgent, BROWSERS);\n const system = firstOf(userAgent, SYSTEMS);\n if (browser === undefined) {\n return 'A browser';\n }\n return system === undefined ? browser : `${browser} on ${system}`;\n}\n\n/** The bytes a base64url key stands for, as the browser's push manager takes the gateway's key. */\nexport function applicationServerKeyOf(key: string): Uint8Array<ArrayBuffer> {\n const binary = atob(key.replaceAll('-', '+').replaceAll('_', '/'));\n return Uint8Array.from(binary, (character) => character.charCodeAt(0));\n}\n\n/** The subscription to send, from the push manager's own, or none when it gave no endpoint or keys. */\nexport function subscriptionInputOf(subscription: { endpoint?: string; keys?: Record<string, string> }, browser: string): SubscriptionInput | undefined {\n const { endpoint, keys } = subscription;\n const p256dh = keys?.p256dh;\n const auth = keys?.auth;\n return endpoint === undefined || p256dh === undefined || auth === undefined ? undefined : { endpoint, keys: { p256dh, auth }, browser };\n}\n"],"names":["$$RSC_SERVER_ACTION_7","$$RSC_SERVER_ACTION_10","$$RSC_SERVER_ACTION_9"],"mappings":"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"}
|
package/web/.next/server/chunks/ssr/web_features_bridge_state_basket-panel_tsx_1qi_kzld0rfgs._.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module.exports=[80198,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(
|
|
1
|
+
module.exports=[80198,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(2305),e=a.i(5050);let f=(0,e.createServerReference)("407bae59090ef46e76fb8d952969288e5bb4583e21",e.callServer,void 0,e.findSourceMapURL,"sendBasket"),g={bridge:"in Bridge",chat:"in a chat",slack:"in Slack",gateway:"by the gateway"},h={withdrawn:"withdrawn",replaced:"replaced",dealtElsewhere:"answered elsewhere"};function i(a){return 1===a?"1 answer":`${String(a)} answers`}var j=a.i(32043),k=a.i(10039),l=a.i(91777);function m({entry:a,opening:c,working:d,onRemove:e}){var f;let{standing:h}=a,i=function(a){let{standing:b}=a;switch(b.kind){case"waiting":return;case"sending":return"Sending... The gateway keeps sending what is stored as sending, with the same key, until it is stored; you can close Bridge.";case"refused":return`Not sent: ${b.reason}. The others were sent.`;case"withdrawn":return`Withdrawn by ${a.from}. Not sent; Send skips it.`;case"replaced":return`Replaced by ${a.from}. Not sent: its options may differ, so answer the new question.`;case"dealtElsewhere":return null===b.option?`Done ${g[b.place]}. Nothing is sent from it.`:`Answered ${g[b.place]}: option ${String(b.option)}. Nothing is sent from it.`}}(a),l=void 0!==d||void 0!==c.openingId,n="replaced"===h.kind?h.replacementId:null;return(0,b.jsxs)("li",{"data-testid":"bridge-basket-entry","data-standing":h.kind,className:`grid min-w-0 gap-2 rounded-md border border-border bg-card p-3 ${"withdrawn"===h.kind||"replaced"===h.kind||"dealtElsewhere"===h.kind?"opacity-60":""}`,children:[(0,b.jsx)("span",{className:"font-medium",children:a.title}),(0,b.jsxs)("span",{className:"text-caption text-muted-foreground",children:["to ",a.from," · asked at ",(0,b.jsx)("time",{dateTime:a.sentAt,children:(f=a.sentAt,`${new Date(f).toISOString().slice(0,16).replace("T"," ")} UTC`)})]}),(0,b.jsxs)("span",{children:["Option ",a.option]}),null===a.note?null:(0,b.jsxs)("span",{className:"text-caption",children:["“",a.note,"”"]}),void 0===i?null:(0,b.jsx)("span",{"data-testid":"bridge-basket-standing",className:"text-caption",children:i}),c.failure?.deliveryId===a.deliveryId?(0,b.jsx)(k.f,{message:c.failure.message,isFailure:!0}):null,(0,b.jsxs)("div",{className:"flex flex-wrap gap-2",children:["waiting"===h.kind?(0,b.jsx)(j.f,{"data-testid":"bridge-basket-change",isPending:c.openingId===a.deliveryId,pendingLabel:"Opening...",disabled:l,onClick:()=>{c.open({deliveryId:a.deliveryId,job:"decide",inBasket:{option:a.option,note:a.note}})},children:"Change"}):null,null===n?null:(0,b.jsx)(j.f,{"data-testid":"bridge-replacement",isPending:c.openingId===n,pendingLabel:"Opening...",disabled:l,onClick:()=>{c.open({deliveryId:n,job:"decide"})},children:"Answer the new question"}),"sending"===h.kind?null:(0,b.jsx)(j.f,{"data-testid":"bridge-basket-remove",isPending:d?.kind==="remove"&&d.questionId===a.questionId,pendingLabel:"Removing...",disabled:l,onClick:()=>{e(a.questionId)},children:"refused"===h.kind?"Remove and mark done":"Remove"})]})]})}a.s(["BasketPanel",0,function({fleetId:a,basket:e}){let[g,n]=(0,c.useState)(),[o,p]=(0,c.useState)(),[,q]=(0,c.useTransition)(),{entries:r,toSend:s}=e,t=b=>{n({kind:"remove",questionId:b}),p(void 0),q(async()=>{let c=await (0,d.f)(a,b);n(void 0),p(c.isOk?void 0:{message:c.message,isFailure:!0})})},u=()=>{n({kind:"send"}),p(void 0),q(async()=>{var b;let c,d=await f(a);n(void 0),p(d.isOk?{message:(c=0===(b=d.value).sending?"Nothing to send.":1===b.sending?"1 answer is being sent.":`${i(b.sending)} are being sent, each on its own, in the order you added them.`,0===b.skipped.length?c:`${c} Not sent: ${b.skipped.map(({title:a,kind:b})=>`“${a}”, ${h[b]}`).join("; ")}.`),isFailure:!1}:{message:d.message,isFailure:!0})})};return(0,b.jsx)(l.f,{fleetId:a,children:a=>(0,b.jsxs)("section",{className:"grid gap-3","aria-labelledby":"bridge-basket-heading",children:[(0,b.jsxs)("div",{children:[(0,b.jsxs)("h1",{id:"bridge-basket-heading",className:"text-heading font-semibold",children:["Basket ",(0,b.jsx)("span",{"data-testid":"bridge-basket-count",children:r.length})]}),(0,b.jsx)("p",{className:"text-muted-foreground",children:"Answers you picked in Bridge, not sent yet. Nothing goes until you press Send."})]}),void 0===o?null:(0,b.jsx)(k.f,{message:o.message,isFailure:o.isFailure}),0===r.length?(0,b.jsxs)("div",{"data-testid":"bridge-basket-empty",className:"grid justify-items-center gap-1 rounded-lg border border-border bg-card p-6 text-center",children:[(0,b.jsx)("span",{className:"font-semibold",children:"The basket is empty"}),(0,b.jsx)("span",{className:"text-muted-foreground",children:"Open a question in Waiting on you and put your answer in the basket."})]}):(0,b.jsx)("ul",{className:"grid gap-2",children:r.map(c=>(0,b.jsx)(m,{entry:c,opening:a,working:g,onRemove:t},c.questionId))}),0===s?null:(0,b.jsxs)("div",{className:"grid gap-1",children:[(0,b.jsx)("p",{className:"text-caption text-muted-foreground",children:"Each asker receives its own answer, as you, as if sent one by one."}),(0,b.jsx)(j.f,{"data-testid":"bridge-basket-send",variant:"primary",className:"justify-self-start",isPending:g?.kind==="send",pendingLabel:"Sending...",disabled:void 0!==g||void 0!==a.openingId,onClick:u,children:`Send ${i(s)}`})]})]})})}],80198)}];
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=web_features_bridge_state_basket-panel_tsx_1qi_kzld0rfgs._.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../web/features/bridge/state/basket-panel.tsx","../../../../../web/features/bridge/hooks/data%3Ae1b3b3%20%3Ctext/javascript%3E","../../../../../web/features/bridge/services/basket.ts"],"sourcesContent":["'use client';\n\nimport { useState, useTransition } from 'react';\n\nimport { removeFromBasket, sendBasket } from '../hooks/bridge-actions';\nimport { sendLabelOf, sentTextOf, standingTextOf, type BasketItem, type BasketView } from '../services/basket';\nimport { ActionButton } from '../ui/action-button';\nimport { Notice } from '../ui/notice';\nimport { ItemFocus, type ItemOpening } from './item-focus';\n\n/** When a question was asked, in UTC, to the minute. */\nfunction askedAtOf(sentAt: string): string {\n return `${new Date(sentAt).toISOString().slice(0, 16).replace('T', ' ')} UTC`;\n}\n\n/** What the person is doing in the basket: removing one entry, or sending it. */\ntype Working = { kind: 'remove'; questionId: string } | { kind: 'send' };\n\n/** One entry: the question, its asker and when it asked, the option and the note, what it stands as, and what can be done with it. */\nfunction BasketEntryRow({ entry, opening, working, onRemove }: { entry: BasketItem; opening: ItemOpening; working: Working | undefined; onRemove: (questionId: string) => void }) {\n const { standing } = entry;\n const said = standingTextOf(entry);\n const isWorking = working !== undefined || opening.openingId !== undefined;\n const replacementId = standing.kind === 'replaced' ? standing.replacementId : null;\n return (\n <li data-testid=\"bridge-basket-entry\" data-standing={standing.kind} className={`grid min-w-0 gap-2 rounded-md border border-border bg-card p-3 ${standing.kind === 'withdrawn' || standing.kind === 'replaced' || standing.kind === 'dealtElsewhere' ? 'opacity-60' : ''}`}>\n <span className=\"font-medium\">{entry.title}</span>\n <span className=\"text-caption text-muted-foreground\">\n to {entry.from} · asked at <time dateTime={entry.sentAt}>{askedAtOf(entry.sentAt)}</time>\n </span>\n <span>Option {entry.option}</span>\n {entry.note === null ? null : <span className=\"text-caption\">“{entry.note}”</span>}\n {said === undefined ? null : <span data-testid=\"bridge-basket-standing\" className=\"text-caption\">{said}</span>}\n {opening.failure?.deliveryId === entry.deliveryId ? <Notice message={opening.failure.message} isFailure /> : null}\n <div className=\"flex flex-wrap gap-2\">\n {standing.kind === 'waiting' ? (\n <ActionButton data-testid=\"bridge-basket-change\" isPending={opening.openingId === entry.deliveryId} pendingLabel=\"Opening...\" disabled={isWorking} onClick={() => { opening.open({ deliveryId: entry.deliveryId, job: 'decide', inBasket: { option: entry.option, note: entry.note } }); }}>\n Change\n </ActionButton>\n ) : null}\n {replacementId === null ? null : (\n <ActionButton data-testid=\"bridge-replacement\" isPending={opening.openingId === replacementId} pendingLabel=\"Opening...\" disabled={isWorking} onClick={() => { opening.open({ deliveryId: replacementId, job: 'decide' }); }}>\n Answer the new question\n </ActionButton>\n )}\n {standing.kind === 'sending' ? null : (\n <ActionButton data-testid=\"bridge-basket-remove\" isPending={working?.kind === 'remove' && working.questionId === entry.questionId} pendingLabel=\"Removing...\" disabled={isWorking} onClick={() => { onRemove(entry.questionId); }}>\n {standing.kind === 'refused' ? 'Remove and mark done' : 'Remove'}\n </ActionButton>\n )}\n </div>\n </li>\n );\n}\n\n/**\n * The basket open (docs/design/bridge, section 9): the answers the person\n * picked in Bridge and has not sent, in the order added. Change opens the\n * question with that option picked; Remove takes the entry out, and the\n * question still waits; one the fleet refused is removed and its question\n * marked done. Send N answers stores each as sending and the gateway sends\n * them, each on its own; a withdrawn, replaced or answered-elsewhere entry is\n * skipped and taken out, and Send says so.\n */\nexport function BasketPanel({ fleetId, basket }: { fleetId: string; basket: BasketView }) {\n const [working, setWorking] = useState<Working | undefined>();\n const [said, setSaid] = useState<{ message: string; isFailure: boolean } | undefined>();\n const [, startTransition] = useTransition();\n const { entries, toSend } = basket;\n\n const remove = (questionId: string) => {\n setWorking({ kind: 'remove', questionId });\n setSaid(undefined);\n startTransition(async () => {\n const outcome = await removeFromBasket(fleetId, questionId);\n setWorking(undefined);\n setSaid(outcome.isOk ? undefined : { message: outcome.message, isFailure: true });\n });\n };\n\n const send = () => {\n setWorking({ kind: 'send' });\n setSaid(undefined);\n startTransition(async () => {\n const outcome = await sendBasket(fleetId);\n setWorking(undefined);\n setSaid(outcome.isOk ? { message: sentTextOf(outcome.value), isFailure: false } : { message: outcome.message, isFailure: true });\n });\n };\n\n return (\n <ItemFocus fleetId={fleetId}>\n {(opening) => (\n <section className=\"grid gap-3\" aria-labelledby=\"bridge-basket-heading\">\n <div>\n <h1 id=\"bridge-basket-heading\" className=\"text-heading font-semibold\">\n Basket <span data-testid=\"bridge-basket-count\">{entries.length}</span>\n </h1>\n <p className=\"text-muted-foreground\">Answers you picked in Bridge, not sent yet. Nothing goes until you press Send.</p>\n </div>\n {said === undefined ? null : <Notice message={said.message} isFailure={said.isFailure} />}\n {entries.length === 0 ? (\n <div data-testid=\"bridge-basket-empty\" className=\"grid justify-items-center gap-1 rounded-lg border border-border bg-card p-6 text-center\">\n <span className=\"font-semibold\">The basket is empty</span>\n <span className=\"text-muted-foreground\">Open a question in Waiting on you and put your answer in the basket.</span>\n </div>\n ) : (\n <ul className=\"grid gap-2\">\n {entries.map((entry) => (\n <BasketEntryRow key={entry.questionId} entry={entry} opening={opening} working={working} onRemove={remove} />\n ))}\n </ul>\n )}\n {toSend === 0 ? null : (\n <div className=\"grid gap-1\">\n <p className=\"text-caption text-muted-foreground\">Each asker receives its own answer, as you, as if sent one by one.</p>\n <ActionButton data-testid=\"bridge-basket-send\" variant=\"primary\" className=\"justify-self-start\" isPending={working?.kind === 'send'} pendingLabel=\"Sending...\" disabled={working !== undefined || opening.openingId !== undefined} onClick={send}>\n {sendLabelOf(toSend)}\n </ActionButton>\n </div>\n )}\n </section>\n )}\n </ItemFocus>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"40b44086e3707ca8b788d4ad4b3ef6615138ffe27a\":{\"name\":\"sendBasket\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_5=/*#__PURE__*/createServerReference(\"40b44086e3707ca8b788d4ad4b3ef6615138ffe27a\",callServer,void 0,findSourceMapURL,\"sendBasket\");export{$$RSC_SERVER_ACTION_5 as sendBasket};","/**\n * The basket as Bridge shows it (docs/design/bridge, section 9): what each\n * entry stands as, what Send sends, and what it did, in words. Pure view\n * logic, no React and no API client.\n */\nimport type { inferRouterOutputs } from '@trpc/server';\n\nimport type { GatewayRouter } from '../../../../src/adapters/trpc/router.js';\n\ntype PeopleOutputs = inferRouterOutputs<GatewayRouter>['people'];\n\n/** The basket as the API answers it: its entries in the order added, and how many answers Send sends. */\nexport type BasketView = PeopleOutputs['basket'];\n\n/** An entry of the basket, with what it stands as. */\nexport type BasketItem = BasketView['entries'][number];\n\n/** What Send did, as the API answers it. */\nexport type BasketSent = PeopleOutputs['sendBasket'];\n\nconst PLACES: Record<Extract<BasketItem['standing'], { kind: 'dealtElsewhere' }>['place'], string> = {\n bridge: 'in Bridge',\n chat: 'in a chat',\n slack: 'in Slack',\n gateway: 'by the gateway',\n};\n\nconst SKIPPED: Record<BasketSent['skipped'][number]['kind'], string> = {\n withdrawn: 'withdrawn',\n replaced: 'replaced',\n dealtElsewhere: 'answered elsewhere',\n};\n\nfunction answersOf(count: number): string {\n return count === 1 ? '1 answer' : `${String(count)} answers`;\n}\n\n/** The Send button, naming how many answers it sends. */\nexport function sendLabelOf(toSend: number): string {\n return `Send ${answersOf(toSend)}`;\n}\n\n/** What an entry stands as, in words; nothing for one waiting for Send. */\nexport function standingTextOf(item: BasketItem): string | undefined {\n const { standing } = item;\n switch (standing.kind) {\n case 'waiting':\n return undefined;\n case 'sending':\n return 'Sending... The gateway keeps sending what is stored as sending, with the same key, until it is stored; you can close Bridge.';\n case 'refused':\n return `Not sent: ${standing.reason}. The others were sent.`;\n case 'withdrawn':\n return `Withdrawn by ${item.from}. Not sent; Send skips it.`;\n case 'replaced':\n return `Replaced by ${item.from}. Not sent: its options may differ, so answer the new question.`;\n case 'dealtElsewhere':\n return standing.option === null ? `Done ${PLACES[standing.place]}. Nothing is sent from it.` : `Answered ${PLACES[standing.place]}: option ${String(standing.option)}. Nothing is sent from it.`;\n }\n}\n\n/** What Send did: how many answers are being sent, and each entry it skipped and took out. */\nexport function sentTextOf(sent: BasketSent): string {\n const sending = sent.sending === 0 ? 'Nothing to send.' : sent.sending === 1 ? '1 answer is being sent.' : `${answersOf(sent.sending)} are being sent, each on its own, in the order you added them.`;\n if (sent.skipped.length === 0) {\n return sending;\n }\n return `${sending} Not sent: ${sent.skipped.map(({ title, kind }) => `“${title}”, ${SKIPPED[kind]}`).join('; ')}.`;\n}\n"],"names":["$$RSC_SERVER_ACTION_5"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../web/features/bridge/state/basket-panel.tsx","../../../../../web/features/bridge/hooks/data%3Adaa397%20%3Ctext/javascript%3E","../../../../../web/features/bridge/services/basket.ts"],"sourcesContent":["'use client';\n\nimport { useState, useTransition } from 'react';\n\nimport { removeFromBasket, sendBasket } from '../hooks/bridge-actions';\nimport { sendLabelOf, sentTextOf, standingTextOf, type BasketItem, type BasketView } from '../services/basket';\nimport { ActionButton } from '../ui/action-button';\nimport { Notice } from '../ui/notice';\nimport { ItemFocus, type ItemOpening } from './item-focus';\n\n/** When a question was asked, in UTC, to the minute. */\nfunction askedAtOf(sentAt: string): string {\n return `${new Date(sentAt).toISOString().slice(0, 16).replace('T', ' ')} UTC`;\n}\n\n/** What the person is doing in the basket: removing one entry, or sending it. */\ntype Working = { kind: 'remove'; questionId: string } | { kind: 'send' };\n\n/** One entry: the question, its asker and when it asked, the option and the note, what it stands as, and what can be done with it. */\nfunction BasketEntryRow({ entry, opening, working, onRemove }: { entry: BasketItem; opening: ItemOpening; working: Working | undefined; onRemove: (questionId: string) => void }) {\n const { standing } = entry;\n const said = standingTextOf(entry);\n const isWorking = working !== undefined || opening.openingId !== undefined;\n const replacementId = standing.kind === 'replaced' ? standing.replacementId : null;\n return (\n <li data-testid=\"bridge-basket-entry\" data-standing={standing.kind} className={`grid min-w-0 gap-2 rounded-md border border-border bg-card p-3 ${standing.kind === 'withdrawn' || standing.kind === 'replaced' || standing.kind === 'dealtElsewhere' ? 'opacity-60' : ''}`}>\n <span className=\"font-medium\">{entry.title}</span>\n <span className=\"text-caption text-muted-foreground\">\n to {entry.from} · asked at <time dateTime={entry.sentAt}>{askedAtOf(entry.sentAt)}</time>\n </span>\n <span>Option {entry.option}</span>\n {entry.note === null ? null : <span className=\"text-caption\">“{entry.note}”</span>}\n {said === undefined ? null : <span data-testid=\"bridge-basket-standing\" className=\"text-caption\">{said}</span>}\n {opening.failure?.deliveryId === entry.deliveryId ? <Notice message={opening.failure.message} isFailure /> : null}\n <div className=\"flex flex-wrap gap-2\">\n {standing.kind === 'waiting' ? (\n <ActionButton data-testid=\"bridge-basket-change\" isPending={opening.openingId === entry.deliveryId} pendingLabel=\"Opening...\" disabled={isWorking} onClick={() => { opening.open({ deliveryId: entry.deliveryId, job: 'decide', inBasket: { option: entry.option, note: entry.note } }); }}>\n Change\n </ActionButton>\n ) : null}\n {replacementId === null ? null : (\n <ActionButton data-testid=\"bridge-replacement\" isPending={opening.openingId === replacementId} pendingLabel=\"Opening...\" disabled={isWorking} onClick={() => { opening.open({ deliveryId: replacementId, job: 'decide' }); }}>\n Answer the new question\n </ActionButton>\n )}\n {standing.kind === 'sending' ? null : (\n <ActionButton data-testid=\"bridge-basket-remove\" isPending={working?.kind === 'remove' && working.questionId === entry.questionId} pendingLabel=\"Removing...\" disabled={isWorking} onClick={() => { onRemove(entry.questionId); }}>\n {standing.kind === 'refused' ? 'Remove and mark done' : 'Remove'}\n </ActionButton>\n )}\n </div>\n </li>\n );\n}\n\n/**\n * The basket open (docs/design/bridge, section 9): the answers the person\n * picked in Bridge and has not sent, in the order added. Change opens the\n * question with that option picked; Remove takes the entry out, and the\n * question still waits; one the fleet refused is removed and its question\n * marked done. Send N answers stores each as sending and the gateway sends\n * them, each on its own; a withdrawn, replaced or answered-elsewhere entry is\n * skipped and taken out, and Send says so.\n */\nexport function BasketPanel({ fleetId, basket }: { fleetId: string; basket: BasketView }) {\n const [working, setWorking] = useState<Working | undefined>();\n const [said, setSaid] = useState<{ message: string; isFailure: boolean } | undefined>();\n const [, startTransition] = useTransition();\n const { entries, toSend } = basket;\n\n const remove = (questionId: string) => {\n setWorking({ kind: 'remove', questionId });\n setSaid(undefined);\n startTransition(async () => {\n const outcome = await removeFromBasket(fleetId, questionId);\n setWorking(undefined);\n setSaid(outcome.isOk ? undefined : { message: outcome.message, isFailure: true });\n });\n };\n\n const send = () => {\n setWorking({ kind: 'send' });\n setSaid(undefined);\n startTransition(async () => {\n const outcome = await sendBasket(fleetId);\n setWorking(undefined);\n setSaid(outcome.isOk ? { message: sentTextOf(outcome.value), isFailure: false } : { message: outcome.message, isFailure: true });\n });\n };\n\n return (\n <ItemFocus fleetId={fleetId}>\n {(opening) => (\n <section className=\"grid gap-3\" aria-labelledby=\"bridge-basket-heading\">\n <div>\n <h1 id=\"bridge-basket-heading\" className=\"text-heading font-semibold\">\n Basket <span data-testid=\"bridge-basket-count\">{entries.length}</span>\n </h1>\n <p className=\"text-muted-foreground\">Answers you picked in Bridge, not sent yet. Nothing goes until you press Send.</p>\n </div>\n {said === undefined ? null : <Notice message={said.message} isFailure={said.isFailure} />}\n {entries.length === 0 ? (\n <div data-testid=\"bridge-basket-empty\" className=\"grid justify-items-center gap-1 rounded-lg border border-border bg-card p-6 text-center\">\n <span className=\"font-semibold\">The basket is empty</span>\n <span className=\"text-muted-foreground\">Open a question in Waiting on you and put your answer in the basket.</span>\n </div>\n ) : (\n <ul className=\"grid gap-2\">\n {entries.map((entry) => (\n <BasketEntryRow key={entry.questionId} entry={entry} opening={opening} working={working} onRemove={remove} />\n ))}\n </ul>\n )}\n {toSend === 0 ? null : (\n <div className=\"grid gap-1\">\n <p className=\"text-caption text-muted-foreground\">Each asker receives its own answer, as you, as if sent one by one.</p>\n <ActionButton data-testid=\"bridge-basket-send\" variant=\"primary\" className=\"justify-self-start\" isPending={working?.kind === 'send'} pendingLabel=\"Sending...\" disabled={working !== undefined || opening.openingId !== undefined} onClick={send}>\n {sendLabelOf(toSend)}\n </ActionButton>\n </div>\n )}\n </section>\n )}\n </ItemFocus>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"407bae59090ef46e76fb8d952969288e5bb4583e21\":{\"name\":\"sendBasket\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_5=/*#__PURE__*/createServerReference(\"407bae59090ef46e76fb8d952969288e5bb4583e21\",callServer,void 0,findSourceMapURL,\"sendBasket\");export{$$RSC_SERVER_ACTION_5 as sendBasket};","/**\n * The basket as Bridge shows it (docs/design/bridge, section 9): what each\n * entry stands as, what Send sends, and what it did, in words. Pure view\n * logic, no React and no API client.\n */\nimport type { inferRouterOutputs } from '@trpc/server';\n\nimport type { GatewayRouter } from '../../../../src/adapters/trpc/router.js';\n\ntype PeopleOutputs = inferRouterOutputs<GatewayRouter>['people'];\n\n/** The basket as the API answers it: its entries in the order added, and how many answers Send sends. */\nexport type BasketView = PeopleOutputs['basket'];\n\n/** An entry of the basket, with what it stands as. */\nexport type BasketItem = BasketView['entries'][number];\n\n/** What Send did, as the API answers it. */\nexport type BasketSent = PeopleOutputs['sendBasket'];\n\nconst PLACES: Record<Extract<BasketItem['standing'], { kind: 'dealtElsewhere' }>['place'], string> = {\n bridge: 'in Bridge',\n chat: 'in a chat',\n slack: 'in Slack',\n gateway: 'by the gateway',\n};\n\nconst SKIPPED: Record<BasketSent['skipped'][number]['kind'], string> = {\n withdrawn: 'withdrawn',\n replaced: 'replaced',\n dealtElsewhere: 'answered elsewhere',\n};\n\nfunction answersOf(count: number): string {\n return count === 1 ? '1 answer' : `${String(count)} answers`;\n}\n\n/** The Send button, naming how many answers it sends. */\nexport function sendLabelOf(toSend: number): string {\n return `Send ${answersOf(toSend)}`;\n}\n\n/** What an entry stands as, in words; nothing for one waiting for Send. */\nexport function standingTextOf(item: BasketItem): string | undefined {\n const { standing } = item;\n switch (standing.kind) {\n case 'waiting':\n return undefined;\n case 'sending':\n return 'Sending... The gateway keeps sending what is stored as sending, with the same key, until it is stored; you can close Bridge.';\n case 'refused':\n return `Not sent: ${standing.reason}. The others were sent.`;\n case 'withdrawn':\n return `Withdrawn by ${item.from}. Not sent; Send skips it.`;\n case 'replaced':\n return `Replaced by ${item.from}. Not sent: its options may differ, so answer the new question.`;\n case 'dealtElsewhere':\n return standing.option === null ? `Done ${PLACES[standing.place]}. Nothing is sent from it.` : `Answered ${PLACES[standing.place]}: option ${String(standing.option)}. Nothing is sent from it.`;\n }\n}\n\n/** What Send did: how many answers are being sent, and each entry it skipped and took out. */\nexport function sentTextOf(sent: BasketSent): string {\n const sending = sent.sending === 0 ? 'Nothing to send.' : sent.sending === 1 ? '1 answer is being sent.' : `${answersOf(sent.sending)} are being sent, each on its own, in the order you added them.`;\n if (sent.skipped.length === 0) {\n return sending;\n }\n return `${sending} Not sent: ${sent.skipped.map(({ title, kind }) => `“${title}”, ${SKIPPED[kind]}`).join('; ')}.`;\n}\n"],"names":["$$RSC_SERVER_ACTION_5"],"mappings":"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"}
|
package/web/.next/server/chunks/ssr/web_features_bridge_state_decision-panel_tsx_1mzsixgc8zuxc._.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module.exports=[20298,a=>{"use strict";var b=a.i(87924),c=a.i(50944),d=a.i(72131),e=a.i(5050);let f=(0,e.createServerReference)("
|
|
1
|
+
module.exports=[20298,a=>{"use strict";var b=a.i(87924),c=a.i(50944),d=a.i(72131),e=a.i(5050);let f=(0,e.createServerReference)("4013cfcec33d2651e37d34a8c0cd6f4b7e2f79d5b8",e.callServer,void 0,e.findSourceMapURL,"decideRequest");var g=a.i(16550),h=a.i(51636),i=a.i(35149);let j=["messages:send","messages:receive"],k={"messages:send":"Send","messages:receive":"Receive","fleet:read":"Read the fleet"};var l=a.i(32043),m=a.i(10039);let n="rounded-md border border-input bg-background px-2 py-1.5";function o({scope:a,isLocked:c}){return(0,b.jsxs)("label",{className:"flex items-center gap-2",children:[c?(0,b.jsx)("input",{type:"checkbox",checked:!0,disabled:!0,"aria-label":k[a]??a}):(0,b.jsx)("input",{type:"checkbox",name:"scope",value:a,"aria-label":k[a]??a}),(0,b.jsx)("span",{children:k[a]??a}),(0,b.jsx)("span",{className:"font-mono text-caption text-muted-foreground",children:a}),c?(0,b.jsx)("span",{className:"text-caption text-muted-foreground",children:"(always)"}):null]})}function p(a,b){let c=a.get(b);return"string"==typeof c?c:""}function q({request:a,heldScopes:c,owners:e,working:f,failure:g,onDecide:k}){let[r,s]=(0,d.useState)("bearer"),t=function(a){switch(a.subject.kind){case"client":return a.subject.clientName;case"server":return URL.canParse(a.subject.url)?new URL(a.subject.url).host:a.subject.url}}(a),u="server"===a.subject.kind;return(0,b.jsxs)("section",{className:"grid gap-3 rounded-lg border border-border bg-card p-6 text-card-foreground shadow-sm",children:[(0,b.jsxs)("h2",{className:"text-heading font-semibold",children:["Approve ",t]}),(0,b.jsxs)("form",{onSubmit:a=>{a.preventDefault();let b=new FormData(a.currentTarget),c=b.getAll("scope").filter(a=>"string"==typeof a);k({kind:"approve",name:p(b,"name"),ownerShipId:p(b,"ownerShipId"),scopes:c,server:u?{credentialKind:(0,h.C)(p(b,"credentialKind")),token:p(b,"token"),clientId:p(b,"clientId"),clientSecret:p(b,"clientSecret"),issuer:p(b,"issuer"),scope:p(b,"scope-requested"),concurrencyLimit:Number(p(b,"concurrencyLimit"))}:void 0})},className:"grid gap-3",children:[(0,b.jsxs)("label",{className:"grid gap-1",children:[(0,b.jsx)("span",{children:"Proxy ship's name"}),(0,b.jsx)("input",{name:"name",defaultValue:t.toLowerCase().replaceAll(/[^a-z0-9]+/g,"-").replaceAll(/^-+|-+$/g,""),required:!0,className:n})]}),(0,b.jsx)(i.f,{choices:e.isOk?e.value:[],current:null,help:"A person ship or argo. It answers for this party.",refusal:e.isOk?void 0:e.refusal}),(0,b.jsxs)("fieldset",{className:"grid gap-1",children:[(0,b.jsx)("legend",{children:"Scopes"}),j.map(a=>(0,b.jsx)(o,{scope:a,isLocked:!0},a)),c.filter(a=>!j.includes(a)).map(a=>(0,b.jsx)(o,{scope:a,isLocked:!1},a))]}),u?(0,b.jsxs)("fieldset",{className:"grid gap-2",children:[(0,b.jsx)("legend",{children:"The server's credential"}),(0,b.jsxs)("label",{className:"grid gap-1",children:[(0,b.jsx)("span",{children:"Credential"}),(0,b.jsx)("select",{name:"credentialKind",value:r,onChange:a=>{s((0,h.C)(a.target.value))},className:n,children:h.x.map(({kind:a,label:c})=>(0,b.jsx)("option",{value:a,children:c},a))})]}),"none"===r?null:"bearer"===r?(0,b.jsxs)("label",{className:"grid gap-1",children:[(0,b.jsx)("span",{children:"Bearer token"}),(0,b.jsx)("input",{type:"password",name:"token",className:n})]}):(0,b.jsxs)(b.Fragment,{children:[(0,b.jsxs)("label",{className:"grid gap-1",children:[(0,b.jsx)("span",{children:"Client id"}),(0,b.jsx)("input",{name:"clientId",className:n})]}),(0,b.jsxs)("label",{className:"grid gap-1",children:[(0,b.jsx)("span",{children:"Client secret"}),(0,b.jsx)("input",{type:"password",name:"clientSecret",className:n})]}),(0,b.jsxs)("label",{className:"grid gap-1",children:[(0,b.jsx)("span",{children:"Issuer"}),(0,b.jsx)("input",{type:"url",name:"issuer",className:n})]}),(0,b.jsxs)("label",{className:"grid gap-1",children:[(0,b.jsx)("span",{children:"Scope"}),(0,b.jsx)("input",{name:"scope-requested",className:n})]})]}),(0,b.jsxs)("label",{className:"grid gap-1",children:[(0,b.jsx)("span",{children:"Calls at once"}),(0,b.jsx)("input",{type:"number",name:"concurrencyLimit",min:1,max:10,defaultValue:4,className:n})]})]}):null,void 0===g?null:(0,b.jsx)(m.f,{message:g,isFailure:!0}),(0,b.jsxs)("div",{className:"flex gap-2",children:[(0,b.jsx)(l.f,{type:"submit",variant:"primary","data-testid":"bridge-approve",isPending:"approve"===f,pendingLabel:"Approving...",disabled:void 0!==f,children:"Approve"}),(0,b.jsx)(l.f,{type:"button","data-testid":"bridge-deny",isPending:"deny"===f,pendingLabel:"Denying...",disabled:void 0!==f,onClick:()=>{k({kind:"deny"})},children:"Deny"})]})]})]})}a.s(["DecisionPanel",0,function({fleetId:a,request:e,heldScopes:h,owners:i}){let j=(0,c.useRouter)(),[k,l]=(0,d.useState)(),[m,n]=(0,d.useState)(),[,o]=(0,d.useTransition)();return(0,b.jsx)(q,{request:e,heldScopes:h,owners:i,working:k,failure:m,onDecide:b=>{l(b.kind),n(void 0),o(async()=>{let c=await f({fleet:a,requestId:e.requestId,input:b});c.isOk?j.push(`${g.T}?${new URLSearchParams({fleet:a,decided:b.kind}).toString()}`):(l(void 0),n(c.message))})}})}],20298)}];
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=web_features_bridge_state_decision-panel_tsx_1mzsixgc8zuxc._.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../web/features/bridge/state/decision-panel.tsx","../../../../../web/features/bridge/hooks/data%3A64ed68%20%3Ctext/javascript%3E","../../../../../web/features/bridge/ui/decision-form.tsx","../../../../../web/features/bridge/services/requests.ts"],"sourcesContent":["'use client';\n\nimport { useRouter } from 'next/navigation';\nimport { useState, useTransition } from 'react';\n\nimport type { OwnerChoices } from '../../forms/ui/owner-field';\nimport { decideRequest } from '../hooks/bridge-actions';\nimport { BRIDGE_SETTINGS_PATH } from '../paths';\nimport type { DecisionInput, ShownRequest } from '../services/requests';\nimport { DecisionForm } from '../ui/decision-form';\n\n/** Decides one request as the signed-in approver, then goes back to Bridge's settings, which say nothing more waits here. */\nexport function DecisionPanel({ fleetId, request, heldScopes, owners }: { fleetId: string; request: ShownRequest; heldScopes: readonly string[]; owners: OwnerChoices }) {\n const router = useRouter();\n const [working, setWorking] = useState<DecisionInput['kind'] | undefined>();\n const [failure, setFailure] = useState<string | undefined>();\n const [, startTransition] = useTransition();\n const decide = (decision: DecisionInput) => {\n setWorking(decision.kind);\n setFailure(undefined);\n startTransition(async () => {\n const outcome = await decideRequest({ fleet: fleetId, requestId: request.requestId, input: decision });\n if (outcome.isOk) {\n router.push(`${BRIDGE_SETTINGS_PATH}?${new URLSearchParams({ fleet: fleetId, decided: decision.kind }).toString()}`);\n return;\n }\n setWorking(undefined);\n setFailure(outcome.message);\n });\n };\n return <DecisionForm request={request} heldScopes={heldScopes} owners={owners} working={working} failure={failure} onDecide={decide} />;\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"4080e6f6510c124a1b0598c7303e7858de44a8b40b\":{\"name\":\"decideRequest\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_11=/*#__PURE__*/createServerReference(\"4080e6f6510c124a1b0598c7303e7858de44a8b40b\",callServer,void 0,findSourceMapURL,\"decideRequest\");export{$$RSC_SERVER_ACTION_11 as decideRequest};","'use client';\n\nimport { useState, type SyntheticEvent } from 'react';\n\nimport { CREDENTIAL_KINDS, credentialKindOf } from '../../forms/server-credential';\nimport { OwnerField, type OwnerChoices } from '../../forms/ui/owner-field';\nimport { EVERY_APP_SHIP_SCOPES, scopeLabel, subjectName, suggestedName, type DecisionInput, type ServerCredentialInput, type ShownRequest } from '../services/requests';\nimport { ActionButton } from './action-button';\nimport { Notice } from './notice';\n\nconst CONCURRENCY_LIMIT_DEFAULT = 4;\n\nconst FIELD = 'rounded-md border border-input bg-background px-2 py-1.5';\n\nfunction ScopeRow({ scope, isLocked }: { scope: string; isLocked: boolean }) {\n return (\n <label className=\"flex items-center gap-2\">\n {isLocked ? <input type=\"checkbox\" checked disabled aria-label={scopeLabel(scope)} /> : <input type=\"checkbox\" name=\"scope\" value={scope} aria-label={scopeLabel(scope)} />}\n <span>{scopeLabel(scope)}</span>\n <span className=\"font-mono text-caption text-muted-foreground\">{scope}</span>\n {isLocked ? <span className=\"text-caption text-muted-foreground\">(always)</span> : null}\n </label>\n );\n}\n\nfunction text(form: FormData, name: string): string {\n const value = form.get(name);\n return typeof value === 'string' ? value : '';\n}\n\nfunction serverOf(form: FormData): ServerCredentialInput {\n return {\n credentialKind: credentialKindOf(text(form, 'credentialKind')),\n token: text(form, 'token'),\n clientId: text(form, 'clientId'),\n clientSecret: text(form, 'clientSecret'),\n issuer: text(form, 'issuer'),\n scope: text(form, 'scope-requested'),\n concurrencyLimit: Number(text(form, 'concurrencyLimit')),\n };\n}\n\n/**\n * The decision on a request: approving names the proxy ship and its owner,\n * and offers as scope rows only the scopes the approver holds, messages:send\n * and messages:receive locked on (0046, 0053); a server's credential too.\n * What was typed stays when the API refuses.\n */\nexport function DecisionForm({\n request,\n heldScopes,\n owners,\n working,\n failure,\n onDecide,\n}: {\n request: ShownRequest;\n heldScopes: readonly string[];\n owners: OwnerChoices;\n working: DecisionInput['kind'] | undefined;\n failure: string | undefined;\n onDecide: (decision: DecisionInput) => void;\n}) {\n const [credentialKind, setCredentialKind] = useState<ServerCredentialInput['credentialKind']>('bearer');\n const name = subjectName(request);\n const isServer = request.subject.kind === 'server';\n const approve = (event: SyntheticEvent<HTMLFormElement>) => {\n event.preventDefault();\n const form = new FormData(event.currentTarget);\n const scopes = form.getAll('scope').filter((scope) => typeof scope === 'string');\n onDecide({ kind: 'approve', name: text(form, 'name'), ownerShipId: text(form, 'ownerShipId'), scopes, server: isServer ? serverOf(form) : undefined });\n };\n return (\n <section className=\"grid gap-3 rounded-lg border border-border bg-card p-6 text-card-foreground shadow-sm\">\n <h2 className=\"text-heading font-semibold\">Approve {name}</h2>\n <form onSubmit={approve} className=\"grid gap-3\">\n <label className=\"grid gap-1\">\n <span>Proxy ship's name</span>\n <input name=\"name\" defaultValue={suggestedName(name)} required className={FIELD} />\n </label>\n <OwnerField choices={owners.isOk ? owners.value : []} current={null} help=\"A person ship or argo. It answers for this party.\" refusal={owners.isOk ? undefined : owners.refusal} />\n <fieldset className=\"grid gap-1\">\n <legend>Scopes</legend>\n {EVERY_APP_SHIP_SCOPES.map((scope) => (\n <ScopeRow key={scope} scope={scope} isLocked />\n ))}\n {heldScopes\n .filter((scope) => !EVERY_APP_SHIP_SCOPES.includes(scope))\n .map((scope) => (\n <ScopeRow key={scope} scope={scope} isLocked={false} />\n ))}\n </fieldset>\n {isServer ? (\n <fieldset className=\"grid gap-2\">\n <legend>The server's credential</legend>\n <label className=\"grid gap-1\">\n <span>Credential</span>\n <select name=\"credentialKind\" value={credentialKind} onChange={(event) => { setCredentialKind(credentialKindOf(event.target.value)); }} className={FIELD}>\n {CREDENTIAL_KINDS.map(({ kind, label }) => (\n <option key={kind} value={kind}>\n {label}\n </option>\n ))}\n </select>\n </label>\n {credentialKind === 'none' ? null : credentialKind === 'bearer' ? (\n <label className=\"grid gap-1\">\n <span>Bearer token</span>\n <input type=\"password\" name=\"token\" className={FIELD} />\n </label>\n ) : (\n <>\n <label className=\"grid gap-1\">\n <span>Client id</span>\n <input name=\"clientId\" className={FIELD} />\n </label>\n <label className=\"grid gap-1\">\n <span>Client secret</span>\n <input type=\"password\" name=\"clientSecret\" className={FIELD} />\n </label>\n <label className=\"grid gap-1\">\n <span>Issuer</span>\n <input type=\"url\" name=\"issuer\" className={FIELD} />\n </label>\n <label className=\"grid gap-1\">\n <span>Scope</span>\n <input name=\"scope-requested\" className={FIELD} />\n </label>\n </>\n )}\n <label className=\"grid gap-1\">\n <span>Calls at once</span>\n <input type=\"number\" name=\"concurrencyLimit\" min={1} max={10} defaultValue={CONCURRENCY_LIMIT_DEFAULT} className={FIELD} />\n </label>\n </fieldset>\n ) : null}\n {failure === undefined ? null : <Notice message={failure} isFailure />}\n <div className=\"flex gap-2\">\n <ActionButton type=\"submit\" variant=\"primary\" data-testid=\"bridge-approve\" isPending={working === 'approve'} pendingLabel=\"Approving...\" disabled={working !== undefined}>\n Approve\n </ActionButton>\n <ActionButton type=\"button\" data-testid=\"bridge-deny\" isPending={working === 'deny'} pendingLabel=\"Denying...\" disabled={working !== undefined} onClick={() => { onDecide({ kind: 'deny' }); }}>\n Deny\n </ActionButton>\n </div>\n </form>\n </section>\n );\n}\n","/**\n * How Bridge's settings name and states a request for an outside party (design\n * B, Signal): pure view logic, no React and no API client.\n */\nimport type { inferRouterOutputs } from '@trpc/server';\n\nimport type { GatewayRouter } from '../../../../src/adapters/trpc/router.js';\nimport type { CredentialKind } from '../../forms/server-credential';\n\n/** A request as the API shows it. */\nexport type ShownRequest = inferRouterOutputs<GatewayRouter>['requests']['toDecide'][number];\n\n/** The tones a status shows, from the design tokens; a status always has its words too. */\nexport type Tone = 'waiting' | 'active' | 'ok' | 'attention' | 'ended';\n\nexport interface Status {\n readonly tone: Tone;\n readonly label: string;\n}\n\nconst DAY_MS = 86_400_000;\n\n/** How an outside party is named before it has a proxy ship: a client by its name, a server by its host. */\nexport function subjectName(request: ShownRequest): string {\n switch (request.subject.kind) {\n case 'client':\n return request.subject.clientName;\n case 'server':\n return URL.canParse(request.subject.url) ? new URL(request.subject.url).host : request.subject.url;\n }\n}\n\nexport function subjectKind(request: ShownRequest): string {\n switch (request.subject.kind) {\n case 'client':\n return 'MCP client';\n case 'server':\n return 'MCP server';\n }\n}\n\n/** Days left of the 7 a request is kept, counted up, so its last day reads as such, in the attention tone. */\nexport function daysLeft(request: ShownRequest, now: Date): Status {\n const days = Math.ceil((Date.parse(request.expiresAt) - now.getTime()) / DAY_MS);\n return days <= 1 ? { tone: 'attention', label: 'Last day' } : { tone: 'waiting', label: `${String(days)} d left` };\n}\n\n/** A request's state as the console shows a status; one still pending shows its days left. */\nexport function requestStatus(request: ShownRequest, now: Date): Status {\n switch (request.state) {\n case 'pending':\n return daysLeft(request, now);\n case 'approving':\n return { tone: 'active', label: 'Approving' };\n case 'approved':\n return { tone: 'ok', label: 'Approved' };\n case 'denied':\n return { tone: 'ended', label: 'Denied' };\n case 'expired':\n return { tone: 'ended', label: 'Expired' };\n }\n}\n\n/** A proxy ship's name offered from the party's own: lower case letters, digits and dashes. */\nexport function suggestedName(name: string): string {\n return name\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/g, '-')\n .replaceAll(/^-+|-+$/g, '');\n}\n\n/** The scopes every app ship gets (0046): shown locked on, and always sent. */\nexport const EVERY_APP_SHIP_SCOPES: readonly string[] = ['messages:send', 'messages:receive'];\n\nconst SCOPE_LABELS: Record<string, string> = {\n 'messages:send': 'Send',\n 'messages:receive': 'Receive',\n 'fleet:read': 'Read the fleet',\n};\n\n/** A scope's plain label, shown beside the scope itself. */\nexport function scopeLabel(scope: string): string {\n return SCOPE_LABELS[scope] ?? scope;\n}\n\n/** The server's credential as the decision form takes it. */\nexport interface ServerCredentialInput {\n readonly credentialKind: CredentialKind;\n readonly token: string;\n readonly clientId: string;\n readonly clientSecret: string;\n readonly issuer: string;\n readonly scope: string;\n readonly concurrencyLimit: number;\n}\n\nexport type DecisionInput = { readonly kind: 'deny' } | { readonly kind: 'approve'; readonly name: string; readonly ownerShipId: string; readonly scopes: readonly string[]; readonly server: ServerCredentialInput | undefined };\n"],"names":["$$RSC_SERVER_ACTION_11"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../web/features/bridge/state/decision-panel.tsx","../../../../../web/features/bridge/hooks/data%3Aaa0b7f%20%3Ctext/javascript%3E","../../../../../web/features/bridge/ui/decision-form.tsx","../../../../../web/features/bridge/services/requests.ts"],"sourcesContent":["'use client';\n\nimport { useRouter } from 'next/navigation';\nimport { useState, useTransition } from 'react';\n\nimport type { OwnerChoices } from '../../forms/ui/owner-field';\nimport { decideRequest } from '../hooks/bridge-actions';\nimport { BRIDGE_SETTINGS_PATH } from '../paths';\nimport type { DecisionInput, ShownRequest } from '../services/requests';\nimport { DecisionForm } from '../ui/decision-form';\n\n/** Decides one request as the signed-in approver, then goes back to Bridge's settings, which say nothing more waits here. */\nexport function DecisionPanel({ fleetId, request, heldScopes, owners }: { fleetId: string; request: ShownRequest; heldScopes: readonly string[]; owners: OwnerChoices }) {\n const router = useRouter();\n const [working, setWorking] = useState<DecisionInput['kind'] | undefined>();\n const [failure, setFailure] = useState<string | undefined>();\n const [, startTransition] = useTransition();\n const decide = (decision: DecisionInput) => {\n setWorking(decision.kind);\n setFailure(undefined);\n startTransition(async () => {\n const outcome = await decideRequest({ fleet: fleetId, requestId: request.requestId, input: decision });\n if (outcome.isOk) {\n router.push(`${BRIDGE_SETTINGS_PATH}?${new URLSearchParams({ fleet: fleetId, decided: decision.kind }).toString()}`);\n return;\n }\n setWorking(undefined);\n setFailure(outcome.message);\n });\n };\n return <DecisionForm request={request} heldScopes={heldScopes} owners={owners} working={working} failure={failure} onDecide={decide} />;\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"4013cfcec33d2651e37d34a8c0cd6f4b7e2f79d5b8\":{\"name\":\"decideRequest\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_11=/*#__PURE__*/createServerReference(\"4013cfcec33d2651e37d34a8c0cd6f4b7e2f79d5b8\",callServer,void 0,findSourceMapURL,\"decideRequest\");export{$$RSC_SERVER_ACTION_11 as decideRequest};","'use client';\n\nimport { useState, type SyntheticEvent } from 'react';\n\nimport { CREDENTIAL_KINDS, credentialKindOf } from '../../forms/server-credential';\nimport { OwnerField, type OwnerChoices } from '../../forms/ui/owner-field';\nimport { EVERY_APP_SHIP_SCOPES, scopeLabel, subjectName, suggestedName, type DecisionInput, type ServerCredentialInput, type ShownRequest } from '../services/requests';\nimport { ActionButton } from './action-button';\nimport { Notice } from './notice';\n\nconst CONCURRENCY_LIMIT_DEFAULT = 4;\n\nconst FIELD = 'rounded-md border border-input bg-background px-2 py-1.5';\n\nfunction ScopeRow({ scope, isLocked }: { scope: string; isLocked: boolean }) {\n return (\n <label className=\"flex items-center gap-2\">\n {isLocked ? <input type=\"checkbox\" checked disabled aria-label={scopeLabel(scope)} /> : <input type=\"checkbox\" name=\"scope\" value={scope} aria-label={scopeLabel(scope)} />}\n <span>{scopeLabel(scope)}</span>\n <span className=\"font-mono text-caption text-muted-foreground\">{scope}</span>\n {isLocked ? <span className=\"text-caption text-muted-foreground\">(always)</span> : null}\n </label>\n );\n}\n\nfunction text(form: FormData, name: string): string {\n const value = form.get(name);\n return typeof value === 'string' ? value : '';\n}\n\nfunction serverOf(form: FormData): ServerCredentialInput {\n return {\n credentialKind: credentialKindOf(text(form, 'credentialKind')),\n token: text(form, 'token'),\n clientId: text(form, 'clientId'),\n clientSecret: text(form, 'clientSecret'),\n issuer: text(form, 'issuer'),\n scope: text(form, 'scope-requested'),\n concurrencyLimit: Number(text(form, 'concurrencyLimit')),\n };\n}\n\n/**\n * The decision on a request: approving names the proxy ship and its owner,\n * and offers as scope rows only the scopes the approver holds, messages:send\n * and messages:receive locked on (0046, 0053); a server's credential too.\n * What was typed stays when the API refuses.\n */\nexport function DecisionForm({\n request,\n heldScopes,\n owners,\n working,\n failure,\n onDecide,\n}: {\n request: ShownRequest;\n heldScopes: readonly string[];\n owners: OwnerChoices;\n working: DecisionInput['kind'] | undefined;\n failure: string | undefined;\n onDecide: (decision: DecisionInput) => void;\n}) {\n const [credentialKind, setCredentialKind] = useState<ServerCredentialInput['credentialKind']>('bearer');\n const name = subjectName(request);\n const isServer = request.subject.kind === 'server';\n const approve = (event: SyntheticEvent<HTMLFormElement>) => {\n event.preventDefault();\n const form = new FormData(event.currentTarget);\n const scopes = form.getAll('scope').filter((scope) => typeof scope === 'string');\n onDecide({ kind: 'approve', name: text(form, 'name'), ownerShipId: text(form, 'ownerShipId'), scopes, server: isServer ? serverOf(form) : undefined });\n };\n return (\n <section className=\"grid gap-3 rounded-lg border border-border bg-card p-6 text-card-foreground shadow-sm\">\n <h2 className=\"text-heading font-semibold\">Approve {name}</h2>\n <form onSubmit={approve} className=\"grid gap-3\">\n <label className=\"grid gap-1\">\n <span>Proxy ship's name</span>\n <input name=\"name\" defaultValue={suggestedName(name)} required className={FIELD} />\n </label>\n <OwnerField choices={owners.isOk ? owners.value : []} current={null} help=\"A person ship or argo. It answers for this party.\" refusal={owners.isOk ? undefined : owners.refusal} />\n <fieldset className=\"grid gap-1\">\n <legend>Scopes</legend>\n {EVERY_APP_SHIP_SCOPES.map((scope) => (\n <ScopeRow key={scope} scope={scope} isLocked />\n ))}\n {heldScopes\n .filter((scope) => !EVERY_APP_SHIP_SCOPES.includes(scope))\n .map((scope) => (\n <ScopeRow key={scope} scope={scope} isLocked={false} />\n ))}\n </fieldset>\n {isServer ? (\n <fieldset className=\"grid gap-2\">\n <legend>The server's credential</legend>\n <label className=\"grid gap-1\">\n <span>Credential</span>\n <select name=\"credentialKind\" value={credentialKind} onChange={(event) => { setCredentialKind(credentialKindOf(event.target.value)); }} className={FIELD}>\n {CREDENTIAL_KINDS.map(({ kind, label }) => (\n <option key={kind} value={kind}>\n {label}\n </option>\n ))}\n </select>\n </label>\n {credentialKind === 'none' ? null : credentialKind === 'bearer' ? (\n <label className=\"grid gap-1\">\n <span>Bearer token</span>\n <input type=\"password\" name=\"token\" className={FIELD} />\n </label>\n ) : (\n <>\n <label className=\"grid gap-1\">\n <span>Client id</span>\n <input name=\"clientId\" className={FIELD} />\n </label>\n <label className=\"grid gap-1\">\n <span>Client secret</span>\n <input type=\"password\" name=\"clientSecret\" className={FIELD} />\n </label>\n <label className=\"grid gap-1\">\n <span>Issuer</span>\n <input type=\"url\" name=\"issuer\" className={FIELD} />\n </label>\n <label className=\"grid gap-1\">\n <span>Scope</span>\n <input name=\"scope-requested\" className={FIELD} />\n </label>\n </>\n )}\n <label className=\"grid gap-1\">\n <span>Calls at once</span>\n <input type=\"number\" name=\"concurrencyLimit\" min={1} max={10} defaultValue={CONCURRENCY_LIMIT_DEFAULT} className={FIELD} />\n </label>\n </fieldset>\n ) : null}\n {failure === undefined ? null : <Notice message={failure} isFailure />}\n <div className=\"flex gap-2\">\n <ActionButton type=\"submit\" variant=\"primary\" data-testid=\"bridge-approve\" isPending={working === 'approve'} pendingLabel=\"Approving...\" disabled={working !== undefined}>\n Approve\n </ActionButton>\n <ActionButton type=\"button\" data-testid=\"bridge-deny\" isPending={working === 'deny'} pendingLabel=\"Denying...\" disabled={working !== undefined} onClick={() => { onDecide({ kind: 'deny' }); }}>\n Deny\n </ActionButton>\n </div>\n </form>\n </section>\n );\n}\n","/**\n * How Bridge's settings name and states a request for an outside party (design\n * B, Signal): pure view logic, no React and no API client.\n */\nimport type { inferRouterOutputs } from '@trpc/server';\n\nimport type { GatewayRouter } from '../../../../src/adapters/trpc/router.js';\nimport type { CredentialKind } from '../../forms/server-credential';\n\n/** A request as the API shows it. */\nexport type ShownRequest = inferRouterOutputs<GatewayRouter>['requests']['toDecide'][number];\n\n/** The tones a status shows, from the design tokens; a status always has its words too. */\nexport type Tone = 'waiting' | 'active' | 'ok' | 'attention' | 'ended';\n\nexport interface Status {\n readonly tone: Tone;\n readonly label: string;\n}\n\nconst DAY_MS = 86_400_000;\n\n/** How an outside party is named before it has a proxy ship: a client by its name, a server by its host. */\nexport function subjectName(request: ShownRequest): string {\n switch (request.subject.kind) {\n case 'client':\n return request.subject.clientName;\n case 'server':\n return URL.canParse(request.subject.url) ? new URL(request.subject.url).host : request.subject.url;\n }\n}\n\nexport function subjectKind(request: ShownRequest): string {\n switch (request.subject.kind) {\n case 'client':\n return 'MCP client';\n case 'server':\n return 'MCP server';\n }\n}\n\n/** Days left of the 7 a request is kept, counted up, so its last day reads as such, in the attention tone. */\nexport function daysLeft(request: ShownRequest, now: Date): Status {\n const days = Math.ceil((Date.parse(request.expiresAt) - now.getTime()) / DAY_MS);\n return days <= 1 ? { tone: 'attention', label: 'Last day' } : { tone: 'waiting', label: `${String(days)} d left` };\n}\n\n/** A request's state as the console shows a status; one still pending shows its days left. */\nexport function requestStatus(request: ShownRequest, now: Date): Status {\n switch (request.state) {\n case 'pending':\n return daysLeft(request, now);\n case 'approving':\n return { tone: 'active', label: 'Approving' };\n case 'approved':\n return { tone: 'ok', label: 'Approved' };\n case 'denied':\n return { tone: 'ended', label: 'Denied' };\n case 'expired':\n return { tone: 'ended', label: 'Expired' };\n }\n}\n\n/** A proxy ship's name offered from the party's own: lower case letters, digits and dashes. */\nexport function suggestedName(name: string): string {\n return name\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/g, '-')\n .replaceAll(/^-+|-+$/g, '');\n}\n\n/** The scopes every app ship gets (0046): shown locked on, and always sent. */\nexport const EVERY_APP_SHIP_SCOPES: readonly string[] = ['messages:send', 'messages:receive'];\n\nconst SCOPE_LABELS: Record<string, string> = {\n 'messages:send': 'Send',\n 'messages:receive': 'Receive',\n 'fleet:read': 'Read the fleet',\n};\n\n/** A scope's plain label, shown beside the scope itself. */\nexport function scopeLabel(scope: string): string {\n return SCOPE_LABELS[scope] ?? scope;\n}\n\n/** The server's credential as the decision form takes it. */\nexport interface ServerCredentialInput {\n readonly credentialKind: CredentialKind;\n readonly token: string;\n readonly clientId: string;\n readonly clientSecret: string;\n readonly issuer: string;\n readonly scope: string;\n readonly concurrencyLimit: number;\n}\n\nexport type DecisionInput = { readonly kind: 'deny' } | { readonly kind: 'approve'; readonly name: string; readonly ownerShipId: string; readonly scopes: readonly string[]; readonly server: ServerCredentialInput | undefined };\n"],"names":["$$RSC_SERVER_ACTION_11"],"mappings":"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"}
|
package/web/.next/server/chunks/ssr/web_features_bridge_state_done-panel_tsx_1m844-qb1z9p_._.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module.exports=[16563,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(5050);let e=(0,d.createServerReference)("
|
|
1
|
+
module.exports=[16563,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(5050);let e=(0,d.createServerReference)("601621f5cf585bae9e540db5aa6e2c5d2a6bbcd72c",d.callServer,void 0,d.findSourceMapURL,"readMoreRecord");var f=a.i(12110),g=a.i(32043),h=a.i(52560),i=a.i(10039),j=a.i(91777);a.s(["DonePanel",0,function({fleetId:a,first:d}){let[k,l]=(0,c.useState)(d.items),[m,n]=(0,c.useState)(d.nextCursor),[o,p]=(0,c.useState)(),[q,r]=(0,c.useTransition)();return(0,b.jsx)(j.f,{fleetId:a,children:c=>(0,b.jsxs)("section",{className:"grid gap-3","aria-labelledby":"bridge-done-heading",children:[(0,b.jsxs)("div",{children:[(0,b.jsx)("h1",{id:"bridge-done-heading",className:"text-heading font-semibold",children:"Done"}),(0,b.jsx)("p",{className:"text-muted-foreground",children:"Everything that reached you, with what came of it and where."})]}),0===k.length?(0,b.jsx)("p",{"data-testid":"bridge-record-empty",className:"rounded-md border border-border bg-card p-3 text-muted-foreground",children:"Nothing has reached you yet."}):(0,b.jsx)("ul",{className:"grid gap-2",children:k.map(a=>(0,b.jsx)(h.f,{item:a,isNew:"new"===a.state,outcome:(0,f.s)(a),failure:c.failure?.deliveryId===a.deliveryId?c.failure.message:void 0,children:"done"===a.state?null:(0,b.jsx)(g.f,{"data-testid":"bridge-open",isPending:c.openingId===a.deliveryId,pendingLabel:"Opening...",disabled:void 0!==c.openingId,onClick:()=>{c.open(a)},children:"Open"})},a.deliveryId))}),void 0===o?null:(0,b.jsx)(i.f,{message:o,isFailure:!0}),null===m?null:(0,b.jsx)(g.f,{"data-testid":"bridge-more",className:"justify-self-start",isPending:q,pendingLabel:"Reading...",onClick:()=>{p(void 0),r(async()=>{let b=await e(a,m);b.isOk?(l(a=>[...a,...b.value.items]),n(b.value.nextCursor)):p(b.message)})},children:"Show older"})]})})}],16563)}];
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=web_features_bridge_state_done-panel_tsx_1m844-qb1z9p_._.js.map
|
package/web/.next/server/chunks/ssr/web_features_bridge_state_done-panel_tsx_1m844-qb1z9p_._.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../web/features/bridge/state/done-panel.tsx","../../../../../web/features/bridge/hooks/data%
|
|
1
|
+
{"version":3,"sources":["../../../../../web/features/bridge/state/done-panel.tsx","../../../../../web/features/bridge/hooks/data%3A0c9549%20%3Ctext/javascript%3E"],"sourcesContent":["'use client';\n\nimport { useState, useTransition } from 'react';\n\nimport { readMoreRecord } from '../hooks/bridge-actions';\nimport { outcomeTextOf, type RecordPage } from '../services/items';\nimport { ActionButton } from '../ui/action-button';\nimport { ItemRow } from '../ui/item-row';\nimport { Notice } from '../ui/notice';\nimport { ItemFocus } from './item-focus';\n\n/**\n * Done (Bridge, CC4): everything that reached the person, newest first, with\n * what came of it and where. The next page is read only when they ask for\n * it. One not done yet opens here, to answer or be done with.\n */\nexport function DonePanel({ fleetId, first }: { fleetId: string; first: RecordPage }) {\n const [items, setItems] = useState(first.items);\n const [nextCursor, setNextCursor] = useState(first.nextCursor);\n const [failure, setFailure] = useState<string | undefined>();\n const [isReading, startTransition] = useTransition();\n\n const readMore = (cursor: string) => {\n setFailure(undefined);\n startTransition(async () => {\n const more = await readMoreRecord(fleetId, cursor);\n if (more.isOk) {\n setItems((shown) => [...shown, ...more.value.items]);\n setNextCursor(more.value.nextCursor);\n } else {\n setFailure(more.message);\n }\n });\n };\n\n return (\n <ItemFocus fleetId={fleetId}>\n {(opening) => (\n <section className=\"grid gap-3\" aria-labelledby=\"bridge-done-heading\">\n <div>\n <h1 id=\"bridge-done-heading\" className=\"text-heading font-semibold\">\n Done\n </h1>\n <p className=\"text-muted-foreground\">Everything that reached you, with what came of it and where.</p>\n </div>\n {items.length === 0 ? (\n <p data-testid=\"bridge-record-empty\" className=\"rounded-md border border-border bg-card p-3 text-muted-foreground\">\n Nothing has reached you yet.\n </p>\n ) : (\n <ul className=\"grid gap-2\">\n {items.map((item) => (\n <ItemRow key={item.deliveryId} item={item} isNew={item.state === 'new'} outcome={outcomeTextOf(item)} failure={opening.failure?.deliveryId === item.deliveryId ? opening.failure.message : undefined}>\n {item.state === 'done' ? null : (\n <ActionButton data-testid=\"bridge-open\" isPending={opening.openingId === item.deliveryId} pendingLabel=\"Opening...\" disabled={opening.openingId !== undefined} onClick={() => { opening.open(item); }}>\n Open\n </ActionButton>\n )}\n </ItemRow>\n ))}\n </ul>\n )}\n {failure === undefined ? null : <Notice message={failure} isFailure />}\n {nextCursor === null ? null : (\n <ActionButton data-testid=\"bridge-more\" className=\"justify-self-start\" isPending={isReading} pendingLabel=\"Reading...\" onClick={() => { readMore(nextCursor); }}>\n Show older\n </ActionButton>\n )}\n </section>\n )}\n </ItemFocus>\n );\n}\n","/* __next_internal_action_entry_do_not_use__ [{\"601621f5cf585bae9e540db5aa6e2c5d2a6bbcd72c\":{\"name\":\"readMoreRecord\"}},\"web/features/bridge/hooks/bridge-actions.ts\",\"\"] */\"use turbopack: no side effects\";import{createServerReference,callServer,findSourceMapURL}from\"private-next-rsc-action-client-wrapper\";const $$RSC_SERVER_ACTION_0=/*#__PURE__*/createServerReference(\"601621f5cf585bae9e540db5aa6e2c5d2a6bbcd72c\",callServer,void 0,findSourceMapURL,\"readMoreRecord\");export{$$RSC_SERVER_ACTION_0 as readMoreRecord};"],"names":["$$RSC_SERVER_ACTION_0"],"mappings":"wDAEA,EAAA,EAAA,CAAA,CAAA,OCF4M,EAAA,EAAA,CAAA,CAAA,MAAsG,IAAMA,EAAmC,CAAA,EAAA,EAAA,iBAAb,IAAa,AAAqB,EAAC,KAAxB,wCAAqE,EAAA,UAAU,CAAC,KAAK,EAAE,EAAA,gBAAgB,CAAC,kBDKjc,IAAA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,OACA,EAAA,EAAA,CAAA,CAAA,0BAOO,SAAS,AAAU,SAAE,CAAO,OAAE,CAAK,CAA0C,EAClF,GAAM,CAAC,EAAO,EAAS,CAAG,CAAA,EAAA,EAAA,QAAQ,AAAR,EAAS,EAAM,KAAK,EACxC,CAAC,EAAY,EAAc,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,EAAC,EAAM,UAAU,EACvD,CAAC,EAAS,EAAW,CAAG,CAAA,EAAA,EAAA,QAAA,AAAQ,IAChC,CAAC,EAAW,EAAgB,CAAG,CAAA,EAAA,EAAA,aAAA,AAAa,IAelD,MACE,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,CAAS,CAAA,CAAC,QAAS,WACjB,AAAC,GACA,CAAA,EAAA,EAAA,IAAA,EAAC,UAAA,CAAQ,UAAU,aAAa,kBAAgB,gCAC9C,CAAA,EAAA,EAAA,IAAA,EAAC,MAAA,WACC,CAAA,EAAA,EAAA,GAAA,EAAC,KAAA,CAAG,GAAG,sBAAsB,UAAU,sCAA6B,SAGpE,CAAA,EAAA,EAAA,GAAA,EAAC,IAAA,CAAE,UAAU,iCAAwB,oEAEtC,AAAiB,MAAX,MAAM,CACX,CAAA,EAAA,EAAA,GAAA,EAAC,IAAA,CAAE,cAAY,sBAAsB,UAAU,6EAAoE,iCAInH,CAAA,EAAA,EAAA,GAAA,EAAC,KAAA,CAAG,UAAU,sBACX,EAAM,GAAG,CAAC,AAAC,GACV,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,CAAO,CAAA,CAAuB,KAAM,EAAM,MAAsB,QAAf,EAAK,KAAK,CAAY,QAAS,CAAA,EAAA,EAAA,CAAA,AAAa,EAAC,GAAO,QAAS,EAAQ,OAAO,EAAE,aAAe,EAAK,UAAU,CAAG,EAAQ,OAAO,CAAC,OAAO,MAAG,WACxL,AAAe,WAAV,KAAK,CAAc,KACvB,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,CAAY,CAAA,CAAC,cAAY,cAAc,UAAW,EAAQ,SAAS,GAAK,EAAK,UAAU,CAAE,aAAa,aAAa,cAAgC,IAAtB,EAAQ,SAAS,CAAgB,QAAS,KAAQ,EAAQ,IAAI,CAAC,EAAO,WAAG,UAF7L,EAAK,UAAU,UAUtB,IAAZ,EAAwB,KAAO,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,CAAM,CAAA,CAAC,QAAS,EAAS,SAAS,CAAA,CAAA,IAClE,AAAe,SAAO,KACrB,CAAA,EAAA,EAAA,GAAA,EAAC,EAAA,CAAY,CAAA,CAAC,cAAY,cAAc,UAAU,qBAAqB,UAAW,EAAW,aAAa,aAAa,QAAS,KAzCxI,OAAW,GACX,EAAgB,UACd,IAAM,EAAO,MAAM,EAAe,EAuCqH,GAtCnJ,EAAK,EADkC,EAC9B,EAAE,AACb,EAAS,AAAC,GAAU,IAAI,KAAU,EAAK,KAAK,CAAC,KAAK,CAAC,EACnD,EAAc,EAAK,KAAK,CAAC,UAAU,GAEnC,EAAW,EAAK,OAAO,CAE3B,EAgCsK,WAAG,mBAQ7K"}
|
package/web/.next/server/chunks/ssr/web_features_frame_ui_gateway-header_tsx_0e9tekx4jf7ng._.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module.exports=[45648,a=>{"use strict";var b=a.i(87924),c=a.i(64831);let d={name:"search",size:24,node:[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]};d.node;let e=(0,c.f)(d);var f=a.i(78400),g=a.i(21244),h=a.i(95725),i=a.i(6158),j=a.i(18486);function k({switcher:a,account:c,onSearch:d,searchLabel:l="Search or jump to",children:m,actions:n,className:o}){return(0,b.jsxs)("header",{"data-slot":"app-header",className:(0,j.f)("sticky top-0 z-30 flex h-(--size-header) shrink-0 items-center justify-between gap-4 border-b border-border bg-background px-6",o),children:[(0,b.jsxs)("div",{className:"flex min-w-0 items-center gap-3",children:[void 0===a?(0,b.jsx)("span",{"data-testid":"app-switcher-loading","aria-hidden":!0,className:"h-4 w-36 shrink-0 animate-pulse rounded bg-muted motion-reduce:animate-none"}):(0,b.jsx)(f.AppSwitcher,{switcher:a,layout:"full",className:"shrink-0"}),m]}),(0,b.jsxs)("div",{className:"flex shrink-0 items-center gap-3",children:[n,d?(0,b.jsxs)(g.f,{size:"sm",icon:(0,b.jsx)(e,{"aria-hidden":!0}),"data-testid":"header-search",className:"w-60 justify-start font-normal text-muted-foreground max-lg:w-auto",onClick:d,children:[(0,b.jsx)("span",{className:"grow text-left max-lg:sr-only",children:l}),(0,b.jsx)(h.f,{children:"⌘K"})]}):null,(0,b.jsx)(i.AvatarMenu,{...c})]})]})}var l=a.i(50944),m=a.i(72131),n=a.i(5050);let o=(0,n.createServerReference)("
|
|
1
|
+
module.exports=[45648,a=>{"use strict";var b=a.i(87924),c=a.i(64831);let d={name:"search",size:24,node:[["path",{d:"m21 21-4.34-4.34",key:"14j7rj"}],["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}]]};d.node;let e=(0,c.f)(d);var f=a.i(78400),g=a.i(21244),h=a.i(95725),i=a.i(6158),j=a.i(18486);function k({switcher:a,account:c,onSearch:d,searchLabel:l="Search or jump to",children:m,actions:n,className:o}){return(0,b.jsxs)("header",{"data-slot":"app-header",className:(0,j.f)("sticky top-0 z-30 flex h-(--size-header) shrink-0 items-center justify-between gap-4 border-b border-border bg-background px-6",o),children:[(0,b.jsxs)("div",{className:"flex min-w-0 items-center gap-3",children:[void 0===a?(0,b.jsx)("span",{"data-testid":"app-switcher-loading","aria-hidden":!0,className:"h-4 w-36 shrink-0 animate-pulse rounded bg-muted motion-reduce:animate-none"}):(0,b.jsx)(f.AppSwitcher,{switcher:a,layout:"full",className:"shrink-0"}),m]}),(0,b.jsxs)("div",{className:"flex shrink-0 items-center gap-3",children:[n,d?(0,b.jsxs)(g.f,{size:"sm",icon:(0,b.jsx)(e,{"aria-hidden":!0}),"data-testid":"header-search",className:"w-60 justify-start font-normal text-muted-foreground max-lg:w-auto",onClick:d,children:[(0,b.jsx)("span",{className:"grow text-left max-lg:sr-only",children:l}),(0,b.jsx)(h.f,{children:"⌘K"})]}):null,(0,b.jsx)(i.AvatarMenu,{...c})]})]})}var l=a.i(50944),m=a.i(72131),n=a.i(5050);let o=(0,n.createServerReference)("4050b261d17e2b265fd019f61804f3eae3b8901966",n.callServer,void 0,n.findSourceMapURL,"setTheme"),p=["light","dark","system"];var q=a.i(25959);let r=[{key:"bridge",label:"Bridge",path:"/bridge"},{key:"outside-parties",label:"Outside parties",path:"/outside-parties"},{key:"parties",label:"Parties",path:"/parties"},{key:"slack",label:"Slack",path:"/slack"}];function s({fleetId:a}){let c=(0,l.usePathname)();return(0,b.jsx)("nav",{"aria-label":"Gateway",className:"flex min-w-0 items-center gap-1 overflow-x-auto",children:r.map(({key:d,label:e,path:f})=>{let g=c===f||c.startsWith(`${f}/`);return(0,b.jsx)("a",{href:(0,q.f)(f,a),"data-testid":`frame-tab-${d}`,"aria-current":g?"page":void 0,className:`whitespace-nowrap rounded-md px-2 py-1 text-sm ${g?"bg-accent font-semibold":"text-muted-foreground hover:bg-accent"}`,children:e},d)})})}a.s(["GatewayHeader",0,function({switchers:a,email:c,accountUrl:d,theme:e}){let g=(0,l.useSearchParams)().get("fleet"),h=a.find(({fleet:a})=>a.id===g)??a[0],[j,n]=(0,m.useState)(e),[q,r]=(0,m.useState)(!1),t=(0,m.useRef)(null),u={name:c,signedInAs:"account",accountUrl:d,theme:j,themes:p,hasTheme:!0,onThemeChange:a=>{let b;n(a),document.documentElement.dataset.theme=a,b="dark"===a||"system"===a&&window.matchMedia("(prefers-color-scheme: dark)").matches,document.documentElement.classList.toggle("dark",b),o(a)},shortcuts:[],helpUrl:"https://github.com/ThomasHendrickx/aeolus-fleet",onSignOut:()=>{r(!0),t.current?.requestSubmit()},isSigningOut:q};return(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)("form",{ref:t,method:"post",action:"/sign-in/end",hidden:!0}),(0,b.jsx)(k,{switcher:h,account:u,className:"max-sm:hidden",children:void 0===h?null:(0,b.jsx)(s,{fleetId:h.fleet.id})}),(0,b.jsxs)("header",{className:"sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border bg-background px-4 sm:hidden",children:[void 0===h?null:(0,b.jsx)(f.AppSwitcher,{switcher:h,layout:"compact",className:"min-w-0"}),void 0===h?null:(0,b.jsx)(s,{fleetId:h.fleet.id}),(0,b.jsx)("span",{className:"grow"}),(0,b.jsx)(i.AvatarMenuSheet,{...u})]})]})}],45648)}];
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=web_features_frame_ui_gateway-header_tsx_0e9tekx4jf7ng._.js.map
|