diff --git a/apps/web/core/issues/mutations.ts b/apps/web/core/issues/mutations.ts index 78493f10..fea8fc62 100644 --- a/apps/web/core/issues/mutations.ts +++ b/apps/web/core/issues/mutations.ts @@ -90,6 +90,10 @@ export function useCreateIssue() { } : old, ); + // Invalidate parent's children query so sub-issues list updates immediately + if (newIssue.parent_issue_id) { + qc.invalidateQueries({ queryKey: issueKeys.children(wsId, newIssue.parent_issue_id) }); + } }, onSettled: () => { qc.invalidateQueries({ queryKey: issueKeys.list(wsId) }); diff --git a/apps/web/core/issues/ws-updaters.ts b/apps/web/core/issues/ws-updaters.ts index 2e9d0450..76cbb38e 100644 --- a/apps/web/core/issues/ws-updaters.ts +++ b/apps/web/core/issues/ws-updaters.ts @@ -17,6 +17,9 @@ export function onIssueCreated( doneTotal: (old.doneTotal ?? 0) + (issue.status === "done" ? 1 : 0), }; }); + if (issue.parent_issue_id) { + qc.invalidateQueries({ queryKey: issueKeys.children(wsId, issue.parent_issue_id) }); + } } export function onIssueUpdated( @@ -53,18 +56,26 @@ export function onIssueDeleted( wsId: string, issueId: string, ) { + // Look up the issue before removing it to check for parent_issue_id + const listData = qc.getQueryData(issueKeys.list(wsId)); + const deleted = listData?.issues.find((i) => i.id === issueId); + qc.setQueryData(issueKeys.list(wsId), (old) => { if (!old) return old; - const deleted = old.issues.find((i) => i.id === issueId); + const del = old.issues.find((i) => i.id === issueId); return { ...old, issues: old.issues.filter((i) => i.id !== issueId), total: old.total - 1, - doneTotal: (old.doneTotal ?? 0) - (deleted?.status === "done" ? 1 : 0), + doneTotal: (old.doneTotal ?? 0) - (del?.status === "done" ? 1 : 0), }; }); qc.removeQueries({ queryKey: issueKeys.detail(wsId, issueId) }); qc.removeQueries({ queryKey: issueKeys.timeline(issueId) }); qc.removeQueries({ queryKey: issueKeys.reactions(issueId) }); qc.removeQueries({ queryKey: issueKeys.subscribers(issueId) }); + qc.removeQueries({ queryKey: issueKeys.children(wsId, issueId) }); + if (deleted?.parent_issue_id) { + qc.invalidateQueries({ queryKey: issueKeys.children(wsId, deleted.parent_issue_id) }); + } } diff --git a/apps/web/features/issues/components/issue-detail.tsx b/apps/web/features/issues/components/issue-detail.tsx index aa23e790..1d004454 100644 --- a/apps/web/features/issues/components/issue-detail.tsx +++ b/apps/web/features/issues/components/issue-detail.tsx @@ -693,6 +693,43 @@ export function IssueDetail({ issueId, onDelete, defaultSidebarOpen = true, layo /> + {/* Sub-issues — below description, like Linear */} + {(childIssues.length > 0 || parentIssue) && ( +
+
+ Sub-issues + {childIssues.length > 0 && ( + {childIssues.length}/{childIssues.length} + )} + +
+
+ {childIssues.map((child) => ( + + + {child.title} + {child.assignee_type && child.assignee_id && ( + + )} + + ))} +
+
+ )} +
{/* Activity / Comments */} @@ -1061,43 +1098,6 @@ export function IssueDetail({ issueId, onDelete, defaultSidebarOpen = true, layo
)} - {/* Sub-issues */} -
-
- - Sub-issues - {childIssues.length > 0 && ( - {childIssues.length} - )} - -
-
- {childIssues.map((child) => ( - - - {child.identifier} - {child.title} - - ))} - {childIssues.length === 0 && ( - No sub-issues - )} -
-
- {/* Details section */}