@shepai/cli 1.172.0 → 1.174.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/presentation/web/app/features/feature-tree-page-client.d.ts +7 -1
- package/dist/src/presentation/web/app/features/feature-tree-page-client.d.ts.map +1 -1
- package/dist/src/presentation/web/app/features/feature-tree-page-client.js +142 -3
- package/dist/src/presentation/web/app/features/get-feature-tree-data.d.ts.map +1 -1
- package/dist/src/presentation/web/app/features/get-feature-tree-data.js +4 -1
- package/dist/src/presentation/web/components/common/repo-group/repo-group.js +1 -1
- package/dist/src/presentation/web/components/features/feature-tree-table/feature-tree-table.d.ts +21 -5
- package/dist/src/presentation/web/components/features/feature-tree-table/feature-tree-table.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/feature-tree-table/feature-tree-table.js +169 -87
- package/dist/src/presentation/web/components/features/feature-tree-table/index.d.ts +2 -2
- package/dist/src/presentation/web/components/features/feature-tree-table/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/feature-tree-table/index.js +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +4 -4
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +2 -2
- package/web/.next/required-server-files.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +29 -29
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/chat/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/chat/page/server-reference-manifest.json +27 -27
- package/web/.next/server/app/(dashboard)/@drawer/chat/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/chat/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +38 -38
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +38 -38
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/chat/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/chat/page/server-reference-manifest.json +27 -27
- package/web/.next/server/app/(dashboard)/chat/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/chat/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +30 -30
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +38 -38
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +38 -38
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +27 -27
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/build-manifest.json +2 -2
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error/page/build-manifest.json +2 -2
- package/web/.next/server/app/_global-error.html +2 -2
- 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/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.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/build-manifest.json +2 -2
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +6 -6
- 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/features/page/build-manifest.json +2 -2
- package/web/.next/server/app/features/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/features/page.js.nft.json +1 -1
- package/web/.next/server/app/features/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/settings/page/build-manifest.json +2 -2
- package/web/.next/server/app/settings/page/server-reference-manifest.json +9 -9
- package/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/build-manifest.json +2 -2
- package/web/.next/server/app/skills/page/server-reference-manifest.json +13 -13
- package/web/.next/server/app/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/build-manifest.json +2 -2
- package/web/.next/server/app/tools/page/server-reference-manifest.json +11 -11
- package/web/.next/server/app/tools/page.js.nft.json +1 -1
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/build-manifest.json +2 -2
- package/web/.next/server/app/version/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/version/page.js.nft.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
- package/web/.next/server/chunks/ssr/403f9_next_dist_a53cb908._.js +1 -1
- package/web/.next/server/chunks/ssr/403f9_next_dist_esm_ceb2fa1e._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_feature-drawer-client_tsx_e9755fc8._.js +2 -2
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_feature-drawer-client_tsx_e9755fc8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__08c912ab._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__08c912ab._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__0c5452c3._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__1cd4327c._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__1cd4327c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__1f389e5d._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__1f389e5d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__22d17c66._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__22d17c66._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__4fb81977._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__4fb81977._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6c7d3936._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6c7d3936._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7dcd0917._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7dcd0917._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__851f6adb._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__86ff0bc5._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__92ffd5ee._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__92ffd5ee._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__b020c17d._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__b020c17d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__b7b96453._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__b7b96453._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__ba7f5873._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__ba7f5873._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c5e09f6f._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c5e09f6f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_02e01240._.js +1 -1
- package/web/.next/server/chunks/ssr/_02e01240._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_05c23ad9._.js +1 -1
- package/web/.next/server/chunks/ssr/_05c23ad9._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_073183f4._.js +3 -0
- package/web/.next/server/chunks/ssr/_073183f4._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_16eb4fec._.js +1 -1
- package/web/.next/server/chunks/ssr/_16eb4fec._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_1879404a._.js +1 -1
- package/web/.next/server/chunks/ssr/_18886033._.js +1 -1
- package/web/.next/server/chunks/ssr/_18886033._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_1b7dae9a._.js +1 -1
- package/web/.next/server/chunks/ssr/_22e00a14._.js +1 -1
- package/web/.next/server/chunks/ssr/_22e00a14._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_5119a3df._.js → _295fffde._.js} +2 -2
- package/web/.next/server/chunks/ssr/_295fffde._.js.map +1 -0
- package/web/.next/server/chunks/ssr/{_7b946eeb._.js → _3a71f39b._.js} +3 -3
- package/web/.next/server/chunks/ssr/{_7b946eeb._.js.map → _3a71f39b._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_43ba79e7._.js +1 -1
- package/web/.next/server/chunks/ssr/{_6f35fb9a._.js → _49b8d085._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_6f35fb9a._.js.map → _49b8d085._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_4cbb7f95._.js +1 -1
- package/web/.next/server/chunks/ssr/_4cbb7f95._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_56b9d60f._.js +1 -1
- package/web/.next/server/chunks/ssr/_56b9d60f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_6abfa39e._.js +3 -0
- package/web/.next/server/chunks/ssr/_6abfa39e._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_7476c702._.js +3 -0
- package/web/.next/server/chunks/ssr/_7476c702._.js.map +1 -0
- package/web/.next/server/chunks/ssr/{_40918326._.js → _7953be4d._.js} +3 -3
- package/web/.next/server/chunks/ssr/{_40918326._.js.map → _7953be4d._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_7cb0396e._.js +3 -0
- package/web/.next/server/chunks/ssr/_7cb0396e._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_a5a5901d._.js +1 -1
- package/web/.next/server/chunks/ssr/_a5a5901d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_ad09f271._.js +1 -1
- package/web/.next/server/chunks/ssr/_ad09f271._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_bb09579b._.js +1 -1
- package/web/.next/server/chunks/ssr/_c3f595c6._.js +1 -1
- package/web/.next/server/chunks/ssr/_c3f595c6._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_0020fddd._.js → _d9c0a97a._.js} +3 -3
- package/web/.next/server/chunks/ssr/_d9c0a97a._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_e680c57c._.js +9 -0
- package/web/.next/server/chunks/ssr/_e680c57c._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_ea9e1556._.js +1 -1
- package/web/.next/server/chunks/ssr/_ea9e1556._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_f1ba9be6._.js +2 -2
- package/web/.next/server/chunks/ssr/_f1ba9be6._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_f33cd07e._.js +2 -2
- package/web/.next/server/chunks/ssr/_f33cd07e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_f8b45233._.js +1 -1
- package/web/.next/server/chunks/ssr/_f8b45233._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_fd595af2._.js +3 -0
- package/web/.next/server/chunks/ssr/_fd595af2._.js.map +1 -0
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/f3a1f_components_common_control-center-drawer_global-chat-drawer-client_tsx_158c4b12._.js +1 -1
- package/web/.next/server/chunks/ssr/f3a1f_components_common_control-center-drawer_repository-drawer-client_tsx_39a00c03._.js +1 -1
- package/web/.next/server/chunks/ssr/f3a1f_components_common_control-center-drawer_repository-drawer-client_tsx_39a00c03._.js.map +1 -1
- package/web/.next/server/chunks/ssr/node_modules__pnpm_63d47a3e._.js +3 -0
- package/web/.next/server/chunks/ssr/node_modules__pnpm_63d47a3e._.js.map +1 -0
- package/web/.next/server/chunks/ssr/node_modules__pnpm_747b43ac._.js +3 -0
- package/web/.next/server/chunks/ssr/node_modules__pnpm_747b43ac._.js.map +1 -0
- package/web/.next/server/chunks/ssr/node_modules__pnpm_ef15a0bd._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_app_actions_open-ide_ts_baaca5d5._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_app_features_feature-tree-page-client_tsx_34c5cbbf._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_app_features_feature-tree-page-client_tsx_34c5cbbf._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_895e5bfa._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_895e5bfa._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_a5e6c910._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_skills_8a174cac._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_skills_8a174cac._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_tools_tools-page-client_tsx_3d0aa70c._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_ui_select_tsx_45d6b8ae._.js +1 -1
- package/web/.next/server/middleware-build-manifest.js +2 -2
- package/web/.next/server/pages/500.html +2 -2
- 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/{e343129b58522131.js → 0baf5a2e56d191cf.js} +1 -1
- package/web/.next/static/chunks/{49e9841d15fa664f.js → 32c46154c31c58fc.js} +1 -1
- package/web/.next/static/chunks/{a92d22657e3e6700.js → 393b27ab5de6c454.js} +1 -1
- package/web/.next/static/chunks/46e2693dbc9262fd.js +5 -0
- package/web/.next/static/chunks/{b2f98efed01ed1e3.js → 534194b584a151ed.js} +1 -1
- package/web/.next/static/chunks/5a2e1c8699897c26.js +3 -0
- package/web/.next/static/chunks/6bc240cd4ae43267.js +1 -0
- package/web/.next/static/chunks/{c055ce9033cd57d7.js → 6dc0a23d333274ae.js} +3 -3
- package/web/.next/static/chunks/8492d8e0b5f9a4e8.js +1 -0
- package/web/.next/static/chunks/84b480808bd74fa9.css +1 -0
- package/web/.next/static/chunks/98e3a7cf58fc912a.js +1 -0
- package/web/.next/static/chunks/{ee2e17b69eb559f8.js → a060ad1bb509687f.js} +1 -1
- package/web/.next/static/chunks/{2a17ddad9b7638db.js → a2a03ecb10000974.js} +1 -1
- package/web/.next/static/chunks/ad47bc6ddec7508e.js +7 -0
- package/web/.next/static/chunks/b49ab0b290e9342d.js +1 -0
- package/web/.next/static/chunks/bb479c31b5b53bac.js +8 -0
- package/web/.next/static/chunks/c10c0d6d458453bc.js +1 -0
- package/web/.next/static/chunks/{43c9aeed1753dd81.js → c164b157638b2a8b.js} +1 -1
- package/web/.next/static/chunks/{785b2df10318bd60.js → d9c9db1823b4dffd.js} +1 -1
- package/web/.next/static/chunks/e30aa6d2cf824fa1.js +1 -0
- package/web/.next/static/chunks/{185e4f36cb6efb24.js → eb1ca1b0a34166f5.js} +1 -1
- package/web/.next/static/chunks/{cde2742a766567a2.js → f5abbc495d496f5b.js} +1 -1
- package/web/.next/static/chunks/fc0232384ec2b48d.js +1 -0
- package/web/.next/static/chunks/{turbopack-b38a68b1b1c41a87.js → turbopack-fa58b7ea4a4b26e7.js} +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fa525872._.js +0 -3
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fa525872._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_0020fddd._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_5119a3df._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_560f2971._.js +0 -3
- package/web/.next/server/chunks/ssr/_560f2971._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_890fafe7._.js +0 -3
- package/web/.next/server/chunks/ssr/_890fafe7._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_a963dd3c._.js +0 -3
- package/web/.next/server/chunks/ssr/_a963dd3c._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_df737cce._.js +0 -3
- package/web/.next/server/chunks/ssr/_df737cce._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_e3f14907._.js +0 -9
- package/web/.next/server/chunks/ssr/_e3f14907._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_f535d854._.js +0 -3
- package/web/.next/server/chunks/ssr/_f535d854._.js.map +0 -1
- package/web/.next/server/chunks/ssr/node_modules__pnpm_1300ae39._.js +0 -3
- package/web/.next/server/chunks/ssr/node_modules__pnpm_1300ae39._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_common_page-header_index_ts_bdf4db0b._.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web_components_common_page-header_index_ts_bdf4db0b._.js.map +0 -1
- package/web/.next/static/chunks/3506d521396f9164.js +0 -1
- package/web/.next/static/chunks/3e393d6a78b2ade4.js +0 -1
- package/web/.next/static/chunks/4559a403ee40dd19.js +0 -7
- package/web/.next/static/chunks/4cb1df635716be2b.js +0 -1
- package/web/.next/static/chunks/5ed47e998707b519.js +0 -8
- package/web/.next/static/chunks/712d96368dc18c1e.js +0 -3
- package/web/.next/static/chunks/a6bc96e6d5328505.js +0 -1
- package/web/.next/static/chunks/b14085e99b88e7f7.css +0 -1
- package/web/.next/static/chunks/c139f0b8a7f76f12.js +0 -1
- package/web/.next/static/chunks/cc5fe8d1d9e1e519.js +0 -5
- package/web/.next/static/chunks/d5366257d6b9f855.js +0 -1
- package/web/.next/static/chunks/f998b42b5cddafd6.js +0 -1
- /package/web/.next/static/{zsdHmcbky5vsmzUX-NSIh → nLYXtW5TIrvQiMl1yyD4O}/_buildManifest.js +0 -0
- /package/web/.next/static/{zsdHmcbky5vsmzUX-NSIh → nLYXtW5TIrvQiMl1yyD4O}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{zsdHmcbky5vsmzUX-NSIh → nLYXtW5TIrvQiMl1yyD4O}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import type { FeatureTreeRow, InventoryRepo } from '../../components/features/feature-tree-table/index.js';
|
|
1
|
+
import type { FeatureTreeRow, InventoryRepo, GroupByField } from '../../components/features/feature-tree-table/index.js';
|
|
2
2
|
export interface FeatureTreePageClientProps {
|
|
3
3
|
features: FeatureTreeRow[];
|
|
4
4
|
repos: InventoryRepo[];
|
|
5
5
|
}
|
|
6
|
+
/** Item sort fields change based on groupBy — exclude the grouped field. */
|
|
7
|
+
export declare function getItemSortOptions(groupBy: GroupByField | null): {
|
|
8
|
+
value: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}[];
|
|
11
|
+
export declare function isArchived(feature: FeatureTreeRow): boolean;
|
|
6
12
|
export declare function FeatureTreePageClient({ features, repos }: FeatureTreePageClientProps): import("react/jsx-runtime").JSX.Element;
|
|
7
13
|
//# sourceMappingURL=feature-tree-page-client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-tree-page-client.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/features/feature-tree-page-client.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-tree-page-client.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/features/feature-tree-page-client.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,YAAY,EAEb,MAAM,0CAA0C,CAAC;AAelD,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAmCD,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;;;IAU9D;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAE3D;AAED,wBAAgB,qBAAqB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,0BAA0B,2CAuWpF"}
|
|
@@ -1,13 +1,152 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
|
-
import { useCallback } from 'react';
|
|
4
|
+
import { useCallback, useState, useMemo } from 'react';
|
|
5
|
+
import { Search, SlidersHorizontal, Archive, Inbox, X, ArrowDownAZ, ArrowUpAZ } from 'lucide-react';
|
|
5
6
|
import { FeatureTreeTable } from '../../components/features/feature-tree-table/index.js';
|
|
6
7
|
import { PageHeader } from '../../components/common/page-header/index.js';
|
|
8
|
+
import { EmptyState } from '../../components/common/empty-state/index.js';
|
|
9
|
+
import { Input } from '../../components/ui/input.js';
|
|
10
|
+
import { Button } from '../../components/ui/button.js';
|
|
11
|
+
import { Badge } from '../../components/ui/badge.js';
|
|
12
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../components/ui/select.js';
|
|
13
|
+
const STATUS_LABELS = {
|
|
14
|
+
'action-needed': 'Action Needed',
|
|
15
|
+
'in-progress': 'In Progress',
|
|
16
|
+
pending: 'Pending',
|
|
17
|
+
blocked: 'Blocked',
|
|
18
|
+
error: 'Error',
|
|
19
|
+
done: 'Done',
|
|
20
|
+
};
|
|
21
|
+
const STATUS_COLORS = {
|
|
22
|
+
'action-needed': 'bg-amber-500/15 text-amber-700 dark:text-amber-400 border-amber-500/20',
|
|
23
|
+
'in-progress': 'bg-blue-500/15 text-blue-700 dark:text-blue-400 border-blue-500/20',
|
|
24
|
+
pending: 'bg-slate-500/15 text-slate-700 dark:text-slate-400 border-slate-500/20',
|
|
25
|
+
blocked: 'bg-gray-500/15 text-gray-700 dark:text-gray-400 border-gray-500/20',
|
|
26
|
+
error: 'bg-red-500/15 text-red-700 dark:text-red-400 border-red-500/20',
|
|
27
|
+
done: 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-400 border-emerald-500/20',
|
|
28
|
+
};
|
|
29
|
+
const GROUP_BY_LABELS = {
|
|
30
|
+
repositoryName: 'Repository',
|
|
31
|
+
status: 'Status',
|
|
32
|
+
lifecycle: 'Lifecycle',
|
|
33
|
+
};
|
|
34
|
+
const GROUP_BY_OPTIONS = [
|
|
35
|
+
{ value: '__none__', label: 'No grouping' },
|
|
36
|
+
{ value: 'repositoryName', label: 'Repository' },
|
|
37
|
+
{ value: 'status', label: 'Status' },
|
|
38
|
+
{ value: 'lifecycle', label: 'Lifecycle' },
|
|
39
|
+
];
|
|
40
|
+
/** Item sort fields change based on groupBy — exclude the grouped field. */
|
|
41
|
+
export function getItemSortOptions(groupBy) {
|
|
42
|
+
const all = [
|
|
43
|
+
{ value: 'name', label: 'Name' },
|
|
44
|
+
{ value: 'repositoryName', label: 'Repository' },
|
|
45
|
+
{ value: 'status', label: 'Status' },
|
|
46
|
+
{ value: 'lifecycle', label: 'Lifecycle' },
|
|
47
|
+
{ value: 'branch', label: 'Branch' },
|
|
48
|
+
];
|
|
49
|
+
if (!groupBy)
|
|
50
|
+
return all;
|
|
51
|
+
return all.filter((o) => o.value !== groupBy);
|
|
52
|
+
}
|
|
53
|
+
export function isArchived(feature) {
|
|
54
|
+
return feature.lifecycle === 'Archived';
|
|
55
|
+
}
|
|
7
56
|
export function FeatureTreePageClient({ features, repos }) {
|
|
8
57
|
const router = useRouter();
|
|
58
|
+
// Filter state
|
|
59
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
60
|
+
const [statusFilter, setStatusFilter] = useState(null);
|
|
61
|
+
const [archiveFilter, setArchiveFilter] = useState('active');
|
|
62
|
+
const [repoFilter, setRepoFilter] = useState(null);
|
|
63
|
+
const [showAdvanced, setShowAdvanced] = useState(false);
|
|
64
|
+
// Group + sort state
|
|
65
|
+
const [groupBy, setGroupBy] = useState('repositoryName');
|
|
66
|
+
const [groupSortDir, setGroupSortDir] = useState('asc');
|
|
67
|
+
const [itemSortField, setItemSortField] = useState('name');
|
|
68
|
+
const [itemSortDir, setItemSortDir] = useState('asc');
|
|
9
69
|
const handleFeatureClick = useCallback((featureId) => {
|
|
10
70
|
router.push(`/feature/${featureId}/overview`);
|
|
11
71
|
}, [router]);
|
|
12
|
-
|
|
72
|
+
const handleGroupByChange = (value) => {
|
|
73
|
+
const next = value === '__none__' ? null : value;
|
|
74
|
+
setGroupBy(next);
|
|
75
|
+
// Reset item sort if current field matches the new groupBy
|
|
76
|
+
if (next && itemSortField === next) {
|
|
77
|
+
setItemSortField('name');
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
// Compute status counts for pills
|
|
81
|
+
const statusCounts = useMemo(() => {
|
|
82
|
+
const counts = {
|
|
83
|
+
'action-needed': 0,
|
|
84
|
+
'in-progress': 0,
|
|
85
|
+
pending: 0,
|
|
86
|
+
blocked: 0,
|
|
87
|
+
error: 0,
|
|
88
|
+
done: 0,
|
|
89
|
+
};
|
|
90
|
+
for (const f of features) {
|
|
91
|
+
counts[f.status]++;
|
|
92
|
+
}
|
|
93
|
+
return counts;
|
|
94
|
+
}, [features]);
|
|
95
|
+
// Unique repo names for advanced filter
|
|
96
|
+
const repoNames = useMemo(() => {
|
|
97
|
+
const names = new Set();
|
|
98
|
+
for (const f of features) {
|
|
99
|
+
names.add(f.repositoryName);
|
|
100
|
+
}
|
|
101
|
+
return Array.from(names).sort();
|
|
102
|
+
}, [features]);
|
|
103
|
+
// Filtered features (sorting handled by table)
|
|
104
|
+
const filteredFeatures = useMemo(() => {
|
|
105
|
+
const query = searchQuery.toLowerCase();
|
|
106
|
+
return features.filter((feature) => {
|
|
107
|
+
if (archiveFilter === 'active' && isArchived(feature))
|
|
108
|
+
return false;
|
|
109
|
+
if (archiveFilter === 'archived' && !isArchived(feature))
|
|
110
|
+
return false;
|
|
111
|
+
if (statusFilter && feature.status !== statusFilter)
|
|
112
|
+
return false;
|
|
113
|
+
if (repoFilter && feature.repositoryName !== repoFilter)
|
|
114
|
+
return false;
|
|
115
|
+
if (query) {
|
|
116
|
+
const matchesName = feature.name.toLowerCase().includes(query);
|
|
117
|
+
const matchesBranch = feature.branch.toLowerCase().includes(query);
|
|
118
|
+
const matchesRepo = feature.repositoryName.toLowerCase().includes(query);
|
|
119
|
+
if (!matchesName && !matchesBranch && !matchesRepo)
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
});
|
|
124
|
+
}, [features, searchQuery, statusFilter, archiveFilter, repoFilter]);
|
|
125
|
+
const hasActiveFilters = searchQuery !== '' ||
|
|
126
|
+
statusFilter !== null ||
|
|
127
|
+
archiveFilter !== 'active' ||
|
|
128
|
+
repoFilter !== null;
|
|
129
|
+
const clearFilters = () => {
|
|
130
|
+
setSearchQuery('');
|
|
131
|
+
setStatusFilter(null);
|
|
132
|
+
setArchiveFilter('active');
|
|
133
|
+
setRepoFilter(null);
|
|
134
|
+
};
|
|
135
|
+
const archivedCount = useMemo(() => features.filter(isArchived).length, [features]);
|
|
136
|
+
const activeCount = features.length - archivedCount;
|
|
137
|
+
const itemSortOptions = useMemo(() => getItemSortOptions(groupBy), [groupBy]);
|
|
138
|
+
return (_jsxs("div", { "data-testid": "feature-tree-page", className: "flex h-full flex-col gap-4", children: [_jsx(PageHeader, { title: "Inventory", description: "All repositories and features" }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "relative flex-1", children: [_jsx(Search, { className: "text-muted-foreground absolute top-1/2 left-3 size-4 -translate-y-1/2" }), _jsx(Input, { placeholder: "Search by name, branch, or repository...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "ps-9" }), searchQuery ? (_jsx("button", { onClick: () => setSearchQuery(''), className: "text-muted-foreground hover:text-foreground absolute top-1/2 right-3 -translate-y-1/2", children: _jsx(X, { className: "size-4" }) })) : null] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium whitespace-nowrap", children: "Group by:" }), _jsxs(Select, { value: groupBy ?? '__none__', onValueChange: handleGroupByChange, children: [_jsx(SelectTrigger, { className: "w-[150px]", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: GROUP_BY_OPTIONS.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] })] }), _jsxs(Button, { variant: showAdvanced ? 'secondary' : 'outline', size: "sm", onClick: () => setShowAdvanced((v) => !v), className: "shrink-0", children: [_jsx(SlidersHorizontal, { className: "mr-1.5 size-3.5" }), "Filters"] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "border-input flex items-center rounded-md border", children: [_jsxs("button", { onClick: () => setArchiveFilter('active'), className: `flex items-center gap-1.5 rounded-l-md px-3 py-1.5 text-xs font-medium transition-colors ${archiveFilter === 'active' ? 'bg-primary text-primary-foreground' : 'hover:bg-muted'}`, children: [_jsx(Inbox, { className: "size-3.5" }), "Active", _jsxs("span", { className: "opacity-70", children: ["(", activeCount, ")"] })] }), _jsxs("button", { onClick: () => setArchiveFilter('archived'), className: `flex items-center gap-1.5 border-x px-3 py-1.5 text-xs font-medium transition-colors ${archiveFilter === 'archived'
|
|
139
|
+
? 'bg-primary text-primary-foreground'
|
|
140
|
+
: 'hover:bg-muted'}`, children: [_jsx(Archive, { className: "size-3.5" }), "Archived", _jsxs("span", { className: "opacity-70", children: ["(", archivedCount, ")"] })] }), _jsx("button", { onClick: () => setArchiveFilter('all'), className: `rounded-r-md px-3 py-1.5 text-xs font-medium transition-colors ${archiveFilter === 'all' ? 'bg-primary text-primary-foreground' : 'hover:bg-muted'}`, children: "All" })] }), _jsx("div", { className: "bg-border h-6 w-px" }), _jsx("button", { onClick: () => setStatusFilter(null), className: `rounded-full px-2.5 py-1 text-xs font-medium transition-colors ${statusFilter === null
|
|
141
|
+
? 'bg-foreground text-background'
|
|
142
|
+
: 'bg-muted text-muted-foreground hover:bg-muted/80'}`, children: "All Status" }), Object.entries(STATUS_LABELS).map(([status, label]) => {
|
|
143
|
+
const count = statusCounts[status];
|
|
144
|
+
if (count === 0)
|
|
145
|
+
return null;
|
|
146
|
+
return (_jsxs("button", { onClick: () => setStatusFilter(statusFilter === status ? null : status), className: `rounded-full px-2.5 py-1 text-xs font-medium transition-colors ${statusFilter === status
|
|
147
|
+
? STATUS_COLORS[status]
|
|
148
|
+
: 'bg-muted text-muted-foreground hover:bg-muted/80'}`, children: [label, _jsx("span", { className: "ml-1 opacity-70", children: count })] }, status));
|
|
149
|
+
}), hasActiveFilters ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "bg-border h-6 w-px" }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: clearFilters, className: "h-7 text-xs", children: [_jsx(X, { className: "mr-1 size-3" }), "Clear"] })] })) : null] }), groupBy ? (_jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: GROUP_BY_LABELS[groupBy] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1 text-xs", onClick: () => setGroupSortDir((d) => (d === 'asc' ? 'desc' : 'asc')), children: [groupSortDir === 'asc' ? (_jsx(ArrowDownAZ, { className: "size-3.5" })) : (_jsx(ArrowUpAZ, { className: "size-3.5" })), groupSortDir === 'asc' ? 'A-Z' : 'Z-A'] })] }), _jsx("div", { className: "bg-border h-5 w-px" }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Sort by" }), _jsxs(Select, { value: itemSortField, onValueChange: setItemSortField, children: [_jsx(SelectTrigger, { className: "h-7 w-[120px] text-xs", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itemSortOptions.map((opt) => (_jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))) })] }), _jsxs(Button, { variant: "outline", size: "sm", className: "h-7 gap-1 text-xs", onClick: () => setItemSortDir((d) => (d === 'asc' ? 'desc' : 'asc')), children: [itemSortDir === 'asc' ? (_jsx(ArrowDownAZ, { className: "size-3.5" })) : (_jsx(ArrowUpAZ, { className: "size-3.5" })), itemSortDir === 'asc' ? 'A-Z' : 'Z-A'] })] })] })) : null, showAdvanced ? (_jsx("div", { className: "bg-muted/50 flex flex-wrap items-center gap-3 rounded-lg border p-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Repository" }), _jsxs(Select, { value: repoFilter ?? '__all__', onValueChange: (v) => setRepoFilter(v === '__all__' ? null : v), children: [_jsx(SelectTrigger, { className: "h-8 w-[200px] text-xs", children: _jsx(SelectValue, { placeholder: "All repositories" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__all__", children: "All repositories" }), repoNames.map((name) => (_jsx(SelectItem, { value: name, children: name }, name)))] })] })] }) })) : null] }), _jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-xs", children: [_jsxs("span", { children: [filteredFeatures.length, " feature", filteredFeatures.length !== 1 ? 's' : ''] }), hasActiveFilters ? (_jsx(Badge, { variant: "secondary", className: "text-xs", children: "filtered" })) : null] }), _jsx("div", { className: "min-h-0 flex-1", children: filteredFeatures.length > 0 ? (_jsx(FeatureTreeTable, { data: filteredFeatures, repos: repos, onFeatureClick: handleFeatureClick, groupBy: groupBy, groupSortDir: groupSortDir, itemSortField: itemSortField, itemSortDir: itemSortDir })) : (_jsx(EmptyState, { icon: _jsx(Search, { className: "size-10" }), title: "No matching features", description: hasActiveFilters
|
|
150
|
+
? 'No features match your current filters. Try adjusting your search or filters.'
|
|
151
|
+
: 'No features found in any repository.', action: hasActiveFilters ? (_jsx(Button, { variant: "outline", onClick: clearFilters, children: "Clear all filters" })) : undefined })) })] }));
|
|
13
152
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-feature-tree-data.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/features/get-feature-tree-data.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAI/E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsBD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC;IAClD,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB,CAAC,
|
|
1
|
+
{"version":3,"file":"get-feature-tree-data.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/app/features/get-feature-tree-data.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAI/E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsBD,wBAAsB,kBAAkB,IAAI,OAAO,CAAC;IAClD,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB,CAAC,CAmCD"}
|
|
@@ -21,7 +21,10 @@ function lifecycleToStatus(lifecycle) {
|
|
|
21
21
|
export async function getFeatureTreeData() {
|
|
22
22
|
const listFeatures = resolve('ListFeaturesUseCase');
|
|
23
23
|
const listRepos = resolve('ListRepositoriesUseCase');
|
|
24
|
-
const [features, repositories] = await Promise.all([
|
|
24
|
+
const [features, repositories] = await Promise.all([
|
|
25
|
+
listFeatures.execute({ includeArchived: true }),
|
|
26
|
+
listRepos.execute(),
|
|
27
|
+
]);
|
|
25
28
|
const repoByPath = new Map();
|
|
26
29
|
for (const repo of repositories) {
|
|
27
30
|
repoByPath.set(repo.path, { name: repo.name, remoteUrl: repo.remoteUrl });
|
|
@@ -8,5 +8,5 @@ export function RepoGroup({ repoName, featureCount, children, defaultOpen = true
|
|
|
8
8
|
return (_jsxs("div", { "data-testid": "repo-group", className: "group/repo mb-1", children: [_jsxs("div", { className: "flex items-center", children: [_jsxs("button", { type: "button", onClick: () => setOpen(!open), className: cn('text-sidebar-foreground hover:bg-sidebar-accent flex min-w-0 flex-1 items-center gap-1.5 rounded-md px-2 py-1.5 text-left text-xs font-semibold', 'transition-colors duration-100'), "aria-expanded": open, children: [_jsx(ChevronDown, { className: cn('text-muted-foreground h-3.5 w-3.5 shrink-0 transition-transform duration-200', !open && '-rotate-90') }), _jsx(GitFork, { className: "text-muted-foreground h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: repoName }), _jsx("span", { "aria-label": `${featureCount} features`, className: "bg-sidebar-accent text-sidebar-accent-foreground ml-auto inline-flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full px-1 text-[0.6rem] font-medium tabular-nums", role: "img", children: featureCount })] }), onAddFeature ? (_jsx("button", { type: "button", "data-testid": "repo-add-feature", onClick: (e) => {
|
|
9
9
|
e.stopPropagation();
|
|
10
10
|
onAddFeature();
|
|
11
|
-
}, className: cn('text-muted-foreground hover:text-sidebar-foreground hover:bg-sidebar-accent shrink-0 rounded-md p-1 opacity-0 transition-all duration-100 group-hover/repo:opacity-100', 'focus-visible:ring-ring focus-visible:opacity-100 focus-visible:ring-1 focus-visible:outline-none'), "aria-label": `Add feature to ${repoName}`, children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })) : null] }), open ? _jsx("div", { className: "pl-2", children: children }) : null] }));
|
|
11
|
+
}, className: cn('text-muted-foreground hover:text-sidebar-foreground hover:bg-sidebar-accent mr-2 shrink-0 rounded-md p-1 opacity-0 transition-all duration-100 group-hover/repo:opacity-100', 'focus-visible:ring-ring focus-visible:opacity-100 focus-visible:ring-1 focus-visible:outline-none'), "aria-label": `Add feature to ${repoName}`, children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })) : null] }), open ? _jsx("div", { className: "pl-2", children: children }) : null] }));
|
|
12
12
|
}
|
package/dist/src/presentation/web/components/features/feature-tree-table/feature-tree-table.d.ts
CHANGED
|
@@ -9,28 +9,44 @@ export interface FeatureTreeRow {
|
|
|
9
9
|
repositoryName: string;
|
|
10
10
|
remoteUrl?: string;
|
|
11
11
|
parentId?: string;
|
|
12
|
-
/**
|
|
12
|
+
/** Internal: child rows for tree hierarchy */
|
|
13
13
|
_children?: FeatureTreeRow[];
|
|
14
|
-
/**
|
|
14
|
+
/** Internal: whether this row is a group header */
|
|
15
|
+
_isGroupHeader?: boolean;
|
|
16
|
+
/** Internal: number of features in this group */
|
|
17
|
+
_groupCount?: number;
|
|
18
|
+
/** Internal: whether this row is a repository group header (legacy tree) */
|
|
15
19
|
_isRepoGroup?: boolean;
|
|
16
|
-
/**
|
|
20
|
+
/** Internal: number of features in this repo group (legacy tree) */
|
|
17
21
|
_featureCount?: number;
|
|
18
22
|
}
|
|
19
23
|
export interface InventoryRepo {
|
|
20
24
|
name: string;
|
|
21
25
|
remoteUrl?: string;
|
|
22
26
|
}
|
|
27
|
+
export type GroupByField = 'repositoryName' | 'status' | 'lifecycle';
|
|
28
|
+
export type SortDir = 'asc' | 'desc';
|
|
23
29
|
export interface FeatureTreeTableProps {
|
|
24
30
|
data: FeatureTreeRow[];
|
|
25
31
|
repos?: InventoryRepo[];
|
|
26
32
|
className?: string;
|
|
27
33
|
onFeatureClick?: (featureId: string) => void;
|
|
34
|
+
/** When set, features are grouped into a tree by this field. */
|
|
35
|
+
groupBy?: GroupByField | null;
|
|
36
|
+
/** Sort direction for group headers. */
|
|
37
|
+
groupSortDir?: SortDir;
|
|
38
|
+
/** Field to sort items within each group (or globally in flat mode). */
|
|
39
|
+
itemSortField?: string;
|
|
40
|
+
/** Sort direction for items. */
|
|
41
|
+
itemSortDir?: SortDir;
|
|
28
42
|
}
|
|
43
|
+
export declare function displayLabel(groupBy: GroupByField, value: string): string;
|
|
29
44
|
/**
|
|
30
|
-
* Build tree-structured data grouped by repository.
|
|
45
|
+
* Build tree-structured data grouped by repository (legacy format).
|
|
31
46
|
* Each repository becomes a parent node with its features as children.
|
|
32
47
|
* Repos without features are included as empty groups.
|
|
33
48
|
*/
|
|
34
49
|
export declare function buildTreeData(flatData: FeatureTreeRow[], repos?: InventoryRepo[]): FeatureTreeRow[];
|
|
35
|
-
export declare function
|
|
50
|
+
export declare function buildGroupedTree(flatData: FeatureTreeRow[], groupBy: GroupByField, groupSortDir: SortDir, itemSortField: string, itemSortDir: SortDir): FeatureTreeRow[];
|
|
51
|
+
export declare function FeatureTreeTable({ data, className, onFeatureClick, groupBy, groupSortDir, itemSortField, itemSortDir, }: FeatureTreeTableProps): import("react/jsx-runtime").JSX.Element;
|
|
36
52
|
//# sourceMappingURL=feature-tree-table.d.ts.map
|
package/dist/src/presentation/web/components/features/feature-tree-table/feature-tree-table.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-tree-table.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/feature-tree-table/feature-tree-table.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAC/E,OAAO,0BAA0B,CAAC;AAElC,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,
|
|
1
|
+
{"version":3,"file":"feature-tree-table.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/feature-tree-table/feature-tree-table.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAC/E,OAAO,0BAA0B,CAAC;AAElC,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,mDAAmD;IACnD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,YAAY,GAAG,gBAAgB,GAAG,QAAQ,GAAG,WAAW,CAAC;AACrE,MAAM,MAAM,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAErC,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,gEAAgE;IAChE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,wCAAwC;IACxC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gCAAgC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAiJD,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGzE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,cAAc,EAAE,EAC1B,KAAK,CAAC,EAAE,aAAa,EAAE,GACtB,cAAc,EAAE,CAkDlB;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,cAAc,EAAE,EAC1B,OAAO,EAAE,YAAY,EACrB,YAAY,EAAE,OAAO,EACrB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,OAAO,GACnB,cAAc,EAAE,CA8ClB;AAID,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,SAAS,EACT,cAAc,EACd,OAAc,EACd,YAAoB,EACpB,aAAsB,EACtB,WAAmB,GACpB,EAAE,qBAAqB,2CAyDvB"}
|
package/dist/src/presentation/web/components/features/feature-tree-table/feature-tree-table.js
CHANGED
|
@@ -4,6 +4,7 @@ import { useEffect, useRef, useCallback } from 'react';
|
|
|
4
4
|
import { TabulatorFull as Tabulator } from 'tabulator-tables';
|
|
5
5
|
import { cn } from '../../../lib/utils.js';
|
|
6
6
|
import './feature-tree-table.css';
|
|
7
|
+
// ── Constants ────────────────────────────────────────────────
|
|
7
8
|
const STATUS_LABELS = {
|
|
8
9
|
'action-needed': 'Action Needed',
|
|
9
10
|
'in-progress': 'In Progress',
|
|
@@ -12,119 +13,155 @@ const STATUS_LABELS = {
|
|
|
12
13
|
error: 'Error',
|
|
13
14
|
done: 'Done',
|
|
14
15
|
};
|
|
15
|
-
/** SVG repo icon — lucide FolderGit2 */
|
|
16
|
-
const REPO_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="
|
|
16
|
+
/** SVG repo icon — lucide FolderGit2 (16px) */
|
|
17
|
+
const REPO_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"/><circle cx="12" cy="13" r="2"/><path d="M14 13h3"/><path d="M7 13h3"/></svg>`;
|
|
18
|
+
/** SVG group icon — lucide Layers (16px) */
|
|
19
|
+
const GROUP_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/><path d="m22.54 12.43-1.42-.65-8.28 3.78a2 2 0 0 1-1.66 0l-8.28-3.78-1.42.65a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/></svg>`;
|
|
20
|
+
// ── Formatters ───────────────────────────────────────────────
|
|
21
|
+
function escapeHtml(text) {
|
|
22
|
+
const div = typeof document !== 'undefined' ? document.createElement('div') : null;
|
|
23
|
+
if (div) {
|
|
24
|
+
div.textContent = text;
|
|
25
|
+
return div.innerHTML;
|
|
26
|
+
}
|
|
27
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
28
|
+
}
|
|
17
29
|
function statusFormatter(cell) {
|
|
18
30
|
const row = cell.getRow().getData();
|
|
19
|
-
if (row.
|
|
31
|
+
if (row._isGroupHeader)
|
|
20
32
|
return '';
|
|
21
33
|
const value = cell.getValue();
|
|
22
34
|
const label = STATUS_LABELS[value] ?? value;
|
|
23
35
|
return `<span class="status-pill status-pill--${value}"><span class="status-dot"></span>${label}</span>`;
|
|
24
36
|
}
|
|
25
|
-
function
|
|
37
|
+
function branchFormatter(cell) {
|
|
26
38
|
const row = cell.getRow().getData();
|
|
27
|
-
if (row.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
if (row._isGroupHeader)
|
|
40
|
+
return '';
|
|
41
|
+
const val = cell.getValue();
|
|
42
|
+
if (!val)
|
|
43
|
+
return '';
|
|
44
|
+
return `<code style="font-size:12px;color:var(--color-muted-foreground,#64748b);font-family:var(--font-mono)">${escapeHtml(val)}</code>`;
|
|
45
|
+
}
|
|
46
|
+
function lifecycleFormatter(cell) {
|
|
47
|
+
const row = cell.getRow().getData();
|
|
48
|
+
if (row._isGroupHeader)
|
|
49
|
+
return '';
|
|
35
50
|
return escapeHtml(cell.getValue());
|
|
36
51
|
}
|
|
37
|
-
function
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
52
|
+
function repoFormatter(cell) {
|
|
53
|
+
const row = cell.getRow().getData();
|
|
54
|
+
if (row._isGroupHeader)
|
|
55
|
+
return '';
|
|
56
|
+
const val = cell.getValue();
|
|
57
|
+
return `<span style="display:inline-flex;align-items:center;gap:6px">${REPO_ICON_SVG}<span>${escapeHtml(val)}</span></span>`;
|
|
58
|
+
}
|
|
59
|
+
function groupHeaderNameFormatter(groupBy) {
|
|
60
|
+
return (cell) => {
|
|
61
|
+
const row = cell.getRow().getData();
|
|
62
|
+
if (!row._isGroupHeader)
|
|
63
|
+
return escapeHtml(cell.getValue());
|
|
64
|
+
const icon = groupBy === 'repositoryName' ? REPO_ICON_SVG : GROUP_ICON_SVG;
|
|
65
|
+
const count = row._groupCount ?? 0;
|
|
66
|
+
const countLabel = count === 1 ? '1 feature' : `${count} features`;
|
|
67
|
+
return `<span style="display:inline-flex;align-items:center;gap:8px;font-weight:600">${icon}<span>${escapeHtml(row.name)}</span><span style="font-weight:400;color:var(--color-muted-foreground,#64748b);font-size:12px">${countLabel}</span></span>`;
|
|
68
|
+
};
|
|
44
69
|
}
|
|
45
|
-
|
|
46
|
-
|
|
70
|
+
/** All possible columns. We'll filter out the grouped-by column in tree mode. */
|
|
71
|
+
function buildColumns({ onFeatureClick, groupBy }) {
|
|
72
|
+
const clickProps = onFeatureClick
|
|
73
|
+
? {
|
|
74
|
+
cellClick: (_e, cell) => {
|
|
75
|
+
const data = cell.getRow().getData();
|
|
76
|
+
if (data._isGroupHeader)
|
|
77
|
+
return;
|
|
78
|
+
onFeatureClick(data.id);
|
|
79
|
+
},
|
|
80
|
+
cssClass: 'cursor-pointer',
|
|
81
|
+
}
|
|
82
|
+
: {};
|
|
83
|
+
const isGrouped = !!groupBy;
|
|
84
|
+
const cols = [
|
|
47
85
|
{
|
|
48
86
|
title: 'Name',
|
|
49
87
|
field: 'name',
|
|
50
88
|
widthGrow: 3,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return;
|
|
57
|
-
onFeatureClick(data.id);
|
|
58
|
-
},
|
|
59
|
-
cssClass: 'cursor-pointer',
|
|
60
|
-
}),
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
title: 'Status',
|
|
64
|
-
field: 'status',
|
|
65
|
-
widthGrow: 1.5,
|
|
66
|
-
formatter: statusFormatter,
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
title: 'Lifecycle',
|
|
70
|
-
field: 'lifecycle',
|
|
71
|
-
widthGrow: 1.5,
|
|
72
|
-
formatter: (cell) => {
|
|
73
|
-
const row = cell.getRow().getData();
|
|
74
|
-
if (row._isRepoGroup)
|
|
75
|
-
return '';
|
|
76
|
-
return cell.getValue();
|
|
77
|
-
},
|
|
89
|
+
headerSort: !isGrouped,
|
|
90
|
+
formatter: isGrouped
|
|
91
|
+
? groupHeaderNameFormatter(groupBy)
|
|
92
|
+
: (cell) => escapeHtml(cell.getValue()),
|
|
93
|
+
...clickProps,
|
|
78
94
|
},
|
|
95
|
+
groupBy !== 'repositoryName'
|
|
96
|
+
? {
|
|
97
|
+
title: 'Repository',
|
|
98
|
+
field: 'repositoryName',
|
|
99
|
+
widthGrow: 2,
|
|
100
|
+
headerSort: !isGrouped,
|
|
101
|
+
formatter: repoFormatter,
|
|
102
|
+
}
|
|
103
|
+
: null,
|
|
104
|
+
groupBy !== 'status'
|
|
105
|
+
? {
|
|
106
|
+
title: 'Status',
|
|
107
|
+
field: 'status',
|
|
108
|
+
widthGrow: 1.5,
|
|
109
|
+
headerSort: !isGrouped,
|
|
110
|
+
formatter: statusFormatter,
|
|
111
|
+
}
|
|
112
|
+
: null,
|
|
113
|
+
groupBy !== 'lifecycle'
|
|
114
|
+
? {
|
|
115
|
+
title: 'Lifecycle',
|
|
116
|
+
field: 'lifecycle',
|
|
117
|
+
widthGrow: 1.5,
|
|
118
|
+
headerSort: !isGrouped,
|
|
119
|
+
formatter: lifecycleFormatter,
|
|
120
|
+
}
|
|
121
|
+
: null,
|
|
79
122
|
{
|
|
80
123
|
title: 'Branch',
|
|
81
124
|
field: 'branch',
|
|
82
125
|
widthGrow: 2,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (row._isRepoGroup)
|
|
86
|
-
return '';
|
|
87
|
-
const val = cell.getValue();
|
|
88
|
-
if (!val)
|
|
89
|
-
return '';
|
|
90
|
-
return `<code style="font-size:12px;color:var(--color-muted-foreground,#64748b);font-family:var(--font-mono)">${escapeHtml(val)}</code>`;
|
|
91
|
-
},
|
|
126
|
+
headerSort: !isGrouped,
|
|
127
|
+
formatter: branchFormatter,
|
|
92
128
|
},
|
|
93
129
|
];
|
|
130
|
+
return cols.filter(Boolean);
|
|
131
|
+
}
|
|
132
|
+
// ── Tree builder ─────────────────────────────────────────────
|
|
133
|
+
export function displayLabel(groupBy, value) {
|
|
134
|
+
if (groupBy === 'status')
|
|
135
|
+
return STATUS_LABELS[value] ?? value;
|
|
136
|
+
return value;
|
|
94
137
|
}
|
|
95
138
|
/**
|
|
96
|
-
* Build tree-structured data grouped by repository.
|
|
139
|
+
* Build tree-structured data grouped by repository (legacy format).
|
|
97
140
|
* Each repository becomes a parent node with its features as children.
|
|
98
141
|
* Repos without features are included as empty groups.
|
|
99
142
|
*/
|
|
100
143
|
export function buildTreeData(flatData, repos) {
|
|
101
|
-
// Group features by repository
|
|
102
144
|
const byRepo = new Map();
|
|
103
145
|
for (const item of flatData) {
|
|
104
146
|
const repoName = item.repositoryName || 'Unknown';
|
|
105
|
-
if (!byRepo.has(repoName))
|
|
147
|
+
if (!byRepo.has(repoName))
|
|
106
148
|
byRepo.set(repoName, []);
|
|
107
|
-
}
|
|
108
149
|
byRepo.get(repoName).push(item);
|
|
109
150
|
}
|
|
110
|
-
// Ensure all known repos are represented (even without features)
|
|
111
151
|
const repoMeta = new Map();
|
|
112
152
|
if (repos) {
|
|
113
153
|
for (const repo of repos) {
|
|
114
154
|
repoMeta.set(repo.name, { remoteUrl: repo.remoteUrl });
|
|
115
|
-
if (!byRepo.has(repo.name))
|
|
155
|
+
if (!byRepo.has(repo.name))
|
|
116
156
|
byRepo.set(repo.name, []);
|
|
117
|
-
}
|
|
118
157
|
}
|
|
119
158
|
}
|
|
120
159
|
const roots = [];
|
|
121
160
|
for (const [repoName, features] of byRepo) {
|
|
122
|
-
// Build parent-child relationships within this repo group
|
|
123
161
|
const lookup = new Map();
|
|
124
162
|
const repoChildren = [];
|
|
125
|
-
for (const item of features)
|
|
163
|
+
for (const item of features)
|
|
126
164
|
lookup.set(item.id, { ...item, _children: [] });
|
|
127
|
-
}
|
|
128
165
|
for (const item of features) {
|
|
129
166
|
const node = lookup.get(item.id);
|
|
130
167
|
if (item.parentId && lookup.has(item.parentId)) {
|
|
@@ -134,14 +171,12 @@ export function buildTreeData(flatData, repos) {
|
|
|
134
171
|
repoChildren.push(node);
|
|
135
172
|
}
|
|
136
173
|
}
|
|
137
|
-
// Clean up empty _children arrays
|
|
138
174
|
for (const node of lookup.values()) {
|
|
139
|
-
if (node._children?.length === 0)
|
|
175
|
+
if (node._children?.length === 0)
|
|
140
176
|
delete node._children;
|
|
141
|
-
}
|
|
142
177
|
}
|
|
143
178
|
const remoteUrl = repoMeta.get(repoName)?.remoteUrl ?? features[0]?.remoteUrl;
|
|
144
|
-
|
|
179
|
+
roots.push({
|
|
145
180
|
id: `repo-${repoName}`,
|
|
146
181
|
name: repoName,
|
|
147
182
|
status: 'pending',
|
|
@@ -152,12 +187,51 @@ export function buildTreeData(flatData, repos) {
|
|
|
152
187
|
_isRepoGroup: true,
|
|
153
188
|
_featureCount: features.length,
|
|
154
189
|
...(repoChildren.length > 0 ? { _children: repoChildren } : {}),
|
|
155
|
-
};
|
|
156
|
-
roots.push(repoGroup);
|
|
190
|
+
});
|
|
157
191
|
}
|
|
158
192
|
return roots;
|
|
159
193
|
}
|
|
160
|
-
export function
|
|
194
|
+
export function buildGroupedTree(flatData, groupBy, groupSortDir, itemSortField, itemSortDir) {
|
|
195
|
+
// Group features by field value
|
|
196
|
+
const groups = new Map();
|
|
197
|
+
for (const item of flatData) {
|
|
198
|
+
const key = item[groupBy] || 'Unknown';
|
|
199
|
+
if (!groups.has(key))
|
|
200
|
+
groups.set(key, []);
|
|
201
|
+
groups.get(key).push(item);
|
|
202
|
+
}
|
|
203
|
+
// Sort items within each group
|
|
204
|
+
const sortItems = (items) => [...items].sort((a, b) => {
|
|
205
|
+
const aVal = String(a[itemSortField] ?? '').toLowerCase();
|
|
206
|
+
const bVal = String(b[itemSortField] ?? '').toLowerCase();
|
|
207
|
+
const cmp = aVal.localeCompare(bVal);
|
|
208
|
+
return itemSortDir === 'asc' ? cmp : -cmp;
|
|
209
|
+
});
|
|
210
|
+
// Build group header rows
|
|
211
|
+
const roots = [];
|
|
212
|
+
for (const [key, features] of groups) {
|
|
213
|
+
const sortedChildren = sortItems(features);
|
|
214
|
+
roots.push({
|
|
215
|
+
id: `group-${groupBy}-${key}`,
|
|
216
|
+
name: displayLabel(groupBy, key),
|
|
217
|
+
status: 'pending',
|
|
218
|
+
lifecycle: '',
|
|
219
|
+
branch: '',
|
|
220
|
+
repositoryName: '',
|
|
221
|
+
_isGroupHeader: true,
|
|
222
|
+
_groupCount: features.length,
|
|
223
|
+
_children: sortedChildren,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
// Sort groups
|
|
227
|
+
roots.sort((a, b) => {
|
|
228
|
+
const cmp = a.name.localeCompare(b.name);
|
|
229
|
+
return groupSortDir === 'asc' ? cmp : -cmp;
|
|
230
|
+
});
|
|
231
|
+
return roots;
|
|
232
|
+
}
|
|
233
|
+
// ── Component ────────────────────────────────────────────────
|
|
234
|
+
export function FeatureTreeTable({ data, className, onFeatureClick, groupBy = null, groupSortDir = 'asc', itemSortField = 'name', itemSortDir = 'asc', }) {
|
|
161
235
|
const containerRef = useRef(null);
|
|
162
236
|
const tabulatorRef = useRef(null);
|
|
163
237
|
const onFeatureClickRef = useRef(onFeatureClick);
|
|
@@ -168,29 +242,37 @@ export function FeatureTreeTable({ data, repos, className, onFeatureClick, }) {
|
|
|
168
242
|
useEffect(() => {
|
|
169
243
|
if (!containerRef.current)
|
|
170
244
|
return;
|
|
171
|
-
const
|
|
172
|
-
const columns = buildColumns(stableOnFeatureClick);
|
|
245
|
+
const isGrouped = !!groupBy;
|
|
246
|
+
const columns = buildColumns({ onFeatureClick: stableOnFeatureClick, groupBy });
|
|
247
|
+
const tableData = isGrouped
|
|
248
|
+
? buildGroupedTree(data, groupBy, groupSortDir, itemSortField, itemSortDir)
|
|
249
|
+
: data;
|
|
173
250
|
const table = new Tabulator(containerRef.current, {
|
|
174
|
-
data:
|
|
251
|
+
data: tableData,
|
|
175
252
|
columns,
|
|
176
|
-
dataTree: true,
|
|
177
|
-
dataTreeStartExpanded: true,
|
|
178
253
|
layout: 'fitColumns',
|
|
179
254
|
height: '100%',
|
|
180
|
-
placeholder: 'No
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
255
|
+
placeholder: 'No features found',
|
|
256
|
+
...(isGrouped
|
|
257
|
+
? {
|
|
258
|
+
dataTree: true,
|
|
259
|
+
dataTreeStartExpanded: true,
|
|
260
|
+
rowFormatter: (row) => {
|
|
261
|
+
const rowData = row.getData();
|
|
262
|
+
if (rowData._isGroupHeader) {
|
|
263
|
+
row.getElement().classList.add('tabulator-row-repo-group');
|
|
264
|
+
}
|
|
265
|
+
},
|
|
186
266
|
}
|
|
187
|
-
|
|
267
|
+
: {
|
|
268
|
+
initialSort: [{ column: 'repositoryName', dir: 'asc' }],
|
|
269
|
+
}),
|
|
188
270
|
});
|
|
189
271
|
tabulatorRef.current = table;
|
|
190
272
|
return () => {
|
|
191
273
|
table.destroy();
|
|
192
274
|
tabulatorRef.current = null;
|
|
193
275
|
};
|
|
194
|
-
}, [data,
|
|
276
|
+
}, [data, stableOnFeatureClick, groupBy, groupSortDir, itemSortField, itemSortDir]);
|
|
195
277
|
return (_jsx("div", { "data-testid": "feature-tree-table", className: cn('h-full w-full', className), ref: containerRef }));
|
|
196
278
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { FeatureTreeTable, buildTreeData } from './feature-tree-table.js';
|
|
2
|
-
export type { FeatureTreeTableProps, FeatureTreeRow, InventoryRepo } from './feature-tree-table.js';
|
|
1
|
+
export { FeatureTreeTable, buildTreeData, buildGroupedTree, displayLabel, } from './feature-tree-table.js';
|
|
2
|
+
export type { FeatureTreeTableProps, FeatureTreeRow, InventoryRepo, GroupByField, SortDir, } from './feature-tree-table.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|