@shepai/cli 1.78.1-pr241.ee88d2d → 1.78.1-pr246.ca9db57
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/apis/json-schema/Attachment.yaml +31 -0
- package/apis/json-schema/Feature.yaml +5 -0
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts +3 -1
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/create/create-feature.use-case.js +35 -3
- package/dist/packages/core/src/application/use-cases/features/create/types.d.ts +7 -1
- package/dist/packages/core/src/application/use-cases/features/create/types.d.ts.map +1 -1
- package/dist/packages/core/src/domain/generated/output.d.ts +33 -0
- package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +3 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/feature.mapper.d.ts +1 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/feature.mapper.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/feature.mapper.js +4 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.js +11 -0
- package/dist/packages/core/src/infrastructure/services/attachment-storage.service.d.ts +28 -0
- package/dist/packages/core/src/infrastructure/services/attachment-storage.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/attachment-storage.service.js +125 -0
- package/dist/src/presentation/cli/commands/feat/new.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/feat/new.command.js +21 -1
- package/dist/src/presentation/web/app/(dashboard)/@drawer/feature/[featureId]/page.d.ts.map +1 -1
- package/dist/src/presentation/web/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -14
- package/dist/src/presentation/web/app/actions/compose-user-input.d.ts +7 -0
- package/dist/src/presentation/web/app/actions/compose-user-input.d.ts.map +1 -0
- package/dist/src/presentation/web/app/actions/compose-user-input.js +7 -0
- package/dist/src/presentation/web/app/actions/create-feature.d.ts +1 -0
- package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
- package/dist/src/presentation/web/app/actions/create-feature.js +3 -9
- package/dist/src/presentation/web/app/api/attachments/upload/route.d.ts +3 -0
- package/dist/src/presentation/web/app/api/attachments/upload/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/attachments/upload/route.js +101 -0
- package/dist/src/presentation/web/app/build-feature-node-data.d.ts +1 -7
- package/dist/src/presentation/web/app/build-feature-node-data.d.ts.map +1 -1
- package/dist/src/presentation/web/app/build-feature-node-data.js +1 -7
- package/dist/src/presentation/web/app/build-graph-nodes.js +3 -7
- package/dist/src/presentation/web/components/common/attachment-card/attachment-card.d.ts +16 -0
- package/dist/src/presentation/web/components/common/attachment-card/attachment-card.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/attachment-card/attachment-card.js +53 -0
- package/dist/src/presentation/web/components/common/attachment-card/attachment-card.stories.d.ts +12 -0
- package/dist/src/presentation/web/components/common/attachment-card/attachment-card.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/attachment-card/attachment-card.stories.js +55 -0
- package/dist/src/presentation/web/components/common/attachment-card/index.d.ts +3 -0
- package/dist/src/presentation/web/components/common/attachment-card/index.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/attachment-card/index.js +1 -0
- package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.d.ts +19 -7
- package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/drawer-view.js +6 -6
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +85 -48
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +11 -2
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +194 -59
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +6 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +18 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.d.ts +9 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.js +60 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts +47 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js +307 -0
- package/dist/src/presentation/web/components/common/feature-drawer/index.d.ts +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer/index.js +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts +0 -12
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/index.d.ts +4 -3
- package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/index.js +4 -3
- package/dist/src/presentation/web/components/common/merge-review/index.d.ts +2 -1
- package/dist/src/presentation/web/components/common/merge-review/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/merge-review/index.js +1 -0
- package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.d.ts +3 -0
- package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/merge-review/merge-review-drawer.js +8 -0
- package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.d.ts +8 -0
- package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/merge-review/merge-review.stories.js +33 -1
- package/dist/src/presentation/web/components/common/prd-questionnaire/index.d.ts +2 -1
- package/dist/src/presentation/web/components/common/prd-questionnaire/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/prd-questionnaire/index.js +1 -0
- package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire-drawer.d.ts +3 -0
- package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire-drawer.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire-drawer.js +8 -0
- package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.d.ts +10 -0
- package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/prd-questionnaire/prd-questionnaire.stories.js +38 -1
- package/dist/src/presentation/web/components/common/review-drawer-shell/index.d.ts +3 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/index.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/index.js +1 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell-config.d.ts +26 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell-config.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell-config.js +1 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.d.ts +3 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.js +29 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.stories.d.ts +14 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/review-drawer-shell/review-drawer-shell.stories.js +33 -0
- package/dist/src/presentation/web/components/common/tech-decisions-review/index.d.ts +2 -1
- package/dist/src/presentation/web/components/common/tech-decisions-review/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/tech-decisions-review/index.js +1 -0
- package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-drawer.d.ts +3 -0
- package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-drawer.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-drawer.js +8 -0
- package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.d.ts +8 -0
- package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/tech-decisions-review/tech-decisions-review.stories.js +29 -1
- package/dist/src/presentation/web/components/common/tech-review-tabs/index.d.ts +3 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/index.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/index.js +1 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs-config.d.ts +21 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs-config.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs-config.js +1 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs.d.ts +3 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs.js +12 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs.stories.d.ts +16 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/tech-review-tabs/tech-review-tabs.stories.js +110 -0
- package/dist/src/presentation/web/next.config.d.ts.map +1 -1
- package/dist/src/presentation/web/next.config.js +6 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +3 -0
- package/web/.next/required-server-files.json +3 -0
- package/web/.next/routes-manifest.json +6 -6
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +18 -18
- 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]/page/server-reference-manifest.json +38 -68
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
- 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]/page/server-reference-manifest.json +14 -14
- 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)/create/page/server-reference-manifest.json +18 -18
- 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]/page/server-reference-manifest.json +38 -68
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
- 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/server-reference-manifest.json +14 -14
- 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]/page/server-reference-manifest.json +14 -14
- 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.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/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/api/attachments/upload/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/attachments/upload/route.js +7 -0
- package/web/.next/server/app/api/attachments/upload/route.js.nft.json +1 -0
- package/web/.next/server/app/api/attachments/upload/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
- 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/server-reference-manifest.json +6 -6
- 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/server-reference-manifest.json +6 -6
- 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/server-reference-manifest.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app-paths-manifest.json +1 -1
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_attachments_upload_route_actions_48fe4a8f.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__b2f9a412._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__b2f9a412._.js.map +1 -0
- 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/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_da7eef33.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_0a9251aa.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__4be9a9bb._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__ae824a5b._.js → [root-of-the-server]__a337ca5c._.js} +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a337ca5c._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__b3a4cef4._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__b3a4cef4._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c3c963c6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c3c963c6._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__f0d8d054._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__f0d8d054._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_3e88ce9d._.js → _06db7efd._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_3e88ce9d._.js.map → _06db7efd._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_1bb131c4._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_1e3cf73b._.js +1 -1
- package/web/.next/server/chunks/ssr/_1e3cf73b._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_34fbed18._.js +1 -1
- package/web/.next/server/chunks/ssr/_34fbed18._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_887258d0._.js → _4031d614._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_887258d0._.js.map → _4031d614._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_5c4ce700._.js +3 -0
- package/web/.next/server/chunks/ssr/{_5d4e7151._.js.map → _5c4ce700._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_63868199._.js +1 -1
- package/web/.next/server/chunks/ssr/_63868199._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_6e08ce21._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
- package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_4b251edf._.js → _c66a5a32._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_4b251edf._.js.map → _c66a5a32._.js.map} +1 -1
- 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/node_modules__pnpm_5412e589._.js +3 -0
- package/web/.next/server/chunks/ssr/node_modules__pnpm_5412e589._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_30d7eeda._.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_30d7eeda._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_565a14f8._.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_565a14f8._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_021a05f2.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_app_(dashboard)_get-graph-data_ts_b018483e._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_c4348b8a._.js +2 -3
- package/web/.next/server/chunks/ssr/src_presentation_web_c4348b8a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_80b5b4bc._.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_components_80b5b4bc._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_components_82416b8f._.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_components_82416b8f._.js.map +1 -0
- 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_e4510d10._.js +2 -3
- package/web/.next/server/chunks/ssr/src_presentation_web_e4510d10._.js.map +1 -1
- 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 +64 -108
- package/web/.next/static/chunks/{2a24ac257fc1c10b.js → 1f57d0c949dbb44d.js} +1 -1
- package/web/.next/static/chunks/27a7dd213b6da211.js +1 -0
- package/web/.next/static/chunks/{a2a8c7488178523b.js → 294840464b615929.js} +2 -2
- package/web/.next/static/chunks/2a0156732d824304.js +1 -0
- package/web/.next/static/chunks/55cfb5d3bc98f2e9.js +1 -0
- package/web/.next/static/chunks/6a3e8ea6e1fd2113.js +1 -0
- package/web/.next/static/chunks/{40b4ba55a08cc340.js → 6beed3db056fe481.js} +1 -1
- package/web/.next/static/chunks/{1ed35030fddfaf83.js → 76ddfd21093dc7e7.js} +1 -1
- package/web/.next/static/chunks/b0c8d477fe52559a.js +1 -0
- package/web/.next/static/chunks/{1943a885d38cec14.js → ca4247da9701312c.js} +2 -2
- package/web/.next/static/chunks/e933e992fdc663fb.js +1 -0
- package/web/.next/static/chunks/f6f770ab32a69286.css +2 -0
- package/web/.next/static/chunks/{bdf60cf8dc75a90f.js → f7ed5cfe0c81a863.js} +1 -1
- package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts +0 -17
- package/dist/src/presentation/web/app/actions/get-feature-phase-timings.d.ts.map +0 -1
- package/dist/src/presentation/web/app/actions/get-feature-phase-timings.js +0 -25
- package/dist/src/presentation/web/app/actions/get-feature-plan.d.ts +0 -18
- package/dist/src/presentation/web/app/actions/get-feature-plan.d.ts.map +0 -1
- package/dist/src/presentation/web/app/actions/get-feature-plan.js +0 -31
- package/dist/src/presentation/web/app/api/feature-logs/route.d.ts +0 -16
- package/dist/src/presentation/web/app/api/feature-logs/route.d.ts.map +0 -1
- package/dist/src/presentation/web/app/api/feature-logs/route.js +0 -184
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts +0 -8
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.js +0 -90
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.stories.d.ts +0 -20
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.stories.js +0 -252
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts +0 -31
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js +0 -123
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.stories.d.ts +0 -20
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.stories.js +0 -315
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/index.d.ts +0 -3
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/index.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/index.js +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.d.ts +0 -7
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.js +0 -35
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.stories.d.ts +0 -18
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/log-tab.stories.js +0 -89
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts +0 -6
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.js +0 -78
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.stories.d.ts +0 -28
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.stories.js +0 -157
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/plan-tab.d.ts +0 -8
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/plan-tab.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/plan-tab.js +0 -40
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/plan-tab.stories.d.ts +0 -22
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/plan-tab.stories.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/plan-tab.stories.js +0 -188
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/use-tab-data-fetch.d.ts +0 -30
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/use-tab-data-fetch.d.ts.map +0 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/use-tab-data-fetch.js +0 -100
- package/dist/src/presentation/web/hooks/use-feature-logs.d.ts +0 -7
- package/dist/src/presentation/web/hooks/use-feature-logs.d.ts.map +0 -1
- package/dist/src/presentation/web/hooks/use-feature-logs.js +0 -52
- package/web/.next/server/app/api/feature-logs/route/app-paths-manifest.json +0 -3
- package/web/.next/server/app/api/feature-logs/route.js +0 -6
- package/web/.next/server/app/api/feature-logs/route.js.nft.json +0 -1
- package/web/.next/server/app/api/feature-logs/route_client-reference-manifest.js +0 -2
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_feature-logs_route_actions_177445e7.js +0 -3
- package/web/.next/server/chunks/[root-of-the-server]__91d44424._.js +0 -12
- package/web/.next/server/chunks/[root-of-the-server]__91d44424._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__ae824a5b._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_08bf9375._.js +0 -3
- package/web/.next/server/chunks/ssr/_08bf9375._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_5d4e7151._.js +0 -3
- package/web/.next/server/chunks/ssr/_e7eb01d5._.js +0 -3
- package/web/.next/server/chunks/ssr/_e7eb01d5._.js.map +0 -1
- package/web/.next/server/chunks/ssr/node_modules__pnpm_d80c1060._.js +0 -3
- package/web/.next/server/chunks/ssr/node_modules__pnpm_d80c1060._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_7efa14ca._.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web_7efa14ca._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_b59d79db._.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web_b59d79db._.js.map +0 -1
- package/web/.next/static/chunks/1ac497a4b2a7a143.js +0 -1
- package/web/.next/static/chunks/201456645dc32523.js +0 -1
- package/web/.next/static/chunks/46de72d1231c72e6.js +0 -1
- package/web/.next/static/chunks/54f920b6a940da52.js +0 -1
- package/web/.next/static/chunks/96bab6d3820504f3.css +0 -2
- package/web/.next/static/chunks/a1c2ecb710b44081.js +0 -1
- package/web/.next/static/chunks/b590b965ec136ece.js +0 -1
- /package/web/.next/server/app/api/{feature-logs → attachments/upload}/route/build-manifest.json +0 -0
- /package/web/.next/server/app/api/{feature-logs → attachments/upload}/route/server-reference-manifest.json +0 -0
- /package/web/.next/server/app/api/{feature-logs → attachments/upload}/route.js.map +0 -0
- /package/web/.next/server/chunks/{744ca_web__next-internal_server_app_api_feature-logs_route_actions_177445e7.js.map → 744ca_web__next-internal_server_app_api_attachments_upload_route_actions_48fe4a8f.js.map} +0 -0
- /package/web/.next/static/{nOcqEXIvW4HbIOdPWtEmW → 2GTN_fvpyiJGx65yUAuBB}/_buildManifest.js +0 -0
- /package/web/.next/static/{nOcqEXIvW4HbIOdPWtEmW → 2GTN_fvpyiJGx65yUAuBB}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{nOcqEXIvW4HbIOdPWtEmW → 2GTN_fvpyiJGx65yUAuBB}/_ssgManifest.js +0 -0
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
4
|
-
import { PlusIcon,
|
|
4
|
+
import { PlusIcon, ChevronsUpDown, CheckIcon } from 'lucide-react';
|
|
5
5
|
import { cn } from '../../../lib/utils.js';
|
|
6
6
|
import { useSoundAction } from '../../../hooks/use-sound-action.js';
|
|
7
7
|
import { BaseDrawer } from '../../common/base-drawer/index.js';
|
|
@@ -15,8 +15,71 @@ import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover.js';
|
|
|
15
15
|
import { Switch } from '../../ui/switch.js';
|
|
16
16
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip.js';
|
|
17
17
|
import { useGuardedDrawerClose } from '../../../hooks/drawer-close-guard.js';
|
|
18
|
+
import { AttachmentCard } from '../../common/attachment-card/index.js';
|
|
18
19
|
import { AgentModelPicker } from '../../features/settings/AgentModelPicker/index.js';
|
|
19
20
|
import { pickFiles } from './pick-files.js';
|
|
21
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
|
22
|
+
const ALLOWED_EXTENSIONS = new Set([
|
|
23
|
+
'.png',
|
|
24
|
+
'.jpg',
|
|
25
|
+
'.jpeg',
|
|
26
|
+
'.gif',
|
|
27
|
+
'.webp',
|
|
28
|
+
'.svg',
|
|
29
|
+
'.bmp',
|
|
30
|
+
'.ico',
|
|
31
|
+
'.pdf',
|
|
32
|
+
'.doc',
|
|
33
|
+
'.docx',
|
|
34
|
+
'.xls',
|
|
35
|
+
'.xlsx',
|
|
36
|
+
'.ppt',
|
|
37
|
+
'.pptx',
|
|
38
|
+
'.txt',
|
|
39
|
+
'.md',
|
|
40
|
+
'.csv',
|
|
41
|
+
'.json',
|
|
42
|
+
'.yaml',
|
|
43
|
+
'.yml',
|
|
44
|
+
'.xml',
|
|
45
|
+
'.ts',
|
|
46
|
+
'.tsx',
|
|
47
|
+
'.js',
|
|
48
|
+
'.jsx',
|
|
49
|
+
'.py',
|
|
50
|
+
'.rb',
|
|
51
|
+
'.go',
|
|
52
|
+
'.rs',
|
|
53
|
+
'.java',
|
|
54
|
+
'.c',
|
|
55
|
+
'.cpp',
|
|
56
|
+
'.h',
|
|
57
|
+
'.hpp',
|
|
58
|
+
'.cs',
|
|
59
|
+
'.swift',
|
|
60
|
+
'.kt',
|
|
61
|
+
'.html',
|
|
62
|
+
'.css',
|
|
63
|
+
'.scss',
|
|
64
|
+
'.less',
|
|
65
|
+
'.sh',
|
|
66
|
+
'.bash',
|
|
67
|
+
'.zsh',
|
|
68
|
+
'.fish',
|
|
69
|
+
'.toml',
|
|
70
|
+
'.ini',
|
|
71
|
+
'.cfg',
|
|
72
|
+
'.conf',
|
|
73
|
+
'.env',
|
|
74
|
+
'.zip',
|
|
75
|
+
'.tar',
|
|
76
|
+
'.gz',
|
|
77
|
+
'.log',
|
|
78
|
+
]);
|
|
79
|
+
function getExtension(filename) {
|
|
80
|
+
const dot = filename.lastIndexOf('.');
|
|
81
|
+
return dot >= 0 ? filename.slice(dot).toLowerCase() : '';
|
|
82
|
+
}
|
|
20
83
|
const AUTO_APPROVE_OPTIONS = [
|
|
21
84
|
{ id: 'allowPrd', label: 'PRD', description: 'Auto-approve requirements move to planning.' },
|
|
22
85
|
{ id: 'allowPlan', label: 'Plan', description: 'Auto-approve planning move to implementation.' },
|
|
@@ -41,6 +104,11 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
41
104
|
const [fast, setFast] = useState(false);
|
|
42
105
|
const [overrideAgent, setOverrideAgent] = useState(undefined);
|
|
43
106
|
const [overrideModel, setOverrideModel] = useState(undefined);
|
|
107
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
108
|
+
const [uploadError, setUploadError] = useState(null);
|
|
109
|
+
// Stable sessionId per mount — used for upload dedup grouping
|
|
110
|
+
const sessionIdRef = useRef(crypto.randomUUID());
|
|
111
|
+
const dragCounterRef = useRef(0);
|
|
44
112
|
// Sync state when workflowDefaults load asynchronously
|
|
45
113
|
useEffect(() => {
|
|
46
114
|
if (workflowDefaults) {
|
|
@@ -65,11 +133,117 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
65
133
|
setFast(false);
|
|
66
134
|
setOverrideAgent(undefined);
|
|
67
135
|
setOverrideModel(undefined);
|
|
136
|
+
setUploadError(null);
|
|
137
|
+
dragCounterRef.current = 0;
|
|
138
|
+
setIsDragOver(false);
|
|
68
139
|
}, [defaultGates, defaultPush, defaultOpenPr]);
|
|
69
140
|
// Track whether the form has unsaved data
|
|
70
141
|
const isDirty = description.trim() !== '' || attachments.length > 0;
|
|
71
142
|
// Shared close guard — shows confirmation when dirty, prevents navigation
|
|
72
143
|
const { attemptClose } = useGuardedDrawerClose({ open, isDirty, onClose, onReset: resetForm });
|
|
144
|
+
/** Validate and upload files from drop or paste. */
|
|
145
|
+
const handleFiles = useCallback(async (fileList) => {
|
|
146
|
+
setUploadError(null);
|
|
147
|
+
for (const file of fileList) {
|
|
148
|
+
// Client-side size validation
|
|
149
|
+
if (file.size > MAX_FILE_SIZE) {
|
|
150
|
+
setUploadError(`"${file.name}" exceeds 10 MB limit`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Client-side extension validation
|
|
154
|
+
const ext = getExtension(file.name);
|
|
155
|
+
if (ext && !ALLOWED_EXTENSIONS.has(ext)) {
|
|
156
|
+
setUploadError(`File type "${ext}" is not allowed`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (const file of fileList) {
|
|
161
|
+
const tempId = crypto.randomUUID();
|
|
162
|
+
// Optimistic loading placeholder
|
|
163
|
+
setAttachments((prev) => [
|
|
164
|
+
...prev,
|
|
165
|
+
{
|
|
166
|
+
id: tempId,
|
|
167
|
+
name: file.name,
|
|
168
|
+
size: file.size,
|
|
169
|
+
mimeType: file.type || 'application/octet-stream',
|
|
170
|
+
path: '',
|
|
171
|
+
loading: true,
|
|
172
|
+
},
|
|
173
|
+
]);
|
|
174
|
+
try {
|
|
175
|
+
const formData = new FormData();
|
|
176
|
+
formData.append('file', file);
|
|
177
|
+
formData.append('sessionId', sessionIdRef.current);
|
|
178
|
+
formData.append('repositoryPath', repositoryPath);
|
|
179
|
+
const res = await fetch('/api/attachments/upload', {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
body: formData,
|
|
182
|
+
});
|
|
183
|
+
if (!res.ok) {
|
|
184
|
+
const body = await res.json().catch(() => ({ error: 'Upload failed' }));
|
|
185
|
+
// Remove loading placeholder on error
|
|
186
|
+
setAttachments((prev) => prev.filter((a) => a.id !== tempId));
|
|
187
|
+
setUploadError(body.error ?? 'Upload failed');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const uploaded = await res.json();
|
|
191
|
+
// Replace loading placeholder with real attachment
|
|
192
|
+
setAttachments((prev) => prev.map((a) => (a.id === tempId ? { ...uploaded, loading: false } : a)));
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
setAttachments((prev) => prev.filter((a) => a.id !== tempId));
|
|
196
|
+
setUploadError('Upload failed');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}, [repositoryPath]);
|
|
200
|
+
const handleDragEnter = useCallback((e) => {
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
e.stopPropagation();
|
|
203
|
+
dragCounterRef.current += 1;
|
|
204
|
+
if (dragCounterRef.current === 1) {
|
|
205
|
+
setIsDragOver(true);
|
|
206
|
+
}
|
|
207
|
+
}, []);
|
|
208
|
+
const handleDragLeave = useCallback((e) => {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
e.stopPropagation();
|
|
211
|
+
dragCounterRef.current -= 1;
|
|
212
|
+
if (dragCounterRef.current === 0) {
|
|
213
|
+
setIsDragOver(false);
|
|
214
|
+
}
|
|
215
|
+
}, []);
|
|
216
|
+
const handleDragOver = useCallback((e) => {
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
}, []);
|
|
220
|
+
const handleDrop = useCallback((e) => {
|
|
221
|
+
e.preventDefault();
|
|
222
|
+
e.stopPropagation();
|
|
223
|
+
dragCounterRef.current = 0;
|
|
224
|
+
setIsDragOver(false);
|
|
225
|
+
const files = Array.from(e.dataTransfer.files);
|
|
226
|
+
if (files.length > 0) {
|
|
227
|
+
handleFiles(files);
|
|
228
|
+
}
|
|
229
|
+
}, [handleFiles]);
|
|
230
|
+
const handlePaste = useCallback((e) => {
|
|
231
|
+
const items = e.clipboardData?.items;
|
|
232
|
+
if (!items)
|
|
233
|
+
return;
|
|
234
|
+
const files = [];
|
|
235
|
+
for (const item of Array.from(items)) {
|
|
236
|
+
if (item.kind === 'file') {
|
|
237
|
+
const file = item.getAsFile();
|
|
238
|
+
if (file)
|
|
239
|
+
files.push(file);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (files.length > 0) {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
handleFiles(files);
|
|
245
|
+
}
|
|
246
|
+
}, [handleFiles]);
|
|
73
247
|
const handleSubmit = useCallback((e) => {
|
|
74
248
|
e.preventDefault();
|
|
75
249
|
if (!description.trim())
|
|
@@ -77,7 +251,7 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
77
251
|
createSound.play();
|
|
78
252
|
onSubmit({
|
|
79
253
|
description: description.trim(),
|
|
80
|
-
attachments,
|
|
254
|
+
attachments: attachments.filter((a) => !a.loading),
|
|
81
255
|
repositoryPath,
|
|
82
256
|
approvalGates: {
|
|
83
257
|
allowPrd: approvalGates.allowPrd ?? false,
|
|
@@ -90,6 +264,7 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
90
264
|
...(overrideAgent ? { agentType: overrideAgent } : {}),
|
|
91
265
|
...(overrideModel ? { model: overrideModel } : {}),
|
|
92
266
|
...(parentId ? { parentId } : {}),
|
|
267
|
+
sessionId: sessionIdRef.current,
|
|
93
268
|
});
|
|
94
269
|
resetForm();
|
|
95
270
|
}, [
|
|
@@ -111,18 +286,30 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
111
286
|
try {
|
|
112
287
|
const files = await pickFiles();
|
|
113
288
|
if (files) {
|
|
114
|
-
setAttachments((prev) =>
|
|
289
|
+
setAttachments((prev) => {
|
|
290
|
+
const existingPaths = new Set(prev.map((f) => f.path));
|
|
291
|
+
const unique = files
|
|
292
|
+
.filter((f) => !existingPaths.has(f.path))
|
|
293
|
+
.map((f) => ({
|
|
294
|
+
id: crypto.randomUUID(),
|
|
295
|
+
name: f.name,
|
|
296
|
+
size: f.size,
|
|
297
|
+
mimeType: 'application/octet-stream',
|
|
298
|
+
path: f.path,
|
|
299
|
+
}));
|
|
300
|
+
return unique.length > 0 ? [...prev, ...unique] : prev;
|
|
301
|
+
});
|
|
115
302
|
}
|
|
116
303
|
}
|
|
117
304
|
catch {
|
|
118
305
|
// Native dialog failed — silently ignore (user can retry)
|
|
119
306
|
}
|
|
120
307
|
}, []);
|
|
121
|
-
const handleRemoveFile = useCallback((
|
|
122
|
-
setAttachments((prev) => prev.filter((f) => f.
|
|
308
|
+
const handleRemoveFile = useCallback((id) => {
|
|
309
|
+
setAttachments((prev) => prev.filter((f) => f.id !== id));
|
|
123
310
|
}, []);
|
|
124
311
|
const hasFeatures = features && features.length > 0;
|
|
125
|
-
return (_jsx(BaseDrawer, { open: open, onClose: attemptClose, size: "md", modal: false, dismissOnOutsideClick: true, "data-testid": "feature-create-drawer", header: _jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), isSubmitting ? (_jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })) : null] }), footer: _jsxs("div", { className: "flex flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: attemptClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !description.trim() || isSubmitting, children: isSubmitting ? 'Creating...' : '+ Create Feature' })] }), children: _jsx("div", { className: "overflow-y-auto p-4", children: _jsx(TooltipProvider, { delayDuration: 400, children: _jsxs("form", { id: "create-feature-form", onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs("div", { className:
|
|
312
|
+
return (_jsx(BaseDrawer, { open: open, onClose: attemptClose, size: "md", modal: false, dismissOnOutsideClick: true, "data-testid": "feature-create-drawer", header: _jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 shrink-0 rounded-full bg-blue-500" }), _jsx(DrawerTitle, { children: "NEW FEATURE" })] }), isSubmitting ? (_jsx(DrawerDescription, { asChild: true, children: _jsx("div", { children: _jsx(Badge, { variant: "secondary", children: "Creating..." }) }) })) : null] }), footer: _jsxs("div", { className: "flex flex-row justify-end gap-2", children: [_jsx(Button, { variant: "outline", onClick: attemptClose, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { type: "submit", form: "create-feature-form", disabled: !description.trim() || isSubmitting, children: isSubmitting ? 'Creating...' : '+ Create Feature' })] }), children: _jsx("div", { className: "overflow-y-auto p-4", children: _jsx(TooltipProvider, { delayDuration: 400, children: _jsxs("form", { id: "create-feature-form", onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsxs("div", { role: "region", "aria-label": "File drop zone", "data-drag-over": isDragOver ? 'true' : 'false', onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn('flex flex-col gap-1.5 rounded-md border-2 border-transparent p-1 transition-colors', isDragOver && 'border-primary/50 bg-primary/5'), children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIBE YOUR FEATURE" }), _jsxs("div", { className: "border-input focus-within:ring-ring/50 focus-within:border-ring overflow-hidden rounded-md border shadow-xs transition-[color,box-shadow] focus-within:ring-[3px]", children: [_jsx(Textarea, { id: "feature-description", placeholder: "e.g. Add GitHub OAuth login with callback handling and token refresh...", value: description, onChange: (e) => setDescription(e.target.value), onPaste: handlePaste, rows: 7, required: true, disabled: isSubmitting, className: "field-sizing-fixed min-h-42! resize-none rounded-none border-0 shadow-none focus-visible:ring-0" }), _jsxs("div", { className: "border-input flex items-center gap-3 border-t px-3 py-1.5", children: [_jsx(AgentModelPicker, { initialAgentType: overrideAgent ?? currentAgentType ?? 'claude-code', initialModel: overrideModel ?? currentModel ?? 'claude-sonnet-4-6', mode: "override", onAgentModelChange: (agent, model) => {
|
|
126
313
|
setOverrideAgent(agent);
|
|
127
314
|
setOverrideModel(model);
|
|
128
315
|
}, disabled: isSubmitting, className: "w-55" }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "ml-auto flex cursor-pointer items-center gap-2", children: [_jsx(Switch, { id: "fast-mode", checked: fast, onCheckedChange: setFast, disabled: isSubmitting }), _jsx(Label, { htmlFor: "fast-mode", className: "cursor-pointer text-sm font-medium", children: "Fast Mode" })] }) }), _jsx(TooltipContent, { side: "bottom", children: "Skip SDLC phases and implement directly from your prompt." })] })] })] })] }), hasFeatures && initialParentId !== undefined ? (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "parent-feature", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "PARENT FEATURE" }), _jsx(ParentFeatureCombobox, { features: features, value: parentId, onChange: setParentId, disabled: isSubmitting })] })) : null, _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "border-input flex items-center gap-4 rounded-md border px-3 py-2.5", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "text-muted-foreground w-16 shrink-0 cursor-default text-xs font-semibold tracking-wider", children: "APPROVE" }) }), _jsx(TooltipContent, { side: "bottom", children: "Auto-approve phase transitions without manual review." })] }), _jsx("div", { className: "flex flex-1 items-center gap-4", children: AUTO_APPROVE_OPTIONS.map((opt) => (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "flex cursor-pointer items-center gap-1.5", children: [_jsx(Switch, { id: `approve-${opt.id}`, size: "sm", checked: approvalGates[opt.id] ?? false, onCheckedChange: (v) => setApprovalGates((prev) => ({ ...prev, [opt.id]: v })), disabled: isSubmitting ||
|
|
@@ -138,7 +325,7 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
138
325
|
setPush(v);
|
|
139
326
|
if (!v && openPr)
|
|
140
327
|
setOpenPr(false);
|
|
141
|
-
}, disabled: isSubmitting }), _jsx(Label, { htmlFor: "push", className: "cursor-pointer text-xs font-medium", children: "Push" })] }) }), _jsx(TooltipContent, { side: "bottom", children: "Push branch to remote after implementation." })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "flex cursor-pointer items-center gap-1.5", children: [_jsx(Switch, { id: "open-pr", size: "sm", checked: openPr, onCheckedChange: setOpenPr, disabled: isSubmitting }), _jsx(Label, { htmlFor: "open-pr", className: "cursor-pointer text-xs font-medium", children: "PR" })] }) }), _jsx(TooltipContent, { side: "bottom", children: "Open a pull request after pushing." })] })] })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: ["ATTACHMENTS", attachments.length > 0 && (_jsxs("span", { className: "text-muted-foreground/60 ml-1.5", children: ["(", attachments.length, ")"] }))] }), _jsxs(Button, { type: "button", variant: "outline", size: "xs", onClick: handleAddFiles, disabled: isSubmitting, children: [_jsx(PlusIcon, { className: "size-3" }), "Add Files"] })] }), attachments.length > 0 && (_jsx("div", { className: "flex flex-col gap-2", children: attachments.map((file) => (_jsx(AttachmentCard, { file: file, onRemove: () => handleRemoveFile(file.
|
|
328
|
+
}, disabled: isSubmitting }), _jsx(Label, { htmlFor: "push", className: "cursor-pointer text-xs font-medium", children: "Push" })] }) }), _jsx(TooltipContent, { side: "bottom", children: "Push branch to remote after implementation." })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "flex cursor-pointer items-center gap-1.5", children: [_jsx(Switch, { id: "open-pr", size: "sm", checked: openPr, onCheckedChange: setOpenPr, disabled: isSubmitting }), _jsx(Label, { htmlFor: "open-pr", className: "cursor-pointer text-xs font-medium", children: "PR" })] }) }), _jsx(TooltipContent, { side: "bottom", children: "Open a pull request after pushing." })] })] })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: ["ATTACHMENTS", attachments.length > 0 && (_jsxs("span", { className: "text-muted-foreground/60 ml-1.5", children: ["(", attachments.length, ")"] }))] }), _jsxs(Button, { type: "button", variant: "outline", size: "xs", onClick: handleAddFiles, disabled: isSubmitting, children: [_jsx(PlusIcon, { className: "size-3" }), "Add Files"] })] }), uploadError ? _jsx("p", { className: "text-destructive text-xs", children: uploadError }) : null, attachments.length > 0 && (_jsx("div", { className: "flex flex-col gap-2", children: attachments.map((file) => (_jsx(AttachmentCard, { name: file.name, size: file.size, mimeType: file.mimeType, subtitle: file.path || undefined, onRemove: () => handleRemoveFile(file.id), disabled: isSubmitting, loading: file.loading }, file.id))) }))] })] }) }) }) }));
|
|
142
329
|
}
|
|
143
330
|
function ParentFeatureCombobox({ features, value, onChange, disabled, }) {
|
|
144
331
|
const [open, setOpen] = useState(false);
|
|
@@ -166,55 +353,3 @@ function ParentFeatureCombobox({ features, value, onChange, disabled, }) {
|
|
|
166
353
|
? `${selectedFeature.name} (${selectedFeature.id.slice(0, 8)})`
|
|
167
354
|
: 'Select parent feature...' }), _jsx(ChevronsUpDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }) }), _jsx(PopoverContent, { className: "w-80 p-0", align: "start", "data-testid": "parent-feature-combobox-content", children: _jsxs("div", { className: "flex flex-col", children: [_jsx("div", { className: "border-b p-2", children: _jsx(Input, { ref: inputRef, placeholder: "Search features...", value: query, onChange: (e) => setQuery(e.target.value), className: "h-8 border-0 p-0 text-sm shadow-none focus-visible:ring-0", "data-testid": "parent-feature-search" }) }), _jsxs("div", { className: "max-h-48 overflow-y-auto py-1", role: "listbox", "aria-label": "Features", children: [_jsxs("button", { type: "button", role: "option", "aria-selected": value === undefined, onClick: () => handleSelect(undefined), className: cn('hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm', value === undefined && 'bg-accent/50'), "data-testid": "parent-feature-option-none", children: [_jsx(CheckIcon, { className: cn('h-4 w-4 shrink-0', value !== undefined && 'invisible') }), _jsx("span", { className: "text-muted-foreground italic", children: "No parent" })] }), filtered.length === 0 && query ? (_jsx("p", { className: "text-muted-foreground px-3 py-2 text-sm", children: "No features found." })) : (filtered.map((f) => (_jsxs("button", { type: "button", role: "option", "aria-selected": value === f.id, onClick: () => handleSelect(f.id), className: cn('hover:bg-accent hover:text-accent-foreground flex w-full items-center gap-2 px-3 py-2 text-sm', value === f.id && 'bg-accent/50'), "data-testid": `parent-feature-option-${f.id}`, children: [_jsx(CheckIcon, { className: cn('h-4 w-4 shrink-0', value !== f.id && 'invisible') }), _jsxs("span", { className: "truncate", children: [f.name, ' ', _jsxs("span", { className: "text-muted-foreground font-mono text-xs", children: ["(", f.id.slice(0, 8), ")"] })] })] }, f.id))))] })] }) })] }));
|
|
168
355
|
}
|
|
169
|
-
/* ---------------------------------------------------------------------------
|
|
170
|
-
* Private sub-components & utilities
|
|
171
|
-
* ------------------------------------------------------------------------- */
|
|
172
|
-
function AttachmentCard({ file, onRemove, disabled, }) {
|
|
173
|
-
const ext = getExtension(file.name);
|
|
174
|
-
const Icon = getFileIcon(ext);
|
|
175
|
-
const iconColorClass = getFileIconColor(ext);
|
|
176
|
-
return (_jsxs("div", { className: "flex items-center gap-3 rounded-md border p-2", children: [_jsx("div", { className: cn('flex h-8 w-8 shrink-0 items-center justify-center rounded', iconColorClass), children: _jsx(Icon, { className: "h-4 w-4" }) }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [_jsx("span", { className: "truncate text-sm font-medium", children: file.name }), _jsx("span", { className: "text-muted-foreground truncate text-xs", children: file.path }), _jsx("span", { className: "text-muted-foreground text-xs", children: formatFileSize(file.size) })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon-xs", onClick: onRemove, disabled: disabled, "aria-label": `Remove ${file.name}`, children: _jsx(Trash2Icon, { className: "h-3 w-3" }) })] }));
|
|
177
|
-
}
|
|
178
|
-
function getExtension(filename) {
|
|
179
|
-
const dot = filename.lastIndexOf('.');
|
|
180
|
-
return dot >= 0 ? filename.slice(dot).toLowerCase() : '';
|
|
181
|
-
}
|
|
182
|
-
const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.bmp']);
|
|
183
|
-
const CODE_EXTS = new Set([
|
|
184
|
-
'.ts',
|
|
185
|
-
'.tsx',
|
|
186
|
-
'.js',
|
|
187
|
-
'.jsx',
|
|
188
|
-
'.json',
|
|
189
|
-
'.yaml',
|
|
190
|
-
'.yml',
|
|
191
|
-
'.xml',
|
|
192
|
-
'.html',
|
|
193
|
-
'.css',
|
|
194
|
-
'.md',
|
|
195
|
-
]);
|
|
196
|
-
function getFileIcon(ext) {
|
|
197
|
-
if (IMAGE_EXTS.has(ext))
|
|
198
|
-
return ImageIcon;
|
|
199
|
-
if (ext === '.pdf')
|
|
200
|
-
return FileTextIcon;
|
|
201
|
-
if (CODE_EXTS.has(ext))
|
|
202
|
-
return CodeIcon;
|
|
203
|
-
return FileIcon;
|
|
204
|
-
}
|
|
205
|
-
function getFileIconColor(ext) {
|
|
206
|
-
if (ext === '.pdf')
|
|
207
|
-
return 'bg-red-50 text-red-600';
|
|
208
|
-
if (IMAGE_EXTS.has(ext))
|
|
209
|
-
return 'bg-blue-50 text-blue-600';
|
|
210
|
-
if (CODE_EXTS.has(ext))
|
|
211
|
-
return 'bg-emerald-50 text-emerald-600';
|
|
212
|
-
return 'bg-gray-50 text-gray-600';
|
|
213
|
-
}
|
|
214
|
-
function formatFileSize(bytes) {
|
|
215
|
-
if (bytes === 0)
|
|
216
|
-
return '0 B';
|
|
217
|
-
const units = ['B', 'KB', 'MB', 'GB'];
|
|
218
|
-
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
219
|
-
return `${(bytes / Math.pow(1024, i)).toFixed(i === 0 ? 0 : 1)} ${units[i]}`;
|
|
220
|
-
}
|
|
@@ -113,5 +113,11 @@ export declare const WithParentFeature: Story;
|
|
|
113
113
|
* "Discard unsaved changes?" dialog. Demonstrates the dirty-form guard.
|
|
114
114
|
*/
|
|
115
115
|
export declare const DiscardConfirmation: Story;
|
|
116
|
+
/**
|
|
117
|
+
* Drawer ready for drag-and-drop — open the drawer and drag a file over
|
|
118
|
+
* the "DESCRIBE YOUR FEATURE" area to see the drag-over highlight.
|
|
119
|
+
* In Storybook, the upload API isn't available, but the visual states render.
|
|
120
|
+
*/
|
|
121
|
+
export declare const DragDropReady: Story;
|
|
116
122
|
export declare const Interactive: Story;
|
|
117
123
|
//# sourceMappingURL=feature-create-drawer.stories.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-create-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAU9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAgC1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAmDlD,uFAAuF;AACvF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAiBvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAMhC,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,EAAE,KAMvB,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,KAUxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,KAUrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAUvB,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,KAUvB,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,KAMzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,KAUxB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAgBjC,CAAC;AAYF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAWlC,CAAC;AAkDF,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,EAAE,KAEtB,CAAC;AAwCF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAM/B,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAgBjC,CAAC;AAMF,eAAO,MAAM,WAAW,EAAE,KAwCzB,CAAC"}
|
|
1
|
+
{"version":3,"file":"feature-create-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAU9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAgC1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAmDlD,uFAAuF;AACvF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAiBvB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAMhC,CAAC;AAEF,4DAA4D;AAC5D,eAAO,MAAM,SAAS,EAAE,KAMvB,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAM1B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,KAUxB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAE,KAUrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,KAUvB,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,KAUtB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,SAAS,EAAE,KAUvB,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,KAMzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,KAUxB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAgBjC,CAAC;AAYF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAWlC,CAAC;AAkDF,+EAA+E;AAC/E,eAAO,MAAM,QAAQ,EAAE,KAEtB,CAAC;AAwCF;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAM/B,CAAC;AAMF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAgBjC,CAAC;AAMF;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,KAY3B,CAAC;AAMF,eAAO,MAAM,WAAW,EAAE,KAwCzB,CAAC"}
|
|
@@ -360,6 +360,24 @@ export const DiscardConfirmation = {
|
|
|
360
360
|
await userEvent.click(cancelButton);
|
|
361
361
|
},
|
|
362
362
|
};
|
|
363
|
+
/* ---------------------------------------------------------------------------
|
|
364
|
+
* Attachment stories
|
|
365
|
+
* ------------------------------------------------------------------------- */
|
|
366
|
+
/**
|
|
367
|
+
* Drawer ready for drag-and-drop — open the drawer and drag a file over
|
|
368
|
+
* the "DESCRIBE YOUR FEATURE" area to see the drag-over highlight.
|
|
369
|
+
* In Storybook, the upload API isn't available, but the visual states render.
|
|
370
|
+
*/
|
|
371
|
+
export const DragDropReady = {
|
|
372
|
+
render: () => _jsx(CreateDrawerTrigger, { label: "Open (Drag & Drop)" }),
|
|
373
|
+
play: async ({ canvasElement }) => {
|
|
374
|
+
const canvas = within(canvasElement);
|
|
375
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Open (Drag & Drop)' }));
|
|
376
|
+
const body = within(canvasElement.ownerDocument.body);
|
|
377
|
+
const descInput = await body.findByPlaceholderText('e.g. Add GitHub OAuth login with callback handling and token refresh...');
|
|
378
|
+
await userEvent.type(descInput, 'Add attachment support with drag and drop');
|
|
379
|
+
},
|
|
380
|
+
};
|
|
363
381
|
/* ---------------------------------------------------------------------------
|
|
364
382
|
* Interactive story — full paths shown in submitted data panel
|
|
365
383
|
* ------------------------------------------------------------------------- */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FeatureNodeData } from '../../common/feature-node/index.js';
|
|
2
|
+
export interface FeatureDrawerProps {
|
|
3
|
+
selectedNode: FeatureNodeData | null;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
onDelete?: (featureId: string) => void;
|
|
6
|
+
isDeleting?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function FeatureDrawer({ selectedNode, onClose, onDelete, isDeleting, }: FeatureDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=feature-drawer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/feature-drawer.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAGxE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,EAC5B,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,UAAkB,GACnB,EAAE,kBAAkB,2CAwJpB"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback } from 'react';
|
|
4
|
+
import { Loader2, Trash2, ExternalLink, GitCommitHorizontal } from 'lucide-react';
|
|
5
|
+
import { PrStatus } from '../../../../../../packages/core/src/domain/generated/output.js';
|
|
6
|
+
import { cn } from '../../../lib/utils.js';
|
|
7
|
+
import { OpenActionMenu } from '../../common/open-action-menu/index.js';
|
|
8
|
+
import { BaseDrawer } from '../../common/base-drawer/index.js';
|
|
9
|
+
import { DrawerTitle, DrawerDescription } from '../../ui/drawer.js';
|
|
10
|
+
import { Button } from '../../ui/button.js';
|
|
11
|
+
import { Badge } from '../../ui/badge.js';
|
|
12
|
+
import { Separator } from '../../ui/separator.js';
|
|
13
|
+
import { CometSpinner } from '../../ui/comet-spinner.js';
|
|
14
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '../../ui/alert-dialog.js';
|
|
15
|
+
import { CiStatusBadge } from '../../common/ci-status-badge/index.js';
|
|
16
|
+
import { featureNodeStateConfig, lifecycleDisplayLabels } from '../../common/feature-node/index.js';
|
|
17
|
+
import { useFeatureActions } from './use-feature-actions.js';
|
|
18
|
+
export function FeatureDrawer({ selectedNode, onClose, onDelete, isDeleting = false, }) {
|
|
19
|
+
const handleClose = useCallback(() => {
|
|
20
|
+
onClose();
|
|
21
|
+
}, [onClose]);
|
|
22
|
+
const actionsInput = selectedNode?.repositoryPath && selectedNode?.branch
|
|
23
|
+
? {
|
|
24
|
+
repositoryPath: selectedNode.repositoryPath,
|
|
25
|
+
branch: selectedNode.branch,
|
|
26
|
+
specPath: selectedNode.specPath,
|
|
27
|
+
}
|
|
28
|
+
: null;
|
|
29
|
+
const actions = useFeatureActions(actionsInput);
|
|
30
|
+
return (_jsx(BaseDrawer, { open: selectedNode !== null, onClose: handleClose, size: "md", modal: false, "data-testid": "feature-drawer", header: selectedNode ? (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-header", children: [_jsx(DrawerTitle, { children: selectedNode.name }), selectedNode.description ? (_jsx(DrawerDescription, { children: selectedNode.description })) : selectedNode.featureId ? (_jsx(DrawerDescription, { className: "sr-only", children: selectedNode.featureId })) : null] }), actionsInput ? (_jsxs("div", { className: "flex items-center gap-2 pt-2", children: [_jsx(OpenActionMenu, { actions: actions, repositoryPath: actionsInput.repositoryPath, showSpecs: !!actionsInput.specPath }), onDelete && selectedNode.featureId ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "bg-border mx-1 h-4 w-px" }), _jsxs(AlertDialog, { children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Delete feature", disabled: isDeleting, className: "text-muted-foreground hover:text-destructive", "data-testid": "feature-drawer-delete", children: isDeleting ? (_jsx(Loader2, { className: "size-4 animate-spin" })) : (_jsx(Trash2, { className: "size-4" })) }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Delete feature?" }), _jsxs(AlertDialogDescription, { children: ["This will permanently delete ", _jsx("strong", { children: selectedNode.name }), " (", selectedNode.featureId, "). This action cannot be undone.", selectedNode.state === 'running' ? (_jsx(_Fragment, { children: " This feature has a running agent that will be stopped." })) : null] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: isDeleting, children: "Cancel" }), _jsx(AlertDialogAction, { variant: "destructive", disabled: isDeleting, onClick: () => onDelete(selectedNode.featureId), children: isDeleting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Deleting\u2026"] })) : ('Delete') })] })] })] })] })) : null] })) : null] })) : undefined, deployTarget: selectedNode?.repositoryPath && selectedNode.branch
|
|
31
|
+
? {
|
|
32
|
+
targetId: selectedNode.featureId,
|
|
33
|
+
targetType: 'feature',
|
|
34
|
+
repositoryPath: selectedNode.repositoryPath,
|
|
35
|
+
branch: selectedNode.branch,
|
|
36
|
+
}
|
|
37
|
+
: undefined, children: selectedNode ? (_jsxs("div", { className: "flex-1 overflow-y-auto", children: [_jsxs("div", { "data-testid": "feature-drawer-status", className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: lifecycleDisplayLabels[selectedNode.lifecycle] }), _jsx(StateBadge, { data: selectedNode }), selectedNode.progress > 0 ? (_jsxs("div", { "data-testid": "feature-drawer-progress", className: "flex flex-col gap-1", children: [_jsxs("div", { className: "text-muted-foreground flex items-center justify-between text-xs", children: [_jsx("span", { children: "Progress" }), _jsxs("span", { children: [selectedNode.progress, "%"] })] }), _jsx("div", { className: "bg-muted h-2 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', featureNodeStateConfig[selectedNode.state].progressClass), style: { width: `${selectedNode.progress}%` } }) })] })) : null] }), selectedNode.pr ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(PrInfoSection, { pr: selectedNode.pr })] })) : null, _jsx(DetailsSection, { data: selectedNode })] })) : null }));
|
|
38
|
+
}
|
|
39
|
+
function StateBadge({ data }) {
|
|
40
|
+
const config = featureNodeStateConfig[data.state];
|
|
41
|
+
const Icon = config.icon;
|
|
42
|
+
return (_jsxs("div", { className: cn('flex items-center gap-2 rounded-full px-3 py-1.5 text-sm font-medium', config.badgeBgClass, config.badgeClass), children: [data.state === 'running' ? (_jsx(CometSpinner, { size: "sm", className: "shrink-0" })) : (_jsx(Icon, { className: "h-4 w-4 shrink-0" })), _jsx("span", { children: config.label })] }));
|
|
43
|
+
}
|
|
44
|
+
function DetailsSection({ data }) {
|
|
45
|
+
const hasAnyDetail = data.agentType ?? data.runtime ?? data.blockedBy ?? data.errorMessage;
|
|
46
|
+
if (!hasAnyDetail)
|
|
47
|
+
return null;
|
|
48
|
+
return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.agentType ? _jsx(DetailRow, { label: "Agent", value: data.agentType }) : null, data.runtime ? _jsx(DetailRow, { label: "Runtime", value: data.runtime }) : null, data.blockedBy ? _jsx(DetailRow, { label: "Blocked by", value: data.blockedBy }) : null, data.errorMessage ? _jsx(DetailRow, { label: "Error", value: data.errorMessage }) : null] })] }));
|
|
49
|
+
}
|
|
50
|
+
function DetailRow({ label, value }) {
|
|
51
|
+
return (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: label }), _jsx("span", { className: "text-sm", children: value })] }));
|
|
52
|
+
}
|
|
53
|
+
const prStatusStyles = {
|
|
54
|
+
[PrStatus.Open]: 'border-transparent bg-blue-50 text-blue-700 hover:bg-blue-50',
|
|
55
|
+
[PrStatus.Merged]: 'border-transparent bg-purple-50 text-purple-700 hover:bg-purple-50',
|
|
56
|
+
[PrStatus.Closed]: 'border-transparent bg-red-50 text-red-700 hover:bg-red-50',
|
|
57
|
+
};
|
|
58
|
+
function PrInfoSection({ pr }) {
|
|
59
|
+
return (_jsx("div", { "data-testid": "feature-drawer-pr", className: "border-border mx-4 rounded-lg border", children: _jsxs("div", { className: "space-y-3 px-4 py-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("a", { href: pr.url, target: "_blank", rel: "noopener noreferrer", className: "text-primary flex items-center gap-1.5 text-sm font-semibold underline underline-offset-2", children: ["PR #", pr.number, _jsx(ExternalLink, { className: "h-3.5 w-3.5" })] }), _jsx(Badge, { className: prStatusStyles[pr.status], children: pr.status })] }), pr.ciStatus ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "CI Status" }), _jsx(CiStatusBadge, { status: pr.ciStatus })] })) : null, pr.commitHash ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Commit" }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(GitCommitHorizontal, { className: "text-muted-foreground h-3.5 w-3.5" }), _jsx("code", { className: "bg-muted text-foreground rounded-md px-1.5 py-0.5 font-mono text-[11px]", children: pr.commitHash.slice(0, 7) })] })] })) : null] }) }));
|
|
60
|
+
}
|
package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { FeatureDrawer } from './feature-drawer.js';
|
|
3
|
+
declare const meta: Meta<typeof FeatureDrawer>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof FeatureDrawer>;
|
|
6
|
+
/** Feature drawer rendered inside a full-page context — starts open. Primary story. */
|
|
7
|
+
export declare const Default: Story;
|
|
8
|
+
export declare const Running: Story;
|
|
9
|
+
export declare const ActionRequired: Story;
|
|
10
|
+
export declare const Done: Story;
|
|
11
|
+
export declare const Blocked: Story;
|
|
12
|
+
/** Review lifecycle + running state — matches the look/feel of review pending-action drawers. */
|
|
13
|
+
export declare const ReviewInProgress: Story;
|
|
14
|
+
export declare const Error: Story;
|
|
15
|
+
/** Minimal data — only required fields, no optional details section. */
|
|
16
|
+
export declare const MinimalData: Story;
|
|
17
|
+
/** All optional fields populated — description, agent, runtime, blockedBy, error. */
|
|
18
|
+
export declare const AllFields: Story;
|
|
19
|
+
/** Cycle through all 5 states at a glance via buttons. */
|
|
20
|
+
export declare const AllStates: Story;
|
|
21
|
+
/** Cycle through all 6 lifecycle phases via buttons. */
|
|
22
|
+
export declare const AllLifecycles: Story;
|
|
23
|
+
/** No repositoryPath — action buttons are hidden. */
|
|
24
|
+
export declare const NoRepositoryPath: Story;
|
|
25
|
+
/**
|
|
26
|
+
* Action buttons visible — click "Open in IDE" or "Open in Shell" to see
|
|
27
|
+
* loading spinner (while API call is in flight) followed by error state
|
|
28
|
+
* (API routes are not available in Storybook).
|
|
29
|
+
*/
|
|
30
|
+
export declare const WithActionButtons: Story;
|
|
31
|
+
/** FeatureDrawer with a delete icon button in the header. */
|
|
32
|
+
export declare const WithDeleteButton: Story;
|
|
33
|
+
/** FeatureDrawer showing the delete button in loading/disabled state. */
|
|
34
|
+
export declare const DeletingState: Story;
|
|
35
|
+
/** FeatureDrawer with a running agent showing the running-agent warning in the AlertDialog. */
|
|
36
|
+
export declare const DeleteRunningAgent: Story;
|
|
37
|
+
/** Done feature with full PR card — number, status, CI, commit hash. */
|
|
38
|
+
export declare const DoneWithPr: Story;
|
|
39
|
+
/** Done feature with partial PR data — no CI status, no commit hash. */
|
|
40
|
+
export declare const DoneWithPartialPr: Story;
|
|
41
|
+
/** PR with Open status — blue badge. */
|
|
42
|
+
export declare const PrStatusOpen: Story;
|
|
43
|
+
/** PR with Merged status — purple badge. */
|
|
44
|
+
export declare const PrStatusMerged: Story;
|
|
45
|
+
/** Feature drawer rendered inside a full-page context — starts open. */
|
|
46
|
+
export declare const InDrawer: Story;
|
|
47
|
+
//# sourceMappingURL=feature-drawer.stories.d.ts.map
|
package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-drawer.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/feature-drawer.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKjD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,aAAa,CAOpC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,aAAa,CAAC,CAAC;AAiH5C,uFAAuF;AACvF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAC;AAEF,eAAO,MAAM,IAAI,EAAE,KAElB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAEnB,CAAC;AAMF,wEAAwE;AACxE,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,SAAS,EAAE,KAqBvB,CAAC;AAmEF,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC;AA6CF,wDAAwD;AACxD,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAMF,qDAAqD;AACrD,eAAO,MAAM,gBAAgB,EAAE,KAO9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,KAE/B,CAAC;AAoCF,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,kBAAkB,EAAE,KAEhC,CAAC;AA0BF,wEAAwE;AACxE,eAAO,MAAM,UAAU,EAAE,KAExB,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,EAAE,KAE/B,CAAC;AAEF,wCAAwC;AACxC,eAAO,MAAM,YAAY,EAAE,KAU1B,CAAC;AAEF,4CAA4C;AAC5C,eAAO,MAAM,cAAc,EAAE,KAE5B,CAAC;AAuBF,wEAAwE;AACxE,eAAO,MAAM,QAAQ,EAAE,KAEtB,CAAC"}
|