Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion apps/web/src/components/thread/github/publish-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
import type { PrSummary } from "./use-pr-data.ts";
import { useSandboxStart } from "@/components/sandbox/hooks/use-sandbox-start";
import { publishToBaseLabel } from "./publish-label.ts";
import { useRecordPrOnBoard } from "@/hooks/use-record-pr-on-board";
import { useResolvedPublishGate } from "@/components/sandbox/hooks/use-publish-gate.ts";
import {
combinePublishDiffs,
Expand Down Expand Up @@ -129,6 +130,7 @@ function PublishDialogBody({
onPublished,
}: PublishDialogProps) {
const t = useT();
const recordPrOnBoard = useRecordPrOnBoard();
const githubClient = useMCPClient({
connectionId: githubConnectionId,
orgId,
Expand Down Expand Up @@ -360,9 +362,14 @@ function PublishDialogBody({
setIsPublishing(true);
setPublishError(undefined);
try {
await runPublishFlow(publishTarget, messageParts(), t);
const pr = await runPublishFlow(publishTarget, messageParts(), t);

toast.success(t("thread.publishDialog.publishedTo", { baseBranch }));
await recordPrOnBoard({
url: pr.htmlUrl,
repo: `${owner}/${repo}`,
merged: true,
});
handleOpenChange(false);
setGitDiff(null);
setPublishTitle("");
Expand Down Expand Up @@ -430,6 +437,7 @@ function PublishDialogBody({
const pr = await runSubmitForReviewFlow(publishTarget, messageParts());

notifySubmittedForReview(pr, t);
await recordPrOnBoard({ url: pr.htmlUrl, repo: `${owner}/${repo}` });
handleOpenChange(false);
await onPullRequestChanged?.();
} catch (error) {
Expand Down
13 changes: 12 additions & 1 deletion apps/web/src/components/thread/github/use-cms-publish-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
type PublishTarget,
} from "./publish-flow.ts";
import { discardGitFiles } from "./sandbox-git-api.ts";
import { useRecordPrOnBoard } from "@/hooks/use-record-pr-on-board";

/** `publish` merges to production; `review` stops at the pull request. */
export type CmsPublishMode = "publish" | "review";
Expand Down Expand Up @@ -66,6 +67,7 @@ export function useCmsPublishActions(
onPublished,
} = args;
const t = useT();
const recordPrOnBoard = useRecordPrOnBoard();
const [isPublishing, setIsPublishing] = useState(false);
const [isDiscarding, setIsDiscarding] = useState(false);
const [publishError, setPublishError] = useState<string>();
Expand All @@ -81,8 +83,13 @@ export function useCmsPublishActions(
setIsPublishing(true);
setPublishError(undefined);
try {
await runPublishFlow(target, noteParts(), t);
const pr = await runPublishFlow(target, noteParts(), t);

await recordPrOnBoard({
url: pr.htmlUrl,
repo: `${target.owner}/${target.repo}`,
merged: true,
});
toast.success(
destinationHost
? t("thread.publishPopover.publishedTo", { host: destinationHost })
Expand Down Expand Up @@ -113,6 +120,10 @@ export function useCmsPublishActions(
const pr = await runSubmitForReviewFlow(target, noteParts());

notifySubmittedForReview(pr, t);
await recordPrOnBoard({
url: pr.htmlUrl,
repo: `${target.owner}/${target.repo}`,
});
onOpenChange(false);
await onPullRequestChanged?.();
} catch (error) {
Expand Down
54 changes: 0 additions & 54 deletions apps/web/src/components/thread/open-in-board-button.tsx

This file was deleted.

74 changes: 74 additions & 0 deletions apps/web/src/hooks/use-record-pr-on-board.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Every PR opened from the product lands on the board.
*
* Publishing from the CMS or from a chat used to leave no trace on the board:
* the work shipped and the card never existed, so the board stopped being the
* record of what the team was doing. A PR is durable work by definition, so
* this promotes the thread (or updates its existing card) the moment one opens.
*
* Shared by the chat publish dialog and the CMS publish popover because they
* share `publish-flow.ts` — one meaning of "this shipped" for both.
*/

import { useNavigate } from "@tanstack/react-router";
import { toast } from "sonner";
import { taskKey } from "@decocms/shared/task-key";
import { useOptionalChatTask } from "@/components/chat/context";
import { useOptionalThreadManager } from "@/components/chat/store/hooks";
import { usePromoteThreadToTask } from "@/hooks/use-promote-thread-to-task";
import { useProjectContext } from "@/sdk";
import { useT } from "@/i18n/use-t.ts";

export interface RecordPrInput {
/** The PR that just opened. */
url: string;
/** `owner/name` of the repo it opened against. */
repo?: string | null;
/** True when the change is already merged (publish-and-merge). */
merged?: boolean;
}

export function useRecordPrOnBoard() {
const t = useT();
const { org } = useProjectContext();
const navigate = useNavigate();
const threadId = useOptionalChatTask()?.taskId;
const manager = useOptionalThreadManager();
const promote = usePromoteThreadToTask();

return async (input: RecordPrInput) => {
if (!threadId) return;
try {
const thread = manager?.threads.get().find((row) => row.id === threadId);
const task = await promote({
threadId,
title: thread?.title?.trim() || t("thread.addToBoard.defaultTitle"),
repo: input.repo ?? null,
prUrl: input.url,
status: input.merged ? "in_review" : "in_progress",
});
toast.success(
t("thread.addToBoard.added", {
key: taskKey(org.slug, task.keySeq) ?? task.title,
}),
{
action: {
label: t("thread.addToBoard.openTask"),
onClick: () =>
navigate({
to: ".",
search: (prev: Record<string, unknown>) => ({
...prev,
main: "board",
task: task.id,
}),
}),
},
},
);
} catch {
/* Publishing succeeded; failing to file the card must not read as a
failed publish. The board reconciles on its next list. */
}
};
}
4 changes: 2 additions & 2 deletions apps/web/src/i18n/en/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,10 @@ export const thread = {
"Getting your environment ready — this only takes a moment",
"thread.headerActions.waitingForSandboxBranchTooltip":
"Getting your environment ready — this only takes a moment",
"thread.openInBoardButton.openTaskAriaLabel": "Open task in board",
"thread.openInBoardButton.openTaskInBoard": "Open task in board",
"thread.taskCrumb.openTask": "Open {key} in the board",
"thread.addToBoard.defaultTitle": "Untitled task",
"thread.addToBoard.added": "Added to board · {key}",
"thread.addToBoard.openTask": "Open task",
"thread.publishDialog.allChangesDiscarded": "All changes discarded",
"thread.publishDialog.branchLabel": "Branch:",
"thread.publishDialog.cancel": "Cancel",
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/i18n/pt-br/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ export const thread = {
"Preparando seu ambiente — leva só um instante",
"thread.headerActions.waitingForSandboxBranchTooltip":
"Preparando seu ambiente — leva só um instante",
"thread.openInBoardButton.openTaskAriaLabel": "Abrir tarefa no quadro",
"thread.openInBoardButton.openTaskInBoard": "Abrir tarefa no quadro",
"thread.taskCrumb.openTask": "Abrir {key} no quadro",
"thread.addToBoard.defaultTitle": "Tarefa sem título",
"thread.addToBoard.added": "Adicionado ao quadro · {key}",
"thread.addToBoard.openTask": "Abrir tarefa",
"thread.publishDialog.allChangesDiscarded":
"Todas as alterações foram descartadas",
"thread.publishDialog.branchLabel": "Branch:",
Expand Down
2 changes: 0 additions & 2 deletions apps/web/src/views/virtual-mcp/header-info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { resolveFastPreview } from "@/sdk/fast-preview";
import { CmsHeaderActions } from "../../components/thread/github/cms-header-actions.tsx";
import { HeaderActions } from "../../components/thread/github/header-actions.tsx";
import { DevAgentControl } from "../../components/dev-agent/dev-agent-control.tsx";
import { OpenInBoardButton } from "../../components/thread/open-in-board-button.tsx";

/**
* The agent's header actions (dev-agent control + GitHub publish/PR buttons),
Expand All @@ -23,7 +22,6 @@ export function VirtualMcpHeaderInfo({

return (
<div className="flex items-center gap-2">
<OpenInBoardButton />
<DevAgentControl virtualMcp={virtualMcp} />
{agentShowsGithubHeaderActions(virtualMcp) ? (
fastPreviewActive ? (
Expand Down
Loading