fix(FN-4228): handle unresolved github tracking detail as loading
Fusion-Task-Id: FN-4228 Fusion-Task-Lineage: 86a2aac1-a45f-4cb0-b004-a95db3676355
This commit is contained in:
@@ -812,10 +812,21 @@ export function TaskDetailContent({
|
|||||||
const canEditGithubTracking = GITHUB_TRACKING_EDITABLE_COLUMNS.has(task.column) && !isSaving;
|
const canEditGithubTracking = GITHUB_TRACKING_EDITABLE_COLUMNS.has(task.column) && !isSaving;
|
||||||
const githubTrackingEnabled = githubTrackingEnabledDraft ?? (workingTask.githubTracking?.enabled === true);
|
const githubTrackingEnabled = githubTrackingEnabledDraft ?? (workingTask.githubTracking?.enabled === true);
|
||||||
const githubTrackedIssue = workingTask.githubTracking?.issue;
|
const githubTrackedIssue = workingTask.githubTracking?.issue;
|
||||||
|
const githubTrackingDetailPending = detailLoading && typeof task.githubTracking === "undefined";
|
||||||
const showInlineGithubTrackingEnableButton =
|
const showInlineGithubTrackingEnableButton =
|
||||||
canEditGithubTracking && !githubTrackedIssue && (!githubTrackingEnabled || (isSavingGithubTracking && workingTask.githubTracking?.enabled !== true));
|
canEditGithubTracking
|
||||||
|
&& !githubTrackedIssue
|
||||||
|
&& !githubTrackingDetailPending
|
||||||
|
&& (!githubTrackingEnabled || (isSavingGithubTracking && workingTask.githubTracking?.enabled !== true));
|
||||||
const showGithubTrackingSection = canEditGithubTracking || githubTrackingEnabled || Boolean(githubTrackedIssue);
|
const showGithubTrackingSection = canEditGithubTracking || githubTrackingEnabled || Boolean(githubTrackedIssue);
|
||||||
const githubTrackingStatus = githubTrackedIssue ? "Linked" : githubTrackingEnabled ? "Enabled" : "Disabled";
|
const githubTrackingStatus = githubTrackingDetailPending
|
||||||
|
? "Loading"
|
||||||
|
: githubTrackedIssue
|
||||||
|
? "Linked"
|
||||||
|
: githubTrackingEnabled
|
||||||
|
? "Enabled"
|
||||||
|
: "Disabled";
|
||||||
|
const showGithubTrackingSpinner = !githubTrackedIssue && (isSavingGithubTracking || githubTrackingDetailPending);
|
||||||
const effectiveGithubRepoDefault = resolveEffectiveGithubRepoDefault(settings ?? null, globalSettings);
|
const effectiveGithubRepoDefault = resolveEffectiveGithubRepoDefault(settings ?? null, globalSettings);
|
||||||
const githubRepoOverrideTrimmed = githubRepoOverrideDraft.trim();
|
const githubRepoOverrideTrimmed = githubRepoOverrideDraft.trim();
|
||||||
|
|
||||||
@@ -2411,7 +2422,11 @@ export function TaskDetailContent({
|
|||||||
</span>
|
</span>
|
||||||
{!githubTrackedIssue && (
|
{!githubTrackedIssue && (
|
||||||
<span className="detail-source-empty">
|
<span className="detail-source-empty">
|
||||||
{githubTrackingEnabled ? "Issue not yet created" : "Tracking is currently disabled"}
|
{githubTrackingDetailPending
|
||||||
|
? "Checking tracking status"
|
||||||
|
: githubTrackingEnabled
|
||||||
|
? "Issue not yet created"
|
||||||
|
: "Tracking is currently disabled"}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -2425,15 +2440,17 @@ export function TaskDetailContent({
|
|||||||
Enable
|
Enable
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{isSavingGithubTracking && !githubTrackedIssue && (
|
{showGithubTrackingSpinner && (
|
||||||
<span
|
<span
|
||||||
className="detail-github-tracking-spinner"
|
className="detail-github-tracking-spinner"
|
||||||
role="status"
|
role="status"
|
||||||
aria-live="polite"
|
aria-live="polite"
|
||||||
aria-label="Enabling GitHub tracking"
|
aria-label={isSavingGithubTracking ? "Enabling GitHub tracking" : "Loading GitHub tracking status"}
|
||||||
>
|
>
|
||||||
<Loader2 size={16} className="spin" aria-hidden="true" />
|
<Loader2 size={16} className="spin" aria-hidden="true" />
|
||||||
<span className="visually-hidden">Enabling GitHub tracking…</span>
|
<span className="visually-hidden">
|
||||||
|
{isSavingGithubTracking ? "Enabling GitHub tracking…" : "Loading GitHub tracking status…"}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -2301,6 +2301,54 @@ describe("TaskDetailModal", () => {
|
|||||||
expect(screen.getByRole("button", { name: "Enable GitHub tracking" })).toHaveTextContent("Enable");
|
expect(screen.getByRole("button", { name: "Enable GitHub tracking" })).toHaveTextContent("Enable");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("FN-4228 shows loading state instead of disabled CTA while detail fetch is unresolved", async () => {
|
||||||
|
const { fetchTaskDetail } = await import("../../api");
|
||||||
|
const mockFetch = vi.mocked(fetchTaskDetail);
|
||||||
|
let resolveFetch: ((value: TaskDetail) => void) | undefined;
|
||||||
|
mockFetch.mockImplementationOnce(
|
||||||
|
() =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
resolveFetch = resolve;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const optimisticTask = makeTask({ id: "FN-001", column: "todo" });
|
||||||
|
delete (optimisticTask as Partial<Task>).prompt;
|
||||||
|
delete (optimisticTask as Partial<Task>).githubTracking;
|
||||||
|
|
||||||
|
render(
|
||||||
|
<TaskDetailModal
|
||||||
|
task={optimisticTask}
|
||||||
|
onClose={noop}
|
||||||
|
onOpenDetail={noopOpenDetail}
|
||||||
|
onMoveTask={noopMove}
|
||||||
|
onDeleteTask={noopDelete}
|
||||||
|
onMergeTask={noopMerge}
|
||||||
|
addToast={noop}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText("GitHub tracking status")).toHaveTextContent("Loading");
|
||||||
|
expect(screen.getByText("Checking tracking status")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: "Enable GitHub tracking" })).toBeNull();
|
||||||
|
expect(screen.getByRole("status", { name: "Loading GitHub tracking status" })).toBeInTheDocument();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
resolveFetch?.({
|
||||||
|
...(makeTask({ id: "FN-001", column: "todo" }) as TaskDetail),
|
||||||
|
prompt: "# Spec",
|
||||||
|
githubTracking: { enabled: false },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText("GitHub tracking status")).toHaveTextContent("Disabled");
|
||||||
|
});
|
||||||
|
expect(screen.getByText("Tracking is currently disabled")).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("status", { name: "Loading GitHub tracking status" })).toBeNull();
|
||||||
|
expect(screen.getByRole("button", { name: "Enable GitHub tracking" })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("enables GitHub tracking via the inline header button without expanding the disclosure", async () => {
|
it("enables GitHub tracking via the inline header button without expanding the disclosure", async () => {
|
||||||
const { updateTask } = await import("../../api");
|
const { updateTask } = await import("../../api");
|
||||||
const mockUpdate = vi.mocked(updateTask);
|
const mockUpdate = vi.mocked(updateTask);
|
||||||
|
|||||||
Reference in New Issue
Block a user