select sprint

This commit is contained in:
Oliver Bryan
2026-01-12 02:53:09 +00:00
parent 8c0cee40a1
commit 6cdb7afa6a
2 changed files with 75 additions and 2 deletions

View File

@@ -1,4 +1,4 @@
import type { IssueResponse, ProjectResponse, UserRecord } from "@issue/shared";
import type { IssueResponse, ProjectResponse, SprintRecord, UserRecord } from "@issue/shared";
import { Check, Link, Trash, X } from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { useSession } from "@/components/session-provider";
@@ -13,9 +13,11 @@ import { SelectTrigger } from "@/components/ui/select";
import { UserSelect } from "@/components/user-select";
import { issue } from "@/lib/server";
import { issueID } from "@/lib/utils";
import { SprintSelect } from "./sprint-select";
export function IssueDetailPane({
project,
sprints,
issueData,
members,
statuses,
@@ -24,6 +26,7 @@ export function IssueDetailPane({
onIssueDelete,
}: {
project: ProjectResponse;
sprints: SprintRecord[];
issueData: IssueResponse;
members: UserRecord[];
statuses: Record<string, string>;
@@ -35,15 +38,17 @@ export function IssueDetailPane({
const [assigneeId, setAssigneeId] = useState<string>(
issueData.Issue.assigneeId?.toString() ?? "unassigned",
);
const [sprintId, setSprintId] = useState<string>(issueData.Issue.sprintId?.toString() ?? "unassigned");
const [status, setStatus] = useState<string>(issueData.Issue.status);
const [deleteOpen, setDeleteOpen] = useState(false);
const [linkCopied, setLinkCopied] = useState(false);
const copyTimeoutRef = useRef<number | null>(null);
useEffect(() => {
setSprintId(issueData.Issue.sprintId?.toString() ?? "unassigned");
setAssigneeId(issueData.Issue.assigneeId?.toString() ?? "unassigned");
setStatus(issueData.Issue.status);
}, [issueData.Issue.assigneeId, issueData.Issue.status]);
}, [issueData.Issue.sprintId, issueData.Issue.assigneeId, issueData.Issue.status]);
useEffect(() => {
return () => {
@@ -53,6 +58,23 @@ export function IssueDetailPane({
};
}, []);
const handleSprintChange = async (value: string) => {
setSprintId(value);
const newSprintId = value === "unassigned" ? null : Number(value);
await issue.update({
issueId: issueData.Issue.id,
sprintId: newSprintId,
onSuccess: () => {
onIssueUpdate?.();
},
onError: (error) => {
console.error("error updating sprint:", error);
setSprintId(issueData.Issue.sprintId?.toString() ?? "unassigned");
},
});
};
const handleAssigneeChange = async (value: string) => {
setAssigneeId(value);
const newAssigneeId = value === "unassigned" ? null : Number(value);
@@ -178,6 +200,11 @@ export function IssueDetailPane({
<p className="text-sm">{issueData.Issue.description}</p>
)}
<div className="flex items-center gap-2">
<span className="text-sm">Sprint:</span>
<SprintSelect sprints={sprints} value={sprintId} onChange={handleSprintChange} />
</div>
<div className="flex items-center gap-2">
<span className="text-sm">Assignee:</span>
<UserSelect

View File

@@ -0,0 +1,46 @@
import type { SprintRecord, UserRecord } from "@issue/shared";
import { useState } from "react";
import SmallSprintDisplay from "@/components/small-sprint-display";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
export function SprintSelect({
sprints,
value,
onChange,
placeholder = "Select sprint",
}: {
sprints: SprintRecord[];
value: string;
onChange: (value: string) => void;
fallbackUser?: UserRecord | null;
placeholder?: string;
}) {
const [isOpen, setIsOpen] = useState(false);
return (
<Select value={value} onValueChange={onChange} onOpenChange={setIsOpen}>
<SelectTrigger
className="group w-auto flex items-center -mt-1"
variant="unstyled"
chevronClassName="hidden"
isOpen={isOpen}
>
<SelectValue placeholder={placeholder} className="hover:opacity-85" />
</SelectTrigger>
<SelectContent
side="bottom"
position="popper"
className="data-[side=bottom]:translate-y-1 data-[side=bottom]:translate-x-1"
>
<SelectItem value="unassigned">
<SmallSprintDisplay />
</SelectItem>
{sprints.map((sprint) => (
<SelectItem key={sprint.id} value={sprint.id.toString()}>
<SmallSprintDisplay sprint={sprint} />
</SelectItem>
))}
</SelectContent>
</Select>
);
}