@shepai/cli 1.31.0 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +13 -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 +4 -13
- package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.stories.d.ts +4 -10
- 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 +7 -19
- package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts +1 -1
- package/dist/src/presentation/web/components/common/feature-create-drawer/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js +11 -0
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts +1 -1
- 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 +10 -0
- 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 +4 -2
- package/dist/src/presentation/web/components/common/feature-node/feature-node.stories.d.ts +1 -0
- 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 +21 -0
- package/dist/src/presentation/web/components/common/index.d.ts +1 -1
- package/dist/src/presentation/web/components/common/index.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/index.js +1 -1
- 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 +2 -2
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +3 -3
- 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 +76 -23
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/features-canvas/features-canvas.js +2 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- 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/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_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +2 -2
- package/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/page_client-reference-manifest.js +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 +2 -2
- package/web/.next/server/app/version.segments/_full.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_index.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
- package/web/.next/server/chunks/ssr/_09afa42a._.js +2 -7
- package/web/.next/server/chunks/ssr/_09afa42a._.js.map +1 -1
- 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/{daacb9d30e4fc428.js → 48b2cea957bfe6ab.js} +2 -7
- package/web/.next/static/chunks/{fd816afd85fd9a19.css → b98242e5ef2a1bd3.css} +1 -1
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- /package/web/.next/static/{zHWQYn5H7ZmzuYWmx7b2E → 9kF5HqLN2Pcx3uc1dCFjr}/_buildManifest.js +0 -0
- /package/web/.next/static/{zHWQYn5H7ZmzuYWmx7b2E → 9kF5HqLN2Pcx3uc1dCFjr}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{zHWQYn5H7ZmzuYWmx7b2E → 9kF5HqLN2Pcx3uc1dCFjr}/_ssgManifest.js +0 -0
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FileAttachment } from '../../../../../../packages/core/src/infrastructure/services/file-dialog.service.js';
|
|
2
2
|
export type { FileAttachment } from '../../../../../../packages/core/src/infrastructure/services/file-dialog.service.js';
|
|
3
|
+
export interface FeatureCreatePayload {
|
|
4
|
+
name: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
attachments: FileAttachment[];
|
|
7
|
+
repositoryPath: string;
|
|
8
|
+
approvalGates: {
|
|
9
|
+
allowPrd: boolean;
|
|
10
|
+
allowPlan: boolean;
|
|
11
|
+
allowMerge: boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
3
14
|
export interface FeatureCreateDrawerProps {
|
|
4
15
|
open: boolean;
|
|
5
16
|
onClose: () => void;
|
|
6
|
-
onSubmit: (data:
|
|
17
|
+
onSubmit: (data: FeatureCreatePayload) => void;
|
|
7
18
|
repositoryPath: string;
|
|
8
19
|
isSubmitting?: boolean;
|
|
9
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAG/F,YAAY,EAAE,cAAc,EAAE,MAAM,0DAA0D,CAAC;AAE/F,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE;QACb,QAAQ,EAAE,OAAO,CAAC;QAClB,SAAS,EAAE,OAAO,CAAC;QACnB,UAAU,EAAE,OAAO,CAAC;KACrB,CAAC;CACH;AAcD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,YAAoB,GACrB,EAAE,wBAAwB,2CA+L1B"}
|
package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js
CHANGED
|
@@ -22,17 +22,6 @@ const EMPTY_GATES = {
|
|
|
22
22
|
allowPlan: false,
|
|
23
23
|
allowMerge: false,
|
|
24
24
|
};
|
|
25
|
-
function composeUserInput(name, description, attachments) {
|
|
26
|
-
let userInput = `Feature: ${name}`;
|
|
27
|
-
if (description) {
|
|
28
|
-
userInput += `\n\n${description}`;
|
|
29
|
-
}
|
|
30
|
-
if (attachments.length > 0) {
|
|
31
|
-
const paths = attachments.map((a) => `- ${a.path}`).join('\n');
|
|
32
|
-
userInput += `\n\nAttached files:\n${paths}`;
|
|
33
|
-
}
|
|
34
|
-
return userInput;
|
|
35
|
-
}
|
|
36
25
|
export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, isSubmitting = false, }) {
|
|
37
26
|
const [name, setName] = useState('');
|
|
38
27
|
const [description, setDescription] = useState('');
|
|
@@ -54,9 +43,11 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
|
|
|
54
43
|
e.preventDefault();
|
|
55
44
|
if (!name.trim())
|
|
56
45
|
return;
|
|
57
|
-
const
|
|
46
|
+
const trimmedDescription = description.trim() || undefined;
|
|
58
47
|
onSubmit({
|
|
59
|
-
|
|
48
|
+
name: name.trim(),
|
|
49
|
+
description: trimmedDescription,
|
|
50
|
+
attachments,
|
|
60
51
|
repositoryPath,
|
|
61
52
|
approvalGates: {
|
|
62
53
|
allowPrd: approvalGates.allowPrd ?? false,
|
|
@@ -18,13 +18,12 @@ import { FeatureCreateDrawer } from './feature-create-drawer.js';
|
|
|
18
18
|
* |------|------|-------------|
|
|
19
19
|
* | `open` | `boolean` | Controls drawer visibility |
|
|
20
20
|
* | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
|
|
21
|
-
* | `onSubmit` | `(data:
|
|
21
|
+
* | `onSubmit` | `(data: FeatureCreatePayload) => void` | Called with `{ name, description, attachments, repositoryPath, approvalGates }` |
|
|
22
22
|
* | `repositoryPath` | `string` | Repository path (mandatory) included in the submitted data |
|
|
23
|
-
* | `isSubmitting` | `boolean` | Disables all fields and shows "Creating..." on submit button |
|
|
24
23
|
*
|
|
25
24
|
* ### Behavior
|
|
26
25
|
* - Form resets (name, description, attachments, checkboxes) when the drawer closes
|
|
27
|
-
* - Submit button is disabled when name is empty
|
|
26
|
+
* - Submit button is disabled when name is empty
|
|
28
27
|
* - `approvalGates` always included: `{ allowPrd, allowPlan, allowMerge }` (all false by default)
|
|
29
28
|
* - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
|
|
30
29
|
* - Fixed width: 384px (`w-96`)
|
|
@@ -41,11 +40,6 @@ export declare const Default: Story;
|
|
|
41
40
|
* Demonstrates the form in a "ready to submit" state.
|
|
42
41
|
*/
|
|
43
42
|
export declare const PreFilled: Story;
|
|
44
|
-
/**
|
|
45
|
-
* Submitting/loading state — all form fields are disabled, submit button shows "Creating...".
|
|
46
|
-
* Click the trigger to open and observe the disabled form.
|
|
47
|
-
*/
|
|
48
|
-
export declare const Submitting: Story;
|
|
49
43
|
/**
|
|
50
44
|
* Validation state — open the drawer with an empty name field.
|
|
51
45
|
* The "+ Create Feature" button is disabled because name validation fails.
|
|
@@ -77,8 +71,8 @@ export declare const MergeOnly: Story;
|
|
|
77
71
|
* Fully interactive story — open the drawer, fill the form, toggle checkboxes,
|
|
78
72
|
* and click "Add Files" to attach files via the native OS file picker.
|
|
79
73
|
*
|
|
80
|
-
* **Submitted data** is displayed in a styled panel showing `
|
|
81
|
-
* with `
|
|
74
|
+
* **Submitted data** is displayed in a styled panel showing `FeatureCreatePayload`
|
|
75
|
+
* with `{ name, description, attachments, repositoryPath, approvalGates }`.
|
|
82
76
|
*
|
|
83
77
|
* In Storybook, the native picker won't work (no backend), but submitted data
|
|
84
78
|
* would show the paths if files were attached programmatically.
|
|
@@ -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;AAQ9D
|
|
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;AAQ9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,mBAAmB,CAyB1C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,mBAAmB,CAAC,CAAC;AA0ClD,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;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,EAAE,KAsCzB,CAAC"}
|
|
@@ -24,13 +24,12 @@ import { Button } from '../../ui/button.js';
|
|
|
24
24
|
* |------|------|-------------|
|
|
25
25
|
* | `open` | `boolean` | Controls drawer visibility |
|
|
26
26
|
* | `onClose` | `() => void` | Called on dismiss (close button, cancel, or backdrop) |
|
|
27
|
-
* | `onSubmit` | `(data:
|
|
27
|
+
* | `onSubmit` | `(data: FeatureCreatePayload) => void` | Called with `{ name, description, attachments, repositoryPath, approvalGates }` |
|
|
28
28
|
* | `repositoryPath` | `string` | Repository path (mandatory) included in the submitted data |
|
|
29
|
-
* | `isSubmitting` | `boolean` | Disables all fields and shows "Creating..." on submit button |
|
|
30
29
|
*
|
|
31
30
|
* ### Behavior
|
|
32
31
|
* - Form resets (name, description, attachments, checkboxes) when the drawer closes
|
|
33
|
-
* - Submit button is disabled when name is empty
|
|
32
|
+
* - Submit button is disabled when name is empty
|
|
34
33
|
* - `approvalGates` always included: `{ allowPrd, allowPlan, allowMerge }` (all false by default)
|
|
35
34
|
* - Non-modal (`modal={false}`) — canvas stays interactive behind the drawer
|
|
36
35
|
* - Fixed width: 384px (`w-96`)
|
|
@@ -53,16 +52,12 @@ const meta = {
|
|
|
53
52
|
description: 'Callback fired when the drawer is dismissed (close button, cancel, or backdrop click).',
|
|
54
53
|
},
|
|
55
54
|
onSubmit: {
|
|
56
|
-
description: 'Callback fired with `
|
|
55
|
+
description: 'Callback fired with `FeatureCreatePayload` when the form is submitted. Receives `{ name, description, attachments, repositoryPath, approvalGates }`.',
|
|
57
56
|
},
|
|
58
57
|
repositoryPath: {
|
|
59
58
|
control: 'text',
|
|
60
59
|
description: 'Repository path (mandatory) included in the submitted data.',
|
|
61
60
|
},
|
|
62
|
-
isSubmitting: {
|
|
63
|
-
control: 'boolean',
|
|
64
|
-
description: 'When true, all form fields and buttons are disabled, and the submit button shows "Creating...".',
|
|
65
|
-
},
|
|
66
61
|
},
|
|
67
62
|
};
|
|
68
63
|
export default meta;
|
|
@@ -75,7 +70,7 @@ const logClose = fn().mockName('onClose');
|
|
|
75
70
|
* Trigger wrapper — every story uses this so nothing auto-opens on Docs page
|
|
76
71
|
* ------------------------------------------------------------------------- */
|
|
77
72
|
/** Starts closed — click button to open. Actions are logged. */
|
|
78
|
-
function CreateDrawerTrigger({
|
|
73
|
+
function CreateDrawerTrigger({ label = 'Open Create Feature' }) {
|
|
79
74
|
const [open, setOpen] = useState(false);
|
|
80
75
|
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: () => {
|
|
81
76
|
setOpen(false);
|
|
@@ -83,7 +78,7 @@ function CreateDrawerTrigger({ isSubmitting = false, label = 'Open Create Featur
|
|
|
83
78
|
}, onSubmit: (data) => {
|
|
84
79
|
logSubmit(data);
|
|
85
80
|
setOpen(false);
|
|
86
|
-
}, repositoryPath: "/Users/dev/my-repo"
|
|
81
|
+
}, repositoryPath: "/Users/dev/my-repo" })] }));
|
|
87
82
|
}
|
|
88
83
|
/* ---------------------------------------------------------------------------
|
|
89
84
|
* Per-state stories
|
|
@@ -109,13 +104,6 @@ export const PreFilled = {
|
|
|
109
104
|
await userEvent.type(descInput, 'Implement OAuth2 authentication with GitHub as the identity provider. Includes login, callback handling, and token refresh.');
|
|
110
105
|
},
|
|
111
106
|
};
|
|
112
|
-
/**
|
|
113
|
-
* Submitting/loading state — all form fields are disabled, submit button shows "Creating...".
|
|
114
|
-
* Click the trigger to open and observe the disabled form.
|
|
115
|
-
*/
|
|
116
|
-
export const Submitting = {
|
|
117
|
-
render: () => _jsx(CreateDrawerTrigger, { isSubmitting: true, label: "Open (Submitting)" }),
|
|
118
|
-
};
|
|
119
107
|
/**
|
|
120
108
|
* Validation state — open the drawer with an empty name field.
|
|
121
109
|
* The "+ Create Feature" button is disabled because name validation fails.
|
|
@@ -198,8 +186,8 @@ export const MergeOnly = {
|
|
|
198
186
|
* Fully interactive story — open the drawer, fill the form, toggle checkboxes,
|
|
199
187
|
* and click "Add Files" to attach files via the native OS file picker.
|
|
200
188
|
*
|
|
201
|
-
* **Submitted data** is displayed in a styled panel showing `
|
|
202
|
-
* with `
|
|
189
|
+
* **Submitted data** is displayed in a styled panel showing `FeatureCreatePayload`
|
|
190
|
+
* with `{ name, description, attachments, repositoryPath, approvalGates }`.
|
|
203
191
|
*
|
|
204
192
|
* In Storybook, the native picker won't work (no backend), but submitted data
|
|
205
193
|
* would show the paths if files were attached programmatically.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { FeatureCreateDrawer, type FeatureCreateDrawerProps, type FileAttachment, } from './feature-create-drawer.js';
|
|
1
|
+
export { FeatureCreateDrawer, type FeatureCreateDrawerProps, type FeatureCreatePayload, type FileAttachment, } from './feature-create-drawer.js';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +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,cAAc,GACpB,MAAM,yBAAyB,CAAC"}
|
|
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,oBAAoB,EACzB,KAAK,cAAc,GACpB,MAAM,yBAAyB,CAAC"}
|
package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts.map
CHANGED
|
@@ -1 +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;AAgG5C,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,KAqBvB,CAAC;
|
|
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;AAgG5C,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,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,wDAAwD;AACxD,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"}
|
package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js
CHANGED
|
@@ -129,7 +129,18 @@ export const AllFields = {
|
|
|
129
129
|
/* ---------------------------------------------------------------------------
|
|
130
130
|
* Matrix stories — interactive state/lifecycle switcher
|
|
131
131
|
* ------------------------------------------------------------------------- */
|
|
132
|
+
const creatingData = {
|
|
133
|
+
name: 'User Onboarding',
|
|
134
|
+
description: 'Implement guided onboarding wizard',
|
|
135
|
+
featureId: '',
|
|
136
|
+
lifecycle: 'requirements',
|
|
137
|
+
state: 'creating',
|
|
138
|
+
progress: 0,
|
|
139
|
+
repositoryPath: '/home/user/my-repo',
|
|
140
|
+
branch: '',
|
|
141
|
+
};
|
|
132
142
|
const stateFixtures = {
|
|
143
|
+
creating: creatingData,
|
|
133
144
|
running: runningData,
|
|
134
145
|
'action-required': actionRequiredData,
|
|
135
146
|
done: doneData,
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type LucideIcon } from 'lucide-react';
|
|
2
2
|
import type { Node } from '@xyflow/react';
|
|
3
3
|
import type { AgentTypeValue } from './agent-type-icons.js';
|
|
4
|
-
export type FeatureNodeState = 'running' | 'action-required' | 'done' | 'blocked' | 'error';
|
|
4
|
+
export type FeatureNodeState = 'creating' | 'running' | 'action-required' | 'done' | 'blocked' | 'error';
|
|
5
5
|
export type FeatureLifecyclePhase = 'requirements' | 'research' | 'implementation' | 'review' | 'deploy' | 'maintain';
|
|
6
6
|
/** Human-readable display labels for lifecycle phases. */
|
|
7
7
|
export declare const lifecycleDisplayLabels: Record<FeatureLifecyclePhase, string>;
|
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,EAAmD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,
|
|
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;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GACxB,UAAU,GACV,SAAS,GACT,iBAAiB,GACjB,MAAM,GACN,SAAS,GACT,OAAO,CAAC;AAEZ,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,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOvE,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,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,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,CA6DnF,CAAC"}
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.js
CHANGED
|
@@ -18,6 +18,16 @@ export const lifecycleRunningVerbs = {
|
|
|
18
18
|
maintain: 'Maintaining',
|
|
19
19
|
};
|
|
20
20
|
export const featureNodeStateConfig = {
|
|
21
|
+
creating: {
|
|
22
|
+
icon: Loader2,
|
|
23
|
+
borderClass: 'border-l-blue-500',
|
|
24
|
+
labelClass: 'text-blue-500',
|
|
25
|
+
progressClass: 'bg-blue-500',
|
|
26
|
+
badgeClass: 'text-blue-700',
|
|
27
|
+
badgeBgClass: 'bg-blue-50',
|
|
28
|
+
label: 'Creating...',
|
|
29
|
+
showProgressBar: false,
|
|
30
|
+
},
|
|
21
31
|
running: {
|
|
22
32
|
icon: Loader2,
|
|
23
33
|
borderClass: 'border-l-blue-500',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA0BnE,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,2CAsKA"}
|
|
@@ -12,6 +12,8 @@ function AgentIcon({ agentType, className }) {
|
|
|
12
12
|
function getBadgeText(data) {
|
|
13
13
|
const config = featureNodeStateConfig[data.state];
|
|
14
14
|
switch (data.state) {
|
|
15
|
+
case 'creating':
|
|
16
|
+
return 'Creating...';
|
|
15
17
|
case 'running':
|
|
16
18
|
return lifecycleRunningVerbs[data.lifecycle];
|
|
17
19
|
case 'done':
|
|
@@ -27,10 +29,10 @@ function getBadgeText(data) {
|
|
|
27
29
|
export function FeatureNode({ data, selected, }) {
|
|
28
30
|
const config = featureNodeStateConfig[data.state];
|
|
29
31
|
const Icon = config.icon;
|
|
30
|
-
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) => {
|
|
32
|
+
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", "aria-busy": data.state === 'creating' ? 'true' : undefined, 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
33
|
e.stopPropagation();
|
|
32
34
|
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: data.state === 'running' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(AgentIcon, { agentType: data.agentType, className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _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: "animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground flex items-center justify-end text-[10px]", children: _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: [_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) => {
|
|
35
|
+
}, 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: data.state === 'creating' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0 animate-spin" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _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: "motion-safe:animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : data.state === 'running' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(AgentIcon, { agentType: data.agentType, className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _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: "animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground flex items-center justify-end text-[10px]", children: _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: [_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
36
|
e.stopPropagation();
|
|
35
37
|
data.onAction?.();
|
|
36
38
|
}, 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,7 @@ export declare const WithAction: Story;
|
|
|
11
11
|
export declare const LongContent: Story;
|
|
12
12
|
export declare const DoneWithRuntime: Story;
|
|
13
13
|
export declare const BlockedByFeature: Story;
|
|
14
|
+
export declare const Creating: Story;
|
|
14
15
|
export declare const ErrorWithMessage: Story;
|
|
15
16
|
export declare const Interactive: Story;
|
|
16
17
|
//# 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;AAKvD,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,6BAA6B,CAAC;AAiCrC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,eAAe,CAc/B,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAEvC,eAAO,MAAM,OAAO,EAAE,KAErB,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;AAKvD,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,6BAA6B,CAAC;AAiCrC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,eAAe,CAc/B,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAEvC,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAoEF,eAAO,MAAM,SAAS,EAAE,KAQvB,CAAC;AAWF,eAAO,MAAM,aAAa,EAAE,KAqB3B,CAAC;AAsEF,eAAO,MAAM,aAAa,EAAE,KAQ3B,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,QAAQ,EAAE,KAUtB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAW9B,CAAC;AA4EF,eAAO,MAAM,WAAW,EAAE,KAOzB,CAAC"}
|
|
@@ -29,6 +29,16 @@ export const Default = {
|
|
|
29
29
|
render: (args) => _jsx(FeatureNodeCanvas, { data: args }),
|
|
30
30
|
};
|
|
31
31
|
const allStatesData = [
|
|
32
|
+
{
|
|
33
|
+
name: 'User Onboarding',
|
|
34
|
+
description: 'Implement guided onboarding wizard',
|
|
35
|
+
featureId: '',
|
|
36
|
+
lifecycle: 'requirements',
|
|
37
|
+
state: 'creating',
|
|
38
|
+
progress: 0,
|
|
39
|
+
repositoryPath: '/home/user/my-repo',
|
|
40
|
+
branch: '',
|
|
41
|
+
},
|
|
32
42
|
{
|
|
33
43
|
name: 'Auth Module',
|
|
34
44
|
description: 'Implement OAuth2 authentication flow',
|
|
@@ -214,6 +224,17 @@ export const BlockedByFeature = {
|
|
|
214
224
|
},
|
|
215
225
|
render: (args) => _jsx(FeatureNodeCanvas, { data: args }),
|
|
216
226
|
};
|
|
227
|
+
export const Creating = {
|
|
228
|
+
args: {
|
|
229
|
+
name: 'User Onboarding',
|
|
230
|
+
description: 'Implement guided onboarding wizard',
|
|
231
|
+
featureId: '',
|
|
232
|
+
lifecycle: 'requirements',
|
|
233
|
+
state: 'creating',
|
|
234
|
+
progress: 0,
|
|
235
|
+
},
|
|
236
|
+
render: (args) => _jsx(FeatureNodeCanvas, { data: args }),
|
|
237
|
+
};
|
|
217
238
|
export const ErrorWithMessage = {
|
|
218
239
|
args: {
|
|
219
240
|
name: 'Email Service',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { AddRepositoryNode, type AddRepositoryNodeData, type AddRepositoryNodeType, } from './add-repository-node/index.js';
|
|
2
2
|
export { ElapsedTime, formatElapsed } from './elapsed-time/index.js';
|
|
3
3
|
export { EmptyState, type EmptyStateProps } from './empty-state/index.js';
|
|
4
|
-
export { FeatureCreateDrawer, type FeatureCreateDrawerProps } from './feature-create-drawer/index.js';
|
|
4
|
+
export { FeatureCreateDrawer, type FeatureCreateDrawerProps, type FeatureCreatePayload, } from './feature-create-drawer/index.js';
|
|
5
5
|
export { FeatureDrawer, type FeatureDrawerProps } from './feature-drawer/index.js';
|
|
6
6
|
export { FeatureListItem, type FeatureListItemProps } from './feature-list-item/index.js';
|
|
7
7
|
export { useFeatureActions, type FeatureActionsInput, type FeatureActionsState, } from './feature-drawer/use-feature-actions.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/common/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,GAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EACL,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,GACzB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EACL,WAAW,EACX,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,sBAAsB,GAC5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,KAAK,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,KAAK,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { AddRepositoryNode, } from './add-repository-node/index.js';
|
|
2
2
|
export { ElapsedTime, formatElapsed } from './elapsed-time/index.js';
|
|
3
3
|
export { EmptyState } from './empty-state/index.js';
|
|
4
|
-
export { FeatureCreateDrawer } from './feature-create-drawer/index.js';
|
|
4
|
+
export { FeatureCreateDrawer, } from './feature-create-drawer/index.js';
|
|
5
5
|
export { FeatureDrawer } from './feature-drawer/index.js';
|
|
6
6
|
export { FeatureListItem } from './feature-list-item/index.js';
|
|
7
7
|
export { useFeatureActions, } from './feature-drawer/use-feature-actions.js';
|
package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAK5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"control-center-inner.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center-inner.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAK5E,UAAU,uBAAuB;IAC/B,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAyDzF"}
|
package/dist/src/presentation/web/components/features/control-center/control-center-inner.js
CHANGED
|
@@ -6,12 +6,12 @@ import { FeatureDrawer, FeatureCreateDrawer } from '../../common/index.js';
|
|
|
6
6
|
import { ControlCenterEmptyState } from './control-center-empty-state.js';
|
|
7
7
|
import { useControlCenterState } from './use-control-center-state.js';
|
|
8
8
|
export function ControlCenterInner({ initialNodes, initialEdges }) {
|
|
9
|
-
const { nodes, edges, selectedNode, isCreateDrawerOpen,
|
|
9
|
+
const { nodes, edges, selectedNode, isCreateDrawerOpen, isDeleting, pendingRepositoryPath, onNodesChange, handleConnect, handleAddFeature, handleAddFeatureToRepo, handleAddFeatureToFeature, handleAddRepository, handleNodeClick, clearSelection, handleCreateFeatureSubmit, handleDeleteFeature, closeCreateDrawer, } = useControlCenterState(initialNodes, initialEdges);
|
|
10
10
|
// Listen for global "open create drawer" events from the sidebar
|
|
11
11
|
useEffect(() => {
|
|
12
12
|
const handler = () => handleAddFeature();
|
|
13
13
|
window.addEventListener('shep:open-create-drawer', handler);
|
|
14
14
|
return () => window.removeEventListener('shep:open-create-drawer', handler);
|
|
15
15
|
}, [handleAddFeature]);
|
|
16
|
-
return (_jsxs(_Fragment, { children: [_jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: selectedNode, onClose: clearSelection, onDelete: handleDeleteFeature, isDeleting: isDeleting }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath
|
|
16
|
+
return (_jsxs(_Fragment, { children: [_jsx(FeaturesCanvas, { nodes: nodes, edges: edges, onNodesChange: onNodesChange, onConnect: handleConnect, onAddFeature: handleAddFeature, onNodeAction: handleAddFeatureToFeature, onNodeClick: handleNodeClick, onPaneClick: clearSelection, onRepositoryAdd: handleAddFeatureToRepo, onRepositorySelect: handleAddRepository, emptyState: _jsx(ControlCenterEmptyState, { onRepositorySelect: handleAddRepository }) }), _jsx(FeatureDrawer, { selectedNode: selectedNode, onClose: clearSelection, onDelete: handleDeleteFeature, isDeleting: isDeleting }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath })] }));
|
|
17
17
|
}
|
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Connection, Edge, NodeChange } from '@xyflow/react';
|
|
2
2
|
import type { FeatureNodeData } from '../../common/feature-node/index.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { FeatureCreatePayload } from '../../common/feature-create-drawer/index.js';
|
|
4
4
|
import type { CanvasNodeType } from '../../features/features-canvas/index.js';
|
|
5
5
|
import { type LayoutDirection } from '../../../lib/layout-with-dagre.js';
|
|
6
6
|
export interface ControlCenterState {
|
|
@@ -8,7 +8,6 @@ export interface ControlCenterState {
|
|
|
8
8
|
edges: Edge[];
|
|
9
9
|
selectedNode: FeatureNodeData | null;
|
|
10
10
|
isCreateDrawerOpen: boolean;
|
|
11
|
-
isSubmitting: boolean;
|
|
12
11
|
pendingRepositoryPath: string;
|
|
13
12
|
onNodesChange: (changes: NodeChange<CanvasNodeType>[]) => void;
|
|
14
13
|
handleConnect: (connection: Connection) => void;
|
|
@@ -19,10 +18,11 @@ export interface ControlCenterState {
|
|
|
19
18
|
handleAddFeatureToFeature: (featureNodeId: string) => void;
|
|
20
19
|
handleAddRepository: (path: string) => void;
|
|
21
20
|
handleLayout: (direction: LayoutDirection) => void;
|
|
22
|
-
handleCreateFeatureSubmit: (data:
|
|
21
|
+
handleCreateFeatureSubmit: (data: FeatureCreatePayload) => void;
|
|
23
22
|
closeCreateDrawer: () => void;
|
|
24
23
|
handleDeleteFeature: (featureId: string) => Promise<void>;
|
|
25
24
|
isDeleting: boolean;
|
|
25
|
+
createFeatureNode: (sourceNodeId: string | null, dataOverride?: Partial<FeatureNodeData>) => string;
|
|
26
26
|
}
|
|
27
27
|
export declare function useControlCenterState(initialNodes: CanvasNodeType[], initialEdges: Edge[]): ControlCenterState;
|
|
28
28
|
//# sourceMappingURL=use-control-center-state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,eAAe,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IACzE,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,sBAAsB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,yBAAyB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3D,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,yBAAyB,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAC;IAChE,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,KACpC,MAAM,CAAC;CACb;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA6YpB"}
|
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js
CHANGED
|
@@ -11,9 +11,46 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
11
11
|
const [edges, setEdges] = useState(initialEdges);
|
|
12
12
|
const [selectedNode, setSelectedNode] = useState(null);
|
|
13
13
|
const [isCreateDrawerOpen, setIsCreateDrawerOpen] = useState(false);
|
|
14
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
15
14
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
16
15
|
const [pendingRepoNodeId, setPendingRepoNodeId] = useState(null);
|
|
16
|
+
// Sync server props into local state when router.refresh() delivers new data
|
|
17
|
+
const initialNodeKey = initialNodes
|
|
18
|
+
.map((n) => n.id)
|
|
19
|
+
.sort()
|
|
20
|
+
.join(',');
|
|
21
|
+
const initialEdgeKey = initialEdges
|
|
22
|
+
.map((e) => e.id)
|
|
23
|
+
.sort()
|
|
24
|
+
.join(',');
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
setNodes((currentNodes) => {
|
|
27
|
+
// Build a lookup of current node positions by ID
|
|
28
|
+
const currentById = new Map(currentNodes.map((n) => [n.id, n]));
|
|
29
|
+
// Identify optimistic "creating" nodes (they have temp IDs not in server data)
|
|
30
|
+
const serverIds = new Set(initialNodes.map((n) => n.id));
|
|
31
|
+
const creatingNodes = currentNodes.filter((n) => n.type === 'featureNode' &&
|
|
32
|
+
n.data.state === 'creating' &&
|
|
33
|
+
!serverIds.has(n.id));
|
|
34
|
+
// Merge server nodes with client positions
|
|
35
|
+
return initialNodes.map((serverNode) => {
|
|
36
|
+
// Node already exists on canvas — keep its position, update data
|
|
37
|
+
const existing = currentById.get(serverNode.id);
|
|
38
|
+
if (existing) {
|
|
39
|
+
return { ...serverNode, position: existing.position };
|
|
40
|
+
}
|
|
41
|
+
// New server node — inherit position from an optimistic creating node if available
|
|
42
|
+
if (serverNode.type === 'featureNode' && creatingNodes.length > 0) {
|
|
43
|
+
const donor = creatingNodes.shift();
|
|
44
|
+
return { ...serverNode, position: donor.position };
|
|
45
|
+
}
|
|
46
|
+
// Truly new node with no optimistic counterpart — use server position
|
|
47
|
+
return serverNode;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}, [initialNodeKey]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
setEdges(initialEdges);
|
|
53
|
+
}, [initialEdgeKey]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
17
54
|
const onNodesChange = useCallback((changes) => {
|
|
18
55
|
setNodes((ns) => applyNodeChanges(changes, ns));
|
|
19
56
|
}, []);
|
|
@@ -22,8 +59,11 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
22
59
|
}, []);
|
|
23
60
|
const handleNodeClick = useCallback((_event, node) => {
|
|
24
61
|
if (node.type === 'featureNode') {
|
|
62
|
+
const data = node.data;
|
|
63
|
+
if (data.state === 'creating')
|
|
64
|
+
return;
|
|
25
65
|
setIsCreateDrawerOpen(false);
|
|
26
|
-
setSelectedNode(
|
|
66
|
+
setSelectedNode(data);
|
|
27
67
|
}
|
|
28
68
|
}, []);
|
|
29
69
|
// Keyboard shortcut: Escape to deselect
|
|
@@ -71,7 +111,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
71
111
|
description: dataOverride?.description ?? 'Describe what this feature does',
|
|
72
112
|
featureId: `#${id.slice(-4)}`,
|
|
73
113
|
lifecycle: 'requirements',
|
|
74
|
-
state: 'running',
|
|
114
|
+
state: dataOverride?.state ?? 'running',
|
|
75
115
|
progress: 0,
|
|
76
116
|
repositoryPath: dataOverride?.repositoryPath ?? '',
|
|
77
117
|
branch: dataOverride?.branch ?? '',
|
|
@@ -163,37 +203,50 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
163
203
|
},
|
|
164
204
|
]);
|
|
165
205
|
}
|
|
166
|
-
|
|
206
|
+
if (newFeatureData.state !== 'creating') {
|
|
207
|
+
setSelectedNode(newFeatureData);
|
|
208
|
+
}
|
|
209
|
+
return id;
|
|
167
210
|
}, [edges]);
|
|
168
211
|
const handleAddFeature = useCallback(() => {
|
|
169
212
|
setSelectedNode(null);
|
|
170
213
|
setIsCreateDrawerOpen(true);
|
|
171
214
|
}, []);
|
|
172
|
-
const handleCreateFeatureSubmit = useCallback(
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
215
|
+
const handleCreateFeatureSubmit = useCallback((data) => {
|
|
216
|
+
// 1. Insert optimistic node instantly
|
|
217
|
+
const tempId = createFeatureNode(pendingRepoNodeId, {
|
|
218
|
+
state: 'creating',
|
|
219
|
+
name: data.name,
|
|
220
|
+
description: data.description,
|
|
221
|
+
repositoryPath: data.repositoryPath,
|
|
222
|
+
});
|
|
223
|
+
// 2. Close drawer and clear pending state immediately
|
|
224
|
+
setIsCreateDrawerOpen(false);
|
|
225
|
+
setPendingRepoNodeId(null);
|
|
226
|
+
// 3. Fire API call in the background
|
|
227
|
+
fetch('/api/features/create', {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
headers: { 'Content-Type': 'application/json' },
|
|
230
|
+
body: JSON.stringify(data),
|
|
231
|
+
})
|
|
232
|
+
.then(async (response) => {
|
|
180
233
|
if (!response.ok) {
|
|
181
234
|
const body = await response.json();
|
|
235
|
+
// Rollback: remove optimistic node and edge
|
|
236
|
+
setNodes((prev) => prev.filter((n) => n.id !== tempId));
|
|
237
|
+
setEdges((prev) => prev.filter((e) => e.target !== tempId));
|
|
182
238
|
toast.error(body.error ?? 'Failed to create feature');
|
|
183
239
|
return;
|
|
184
240
|
}
|
|
185
|
-
setIsCreateDrawerOpen(false);
|
|
186
|
-
setPendingRepoNodeId(null);
|
|
187
|
-
toast.success('Feature created successfully');
|
|
188
241
|
router.refresh();
|
|
189
|
-
}
|
|
190
|
-
|
|
242
|
+
})
|
|
243
|
+
.catch(() => {
|
|
244
|
+
// Rollback: remove optimistic node and edge
|
|
245
|
+
setNodes((prev) => prev.filter((n) => n.id !== tempId));
|
|
246
|
+
setEdges((prev) => prev.filter((e) => e.target !== tempId));
|
|
191
247
|
toast.error('Failed to create feature');
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
setIsSubmitting(false);
|
|
195
|
-
}
|
|
196
|
-
}, [router]);
|
|
248
|
+
});
|
|
249
|
+
}, [router, createFeatureNode, pendingRepoNodeId]);
|
|
197
250
|
const closeCreateDrawer = useCallback(() => {
|
|
198
251
|
setIsCreateDrawerOpen(false);
|
|
199
252
|
}, []);
|
|
@@ -273,7 +326,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
273
326
|
edges,
|
|
274
327
|
selectedNode,
|
|
275
328
|
isCreateDrawerOpen,
|
|
276
|
-
isSubmitting,
|
|
277
329
|
pendingRepositoryPath,
|
|
278
330
|
onNodesChange,
|
|
279
331
|
handleConnect,
|
|
@@ -288,5 +340,6 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
288
340
|
closeCreateDrawer,
|
|
289
341
|
handleDeleteFeature,
|
|
290
342
|
isDeleting,
|
|
343
|
+
createFeatureNode,
|
|
291
344
|
};
|
|
292
345
|
}
|