fix(FN-2714): restore task timer chip to footer metadata row

- Move TaskCard time indicator rendering from the header back into the footer metadata row
- Render timer and files-changed metadata together in the shared footer row layout
- Update timer chip CSS to stay right-aligned with margin-left:auto and prevent shrinking
- Update TaskCard and mobile board tests to assert footer placement and alignment behavior
This commit is contained in:
Fusion
2026-04-27 12:45:53 -07:00
committed by gsxdsm
parent fce5eb490d
commit 3e43477dc8
4 changed files with 19 additions and 17 deletions

View File

@@ -482,6 +482,8 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: var(--space-xs); gap: var(--space-xs);
margin-left: auto;
flex-shrink: 0;
padding: var(--space-xs) var(--space-sm); padding: var(--space-xs) var(--space-sm);
border: 1px solid color-mix(in srgb, var(--text-muted) 30%, transparent); border: 1px solid color-mix(in srgb, var(--text-muted) 30%, transparent);
border-radius: var(--radius-pill); border-radius: var(--radius-pill);

View File

@@ -1208,16 +1208,6 @@ function TaskCardComponent({
{abbreviateMissionTitle(missionTitle ?? task.missionId)} {abbreviateMissionTitle(missionTitle ?? task.missionId)}
</span> </span>
)} )}
{timeIndicator && (
<span
className="card-time-indicator"
title={timeIndicator.title}
aria-label={timeIndicator.ariaLabel}
>
<Clock size={12} />
<span>{timeIndicator.label}</span>
</span>
)}
<div className="card-header-actions"> <div className="card-header-actions">
{canEdit && ( {canEdit && (
<button <button
@@ -1394,9 +1384,19 @@ function TaskCardComponent({
</> </>
); );
})()} })()}
{filesChangedButton && ( {(filesChangedButton || timeIndicator) && (
<div className="card-footer-row"> <div className="card-footer-row">
{filesChangedButton} {filesChangedButton}
{timeIndicator && (
<span
className="card-time-indicator"
title={timeIndicator.title}
aria-label={timeIndicator.ariaLabel}
>
<Clock size={12} />
<span>{timeIndicator.label}</span>
</span>
)}
</div> </div>
)} )}
{((task.dependencies && task.dependencies.length > 0) || queued || task.status === "queued" || task.blockedBy) && ( {((task.dependencies && task.dependencies.length > 0) || queued || task.status === "queued" || task.blockedBy) && (

View File

@@ -503,7 +503,7 @@ describe("TaskCard", () => {
expect(timer?.getAttribute("aria-label")).toContain("Completed processing duration 2h"); expect(timer?.getAttribute("aria-label")).toContain("Completed processing duration 2h");
}); });
it("renders files-changed metadata in footer row and timer chip in header", () => { it("renders files-changed metadata and timer chip in footer row", () => {
vi.useFakeTimers(); vi.useFakeTimers();
vi.setSystemTime(new Date("2026-04-25T18:00:00.000Z")); vi.setSystemTime(new Date("2026-04-25T18:00:00.000Z"));
@@ -539,9 +539,9 @@ describe("TaskCard", () => {
expect(filesChanged).not.toBeNull(); expect(filesChanged).not.toBeNull();
expect(timer).not.toBeNull(); expect(timer).not.toBeNull();
expect(footerRow?.contains(filesChanged)).toBe(true); expect(footerRow?.contains(filesChanged)).toBe(true);
expect(footerRow?.contains(timer)).toBe(false); expect(footerRow?.contains(timer)).toBe(true);
expect(header?.contains(timer)).toBe(true); expect(header?.contains(timer)).toBe(false);
expect(Array.from(footerRow?.children ?? [])).toEqual([filesChanged]); expect(Array.from(footerRow?.children ?? [])).toEqual([filesChanged, timer]);
}); });
it.each(["triage", "todo", "in-review", "archived"] as const)( it.each(["triage", "todo", "in-review", "archived"] as const)(
"does not render timer chip for %s cards", "does not render timer chip for %s cards",

View File

@@ -274,9 +274,9 @@ describe("TaskCard mobile", () => {
expectRuleToContain(css, ".card-footer-row", "flex-wrap: nowrap;"); expectRuleToContain(css, ".card-footer-row", "flex-wrap: nowrap;");
}); });
it("keeps TaskCard timer chip in-flow and reserves right alignment for header actions", () => { it("keeps TaskCard timer chip in-flow and right-aligned in footer metadata row", () => {
const css = loadAllAppCss(); const css = loadAllAppCss();
expectRuleNotToContain(css, ".card-time-indicator", "margin-left: auto;"); expectRuleToContain(css, ".card-time-indicator", "margin-left: auto;");
expectRuleToContain(css, ".card-header-actions", "margin-left: auto;"); expectRuleToContain(css, ".card-header-actions", "margin-left: auto;");
}); });