@shepai/cli 1.28.0 → 1.29.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/di/populate-use-cases-bridge.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/populate-use-cases-bridge.js +2 -0
- package/dist/packages/core/src/infrastructure/di/use-cases-bridge.d.ts +7 -0
- package/dist/packages/core/src/infrastructure/di/use-cases-bridge.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/use-cases-bridge.js +17 -1
- package/dist/src/presentation/web/app/api/features/[id]/route.d.ts +11 -0
- package/dist/src/presentation/web/app/api/features/[id]/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/features/[id]/route.js +16 -0
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.d.ts +3 -1
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.js +4 -3
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts +6 -0
- 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 +22 -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 +2 -2
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +2 -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 +27 -0
- package/dist/src/presentation/web/components/ui/alert-dialog.d.ts +19 -0
- package/dist/src/presentation/web/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/alert-dialog.js +42 -0
- package/dist/src/presentation/web/components/ui/alert-dialog.stories.d.ts +9 -0
- package/dist/src/presentation/web/components/ui/alert-dialog.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/ui/alert-dialog.stories.js +21 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +1 -0
- 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/routes-manifest.json +10 -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/api/features/[id]/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/features/[id]/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/features/[id]/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/features/[id]/route.js +7 -0
- package/web/.next/server/app/api/features/[id]/route.js.map +5 -0
- package/web/.next/server/app/api/features/[id]/route.js.nft.json +1 -0
- package/web/.next/server/app/api/features/[id]/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/api/features/create/route.js +1 -1
- package/web/.next/server/app/api/features/create/route.js.nft.json +1 -1
- 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/app-paths-manifest.json +1 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_features_[id]_route_actions_03b9a4b5.js +3 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_features_[id]_route_actions_03b9a4b5.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__0c45619b._.js +8 -0
- package/web/.next/server/chunks/[root-of-the-server]__0c45619b._.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__25329f72._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__25329f72._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
- package/web/.next/server/chunks/ssr/_09afa42a._.js +8 -2
- package/web/.next/server/chunks/ssr/_09afa42a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.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/{28c05929f2636c01.js → daacb9d30e4fc428.js} +9 -3
- package/web/.next/static/chunks/fd816afd85fd9a19.css +2 -0
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/.next/types/link.d.ts +2 -1
- package/web/.next/types/routes.d.ts +2 -1
- package/web/.next/types/validator.ts +9 -0
- package/web/.next/server/chunks/[root-of-the-server]__4e987cf3._.js +0 -8
- package/web/.next/server/chunks/[root-of-the-server]__4e987cf3._.js.map +0 -1
- package/web/.next/static/chunks/a7273c84449394e0.css +0 -2
- /package/web/.next/static/{wymVMk3X_cTuoYKeS0Gbf → LkrVnyOa_e5oKSoKOC3uq}/_buildManifest.js +0 -0
- /package/web/.next/static/{wymVMk3X_cTuoYKeS0Gbf → LkrVnyOa_e5oKSoKOC3uq}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{wymVMk3X_cTuoYKeS0Gbf → LkrVnyOa_e5oKSoKOC3uq}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"populate-use-cases-bridge.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/populate-use-cases-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"populate-use-cases-bridge.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/populate-use-cases-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAOpD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,mBAAmB,GAAG,IAAI,CASnE"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { ListFeaturesUseCase } from '../../application/use-cases/features/list-features.use-case.js';
|
|
15
15
|
import { CreateFeatureUseCase } from '../../application/use-cases/features/create/create-feature.use-case.js';
|
|
16
|
+
import { DeleteFeatureUseCase } from '../../application/use-cases/features/delete-feature.use-case.js';
|
|
16
17
|
import { SHEP_USE_CASES_KEY } from './use-cases-bridge.js';
|
|
17
18
|
/**
|
|
18
19
|
* Populate the globalThis bridge with resolved use cases from the DI container.
|
|
@@ -23,6 +24,7 @@ export function populateUseCasesBridge(c) {
|
|
|
23
24
|
listFeatures: c.resolve(ListFeaturesUseCase),
|
|
24
25
|
agentRunRepo: c.resolve('IAgentRunRepository'),
|
|
25
26
|
createFeature: c.resolve(CreateFeatureUseCase),
|
|
27
|
+
deleteFeature: c.resolve(DeleteFeatureUseCase),
|
|
26
28
|
};
|
|
27
29
|
globalThis[SHEP_USE_CASES_KEY] = bridge;
|
|
28
30
|
process[SHEP_USE_CASES_KEY] = bridge;
|
|
@@ -39,4 +39,11 @@ export declare function getAgentRun(id: string): Promise<AgentRun | null>;
|
|
|
39
39
|
* Throws if the bridge is not initialized or the use case fails.
|
|
40
40
|
*/
|
|
41
41
|
export declare function createFeature(input: CreateFeatureInput): Promise<CreateFeatureResult>;
|
|
42
|
+
/**
|
|
43
|
+
* Delete a feature via the DI-managed use case.
|
|
44
|
+
* Like createFeature, this propagates errors so callers (API routes) can
|
|
45
|
+
* return proper HTTP error responses.
|
|
46
|
+
* Throws if the bridge is not initialized or the use case fails.
|
|
47
|
+
*/
|
|
48
|
+
export declare function deleteFeature(featureId: string): Promise<Feature>;
|
|
42
49
|
//# sourceMappingURL=use-cases-bridge.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-cases-bridge.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6EAA6E,CAAC;AACtH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,sDAAsD,CAAC;AAE9D,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,CAAC;AAE5E,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-cases-bridge.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6EAA6E,CAAC;AACtH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,sDAAsD,CAAC;AAE9D,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,CAAC;AAE5E,2DAA2D;AAC3D,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAyCnD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAWlF;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAStE;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAO3F;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOvE"}
|
|
@@ -24,12 +24,15 @@ function isShepUseCases(value) {
|
|
|
24
24
|
const listFeatures = maybe.listFeatures;
|
|
25
25
|
const agentRunRepo = maybe.agentRunRepo;
|
|
26
26
|
const createFeature = maybe.createFeature;
|
|
27
|
+
const deleteFeature = maybe.deleteFeature;
|
|
27
28
|
return (!!listFeatures &&
|
|
28
29
|
typeof listFeatures.execute === 'function' &&
|
|
29
30
|
!!agentRunRepo &&
|
|
30
31
|
typeof agentRunRepo.findById === 'function' &&
|
|
31
32
|
!!createFeature &&
|
|
32
|
-
typeof createFeature.execute === 'function'
|
|
33
|
+
typeof createFeature.execute === 'function' &&
|
|
34
|
+
!!deleteFeature &&
|
|
35
|
+
typeof deleteFeature.execute === 'function');
|
|
33
36
|
}
|
|
34
37
|
function getUseCases() {
|
|
35
38
|
const globalBridge = globalThis[SHEP_USE_CASES_KEY];
|
|
@@ -86,3 +89,16 @@ export async function createFeature(input) {
|
|
|
86
89
|
}
|
|
87
90
|
return useCases.createFeature.execute(input);
|
|
88
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Delete a feature via the DI-managed use case.
|
|
94
|
+
* Like createFeature, this propagates errors so callers (API routes) can
|
|
95
|
+
* return proper HTTP error responses.
|
|
96
|
+
* Throws if the bridge is not initialized or the use case fails.
|
|
97
|
+
*/
|
|
98
|
+
export async function deleteFeature(featureId) {
|
|
99
|
+
const useCases = getUseCases();
|
|
100
|
+
if (!useCases?.deleteFeature) {
|
|
101
|
+
throw new Error('Use-cases bridge is not initialized. Ensure the CLI process is running.');
|
|
102
|
+
}
|
|
103
|
+
return useCases.deleteFeature.execute(featureId);
|
|
104
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
export declare function DELETE(_request: Request, { params }: {
|
|
3
|
+
params: Promise<{
|
|
4
|
+
id: string;
|
|
5
|
+
}>;
|
|
6
|
+
}): Promise<NextResponse<{
|
|
7
|
+
error: string;
|
|
8
|
+
}> | NextResponse<{
|
|
9
|
+
feature: import("../../../../../../../packages/core/src/domain/index.js").Feature;
|
|
10
|
+
}>>;
|
|
11
|
+
//# sourceMappingURL=route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../../../../../src/presentation/web/app/api/features/[id]/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,wBAAsB,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE;;;;IAc9F"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { deleteFeature } from '../../../../../../../packages/core/src/infrastructure/di/use-cases-bridge.js';
|
|
3
|
+
export async function DELETE(_request, { params }) {
|
|
4
|
+
const { id } = await params;
|
|
5
|
+
if (!id?.trim()) {
|
|
6
|
+
return NextResponse.json({ error: 'id is required' }, { status: 400 });
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const feature = await deleteFeature(id);
|
|
10
|
+
return NextResponse.json({ feature });
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
const message = error instanceof Error ? error.message : 'Failed to delete feature';
|
|
14
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -2,6 +2,8 @@ import type { FeatureNodeData } from '../../common/feature-node/index.js';
|
|
|
2
2
|
export interface FeatureDrawerProps {
|
|
3
3
|
selectedNode: FeatureNodeData | null;
|
|
4
4
|
onClose: () => void;
|
|
5
|
+
onDelete?: (featureId: string) => void;
|
|
6
|
+
isDeleting?: boolean;
|
|
5
7
|
}
|
|
6
|
-
export declare function FeatureDrawer({ selectedNode, onClose }: FeatureDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function FeatureDrawer({ selectedNode, onClose, onDelete, isDeleting, }: FeatureDrawerProps): import("react/jsx-runtime").JSX.Element;
|
|
7
9
|
//# sourceMappingURL=feature-drawer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/feature-drawer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer/feature-drawer.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAGxE,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,EAC5B,YAAY,EACZ,OAAO,EACP,QAAQ,EACR,UAAkB,GACnB,EAAE,kBAAkB,2CAyHpB"}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { XIcon, Code2, Terminal, Loader2, CircleAlert } from 'lucide-react';
|
|
3
|
+
import { XIcon, Code2, Terminal, Loader2, CircleAlert, Trash2 } from 'lucide-react';
|
|
4
4
|
import { cn } from '../../../lib/utils.js';
|
|
5
5
|
import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, } from '../../ui/drawer.js';
|
|
6
6
|
import { Button } from '../../ui/button.js';
|
|
7
7
|
import { Separator } from '../../ui/separator.js';
|
|
8
8
|
import { CometSpinner } from '../../ui/comet-spinner.js';
|
|
9
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '../../ui/alert-dialog.js';
|
|
9
10
|
import { featureNodeStateConfig, lifecycleDisplayLabels } from '../../common/feature-node/index.js';
|
|
10
11
|
import { useFeatureActions } from './use-feature-actions.js';
|
|
11
|
-
export function FeatureDrawer({ selectedNode, onClose }) {
|
|
12
|
+
export function FeatureDrawer({ selectedNode, onClose, onDelete, isDeleting = false, }) {
|
|
12
13
|
return (_jsx(Drawer, { direction: "right", modal: false, open: selectedNode !== null, onOpenChange: (open) => {
|
|
13
14
|
if (!open)
|
|
14
15
|
onClose();
|
|
15
|
-
}, 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 })] }), selectedNode.repositoryPath && selectedNode.branch ? (_jsx(DrawerActions, { repositoryPath: selectedNode.repositoryPath, branch: selectedNode.branch })) : null, _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 }) }));
|
|
16
|
+
}, 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 })] }), selectedNode.repositoryPath && selectedNode.branch ? (_jsx(DrawerActions, { repositoryPath: selectedNode.repositoryPath, branch: selectedNode.branch })) : null, _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 }), onDelete ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx("div", { "data-testid": "feature-drawer-delete", className: "p-4", children: _jsxs(AlertDialog, { children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsxs(Button, { variant: "destructive", className: "w-full", disabled: isDeleting, children: [_jsx(Trash2, { className: "mr-2 h-4 w-4" }), "Delete feature"] }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Delete feature?" }), _jsxs(AlertDialogDescription, { children: ["This will permanently delete ", _jsx("strong", { children: selectedNode.name }), " (", selectedNode.featureId, "). This action cannot be undone.", selectedNode.state === 'running' ? (_jsx(_Fragment, { children: " This feature has a running agent that will be stopped." })) : null] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: isDeleting, children: "Cancel" }), _jsx(AlertDialogAction, { variant: "destructive", disabled: isDeleting, onClick: () => onDelete(selectedNode.featureId), children: isDeleting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Deleting\u2026"] })) : ('Delete') })] })] })] }) })] })) : null] })) : null }) }));
|
|
16
17
|
}
|
|
17
18
|
function StateBadge({ data }) {
|
|
18
19
|
const config = featureNodeStateConfig[data.state];
|
package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.d.ts
CHANGED
|
@@ -24,4 +24,10 @@ export declare const NoRepositoryPath: Story;
|
|
|
24
24
|
* (API routes are not available in Storybook).
|
|
25
25
|
*/
|
|
26
26
|
export declare const WithActionButtons: Story;
|
|
27
|
+
/** FeatureDrawer with a delete button in the footer. */
|
|
28
|
+
export declare const WithDeleteButton: Story;
|
|
29
|
+
/** FeatureDrawer showing the delete button in loading/disabled state. */
|
|
30
|
+
export declare const DeletingState: Story;
|
|
31
|
+
/** FeatureDrawer with a running agent showing the running-agent warning in the AlertDialog. */
|
|
32
|
+
export declare const DeleteRunningAgent: Story;
|
|
27
33
|
//# sourceMappingURL=feature-drawer.stories.d.ts.map
|
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;AAuDF,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"}
|
|
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;AAuDF,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
|
@@ -196,3 +196,25 @@ export const NoRepositoryPath = {
|
|
|
196
196
|
export const WithActionButtons = {
|
|
197
197
|
render: () => _jsx(DrawerTrigger, { data: runningData, label: "Open With Actions" }),
|
|
198
198
|
};
|
|
199
|
+
/* ---------------------------------------------------------------------------
|
|
200
|
+
* Delete stories
|
|
201
|
+
* ------------------------------------------------------------------------- */
|
|
202
|
+
function noop() {
|
|
203
|
+
// intentional no-op for stories
|
|
204
|
+
}
|
|
205
|
+
function DrawerTriggerWithDelete({ data, label, isDeleting = false, }) {
|
|
206
|
+
const [selected, setSelected] = useState(null);
|
|
207
|
+
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), onDelete: noop, isDeleting: isDeleting })] }));
|
|
208
|
+
}
|
|
209
|
+
/** FeatureDrawer with a delete button in the footer. */
|
|
210
|
+
export const WithDeleteButton = {
|
|
211
|
+
render: () => _jsx(DrawerTriggerWithDelete, { data: doneData, label: "Open With Delete" }),
|
|
212
|
+
};
|
|
213
|
+
/** FeatureDrawer showing the delete button in loading/disabled state. */
|
|
214
|
+
export const DeletingState = {
|
|
215
|
+
render: () => _jsx(DrawerTriggerWithDelete, { data: doneData, label: "Open Deleting State", isDeleting: true }),
|
|
216
|
+
};
|
|
217
|
+
/** FeatureDrawer with a running agent showing the running-agent warning in the AlertDialog. */
|
|
218
|
+
export const DeleteRunningAgent = {
|
|
219
|
+
render: () => _jsx(DrawerTriggerWithDelete, { data: runningData, label: "Open Running Agent Delete" }),
|
|
220
|
+
};
|
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,2CA2DzF"}
|
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, isSubmitting, pendingRepositoryPath, onNodesChange, handleConnect, handleAddFeature, handleAddFeatureToRepo, handleAddFeatureToFeature, handleAddRepository, handleNodeClick, clearSelection, handleCreateFeatureSubmit, closeCreateDrawer, } = useControlCenterState(initialNodes, initialEdges);
|
|
9
|
+
const { nodes, edges, selectedNode, isCreateDrawerOpen, isSubmitting, 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 }), _jsx(FeatureCreateDrawer, { open: isCreateDrawerOpen, onClose: closeCreateDrawer, onSubmit: handleCreateFeatureSubmit, repositoryPath: pendingRepositoryPath, isSubmitting: isSubmitting })] }));
|
|
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, isSubmitting: isSubmitting })] }));
|
|
17
17
|
}
|
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface ControlCenterState {
|
|
|
21
21
|
handleLayout: (direction: LayoutDirection) => void;
|
|
22
22
|
handleCreateFeatureSubmit: (data: CreateFeatureInput) => void;
|
|
23
23
|
closeCreateDrawer: () => void;
|
|
24
|
+
handleDeleteFeature: (featureId: string) => Promise<void>;
|
|
25
|
+
isDeleting: boolean;
|
|
24
26
|
}
|
|
25
27
|
export declare function useControlCenterState(initialNodes: CanvasNodeType[], initialEdges: Edge[]): ControlCenterState;
|
|
26
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,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAC1F,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,YAAY,EAAE,OAAO,CAAC;IACtB,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,kBAAkB,KAAK,IAAI,CAAC;IAC9D,iBAAiB,EAAE,MAAM,IAAI,CAAC;
|
|
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,kBAAkB,EAAE,MAAM,iDAAiD,CAAC;AAC1F,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,YAAY,EAAE,OAAO,CAAC;IACtB,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,kBAAkB,KAAK,IAAI,CAAC;IAC9D,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAC9B,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,UAAU,EAAE,OAAO,CAAC;CACrB;AAID,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA2UpB"}
|
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js
CHANGED
|
@@ -12,6 +12,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
12
12
|
const [selectedNode, setSelectedNode] = useState(null);
|
|
13
13
|
const [isCreateDrawerOpen, setIsCreateDrawerOpen] = useState(false);
|
|
14
14
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
15
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
15
16
|
const [pendingRepoNodeId, setPendingRepoNodeId] = useState(null);
|
|
16
17
|
const onNodesChange = useCallback((changes) => {
|
|
17
18
|
setNodes((ns) => applyNodeChanges(changes, ns));
|
|
@@ -196,6 +197,30 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
196
197
|
const closeCreateDrawer = useCallback(() => {
|
|
197
198
|
setIsCreateDrawerOpen(false);
|
|
198
199
|
}, []);
|
|
200
|
+
const handleDeleteFeature = useCallback(async (featureId) => {
|
|
201
|
+
setIsDeleting(true);
|
|
202
|
+
try {
|
|
203
|
+
const response = await fetch(`/api/features/${featureId}`, {
|
|
204
|
+
method: 'DELETE',
|
|
205
|
+
});
|
|
206
|
+
if (!response.ok) {
|
|
207
|
+
const body = await response.json();
|
|
208
|
+
toast.error(body.error ?? 'Failed to delete feature');
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
setSelectedNode(null);
|
|
212
|
+
setNodes((prev) => prev.filter((n) => n.id !== featureId));
|
|
213
|
+
setEdges((prev) => prev.filter((e) => e.source !== featureId && e.target !== featureId));
|
|
214
|
+
toast.success('Feature deleted successfully');
|
|
215
|
+
router.refresh();
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
toast.error('Failed to delete feature');
|
|
219
|
+
}
|
|
220
|
+
finally {
|
|
221
|
+
setIsDeleting(false);
|
|
222
|
+
}
|
|
223
|
+
}, [router]);
|
|
199
224
|
const handleAddFeatureToRepo = useCallback((repoNodeId) => {
|
|
200
225
|
setSelectedNode(null);
|
|
201
226
|
setPendingRepoNodeId(repoNodeId);
|
|
@@ -261,5 +286,7 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
261
286
|
handleLayout,
|
|
262
287
|
handleCreateFeatureSubmit,
|
|
263
288
|
closeCreateDrawer,
|
|
289
|
+
handleDeleteFeature,
|
|
290
|
+
isDeleting,
|
|
264
291
|
};
|
|
265
292
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { AlertDialog as AlertDialogPrimitive } from 'radix-ui';
|
|
3
|
+
import { Button } from '../ui/button.js';
|
|
4
|
+
declare function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Root>): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
declare function AlertDialogTrigger({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
declare function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
declare function AlertDialogOverlay({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare function AlertDialogContent({ className, size, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
|
|
9
|
+
size?: 'default' | 'sm';
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
declare function AlertDialogHeader({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
declare function AlertDialogFooter({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
declare function AlertDialogTitle({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Title>): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
declare function AlertDialogDescription({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Description>): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
declare function AlertDialogMedia({ className, ...props }: React.ComponentProps<'div'>): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
declare function AlertDialogAction({ className, variant, size, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Action> & Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
declare function AlertDialogCancel({ className, variant, size, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> & Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, };
|
|
19
|
+
//# sourceMappingURL=alert-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/ui/alert-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAG/D,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAEhD,iBAAS,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC,2CAExF;AAED,iBAAS,kBAAkB,CAAC,EAC1B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,2CAE3D;AAED,iBAAS,iBAAiB,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,2CAEhG;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,2CAW3D;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,OAAO,CAAC,GAAG;IAC7D,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;CACzB,2CAeA;AAED,iBAAS,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW9E;AAED,iBAAS,iBAAiB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW9E;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,KAAK,CAAC,2CAWzD;AAED,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,WAAW,CAAC,2CAQ/D;AAED,iBAAS,gBAAgB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,2CAW7E;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,GACzD,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,2CAU9D;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,MAAM,CAAC,GACzD,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC,2CAU9D;AAED,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,GACnB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AlertDialog as AlertDialogPrimitive } from 'radix-ui';
|
|
4
|
+
import { cn } from '../../lib/utils.js';
|
|
5
|
+
import { Button } from '../ui/button.js';
|
|
6
|
+
function AlertDialog({ ...props }) {
|
|
7
|
+
return _jsx(AlertDialogPrimitive.Root, { "data-slot": "alert-dialog", ...props });
|
|
8
|
+
}
|
|
9
|
+
function AlertDialogTrigger({ ...props }) {
|
|
10
|
+
return _jsx(AlertDialogPrimitive.Trigger, { "data-slot": "alert-dialog-trigger", ...props });
|
|
11
|
+
}
|
|
12
|
+
function AlertDialogPortal({ ...props }) {
|
|
13
|
+
return _jsx(AlertDialogPrimitive.Portal, { "data-slot": "alert-dialog-portal", ...props });
|
|
14
|
+
}
|
|
15
|
+
function AlertDialogOverlay({ className, ...props }) {
|
|
16
|
+
return (_jsx(AlertDialogPrimitive.Overlay, { "data-slot": "alert-dialog-overlay", className: cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50', className), ...props }));
|
|
17
|
+
}
|
|
18
|
+
function AlertDialogContent({ className, size = 'default', ...props }) {
|
|
19
|
+
return (_jsxs(AlertDialogPortal, { children: [_jsx(AlertDialogOverlay, {}), _jsx(AlertDialogPrimitive.Content, { "data-slot": "alert-dialog-content", "data-size": size, className: cn('bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg', className), ...props })] }));
|
|
20
|
+
}
|
|
21
|
+
function AlertDialogHeader({ className, ...props }) {
|
|
22
|
+
return (_jsx("div", { "data-slot": "alert-dialog-header", className: cn('grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]', className), ...props }));
|
|
23
|
+
}
|
|
24
|
+
function AlertDialogFooter({ className, ...props }) {
|
|
25
|
+
return (_jsx("div", { "data-slot": "alert-dialog-footer", className: cn('flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end', className), ...props }));
|
|
26
|
+
}
|
|
27
|
+
function AlertDialogTitle({ className, ...props }) {
|
|
28
|
+
return (_jsx(AlertDialogPrimitive.Title, { "data-slot": "alert-dialog-title", className: cn('text-lg font-semibold sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2', className), ...props }));
|
|
29
|
+
}
|
|
30
|
+
function AlertDialogDescription({ className, ...props }) {
|
|
31
|
+
return (_jsx(AlertDialogPrimitive.Description, { "data-slot": "alert-dialog-description", className: cn('text-muted-foreground text-sm', className), ...props }));
|
|
32
|
+
}
|
|
33
|
+
function AlertDialogMedia({ className, ...props }) {
|
|
34
|
+
return (_jsx("div", { "data-slot": "alert-dialog-media", className: cn("bg-muted mb-2 inline-flex size-16 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8", className), ...props }));
|
|
35
|
+
}
|
|
36
|
+
function AlertDialogAction({ className, variant = 'default', size = 'default', ...props }) {
|
|
37
|
+
return (_jsx(Button, { variant: variant, size: size, asChild: true, children: _jsx(AlertDialogPrimitive.Action, { "data-slot": "alert-dialog-action", className: cn(className), ...props }) }));
|
|
38
|
+
}
|
|
39
|
+
function AlertDialogCancel({ className, variant = 'outline', size = 'default', ...props }) {
|
|
40
|
+
return (_jsx(Button, { variant: variant, size: size, asChild: true, children: _jsx(AlertDialogPrimitive.Cancel, { "data-slot": "alert-dialog-cancel", className: cn(className), ...props }) }));
|
|
41
|
+
}
|
|
42
|
+
export { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { AlertDialog } from './alert-dialog.js';
|
|
3
|
+
declare const meta: Meta<typeof AlertDialog>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof meta>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const DestructiveAction: Story;
|
|
8
|
+
export declare const WithDescription: Story;
|
|
9
|
+
//# sourceMappingURL=alert-dialog.stories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alert-dialog.stories.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/web/components/ui/alert-dialog.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EACL,WAAW,EASZ,MAAM,gBAAgB,CAAC;AAGxB,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,WAAW,CAOlC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,OAAO,EAAE,KAoBrB,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,KAqB/B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,KAqB7B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from './alert-dialog.js';
|
|
3
|
+
import { Button } from './button.js';
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Primitives/AlertDialog',
|
|
6
|
+
component: AlertDialog,
|
|
7
|
+
parameters: {
|
|
8
|
+
layout: 'centered',
|
|
9
|
+
},
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
};
|
|
12
|
+
export default meta;
|
|
13
|
+
export const Default = {
|
|
14
|
+
render: () => (_jsxs(AlertDialog, { children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Show Dialog" }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Are you sure?" }), _jsx(AlertDialogDescription, { children: "This action cannot be undone. This will permanently apply the changes." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Cancel" }), _jsx(AlertDialogAction, { children: "Continue" })] })] })] })),
|
|
15
|
+
};
|
|
16
|
+
export const DestructiveAction = {
|
|
17
|
+
render: () => (_jsxs(AlertDialog, { children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsx(Button, { variant: "destructive", children: "Delete Account" }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Delete account?" }), _jsx(AlertDialogDescription, { children: "This will permanently delete your account and remove all associated data. This action cannot be undone." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Cancel" }), _jsx(AlertDialogAction, { variant: "destructive", children: "Delete" })] })] })] })),
|
|
18
|
+
};
|
|
19
|
+
export const WithDescription = {
|
|
20
|
+
render: () => (_jsxs(AlertDialog, { children: [_jsx(AlertDialogTrigger, { asChild: true, children: _jsx(Button, { variant: "outline", children: "Discard Changes" }) }), _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Discard unsaved changes?" }), _jsxs(AlertDialogDescription, { children: ["You have unsaved changes in ", _jsx("strong", { children: "3 files" }), ". If you discard now, all changes will be lost and cannot be recovered."] })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { children: "Keep Editing" }), _jsx(AlertDialogAction, { variant: "destructive", children: "Discard" })] })] })] })),
|
|
21
|
+
};
|