@shepai/cli 1.22.0 → 1.23.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/packages/core/src/infrastructure/services/file-dialog.service.d.ts +28 -0
- package/dist/packages/core/src/infrastructure/services/file-dialog.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/file-dialog.service.js +63 -0
- package/dist/packages/core/src/infrastructure/services/tool-installer/tool-installer.service.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/tool-installer/tool-installer.service.js +16 -2
- package/dist/packages/core/src/infrastructure/services/tool-installer/tools/cursor-cli.json +5 -5
- package/dist/packages/core/src/infrastructure/services/tool-installer/tools/cursor.json +5 -5
- package/dist/packages/core/src/infrastructure/services/tool-installer/tools/vscode.json +2 -2
- package/dist/packages/core/src/infrastructure/services/tool-installer/tools/windsurf.json +1 -1
- package/dist/src/presentation/web/app/api/dialog/pick-files/route.d.ts +11 -0
- package/dist/src/presentation/web/app/api/dialog/pick-files/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/dialog/pick-files/route.js +16 -0
- package/dist/src/presentation/web/app/derive-state.d.ts +12 -0
- package/dist/src/presentation/web/app/derive-state.d.ts.map +1 -0
- package/dist/src/presentation/web/app/derive-state.js +21 -0
- package/dist/src/presentation/web/app/page.d.ts.map +1 -1
- package/dist/src/presentation/web/app/page.js +23 -12
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +15 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +102 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +63 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.js +164 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts +2 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/index.js +1 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/pick-files.d.ts +7 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/pick-files.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/feature-create-drawer/pick-files.js +13 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.d.ts +7 -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 +28 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts +19 -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 +167 -0
- package/dist/src/presentation/web/components/common/feature-drawer/index.d.ts +2 -0
- package/dist/src/presentation/web/components/common/feature-drawer/index.d.ts.map +1 -0
- package/dist/src/presentation/web/components/common/feature-drawer/index.js +1 -0
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts +2 -5
- 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/feature-node/feature-node-state-config.js +4 -13
- package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node.js +7 -9
- package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts +0 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.js +4 -23
- package/dist/src/presentation/web/components/common/feature-node/index.d.ts +1 -3
- package/dist/src/presentation/web/components/common/feature-node/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/index.js +1 -3
- package/dist/src/presentation/web/components/common/index.d.ts +2 -0
- package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/index.js +2 -0
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +11 -3
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +4 -0
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +20 -4
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.d.ts +1 -1
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.d.ts.map +1 -1
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +5 -1
- package/dist/src/presentation/web/components/ui/button.d.ts +1 -1
- package/dist/src/presentation/web/components/ui/drawer.d.ts +17 -0
- package/dist/src/presentation/web/components/ui/drawer.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/drawer.js +45 -0
- package/dist/src/presentation/web/components/ui/drawer.stories.d.ts +17 -0
- package/dist/src/presentation/web/components/ui/drawer.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/drawer.stories.js +29 -0
- package/dist/src/presentation/web/components/ui/textarea.d.ts +4 -0
- package/dist/src/presentation/web/components/ui/textarea.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/textarea.js +6 -0
- package/dist/src/presentation/web/components/ui/textarea.stories.d.ts +10 -0
- package/dist/src/presentation/web/components/ui/textarea.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/textarea.stories.js +28 -0
- package/dist/src/presentation/web/lib/features.d.ts +30 -0
- package/dist/src/presentation/web/lib/features.d.ts.map +1 -0
- package/dist/src/presentation/web/lib/features.js +50 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +1 -0
- package/web/.next/build-manifest.json +5 -5
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/.tsbuildinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/routes-manifest.json +6 -0
- package/web/.next/server/app/_global-error/page/build-manifest.json +3 -3
- package/web/.next/server/app/_global-error/page.js +1 -1
- package/web/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/web/.next/server/app/_global-error/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/build-manifest.json +3 -3
- package/web/.next/server/app/_not-found/page.js +1 -1
- package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +2 -2
- package/web/.next/server/app/_not-found.rsc +9 -9
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +9 -9
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +5 -5
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/api/dialog/pick-files/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/dialog/pick-files/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/dialog/pick-files/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/dialog/pick-files/route.js +6 -0
- package/web/.next/server/app/api/dialog/pick-files/route.js.map +5 -0
- package/web/.next/server/app/api/dialog/pick-files/route.js.nft.json +1 -0
- package/web/.next/server/app/api/dialog/pick-files/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/api/dialog/pick-folder/route.js +2 -2
- package/web/.next/server/app/api/dialog/pick-folder/route.js.nft.json +1 -1
- package/web/.next/server/app/index.html +2 -2
- package/web/.next/server/app/index.rsc +4 -4
- package/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/index.segments/_index.segment.rsc +3 -3
- package/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/page/build-manifest.json +3 -3
- package/web/.next/server/app/page.js +3 -3
- package/web/.next/server/app/page.js.nft.json +1 -1
- package/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/build-manifest.json +3 -3
- package/web/.next/server/app/version/page.js +1 -1
- 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/app/version.html +2 -2
- package/web/.next/server/app/version.rsc +4 -4
- package/web/.next/server/app/version.segments/_full.segment.rsc +4 -4
- package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_index.segment.rsc +3 -3
- package/web/.next/server/app/version.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
- package/web/.next/server/app-paths-manifest.json +1 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_dialog_pick-files_route_actions_f9868286.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__00833626._.js +21 -0
- package/web/.next/server/chunks/[root-of-the-server]__00833626._.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__22cabd43._.js +4 -0
- package/web/.next/server/chunks/[root-of-the-server]__22cabd43._.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__95785ac7._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__95785ac7._.js.map +1 -0
- package/web/.next/server/chunks/ssr/403f9_next_dist_esm_build_templates_app-page_dbf21e04.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__1eade43f._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__1eade43f._.js.map +1 -0
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__6b3c1f2b._.js → [root-of-the-server]__685eaa45._.js} +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__71515c64._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__757c7912._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7c64f85c._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7c64f85c._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8507ac68._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8507ac68._.js.map +1 -0
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__e55e9c7f._.js → [root-of-the-server]__dbd220d2._.js} +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fa87595c._.js +1 -1
- package/web/.next/server/chunks/ssr/_09afa42a._.js +1 -1
- package/web/.next/server/chunks/ssr/_09afa42a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_1f9347c8._.js +7 -0
- package/web/.next/server/chunks/ssr/_1f9347c8._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_884ddade._.js +1 -1
- package/web/.next/server/chunks/ssr/_937b37e2._.js +3 -0
- package/web/.next/server/chunks/ssr/_937b37e2._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_a0c04017._.js +1 -5
- package/web/.next/server/chunks/ssr/_a0c04017._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_c5d377cc._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_version_version-page-client_tsx_fe9db753._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_version_version-page-client_tsx_fe9db753._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_ui_badge_tsx_964c2a3b._.js +3 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_components_ui_badge_tsx_964c2a3b._.js.map +1 -0
- package/web/.next/server/middleware-build-manifest.js +3 -3
- package/web/.next/server/pages/404.html +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 +1 -1
- package/web/.next/static/chunks/4431cc2d3628b6f6.js +1 -0
- package/web/.next/static/chunks/49488cbe99543598.css +1 -0
- package/web/.next/static/chunks/70993ebedee7eab0.js +2 -0
- package/web/.next/static/chunks/8bc34062bc0f4ddc.js +1 -0
- package/web/.next/static/chunks/c95b4cf170b7f301.js +1 -0
- package/web/.next/static/chunks/df7d29673e0ac11d.js +1 -0
- package/web/.next/static/chunks/e78d1b265b8110c3.js +1 -0
- package/web/.next/static/chunks/f5099c5888ef9a74.js +5 -0
- package/web/.next/static/chunks/{b29c0572633eb9e4.js → f6b5484377f4e78e.js} +2 -2
- package/web/.next/static/chunks/{turbopack-e0a0178ee911c212.js → turbopack-a5a88a85e7ac8e8d.js} +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/.next/types/link.d.ts +1 -0
- package/web/.next/types/routes.d.ts +2 -1
- package/web/.next/types/validator.ts +9 -0
- package/web/next.config.mjs +1 -1
- package/web/package.json +3 -3
- package/web/.next/server/chunks/[externals]_next_dist_a6d89067._.js +0 -3
- package/web/.next/server/chunks/[root-of-the-server]__ebcd3642._.js +0 -21
- package/web/.next/server/chunks/[root-of-the-server]__ebcd3642._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2127336f._.js +0 -3
- package/web/.next/server/chunks/ssr/[root-of-the-server]__44c4e191._.js +0 -3
- package/web/.next/server/chunks/ssr/[root-of-the-server]__44c4e191._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__492e833d._.js +0 -3
- package/web/.next/server/chunks/ssr/[root-of-the-server]__492e833d._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__80e5bed2._.js +0 -3
- package/web/.next/server/chunks/ssr/_3d152196._.js +0 -3
- package/web/.next/server/chunks/ssr/_3d152196._.js.map +0 -1
- package/web/.next/server/chunks/ssr/node_modules__pnpm_b11d5ad4._.js +0 -3
- package/web/.next/server/chunks/ssr/node_modules__pnpm_b11d5ad4._.js.map +0 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +0 -3
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +0 -1
- package/web/.next/static/chunks/3701b9d5749e6143.css +0 -1
- package/web/.next/static/chunks/a8030d19b4d73c95.js +0 -1
- package/web/.next/static/chunks/b0d13220be9ac29c.js +0 -1
- package/web/.next/static/chunks/be34161188b4e0a0.js +0 -2
- package/web/.next/static/chunks/c87445f8cbe36c74.js +0 -1
- package/web/.next/static/chunks/f220dad9d38bff95.js +0 -5
- package/web/.next/static/chunks/f74cb59a5a6c94f1.js +0 -1
- /package/web/.next/server/chunks/{[externals]_next_dist_a6d89067._.js.map → 744ca_web__next-internal_server_app_api_dialog_pick-files_route_actions_f9868286.js.map} +0 -0
- /package/web/.next/server/chunks/ssr/{[root-of-the-server]__6b3c1f2b._.js.map → [root-of-the-server]__685eaa45._.js.map} +0 -0
- /package/web/.next/server/chunks/ssr/{[root-of-the-server]__80e5bed2._.js.map → [root-of-the-server]__71515c64._.js.map} +0 -0
- /package/web/.next/server/chunks/ssr/{[root-of-the-server]__2127336f._.js.map → [root-of-the-server]__757c7912._.js.map} +0 -0
- /package/web/.next/server/chunks/ssr/{[root-of-the-server]__e55e9c7f._.js.map → [root-of-the-server]__dbd220d2._.js.map} +0 -0
- /package/web/.next/static/{50v5TcK4kMIcF3xxCPBNl → WqMlhB4Xv4T13f0oXQ5uG}/_buildManifest.js +0 -0
- /package/web/.next/static/{50v5TcK4kMIcF3xxCPBNl → WqMlhB4Xv4T13f0oXQ5uG}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{50v5TcK4kMIcF3xxCPBNl → WqMlhB4Xv4T13f0oXQ5uG}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { within, userEvent, fn } from '@storybook/test';
|
|
4
|
+
import { FeatureCreateDrawer } from './feature-create-drawer.js';
|
|
5
|
+
import { Button } from '../../ui/button.js';
|
|
6
|
+
/* ---------------------------------------------------------------------------
|
|
7
|
+
* Meta — component-level docs + interactive controls
|
|
8
|
+
* ------------------------------------------------------------------------- */
|
|
9
|
+
/**
|
|
10
|
+
* **FeatureCreateDrawer** is a right-side sliding drawer containing a form to
|
|
11
|
+
* create a new feature in the Control Center.
|
|
12
|
+
*
|
|
13
|
+
* ### Form sections
|
|
14
|
+
* - **Feature Name** (required) — text input, submit is disabled when empty
|
|
15
|
+
* - **Description** (optional) — multi-line textarea
|
|
16
|
+
* - **Attachments** (optional) — native OS file picker with extension-based icon system:
|
|
17
|
+
* image → blue, PDF → red, code/text → emerald, generic → gray.
|
|
18
|
+
* Each attachment card shows the **full absolute file path**.
|
|
19
|
+
*
|
|
20
|
+
* ### Props
|
|
21
|
+
* | Prop | Type | Description |
|
|
22
|
+
* |------|------|-------------|
|
|
23
|
+
* | `open` | `boolean` | Controls drawer visibility |
|
|
24
|
+
* | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
|
|
25
|
+
* | `onSubmit` | `(data: CreateFeatureFormData) => void` | Called with `{ name, description, attachments }` |
|
|
26
|
+
* | `isSubmitting` | `boolean` | Disables all fields and shows "Creating..." on submit button |
|
|
27
|
+
*
|
|
28
|
+
* ### Behavior
|
|
29
|
+
* - Form resets (name, description, attachments cleared) when the drawer closes
|
|
30
|
+
* - Submit button is disabled when name is empty OR `isSubmitting` is true
|
|
31
|
+
* - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
|
|
32
|
+
* - Fixed width: 384px (`w-96`)
|
|
33
|
+
* - Attachments use native OS file picker via `pickFiles()` — returns `FileAttachment[]`
|
|
34
|
+
* with full absolute paths, filenames, and sizes
|
|
35
|
+
*/
|
|
36
|
+
const meta = {
|
|
37
|
+
title: 'Composed/FeatureCreateDrawer',
|
|
38
|
+
component: FeatureCreateDrawer,
|
|
39
|
+
tags: ['autodocs'],
|
|
40
|
+
parameters: {
|
|
41
|
+
layout: 'fullscreen',
|
|
42
|
+
},
|
|
43
|
+
argTypes: {
|
|
44
|
+
open: {
|
|
45
|
+
control: 'boolean',
|
|
46
|
+
description: 'Whether the drawer is visible.',
|
|
47
|
+
},
|
|
48
|
+
onClose: {
|
|
49
|
+
description: 'Callback fired when the drawer is dismissed (close button, cancel, or backdrop click).',
|
|
50
|
+
},
|
|
51
|
+
onSubmit: {
|
|
52
|
+
description: 'Callback fired with `CreateFeatureFormData` when the form is submitted. Receives `{ name, description, attachments }`.',
|
|
53
|
+
},
|
|
54
|
+
isSubmitting: {
|
|
55
|
+
control: 'boolean',
|
|
56
|
+
description: 'When true, all form fields and buttons are disabled, and the submit button shows "Creating...".',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
export default meta;
|
|
61
|
+
/* ---------------------------------------------------------------------------
|
|
62
|
+
* Shared action loggers
|
|
63
|
+
* ------------------------------------------------------------------------- */
|
|
64
|
+
const logSubmit = fn().mockName('onSubmit');
|
|
65
|
+
const logClose = fn().mockName('onClose');
|
|
66
|
+
/* ---------------------------------------------------------------------------
|
|
67
|
+
* Trigger wrapper — every story uses this so nothing auto-opens on Docs page
|
|
68
|
+
* ------------------------------------------------------------------------- */
|
|
69
|
+
/** Starts closed — click button to open. Actions are logged. */
|
|
70
|
+
function CreateDrawerTrigger({ isSubmitting = false, label = 'Open Create Feature', }) {
|
|
71
|
+
const [open, setOpen] = useState(false);
|
|
72
|
+
return (_jsxs("div", { className: "flex h-screen items-start p-4", children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(true), children: label }), _jsx(FeatureCreateDrawer, { open: open, onClose: () => {
|
|
73
|
+
setOpen(false);
|
|
74
|
+
logClose();
|
|
75
|
+
}, onSubmit: (data) => {
|
|
76
|
+
logSubmit(data);
|
|
77
|
+
setOpen(false);
|
|
78
|
+
}, isSubmitting: isSubmitting })] }));
|
|
79
|
+
}
|
|
80
|
+
/* ---------------------------------------------------------------------------
|
|
81
|
+
* Per-state stories
|
|
82
|
+
* ------------------------------------------------------------------------- */
|
|
83
|
+
/** Default empty form — click the trigger button to open the create feature drawer. */
|
|
84
|
+
export const Default = {
|
|
85
|
+
render: () => _jsx(CreateDrawerTrigger, {}),
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Pre-filled form — click to open, then name and description are typed in.
|
|
89
|
+
* Demonstrates the form in a "ready to submit" state.
|
|
90
|
+
*/
|
|
91
|
+
export const PreFilled = {
|
|
92
|
+
render: () => _jsx(CreateDrawerTrigger, { label: "Open Pre-Filled" }),
|
|
93
|
+
play: async ({ canvasElement }) => {
|
|
94
|
+
const canvas = within(canvasElement);
|
|
95
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Open Pre-Filled' }));
|
|
96
|
+
// Drawer renders in a portal — query from document body
|
|
97
|
+
const body = within(canvasElement.ownerDocument.body);
|
|
98
|
+
const nameInput = await body.findByPlaceholderText('e.g. GitHub OAuth Login');
|
|
99
|
+
const descInput = body.getByPlaceholderText('Describe what this feature does...');
|
|
100
|
+
await userEvent.type(nameInput, 'GitHub OAuth Login');
|
|
101
|
+
await userEvent.type(descInput, 'Implement OAuth2 authentication with GitHub as the identity provider. Includes login, callback handling, and token refresh.');
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Submitting/loading state — all form fields are disabled, submit button shows "Creating...".
|
|
106
|
+
* Click the trigger to open and observe the disabled form.
|
|
107
|
+
*/
|
|
108
|
+
export const Submitting = {
|
|
109
|
+
render: () => _jsx(CreateDrawerTrigger, { isSubmitting: true, label: "Open (Submitting)" }),
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Validation state — open the drawer with an empty name field.
|
|
113
|
+
* The "+ Create Feature" button is disabled because name validation fails.
|
|
114
|
+
*/
|
|
115
|
+
export const ValidationDisabled = {
|
|
116
|
+
render: () => _jsx(CreateDrawerTrigger, { label: "Open Validation Demo" }),
|
|
117
|
+
play: async ({ canvasElement }) => {
|
|
118
|
+
const canvas = within(canvasElement);
|
|
119
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Open Validation Demo' }));
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
/** Click to open the drawer for quick visual inspection. */
|
|
123
|
+
export const PreOpened = {
|
|
124
|
+
render: () => _jsx(CreateDrawerTrigger, { label: "Open Drawer" }),
|
|
125
|
+
play: async ({ canvasElement }) => {
|
|
126
|
+
const canvas = within(canvasElement);
|
|
127
|
+
await userEvent.click(canvas.getByRole('button', { name: 'Open Drawer' }));
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
/* ---------------------------------------------------------------------------
|
|
131
|
+
* Interactive story — full paths shown in submitted data panel
|
|
132
|
+
* ------------------------------------------------------------------------- */
|
|
133
|
+
/**
|
|
134
|
+
* Fully interactive story — open the drawer, fill the form, and click
|
|
135
|
+
* "Add Files" to attach files via the native OS file picker.
|
|
136
|
+
*
|
|
137
|
+
* **Submitted data** is displayed in a styled panel showing `FileAttachment[]`
|
|
138
|
+
* with **full absolute file paths**, filenames, and sizes.
|
|
139
|
+
*
|
|
140
|
+
* In Storybook, the native picker won't work (no backend), but submitted data
|
|
141
|
+
* would show the paths if files were attached programmatically.
|
|
142
|
+
*/
|
|
143
|
+
export const Interactive = {
|
|
144
|
+
render: function InteractiveRender() {
|
|
145
|
+
const [open, setOpen] = useState(false);
|
|
146
|
+
const [submitted, setSubmitted] = useState(null);
|
|
147
|
+
return (_jsxs("div", { className: "flex h-screen items-start gap-4 p-4", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(Button, { variant: "outline", onClick: () => setOpen(true), children: "Open Create Feature" }), submitted ? (_jsxs("div", { className: "bg-muted/50 max-w-sm rounded-md border p-3", children: [_jsx("span", { className: "text-muted-foreground mb-1 text-xs font-semibold tracking-wider uppercase", children: "Last submitted" }), _jsx("pre", { className: "mt-1 text-xs whitespace-pre-wrap", children: JSON.stringify({
|
|
148
|
+
name: submitted.name,
|
|
149
|
+
description: submitted.description,
|
|
150
|
+
attachments: submitted.attachments.map((f) => ({
|
|
151
|
+
path: f.path,
|
|
152
|
+
name: f.name,
|
|
153
|
+
size: f.size,
|
|
154
|
+
})),
|
|
155
|
+
}, null, 2) })] })) : null] }), _jsx(FeatureCreateDrawer, { open: open, onClose: () => {
|
|
156
|
+
setOpen(false);
|
|
157
|
+
logClose();
|
|
158
|
+
}, onSubmit: (data) => {
|
|
159
|
+
logSubmit(data);
|
|
160
|
+
setSubmitted(data);
|
|
161
|
+
setOpen(false);
|
|
162
|
+
} })] }));
|
|
163
|
+
},
|
|
164
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,cAAc,GACpB,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FeatureCreateDrawer, } from './feature-create-drawer.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FileAttachment } from '../../../../../../packages/core/src/infrastructure/services/file-dialog.service.js';
|
|
2
|
+
/**
|
|
3
|
+
* Calls the backend API to open a native OS file picker dialog.
|
|
4
|
+
* Returns the selected files with metadata, or null if the user cancelled.
|
|
5
|
+
*/
|
|
6
|
+
export declare function pickFiles(): Promise<FileAttachment[] | null>;
|
|
7
|
+
//# sourceMappingURL=pick-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pick-files.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/pick-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAE/F;;;GAGG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,CAUlE"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calls the backend API to open a native OS file picker dialog.
|
|
3
|
+
* Returns the selected files with metadata, or null if the user cancelled.
|
|
4
|
+
*/
|
|
5
|
+
export async function pickFiles() {
|
|
6
|
+
const response = await fetch('/api/dialog/pick-files', { method: 'POST' });
|
|
7
|
+
if (!response.ok) {
|
|
8
|
+
const body = await response.json().catch(() => null);
|
|
9
|
+
throw new Error(body?.error ?? 'Failed to open file dialog');
|
|
10
|
+
}
|
|
11
|
+
const body = await response.json();
|
|
12
|
+
return body.files;
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FeatureNodeData } from '../../common/feature-node/index.js';
|
|
2
|
+
export interface FeatureDrawerProps {
|
|
3
|
+
selectedNode: FeatureNodeData | null;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function FeatureDrawer({ selectedNode, onClose }: FeatureDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
//# 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":"AAcA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,kBAAkB,2CAmE1E"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { XIcon } from 'lucide-react';
|
|
4
|
+
import { cn } from '../../../lib/utils.js';
|
|
5
|
+
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, } from '../../ui/drawer.js';
|
|
6
|
+
import { Separator } from '../../ui/separator.js';
|
|
7
|
+
import { CometSpinner } from '../../ui/comet-spinner.js';
|
|
8
|
+
import { featureNodeStateConfig, lifecycleDisplayLabels } from '../../common/feature-node/index.js';
|
|
9
|
+
export function FeatureDrawer({ selectedNode, onClose }) {
|
|
10
|
+
return (_jsx(Drawer, { direction: "right", modal: false, open: selectedNode !== null, onOpenChange: (open) => {
|
|
11
|
+
if (!open)
|
|
12
|
+
onClose();
|
|
13
|
+
}, children: _jsx(DrawerContent, { direction: "right", className: "w-96", showCloseButton: false, children: selectedNode ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", "aria-label": "Close", onClick: onClose, className: "ring-offset-background focus:ring-ring absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden", children: _jsx(XIcon, { className: "size-4" }) }), _jsxs(DrawerHeader, { "data-testid": "feature-drawer-header", children: [_jsx(DrawerTitle, { children: selectedNode.name }), _jsx(DrawerDescription, { children: selectedNode.featureId })] }), _jsx(Separator, {}), _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] }), _jsx(Separator, {}), _jsx(DetailsSection, { data: selectedNode })] })) : null }) }));
|
|
14
|
+
}
|
|
15
|
+
function StateBadge({ data }) {
|
|
16
|
+
const config = featureNodeStateConfig[data.state];
|
|
17
|
+
const Icon = config.icon;
|
|
18
|
+
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 })] }));
|
|
19
|
+
}
|
|
20
|
+
function DetailsSection({ data }) {
|
|
21
|
+
const hasAnyDetail = data.description ?? data.agentName ?? data.runtime ?? data.blockedBy ?? data.errorMessage;
|
|
22
|
+
if (!hasAnyDetail)
|
|
23
|
+
return null;
|
|
24
|
+
return (_jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.description ? _jsx(DetailRow, { label: "Description", value: data.description }) : null, data.agentName ? _jsx(DetailRow, { label: "Agent", value: data.agentName }) : 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] }));
|
|
25
|
+
}
|
|
26
|
+
function DetailRow({ label, value }) {
|
|
27
|
+
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 })] }));
|
|
28
|
+
}
|
package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
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
|
+
export declare const Running: Story;
|
|
7
|
+
export declare const ActionRequired: Story;
|
|
8
|
+
export declare const Done: Story;
|
|
9
|
+
export declare const Blocked: Story;
|
|
10
|
+
export declare const Error: Story;
|
|
11
|
+
/** Minimal data — only required fields, no optional details section. */
|
|
12
|
+
export declare const MinimalData: Story;
|
|
13
|
+
/** All optional fields populated — description, agent, runtime, blockedBy, error. */
|
|
14
|
+
export declare const AllFields: Story;
|
|
15
|
+
/** Cycle through all 5 states at a glance via buttons. */
|
|
16
|
+
export declare const AllStates: Story;
|
|
17
|
+
/** Cycle through all 6 lifecycle phases via buttons. */
|
|
18
|
+
export declare const AllLifecycles: Story;
|
|
19
|
+
//# 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;AACvD,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;AAoF5C,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,eAAO,MAAM,KAAK,EAAE,KAEnB,CAAC;AAMF,wEAAwE;AACxE,eAAO,MAAM,WAAW,EAAE,KAEzB,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,SAAS,EAAE,KAmBvB,CAAC;AAuDF,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC;AA2CF,wDAAwD;AACxD,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { FeatureDrawer } from './feature-drawer.js';
|
|
4
|
+
import { Button } from '../../ui/button.js';
|
|
5
|
+
const meta = {
|
|
6
|
+
title: 'Composed/FeatureDrawer',
|
|
7
|
+
component: FeatureDrawer,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'fullscreen',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
export default meta;
|
|
14
|
+
/* ---------------------------------------------------------------------------
|
|
15
|
+
* Data fixtures
|
|
16
|
+
* ------------------------------------------------------------------------- */
|
|
17
|
+
const baseData = {
|
|
18
|
+
name: 'Auth Module',
|
|
19
|
+
featureId: '#f1',
|
|
20
|
+
lifecycle: 'implementation',
|
|
21
|
+
state: 'running',
|
|
22
|
+
progress: 45,
|
|
23
|
+
};
|
|
24
|
+
const runningData = {
|
|
25
|
+
name: 'Auth Module',
|
|
26
|
+
description: 'Implement OAuth2 authentication flow',
|
|
27
|
+
featureId: '#f1',
|
|
28
|
+
lifecycle: 'implementation',
|
|
29
|
+
state: 'running',
|
|
30
|
+
progress: 45,
|
|
31
|
+
agentName: 'Planner',
|
|
32
|
+
};
|
|
33
|
+
const actionRequiredData = {
|
|
34
|
+
name: 'API Rate Limiting',
|
|
35
|
+
description: 'Implement sliding window rate limiting for public endpoints',
|
|
36
|
+
featureId: '#bi1',
|
|
37
|
+
lifecycle: 'requirements',
|
|
38
|
+
state: 'action-required',
|
|
39
|
+
progress: 22,
|
|
40
|
+
};
|
|
41
|
+
const doneData = {
|
|
42
|
+
name: 'Payment Gateway',
|
|
43
|
+
description: 'Stripe integration for subscriptions',
|
|
44
|
+
featureId: '#f3',
|
|
45
|
+
lifecycle: 'deploy',
|
|
46
|
+
state: 'done',
|
|
47
|
+
progress: 100,
|
|
48
|
+
runtime: '1h 42m',
|
|
49
|
+
};
|
|
50
|
+
const blockedData = {
|
|
51
|
+
name: 'Search Index',
|
|
52
|
+
description: 'Elasticsearch full-text search setup',
|
|
53
|
+
featureId: '#f4',
|
|
54
|
+
lifecycle: 'implementation',
|
|
55
|
+
state: 'blocked',
|
|
56
|
+
progress: 20,
|
|
57
|
+
blockedBy: 'Auth Module',
|
|
58
|
+
};
|
|
59
|
+
const errorData = {
|
|
60
|
+
name: 'Email Service',
|
|
61
|
+
description: 'Transactional email with SendGrid',
|
|
62
|
+
featureId: '#f5',
|
|
63
|
+
lifecycle: 'review',
|
|
64
|
+
state: 'error',
|
|
65
|
+
progress: 30,
|
|
66
|
+
errorMessage: 'Build failed: type mismatch',
|
|
67
|
+
};
|
|
68
|
+
/* ---------------------------------------------------------------------------
|
|
69
|
+
* Trigger wrapper — starts closed, click to open
|
|
70
|
+
* ------------------------------------------------------------------------- */
|
|
71
|
+
function DrawerTrigger({ data, label }) {
|
|
72
|
+
const [selected, setSelected] = useState(null);
|
|
73
|
+
return (_jsxs("div", { className: "flex h-screen items-start p-4", children: [_jsx(Button, { variant: "outline", onClick: () => setSelected(data), children: label }), _jsx(FeatureDrawer, { selectedNode: selected, onClose: () => setSelected(null) })] }));
|
|
74
|
+
}
|
|
75
|
+
/* ---------------------------------------------------------------------------
|
|
76
|
+
* Per-state stories
|
|
77
|
+
* ------------------------------------------------------------------------- */
|
|
78
|
+
export const Running = {
|
|
79
|
+
render: () => _jsx(DrawerTrigger, { data: runningData, label: "Open Running" }),
|
|
80
|
+
};
|
|
81
|
+
export const ActionRequired = {
|
|
82
|
+
render: () => _jsx(DrawerTrigger, { data: actionRequiredData, label: "Open Action Required" }),
|
|
83
|
+
};
|
|
84
|
+
export const Done = {
|
|
85
|
+
render: () => _jsx(DrawerTrigger, { data: doneData, label: "Open Done" }),
|
|
86
|
+
};
|
|
87
|
+
export const Blocked = {
|
|
88
|
+
render: () => _jsx(DrawerTrigger, { data: blockedData, label: "Open Blocked" }),
|
|
89
|
+
};
|
|
90
|
+
export const Error = {
|
|
91
|
+
render: () => _jsx(DrawerTrigger, { data: errorData, label: "Open Error" }),
|
|
92
|
+
};
|
|
93
|
+
/* ---------------------------------------------------------------------------
|
|
94
|
+
* Edge-case stories
|
|
95
|
+
* ------------------------------------------------------------------------- */
|
|
96
|
+
/** Minimal data — only required fields, no optional details section. */
|
|
97
|
+
export const MinimalData = {
|
|
98
|
+
render: () => _jsx(DrawerTrigger, { data: { ...baseData, progress: 0 }, label: "Open Minimal" }),
|
|
99
|
+
};
|
|
100
|
+
/** All optional fields populated — description, agent, runtime, blockedBy, error. */
|
|
101
|
+
export const AllFields = {
|
|
102
|
+
render: () => (_jsx(DrawerTrigger, { data: {
|
|
103
|
+
name: 'Enterprise Authentication Module',
|
|
104
|
+
description: 'Implement a comprehensive OAuth2 authentication flow with support for multiple identity providers including Google, GitHub, and custom SAML-based enterprise SSO.',
|
|
105
|
+
featureId: '#f99',
|
|
106
|
+
lifecycle: 'implementation',
|
|
107
|
+
state: 'running',
|
|
108
|
+
progress: 68,
|
|
109
|
+
agentName: 'Implementer',
|
|
110
|
+
runtime: '3h 15m',
|
|
111
|
+
blockedBy: 'Database Migration',
|
|
112
|
+
errorMessage: 'Token refresh failed: invalid_grant',
|
|
113
|
+
}, label: "Open All Fields" })),
|
|
114
|
+
};
|
|
115
|
+
/* ---------------------------------------------------------------------------
|
|
116
|
+
* Matrix stories — interactive state/lifecycle switcher
|
|
117
|
+
* ------------------------------------------------------------------------- */
|
|
118
|
+
const stateFixtures = {
|
|
119
|
+
running: runningData,
|
|
120
|
+
'action-required': actionRequiredData,
|
|
121
|
+
done: doneData,
|
|
122
|
+
blocked: blockedData,
|
|
123
|
+
error: errorData,
|
|
124
|
+
};
|
|
125
|
+
const allLifecycles = [
|
|
126
|
+
'requirements',
|
|
127
|
+
'research',
|
|
128
|
+
'implementation',
|
|
129
|
+
'review',
|
|
130
|
+
'deploy',
|
|
131
|
+
'maintain',
|
|
132
|
+
];
|
|
133
|
+
function AllStatesRender() {
|
|
134
|
+
const [state, setState] = useState('running');
|
|
135
|
+
const [selected, setSelected] = useState(null);
|
|
136
|
+
const _data = stateFixtures[state];
|
|
137
|
+
return (_jsxs("div", { className: "flex h-screen", children: [_jsxs("div", { className: "flex flex-col gap-2 p-4", children: [_jsx("span", { className: "text-muted-foreground mb-2 text-xs font-semibold tracking-wider uppercase", children: "Select state" }), Object.keys(stateFixtures).map((s) => (_jsx("button", { type: "button", onClick: () => {
|
|
138
|
+
setState(s);
|
|
139
|
+
setSelected(stateFixtures[s]);
|
|
140
|
+
}, className: `rounded-md px-3 py-1.5 text-left text-sm ${s === state ? 'bg-primary text-primary-foreground' : 'bg-muted hover:bg-muted/80'}`, children: s }, s)))] }), _jsx(FeatureDrawer, { selectedNode: selected, onClose: () => setSelected(null) })] }));
|
|
141
|
+
}
|
|
142
|
+
/** Cycle through all 5 states at a glance via buttons. */
|
|
143
|
+
export const AllStates = {
|
|
144
|
+
render: () => _jsx(AllStatesRender, {}),
|
|
145
|
+
};
|
|
146
|
+
function AllLifecyclesRender() {
|
|
147
|
+
const [lifecycle, setLifecycle] = useState('requirements');
|
|
148
|
+
const [selected, setSelected] = useState(null);
|
|
149
|
+
return (_jsxs("div", { className: "flex h-screen", children: [_jsxs("div", { className: "flex flex-col gap-2 p-4", children: [_jsx("span", { className: "text-muted-foreground mb-2 text-xs font-semibold tracking-wider uppercase", children: "Select phase" }), allLifecycles.map((phase) => (_jsx("button", { type: "button", onClick: () => {
|
|
150
|
+
setLifecycle(phase);
|
|
151
|
+
setSelected({
|
|
152
|
+
name: 'Feature Name',
|
|
153
|
+
description: `Currently in ${phase} phase`,
|
|
154
|
+
featureId: '#f1',
|
|
155
|
+
lifecycle: phase,
|
|
156
|
+
state: 'running',
|
|
157
|
+
progress: 50,
|
|
158
|
+
agentName: 'Agent',
|
|
159
|
+
});
|
|
160
|
+
}, className: `rounded-md px-3 py-1.5 text-left text-sm ${phase === lifecycle
|
|
161
|
+
? 'bg-primary text-primary-foreground'
|
|
162
|
+
: 'bg-muted hover:bg-muted/80'}`, children: phase }, phase)))] }), _jsx(FeatureDrawer, { selectedNode: selected, onClose: () => setSelected(null) })] }));
|
|
163
|
+
}
|
|
164
|
+
/** Cycle through all 6 lifecycle phases via buttons. */
|
|
165
|
+
export const AllLifecycles = {
|
|
166
|
+
render: () => _jsx(AllLifecyclesRender, {}),
|
|
167
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FeatureDrawer } from './feature-drawer.js';
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { type LucideIcon } from 'lucide-react';
|
|
2
2
|
import type { Node } from '@xyflow/react';
|
|
3
|
-
import type { AgentTypeValue } from './agent-type-icons.js';
|
|
4
3
|
export type FeatureNodeState = 'running' | 'action-required' | 'done' | 'blocked' | 'error';
|
|
5
4
|
export type FeatureLifecyclePhase = 'requirements' | 'research' | 'implementation' | 'review' | 'deploy' | 'maintain';
|
|
6
5
|
/** Human-readable display labels for lifecycle phases. */
|
|
7
6
|
export declare const lifecycleDisplayLabels: Record<FeatureLifecyclePhase, string>;
|
|
8
|
-
/** Present-participle verbs for the running badge, keyed by lifecycle phase. */
|
|
9
|
-
export declare const lifecycleRunningVerbs: Record<FeatureLifecyclePhase, string>;
|
|
10
7
|
export interface FeatureNodeData {
|
|
11
8
|
[key: string]: unknown;
|
|
12
9
|
name: string;
|
|
@@ -21,8 +18,8 @@ export interface FeatureNodeData {
|
|
|
21
18
|
blockedBy?: string;
|
|
22
19
|
/** Short error message for error state */
|
|
23
20
|
errorMessage?: string;
|
|
24
|
-
/** Agent
|
|
25
|
-
|
|
21
|
+
/** Agent name shown in the running badge (e.g. "Planner") */
|
|
22
|
+
agentName?: string;
|
|
26
23
|
onAction?: () => void;
|
|
27
24
|
onSettings?: () => void;
|
|
28
25
|
showHandles?: boolean;
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,iBAAiB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5F,MAAM,MAAM,qBAAqB,GAC7B,cAAc,GACd,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,QAAQ,GACR,UAAU,CAAC;AAEf,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOxE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,qBAAqB,CAAC;IACjC,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAmDnF,CAAC"}
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Loader2,
|
|
1
|
+
import { Loader2, CircleAlert, CircleCheck, Ban, CircleX } from 'lucide-react';
|
|
2
2
|
/** Human-readable display labels for lifecycle phases. */
|
|
3
3
|
export const lifecycleDisplayLabels = {
|
|
4
4
|
requirements: 'REQUIREMENTS',
|
|
@@ -8,15 +8,6 @@ export const lifecycleDisplayLabels = {
|
|
|
8
8
|
deploy: 'DEPLOY & QA',
|
|
9
9
|
maintain: 'COMPLETED',
|
|
10
10
|
};
|
|
11
|
-
/** Present-participle verbs for the running badge, keyed by lifecycle phase. */
|
|
12
|
-
export const lifecycleRunningVerbs = {
|
|
13
|
-
requirements: 'Analyzing',
|
|
14
|
-
research: 'Researching',
|
|
15
|
-
implementation: 'Implementing',
|
|
16
|
-
review: 'Reviewing',
|
|
17
|
-
deploy: 'Deploying',
|
|
18
|
-
maintain: 'Maintaining',
|
|
19
|
-
};
|
|
20
11
|
export const featureNodeStateConfig = {
|
|
21
12
|
running: {
|
|
22
13
|
icon: Loader2,
|
|
@@ -29,12 +20,12 @@ export const featureNodeStateConfig = {
|
|
|
29
20
|
showProgressBar: false,
|
|
30
21
|
},
|
|
31
22
|
'action-required': {
|
|
32
|
-
icon:
|
|
23
|
+
icon: CircleAlert,
|
|
33
24
|
borderClass: 'border-l-amber-500',
|
|
34
25
|
labelClass: 'text-amber-500',
|
|
35
26
|
progressClass: 'bg-amber-500',
|
|
36
|
-
badgeClass: 'text-amber-
|
|
37
|
-
badgeBgClass: 'bg-amber-50
|
|
27
|
+
badgeClass: 'text-amber-700',
|
|
28
|
+
badgeBgClass: 'bg-amber-50',
|
|
38
29
|
label: 'User action required',
|
|
39
30
|
showProgressBar: false,
|
|
40
31
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAoBnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CA0IA"}
|
|
@@ -3,17 +3,15 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { Handle, Position } from '@xyflow/react';
|
|
4
4
|
import { Settings, Plus } from 'lucide-react';
|
|
5
5
|
import { cn } from '../../../lib/utils.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
function AgentIcon({ agentType, className }) {
|
|
9
|
-
const IconComponent = getAgentTypeIcon(agentType);
|
|
10
|
-
return _jsx(IconComponent, { className: className });
|
|
11
|
-
}
|
|
6
|
+
import { CometSpinner } from '../../ui/comet-spinner.js';
|
|
7
|
+
import { featureNodeStateConfig, lifecycleDisplayLabels } from './feature-node-state-config.js';
|
|
12
8
|
function getBadgeText(data) {
|
|
13
9
|
const config = featureNodeStateConfig[data.state];
|
|
14
10
|
switch (data.state) {
|
|
15
|
-
case 'running':
|
|
16
|
-
|
|
11
|
+
case 'running': {
|
|
12
|
+
const agent = data.agentName ?? 'Agent';
|
|
13
|
+
return `${agent} ${data.featureId} running`;
|
|
14
|
+
}
|
|
17
15
|
case 'done':
|
|
18
16
|
return data.runtime ? `Completed in ${data.runtime}` : 'Completed';
|
|
19
17
|
case 'blocked':
|
|
@@ -30,7 +28,7 @@ export function FeatureNode({ data, selected, }) {
|
|
|
30
28
|
return (_jsxs("div", { className: "group relative", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, _jsxs("div", { "data-testid": "feature-node-card", className: cn('bg-card flex min-h-35 w-72 flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: lifecycleDisplayLabels[data.lifecycle] }), data.onSettings ? (_jsx("button", { type: "button", "aria-label": "Settings", "data-testid": "feature-node-settings-button", onClick: (e) => {
|
|
31
29
|
e.stopPropagation();
|
|
32
30
|
data.onSettings?.();
|
|
33
|
-
}, className: "nodrag text-muted-foreground hover:text-foreground -mt-1 -mr-1 p-1", children: _jsx(Settings, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("h3", { className: "mt-1 truncate text-sm font-bold", children: data.name }), data.description ? (_jsx("p", { "data-testid": "feature-node-description", className: "text-muted-foreground mt-0.5 truncate text-xs", children: data.description })) : null, _jsx("div", { className: "mt-auto pt-2", children:
|
|
31
|
+
}, className: "nodrag text-muted-foreground hover:text-foreground -mt-1 -mr-1 p-1", children: _jsx(Settings, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("h3", { className: "mt-1 truncate text-sm font-bold", children: data.name }), data.description ? (_jsx("p", { "data-testid": "feature-node-description", className: "text-muted-foreground mt-0.5 truncate text-xs", children: data.description })) : null, _jsx("div", { className: "mt-auto pt-2", children: config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "text-muted-foreground flex items-center justify-between text-[10px]", children: [_jsx("span", { children: data.featureId }), _jsxs("span", { children: [data.progress, "%"] })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', config.progressClass), style: { width: `${data.progress}%` } }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-[10px]", children: _jsx("span", { children: data.featureId }) }), _jsxs("div", { "data-testid": "feature-node-badge", className: cn('mt-1.5 flex items-center justify-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium', config.badgeBgClass, config.badgeClass), children: [data.state === 'running' ? (_jsx(CometSpinner, { size: "sm", className: "shrink-0" })) : (_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0" })), _jsx("span", { className: "truncate", children: getBadgeText(data) })] })] })) })] }), data.onAction ? (_jsx(Handle, { type: "source", position: Position.Right, className: "h-0! w-0! border-0! bg-transparent!", children: _jsx("button", { type: "button", "aria-label": "Add", "data-testid": "feature-node-action-button", onClick: (e) => {
|
|
34
32
|
e.stopPropagation();
|
|
35
33
|
data.onAction?.();
|
|
36
34
|
}, className: "nodrag absolute top-1/2 left-1/2 flex h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-blue-500 text-white opacity-0 shadow-md transition-opacity group-hover:opacity-100 hover:bg-blue-600", children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) })) : data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: false, className: "opacity-0!" })) : null] }));
|
|
@@ -11,6 +11,5 @@ export declare const LongContent: Story;
|
|
|
11
11
|
export declare const DoneWithRuntime: Story;
|
|
12
12
|
export declare const BlockedByFeature: Story;
|
|
13
13
|
export declare const ErrorWithMessage: Story;
|
|
14
|
-
export declare const AllAgentTypes: Story;
|
|
15
14
|
export declare const Interactive: Story;
|
|
16
15
|
//# sourceMappingURL=feature-node.stories.d.ts.map
|
package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-node.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIvD,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"feature-node.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIvD,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,6BAA6B,CAAC;AAiCrC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,eAAe,CAe/B,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAEvC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAiDF,eAAO,MAAM,SAAS,EAAE,KAQvB,CAAC;AAWF,eAAO,MAAM,aAAa,EAAE,KAoB3B,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,KAMxB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAW7B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AAuEF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC"}
|