import { useState } from "react"; import { Link, useNavigate } from "@tanstack/react-router"; import { listTransferTargets, setIssuePinned, setIssuePlanning, setSubIssue, transferIssueTo } from "~/fns/planning"; import { convertIssueToDiscussion } from "~/fns/discussion-admin"; import { useAction } from "~/hooks/use-action"; import type { getIssuePlanning } from "~/fns/planning"; import { ProgressBar } from "./ProgressBar"; import { Button } from "~/ui/Button"; import { InlineSelect, inputClass } from "~/ui/Field"; import { LabelChip } from "~/ui/LabelChip"; import { cn } from "~/utils/cn"; import { SidebarSection } from "~/ui/Heading"; type Planning = Awaited>; type Props = { owner: string; repo: string; number: number; planning: Planning }; function Choice({ label, value, options, disabled, onChange, }: { label: string; value: string; options: Array<{ id: string; name: string }>; disabled: boolean; onChange: (value: string | null) => void; }) { return ( onChange(next || null)} className="w-full" options={[{ value: "", label: "None" }, ...options.map((option) => ({ value: option.id, label: option.name }))]} /> ); } function SubIssues({ owner, repo, number, planning }: Props) { const [child, setChild] = useState(""); const change = useAction(setSubIssue); const closed = planning.subIssues.filter((issue) => issue.state === "closed").length; return ( {planning.subIssues.length > 0 && }
    {planning.subIssues.map((issue) => (
  • ● {issue.title} #{issue.number} {planning.canMaintain && ( )}
  • ))}
{planning.subIssues.length === 0 &&

None

} {planning.canMaintain && (
{ event.preventDefault(); const value = Number(child.replace(/^#/, "")); if (value > 0 && (await change.run({ data: { owner, repo, number, child: value, attach: true } }))) setChild(""); }} > setChild(event.target.value)} className={cn(inputClass, "h-7 text-xs")} />
)} {change.error &&

{change.error}

}
); } function Transfer({ owner, repo, number }: Omit) { const navigate = useNavigate(); const [targets, setTargets] = useState(null); const transfer = useAction(transferIssueTo, { invalidate: false }); if (!targets) return ( ); return (

Transfer to…

{targets.length === 0 &&

No other repositories you can write to.

} {targets.map((target) => ( ))} {transfer.error &&

{transfer.error}

}
); } /** Milestone, type, parent/sub-issues, pinning, transfer and convert-to-discussion controls for an issue. */ export function IssuePlanningSidebar({ owner, repo, number, planning }: Props) { const navigate = useNavigate(); const save = useAction(setIssuePlanning); const pin = useAction(setIssuePinned); const convert = useAction(convertIssueToDiscussion, { invalidate: false }); const types = planning.types.map((type) => ({ id: type.id, name: type.name })); const milestones = planning.milestones.map((milestone) => ({ id: milestone.id, name: milestone.title })); if (planning.milestone && !milestones.some((option) => option.id === planning.milestone?.id)) milestones.push({ id: planning.milestone.id, name: planning.milestone.title }); return (
{planning.canMaintain && types.length > 0 ? ( save.run({ data: { owner, repo, number, typeId } })} /> ) : planning.type ? ( ) : planning.canMaintain ? (

This repository has no issue types yet.{" "} Add types

) : (

None

)}
{planning.canMaintain ? ( save.run({ data: { owner, repo, number, milestoneId } })} /> ) : planning.milestone ? ( {planning.milestone.title} ) : (

None

)} {save.error &&

{save.error}

}
{planning.parent && ( {planning.parent.title} #{planning.parent.number} )} {planning.canMaintain && (
{pin.error &&

{pin.error}

} {convert.error &&

{convert.error}

}
)}
); }