From 98f6fbe9fadfe8bbf7196b87d0b65907e1ef09b7 Mon Sep 17 00:00:00 2001 From: David Butenhof Date: Fri, 9 Oct 2026 11:41:03 -0400 Subject: [PATCH] Allow attaching issues to closed milestones Sometimes we encounter an issue that should have been closed against a milestone that's now closed: allow listing them in the dialog. Signed-off-by: David Butenhof --- frontend/src/components/IssueCard.jsx | 236 +++++++++++++++------ frontend/src/components/IssueCard.test.jsx | 49 +++++ 2 files changed, 224 insertions(+), 61 deletions(-) diff --git a/frontend/src/components/IssueCard.jsx b/frontend/src/components/IssueCard.jsx index c9a51dd..0c850dc 100644 --- a/frontend/src/components/IssueCard.jsx +++ b/frontend/src/components/IssueCard.jsx @@ -39,6 +39,7 @@ import { addLabel, removeLabel, createLabel, + fetchMilestones, setIssueMilestone, removeIssueMilestone, fetchIssueReactions, @@ -150,6 +151,11 @@ const IssueCard = ({ const [isCreatingLabel, setIsCreatingLabel] = useState(false); const [currentMilestone, setCurrentMilestone] = useState(issue.milestone); const [isMilestoneMenuOpen, setIsMilestoneMenuOpen] = useState(false); + const [milestoneTab, setMilestoneTab] = useState(0); + const [closedMilestones, setClosedMilestones] = useState([]); + const [closedMilestonesLoaded, setClosedMilestonesLoaded] = useState(false); + const [closedMilestonesLoading, setClosedMilestonesLoading] = useState(false); + const [closedMilestonesError, setClosedMilestonesError] = useState(null); const milestoneMenuRef = useRef(null); const milestoneToggleRef = useRef(null); const menuRef = useRef(null); @@ -825,9 +831,10 @@ const IssueCard = ({ } else { // Set milestone await setIssueMilestone(issue.number, milestoneNumber); - const newMilestone = milestonesCache.data.find( - (m) => m.number === milestoneNumber - ); + const newMilestone = [ + ...milestonesCache.data, + ...closedMilestones, + ].find((m) => m.number === milestoneNumber); if (newMilestone) { setCurrentMilestone(newMilestone); } @@ -845,6 +852,32 @@ const IssueCard = ({ } }; + // Assisted-by: openai-code-assist + const loadClosedMilestones = () => { + if (closedMilestonesLoaded || closedMilestonesLoading) return; + + setClosedMilestonesLoading(true); + setClosedMilestonesError(null); + fetchMilestones('closed') + .then((data) => { + setClosedMilestones(data); + setClosedMilestonesLoaded(true); + setClosedMilestonesLoading(false); + }) + .catch((err) => { + setClosedMilestonesError(err.message); + setClosedMilestonesLoading(false); + }); + }; + + const handleMilestoneTabSelect = (_event, key) => { + const nextTab = Number(key); + setMilestoneTab(nextTab); + if (nextTab === 1) { + loadClosedMilestones(); + } + }; + const isLabelSelected = (labelName) => { return currentLabels.some((label) => label.name === labelName); }; @@ -1903,7 +1936,12 @@ const IssueCard = ({ >