feat: show parent task and sub-tasks at bottom of issue properties panel

Move parent-task link out of the 2-column PropertyRow layout and into
a dedicated full-width section at the bottom of the panel, separated
by a Separator. Sub-tasks are listed in the same section when present.
Each item shows a StatusIcon aligned with the first line of wrapped
title text (items-start + mt-0.5 on the icon wrapper).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Darren Davison
2026-04-04 03:55:40 +01:00
parent e23d148be1
commit b380d6000f
2 changed files with 50 additions and 14 deletions
+48 -12
View File
@@ -44,6 +44,7 @@ interface IssuePropertiesProps {
issue: Issue;
onUpdate: (data: Record<string, unknown>) => void;
inline?: boolean;
childIssues?: Issue[];
}
function PropertyRow({ label, children }: { label: string; children: React.ReactNode }) {
@@ -117,7 +118,7 @@ function PropertyPicker({
);
}
export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProps) {
export function IssueProperties({ issue, onUpdate, inline, childIssues }: IssuePropertiesProps) {
const { selectedCompanyId } = useCompany();
const queryClient = useQueryClient();
const companyId = issue.companyId ?? selectedCompanyId;
@@ -560,17 +561,6 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp
{projectContent}
</PropertyPicker>
{issue.parentId && (
<PropertyRow label="Parent">
<Link
to={`/issues/${issue.ancestors?.[0]?.identifier ?? issue.parentId}`}
className="text-sm hover:underline"
>
{issue.ancestors?.[0]?.title ?? issue.parentId.slice(0, 8)}
</Link>
</PropertyRow>
)}
{issue.requestDepth > 0 && (
<PropertyRow label="Depth">
<span className="text-sm font-mono">{issue.requestDepth}</span>
@@ -615,6 +605,52 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp
<span className="text-sm">{timeAgo(issue.updatedAt)}</span>
</PropertyRow>
</div>
{(issue.parentId || (childIssues && childIssues.length > 0)) && (
<>
<Separator />
<div className="space-y-3">
{issue.parentId && (
<div>
<p className="text-xs text-muted-foreground mb-1">Parent task</p>
<div className="flex items-start gap-1.5">
{issue.ancestors?.[0] != null && (
<div className="shrink-0 mt-0.5">
<StatusIcon status={issue.ancestors[0].status} />
</div>
)}
<Link
to={`/issues/${issue.ancestors?.[0]?.identifier ?? issue.parentId}`}
className="text-sm hover:underline"
>
{issue.ancestors?.[0]?.title ?? issue.parentId.slice(0, 8)}
</Link>
</div>
</div>
)}
{childIssues && childIssues.length > 0 && (
<div>
<p className="text-xs text-muted-foreground mb-1">Sub-tasks</p>
<div className="space-y-0.5">
{childIssues.map((child) => (
<div key={child.id} className="flex items-start gap-1.5">
<div className="shrink-0 mt-0.5">
<StatusIcon status={child.status} />
</div>
<Link
to={`/issues/${child.identifier ?? child.id}`}
className="text-sm hover:underline"
>
{child.title}
</Link>
</div>
))}
</div>
</div>
)}
</div>
</>
)}
</div>
);
}
+2 -2
View File
@@ -985,7 +985,7 @@ export function IssueDetail() {
useEffect(() => {
if (issue) {
openPanel(
<IssueProperties issue={issue} onUpdate={(data) => updateIssue.mutate(data)} />
<IssueProperties issue={issue} onUpdate={(data) => updateIssue.mutate(data)} childIssues={childIssues} />
);
}
return () => closePanel();
@@ -1699,7 +1699,7 @@ export function IssueDetail() {
</SheetHeader>
<ScrollArea className="flex-1 overflow-y-auto">
<div className="px-4 pb-4">
<IssueProperties issue={issue} onUpdate={(data) => updateIssue.mutate(data)} inline />
<IssueProperties issue={issue} onUpdate={(data) => updateIssue.mutate(data)} inline childIssues={childIssues} />
</div>
</ScrollArea>
</SheetContent>