Add direct mailbox navigation to related tasks and planning sessions. - Extend mailbox message metadata with task and planning-session references. - Render accessible related-work actions in mailbox views and avoid duplicate artifact task links. - Route planning clarification actions to the associated Planning session and cover the flows with tests. Files changed: packages/core/src/types/messages.ts | 6 +++ .../app/components/MailboxArtifactAttachment.tsx | 5 +- packages/dashboard/app/components/MailboxModal.tsx | 15 ++++++ .../app/components/MailboxRelatedWorkLink.tsx | 63 ++++++++++++++++++++++ packages/dashboard/app/components/MailboxView.tsx | 15 ++++++ .../app/components/__tests__/MailboxModal.test.tsx | 31 +++++++++++ .../__tests__/MailboxRelatedWorkLink.test.tsx | 54 +++++++++++++++++++ .../app/components/__tests__/MailboxView.test.tsx | 41 ++++++++++++-- .../app/components/dashboard/MainContent.tsx | 10 ++++ .../MainContent.mailbox-view-task.test.tsx | 13 +++++ .../__tests__/planning-infinite-interview.test.ts | 35 ++++++++++++ packages/dashboard/src/planning.ts | 5 ++ 12 files changed, 288 insertions(+), 5 deletions(-) Fusion-Task-Id: FN-8428 Fusion-Task-Lineage: d0c6bf0e-7fb4-4bff-8cb2-3c75045a43da Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
64 lines
2.1 KiB
TypeScript
64 lines
2.1 KiB
TypeScript
import { useTranslation } from "react-i18next";
|
|
import type { MessageMetadata } from "@fusion/core";
|
|
|
|
export interface MailboxRelatedWorkLinkProps {
|
|
metadata?: MessageMetadata;
|
|
onOpenTask?: (taskId: string) => void;
|
|
onOpenPlanningSession?: (sessionId: string) => void;
|
|
}
|
|
|
|
function readNonEmptyString(value: unknown): string | undefined {
|
|
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
}
|
|
|
|
export function hasRelatedTaskLink(metadata: MessageMetadata | undefined, onOpenTask: MailboxRelatedWorkLinkProps["onOpenTask"]): boolean {
|
|
return Boolean(readNonEmptyString(metadata?.taskId) && onOpenTask);
|
|
}
|
|
|
|
/**
|
|
* FNXC:MailboxRelatedWork 2026-07-20-09:30:
|
|
* FN-8428 requires every response-needed mailbox message to provide a first-class route back to
|
|
* its task or planning session. Task metadata wins when both targets are present, and missing
|
|
* metadata or navigation handlers deliberately render no button so the detail never contains a
|
|
* dead affordance.
|
|
*/
|
|
export function MailboxRelatedWorkLink({
|
|
metadata,
|
|
onOpenTask,
|
|
onOpenPlanningSession,
|
|
}: MailboxRelatedWorkLinkProps) {
|
|
const { t } = useTranslation("app");
|
|
const taskId = readNonEmptyString(metadata?.taskId);
|
|
const sessionId = readNonEmptyString(metadata?.sessionId);
|
|
|
|
if (taskId && onOpenTask) {
|
|
return (
|
|
<button
|
|
type="button"
|
|
className="btn mailbox-related-work-link"
|
|
aria-label={t("mailbox.viewTaskAria", "View task: {{taskId}}", { taskId })}
|
|
data-testid="mailbox-view-task"
|
|
onClick={() => onOpenTask(taskId)}
|
|
>
|
|
{t("mailbox.viewTask", "View task")}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
if (metadata?.kind === "planning-clarification" && sessionId && onOpenPlanningSession) {
|
|
return (
|
|
<button
|
|
type="button"
|
|
className="btn mailbox-related-work-link"
|
|
aria-label={t("mailbox.openPlanningSessionAria", "Open planning session: {{sessionId}}", { sessionId })}
|
|
data-testid="mailbox-open-planning-session"
|
|
onClick={() => onOpenPlanningSession(sessionId)}
|
|
>
|
|
{t("mailbox.openPlanningSession", "Open planning session")}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|