story-kit: merge 342_story_web_ui_button_to_delete_a_story_from_the_pipeline
This commit is contained in:
@@ -373,6 +373,10 @@ export const api = {
|
||||
launchQaApp(storyId: string) {
|
||||
return callMcpTool("launch_qa_app", { story_id: storyId });
|
||||
},
|
||||
/** Delete a story from the pipeline, stopping any running agent and removing the worktree. */
|
||||
deleteStory(storyId: string) {
|
||||
return callMcpTool("delete_story", { story_id: storyId });
|
||||
},
|
||||
};
|
||||
|
||||
async function callMcpTool(
|
||||
|
||||
@@ -213,6 +213,15 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
const [selectedWorkItemId, setSelectedWorkItemId] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const handleDeleteItem = React.useCallback(
|
||||
(item: import("../api/client").PipelineStageItem) => {
|
||||
api.deleteStory(item.story_id).catch((err: unknown) => {
|
||||
console.error("Failed to delete story:", err);
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
const [queuedMessages, setQueuedMessages] = useState<
|
||||
{ id: string; text: string }[]
|
||||
>([]);
|
||||
@@ -1089,6 +1098,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
costs={storyTokenCosts}
|
||||
onItemClick={(item) => setSelectedWorkItemId(item.story_id)}
|
||||
onStopAgent={handleStopAgent}
|
||||
onDeleteItem={handleDeleteItem}
|
||||
/>
|
||||
<StagePanel
|
||||
title="To Merge"
|
||||
@@ -1096,6 +1106,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
costs={storyTokenCosts}
|
||||
onItemClick={(item) => setSelectedWorkItemId(item.story_id)}
|
||||
onStopAgent={handleStopAgent}
|
||||
onDeleteItem={handleDeleteItem}
|
||||
/>
|
||||
<StagePanel
|
||||
title="QA"
|
||||
@@ -1103,6 +1114,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
costs={storyTokenCosts}
|
||||
onItemClick={(item) => setSelectedWorkItemId(item.story_id)}
|
||||
onStopAgent={handleStopAgent}
|
||||
onDeleteItem={handleDeleteItem}
|
||||
/>
|
||||
<StagePanel
|
||||
title="Current"
|
||||
@@ -1113,6 +1125,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
busyAgentNames={busyAgentNames}
|
||||
onStartAgent={handleStartAgent}
|
||||
onStopAgent={handleStopAgent}
|
||||
onDeleteItem={handleDeleteItem}
|
||||
/>
|
||||
<StagePanel
|
||||
title="Backlog"
|
||||
@@ -1123,6 +1136,7 @@ export function Chat({ projectPath, onCloseProject }: ChatProps) {
|
||||
busyAgentNames={busyAgentNames}
|
||||
onStartAgent={handleStartAgent}
|
||||
onStopAgent={handleStopAgent}
|
||||
onDeleteItem={handleDeleteItem}
|
||||
/>
|
||||
<ServerLogsPanel logs={serverLogs} />
|
||||
</>
|
||||
|
||||
@@ -44,6 +44,7 @@ interface StagePanelProps {
|
||||
emptyMessage?: string;
|
||||
onItemClick?: (item: PipelineStageItem) => void;
|
||||
onStopAgent?: (storyId: string, agentName: string) => void;
|
||||
onDeleteItem?: (item: PipelineStageItem) => void;
|
||||
/** Map of story_id → total_cost_usd for displaying cost badges. */
|
||||
costs?: Map<string, number>;
|
||||
/** Agent roster to populate the start agent dropdown. */
|
||||
@@ -253,6 +254,7 @@ export function StagePanel({
|
||||
emptyMessage = "Empty.",
|
||||
onItemClick,
|
||||
onStopAgent,
|
||||
onDeleteItem,
|
||||
costs,
|
||||
agentRoster,
|
||||
busyAgentNames,
|
||||
@@ -444,9 +446,8 @@ export function StagePanel({
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return onItemClick ? (
|
||||
const card = onItemClick ? (
|
||||
<button
|
||||
key={`${title}-${item.story_id}`}
|
||||
type="button"
|
||||
data-testid={`card-${item.story_id}`}
|
||||
onClick={() => onItemClick(item)}
|
||||
@@ -455,12 +456,57 @@ export function StagePanel({
|
||||
{cardInner}
|
||||
</button>
|
||||
) : (
|
||||
<div data-testid={`card-${item.story_id}`} style={cardStyle}>
|
||||
{cardInner}
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
key={`${title}-${item.story_id}`}
|
||||
data-testid={`card-${item.story_id}`}
|
||||
style={cardStyle}
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
{cardInner}
|
||||
{card}
|
||||
{onDeleteItem && (
|
||||
<button
|
||||
type="button"
|
||||
data-testid={`delete-btn-${item.story_id}`}
|
||||
title={`Delete ${item.name ?? item.story_id}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
const label = item.name ?? item.story_id;
|
||||
if (
|
||||
window.confirm(
|
||||
`Delete "${label}"? This cannot be undone.`,
|
||||
)
|
||||
) {
|
||||
onDeleteItem(item);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "4px",
|
||||
right: "4px",
|
||||
background: "transparent",
|
||||
border: "none",
|
||||
color: "#555",
|
||||
cursor: "pointer",
|
||||
fontSize: "0.85em",
|
||||
lineHeight: 1,
|
||||
padding: "2px 4px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
(e.currentTarget as HTMLButtonElement).style.color =
|
||||
"#f85149";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
(e.currentTarget as HTMLButtonElement).style.color =
|
||||
"#555";
|
||||
}}
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user