feat: triage y estimación (Fase 4)
This commit is contained in:
1 parent
9acaad4f62
commit
8f33add874
11 files changed
+208
No files matched your search
@@ -93,6 +93,13 @@ export default function CyclesPage() {
|
|||||||
</span>
|
</span>
|
||||||
<span className="ms-auto" style={{ fontSize: 12.5, color: "var(--lp-text-faint)" }}>
|
<span className="ms-auto" style={{ fontSize: 12.5, color: "var(--lp-text-faint)" }}>
|
||||||
{doneCount}/{cycleIssues.length} issues
|
{doneCount}/{cycleIssues.length} issues
|
||||||
|
{(() => {
|
||||||
|
const total = cycleIssues.reduce((sum, i) => sum + (i.estimate ?? 0), 0);
|
||||||
|
const done = cycleIssues
|
||||||
|
.filter((i) => doneKeys.has(i.status))
|
||||||
|
.reduce((sum, i) => sum + (i.estimate ?? 0), 0);
|
||||||
|
return total > 0 ? ` · ${done}/${total} pts` : "";
|
||||||
|
})()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -240,6 +240,28 @@ function IssueDetail({ issueId }: { issueId: string }) {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="prop-row">
|
||||||
|
<span className="prop-label">Estimación</span>
|
||||||
|
<select
|
||||||
|
className="prop-select"
|
||||||
|
value={issue.estimate ?? ""}
|
||||||
|
onChange={(event) =>
|
||||||
|
patch(
|
||||||
|
event.target.value === ""
|
||||||
|
? { estimate: null }
|
||||||
|
: { estimate: Number(event.target.value) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<option value="">Sin estimar</option>
|
||||||
|
{[0, 1, 2, 3, 5, 8, 13].map((points) => (
|
||||||
|
<option key={points} value={points}>
|
||||||
|
{points} pts
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="prop-row">
|
<div className="prop-row">
|
||||||
<span className="prop-label">Asignado</span>
|
<span className="prop-label">Asignado</span>
|
||||||
<select
|
<select
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { api, ApiError } from "@/lib/api";
|
||||||
|
import type { Issue, Label, User } from "@/lib/types";
|
||||||
|
import { useReference } from "@/lib/store";
|
||||||
|
import { useAuth } from "@/lib/auth";
|
||||||
|
import { useIssues } from "@/hooks/useIssues";
|
||||||
|
import { AppShell } from "@/components/layout/AppShell";
|
||||||
|
import { Avatar } from "@/components/ui/Avatar";
|
||||||
|
import { EmptyState } from "@/components/ui/EmptyState";
|
||||||
|
import { PriorityIcon, TypeIcon } from "@/components/ui/Icons";
|
||||||
|
import { LabelChip } from "@/components/ui/LabelChip";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triage (Fase 4): cola de issues nuevos sin revisar.
|
||||||
|
*
|
||||||
|
* Cada fila permite asignar estado y responsable, y el botón "Triar"
|
||||||
|
* marca needsTriage = false. Triar es "ya lo miró un humano", no un
|
||||||
|
* estado más: funciona con cualquier workflow configurado.
|
||||||
|
*/
|
||||||
|
export default function TriagePage() {
|
||||||
|
const { user } = useAuth();
|
||||||
|
const { users, labels } = useReference();
|
||||||
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const { issues, loading } = useIssues({ needsTriage: true }, refreshKey);
|
||||||
|
const userById = new Map(users.map((u) => [u.id, u]));
|
||||||
|
const labelById = new Map(labels.map((l) => [l.id, l]));
|
||||||
|
|
||||||
|
async function triage(issueId: string) {
|
||||||
|
setError(null);
|
||||||
|
try {
|
||||||
|
await api.updateIssue(issueId, {
|
||||||
|
needsTriage: false,
|
||||||
|
...(user ? { actorId: user.id } : {}),
|
||||||
|
});
|
||||||
|
setRefreshKey((k) => k + 1);
|
||||||
|
} catch (err) {
|
||||||
|
setError(err instanceof ApiError ? err.message : err instanceof Error ? err.message : "Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppShell
|
||||||
|
title="Triage"
|
||||||
|
subtitle={`${issues.length} pendiente${issues.length === 1 ? "" : "s"}`}
|
||||||
|
icon="bx-filter-alt"
|
||||||
|
>
|
||||||
|
<div className="page-pad pt-3" style={{ maxWidth: 900 }}>
|
||||||
|
{error && (
|
||||||
|
<div className="alert alert-danger py-2" role="alert">
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div className="skeleton" style={{ height: 220, borderRadius: 10 }} />
|
||||||
|
) : issues.length === 0 ? (
|
||||||
|
<EmptyState
|
||||||
|
icon="bx-check-circle"
|
||||||
|
title="Nada por triar"
|
||||||
|
description="Todos los issues ya fueron revisados."
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="d-flex flex-column gap-2">
|
||||||
|
{issues.map((issue) => (
|
||||||
|
<TriageRow
|
||||||
|
key={issue.id}
|
||||||
|
issue={issue}
|
||||||
|
userById={userById}
|
||||||
|
labelById={labelById}
|
||||||
|
onTriaged={() => void triage(issue.id)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</AppShell>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TriageRow({
|
||||||
|
issue,
|
||||||
|
userById,
|
||||||
|
labelById,
|
||||||
|
onTriaged,
|
||||||
|
}: {
|
||||||
|
issue: Issue;
|
||||||
|
userById: Map<string, User>;
|
||||||
|
labelById: Map<string, Label>;
|
||||||
|
onTriaged: () => void;
|
||||||
|
}) {
|
||||||
|
const assignee = issue.assigneeId ? userById.get(issue.assigneeId) : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="lp-card p-3 d-flex align-items-center gap-3 flex-wrap">
|
||||||
|
<PriorityIcon priority={issue.priority} />
|
||||||
|
<TypeIcon type={issue.type} />
|
||||||
|
|
||||||
|
<div className="min-w-0 flex-grow-1">
|
||||||
|
<Link href={`/issues/${issue.id}`} className="d-flex gap-2 align-items-baseline">
|
||||||
|
<span className="issue-identifier">{issue.identifier}</span>
|
||||||
|
<span className="issue-title text-truncate">{issue.title}</span>
|
||||||
|
</Link>
|
||||||
|
<div className="d-flex gap-1 mt-1 flex-wrap">
|
||||||
|
{issue.labelIds
|
||||||
|
.map((id) => labelById.get(id))
|
||||||
|
.filter((l): l is NonNullable<typeof l> => Boolean(l))
|
||||||
|
.map((label) => (
|
||||||
|
<LabelChip key={label.id} label={label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Avatar user={assignee} />
|
||||||
|
|
||||||
|
<button type="button" className="btn btn-accent btn-sm" onClick={onTriaged}>
|
||||||
|
<i className="bx bx-check me-1" />
|
||||||
|
Triar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -55,6 +55,14 @@ export function IssueRow({ issue, assignee, labels, showProject = true }: IssueR
|
|||||||
<span className="issue-identifier">{issue.createdAt.slice(0, 10)}</span>
|
<span className="issue-identifier">{issue.createdAt.slice(0, 10)}</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<td className="issue-cell issue-cell-secondary" style={{ width: 52 }}>
|
||||||
|
{issue.estimate != null && (
|
||||||
|
<span className="label-chip" title={`${issue.estimate} puntos`}>
|
||||||
|
{issue.estimate}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
|
||||||
<td className="issue-cell" style={{ width: 44 }}>
|
<td className="issue-cell" style={{ width: 44 }}>
|
||||||
<Avatar user={assignee} />
|
<Avatar user={assignee} />
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export function AppShell({ title, subtitle, actions, icon = "bx-briefcase", chil
|
|||||||
board: openIssues.length,
|
board: openIssues.length,
|
||||||
cycles: openIssues.filter((i) => i.cycleId).length,
|
cycles: openIssues.filter((i) => i.cycleId).length,
|
||||||
projects: projects.length,
|
projects: projects.length,
|
||||||
|
triage: issues.filter((i) => i.needsTriage).length,
|
||||||
};
|
};
|
||||||
}, [issues, projects, user]);
|
}, [issues, projects, user]);
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ export function CommandPalette({ open, onClose, issues }: CommandPaletteProps) {
|
|||||||
{ id: "nav-cycles", icon: "bx-reset", label: "Ir a Ciclos", hint: "C", run: () => router.push("/cycles") },
|
{ id: "nav-cycles", icon: "bx-reset", label: "Ir a Ciclos", hint: "C", run: () => router.push("/cycles") },
|
||||||
{ id: "nav-projects", icon: "bx-folder", label: "Ir a Proyectos", hint: "P", run: () => router.push("/projects") },
|
{ id: "nav-projects", icon: "bx-folder", label: "Ir a Proyectos", hint: "P", run: () => router.push("/projects") },
|
||||||
{ id: "nav-settings", icon: "bx-cog", label: "Ir a Configuración", hint: "S", run: () => router.push("/settings") },
|
{ id: "nav-settings", icon: "bx-cog", label: "Ir a Configuración", hint: "S", run: () => router.push("/settings") },
|
||||||
|
{ id: "nav-triage", icon: "bx-filter-alt", label: "Ir a Triage", hint: "T", run: () => router.push("/triage") },
|
||||||
{
|
{
|
||||||
id: "new-issue",
|
id: "new-issue",
|
||||||
icon: "bx-plus-circle",
|
icon: "bx-plus-circle",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface SidebarProps {
|
|||||||
board: number;
|
board: number;
|
||||||
cycles: number;
|
cycles: number;
|
||||||
projects: number;
|
projects: number;
|
||||||
|
triage: number;
|
||||||
};
|
};
|
||||||
projects: Array<{ id: string; name: string }>;
|
projects: Array<{ id: string; name: string }>;
|
||||||
/** Controla el estado abierto/colapsado del shell. */
|
/** Controla el estado abierto/colapsado del shell. */
|
||||||
@@ -33,6 +34,7 @@ export function Sidebar({ counts, projects, open }: SidebarProps) {
|
|||||||
{ href: "/cycles", icon: "bx-reset", label: "Ciclos", count: counts.cycles },
|
{ href: "/cycles", icon: "bx-reset", label: "Ciclos", count: counts.cycles },
|
||||||
{ href: "/projects", icon: "bx-folder", label: "Proyectos", count: counts.projects },
|
{ href: "/projects", icon: "bx-folder", label: "Proyectos", count: counts.projects },
|
||||||
{ href: "/settings", icon: "bx-cog", label: "Configuración", count: 0 },
|
{ href: "/settings", icon: "bx-cog", label: "Configuración", count: 0 },
|
||||||
|
{ href: "/triage", icon: "bx-filter-alt", label: "Triage", count: counts.triage },
|
||||||
];
|
];
|
||||||
|
|
||||||
const isActive = (href: string) =>
|
const isActive = (href: string) =>
|
||||||
|
|||||||
@@ -95,6 +95,12 @@ function buildQuery(filters?: IssueFilters): string {
|
|||||||
// El backend llama `q` a la búsqueda de texto.
|
// El backend llama `q` a la búsqueda de texto.
|
||||||
if (filters.query?.trim()) params.append("q", filters.query.trim());
|
if (filters.query?.trim()) params.append("q", filters.query.trim());
|
||||||
|
|
||||||
|
// Triage y estimación (Fase 4 en sam).
|
||||||
|
if (filters.needsTriage !== undefined)
|
||||||
|
params.append("needsTriage", String(filters.needsTriage));
|
||||||
|
if (filters.estimated !== undefined)
|
||||||
|
params.append("estimated", String(filters.estimated));
|
||||||
|
|
||||||
const qs = params.toString();
|
const qs = params.toString();
|
||||||
return qs ? `?${qs}` : "";
|
return qs ? `?${qs}` : "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,10 @@ function matchesFilters(issue: Issue, filters?: IssueFilters): boolean {
|
|||||||
if (filters.cycleId?.length && !filters.cycleId.includes(issue.cycleId ?? "")) return false;
|
if (filters.cycleId?.length && !filters.cycleId.includes(issue.cycleId ?? "")) return false;
|
||||||
if (filters.labelIds?.length && !filters.labelIds.some((id) => issue.labelIds.includes(id)))
|
if (filters.labelIds?.length && !filters.labelIds.some((id) => issue.labelIds.includes(id)))
|
||||||
return false;
|
return false;
|
||||||
|
if (filters.needsTriage !== undefined && issue.needsTriage !== filters.needsTriage)
|
||||||
|
return false;
|
||||||
|
if (filters.estimated === true && issue.estimate == null) return false;
|
||||||
|
if (filters.estimated === false && issue.estimate != null) return false;
|
||||||
|
|
||||||
if (filters.query?.trim()) {
|
if (filters.query?.trim()) {
|
||||||
const q = filters.query.trim().toLowerCase();
|
const q = filters.query.trim().toLowerCase();
|
||||||
@@ -206,6 +210,8 @@ export const mockApi: ProyectosApi = {
|
|||||||
const status = input.status ?? "todo";
|
const status = input.status ?? "todo";
|
||||||
const known = state.states.find((s) => s.key === status && s.isActive);
|
const known = state.states.find((s) => s.key === status && s.isActive);
|
||||||
if (!known) throw new Error(`Estado desconocido o inactivo: '${status}'.`);
|
if (!known) throw new Error(`Estado desconocido o inactivo: '${status}'.`);
|
||||||
|
if (input.estimate != null && input.estimate < 0)
|
||||||
|
throw new Error("La estimación no puede ser negativa.");
|
||||||
const project = state.projects.find((p) => p.id === input.projectId);
|
const project = state.projects.find((p) => p.id === input.projectId);
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
|
|
||||||
@@ -216,6 +222,8 @@ export const mockApi: ProyectosApi = {
|
|||||||
description: input.description ?? "",
|
description: input.description ?? "",
|
||||||
status,
|
status,
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
needsTriage: true,
|
||||||
|
estimate: input.estimate ?? null,
|
||||||
priority: input.priority ?? 0,
|
priority: input.priority ?? 0,
|
||||||
type: input.type ?? "task",
|
type: input.type ?? "task",
|
||||||
assigneeId: input.assigneeId ?? null,
|
assigneeId: input.assigneeId ?? null,
|
||||||
@@ -255,6 +263,9 @@ export const mockApi: ProyectosApi = {
|
|||||||
if (patch.projectId !== undefined) track("project", issue.projectId, patch.projectId);
|
if (patch.projectId !== undefined) track("project", issue.projectId, patch.projectId);
|
||||||
if (patch.cycleId !== undefined) track("cycle", issue.cycleId, patch.cycleId);
|
if (patch.cycleId !== undefined) track("cycle", issue.cycleId, patch.cycleId);
|
||||||
if (patch.parentId !== undefined) track("parent", issue.parentId ?? null, patch.parentId);
|
if (patch.parentId !== undefined) track("parent", issue.parentId ?? null, patch.parentId);
|
||||||
|
if (patch.needsTriage !== undefined)
|
||||||
|
track("triage", issue.needsTriage ? "pendiente" : "triado", patch.needsTriage ? "pendiente" : "triado");
|
||||||
|
if (patch.estimate !== undefined) track("estimate", issue.estimate, patch.estimate);
|
||||||
if (patch.labelIds !== undefined)
|
if (patch.labelIds !== undefined)
|
||||||
track("labels", issue.labelIds.join(","), patch.labelIds.join(","));
|
track("labels", issue.labelIds.join(","), patch.labelIds.join(","));
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ export const issues: Issue[] = [
|
|||||||
projectId: "p1",
|
projectId: "p1",
|
||||||
cycleId: "c3",
|
cycleId: "c3",
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
needsTriage: false,
|
||||||
|
estimate: 5,
|
||||||
labelIds: ["l2"],
|
labelIds: ["l2"],
|
||||||
createdAt: "2026-10-01T09:15:00Z",
|
createdAt: "2026-10-01T09:15:00Z",
|
||||||
updatedAt: "2026-10-12T15:40:00Z",
|
updatedAt: "2026-10-12T15:40:00Z",
|
||||||
@@ -131,6 +133,8 @@ export const issues: Issue[] = [
|
|||||||
projectId: "p1",
|
projectId: "p1",
|
||||||
cycleId: "c3",
|
cycleId: "c3",
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
needsTriage: false,
|
||||||
|
estimate: 3,
|
||||||
labelIds: ["l1", "l4"],
|
labelIds: ["l1", "l4"],
|
||||||
createdAt: "2026-10-03T11:00:00Z",
|
createdAt: "2026-10-03T11:00:00Z",
|
||||||
updatedAt: "2026-10-10T08:20:00Z",
|
updatedAt: "2026-10-10T08:20:00Z",
|
||||||
@@ -148,6 +152,8 @@ export const issues: Issue[] = [
|
|||||||
projectId: "p1",
|
projectId: "p1",
|
||||||
cycleId: "c3",
|
cycleId: "c3",
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
needsTriage: false,
|
||||||
|
estimate: 8,
|
||||||
labelIds: ["l3"],
|
labelIds: ["l3"],
|
||||||
createdAt: "2026-09-28T14:30:00Z",
|
createdAt: "2026-09-28T14:30:00Z",
|
||||||
updatedAt: "2026-10-11T17:05:00Z",
|
updatedAt: "2026-10-11T17:05:00Z",
|
||||||
@@ -165,6 +171,8 @@ export const issues: Issue[] = [
|
|||||||
projectId: "p1",
|
projectId: "p1",
|
||||||
cycleId: "c3",
|
cycleId: "c3",
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
needsTriage: false,
|
||||||
|
estimate: null,
|
||||||
labelIds: ["l2"],
|
labelIds: ["l2"],
|
||||||
createdAt: "2026-09-20T10:00:00Z",
|
createdAt: "2026-09-20T10:00:00Z",
|
||||||
updatedAt: "2026-10-08T12:00:00Z",
|
updatedAt: "2026-10-08T12:00:00Z",
|
||||||
@@ -182,6 +190,8 @@ export const issues: Issue[] = [
|
|||||||
projectId: "p2",
|
projectId: "p2",
|
||||||
cycleId: null,
|
cycleId: null,
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
needsTriage: false,
|
||||||
|
estimate: null,
|
||||||
labelIds: ["l1", "l3"],
|
labelIds: ["l1", "l3"],
|
||||||
createdAt: "2026-10-06T09:00:00Z",
|
createdAt: "2026-10-06T09:00:00Z",
|
||||||
updatedAt: "2026-10-06T09:00:00Z",
|
updatedAt: "2026-10-06T09:00:00Z",
|
||||||
@@ -199,6 +209,8 @@ export const issues: Issue[] = [
|
|||||||
projectId: "p2",
|
projectId: "p2",
|
||||||
cycleId: "c4",
|
cycleId: "c4",
|
||||||
parentId: "i5",
|
parentId: "i5",
|
||||||
|
needsTriage: false,
|
||||||
|
estimate: 5,
|
||||||
labelIds: ["l2"],
|
labelIds: ["l2"],
|
||||||
createdAt: "2026-10-07T16:45:00Z",
|
createdAt: "2026-10-07T16:45:00Z",
|
||||||
updatedAt: "2026-10-09T10:10:00Z",
|
updatedAt: "2026-10-09T10:10:00Z",
|
||||||
@@ -216,6 +228,8 @@ export const issues: Issue[] = [
|
|||||||
projectId: "p3",
|
projectId: "p3",
|
||||||
cycleId: "c2",
|
cycleId: "c2",
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
needsTriage: false,
|
||||||
|
estimate: null,
|
||||||
labelIds: ["l5"],
|
labelIds: ["l5"],
|
||||||
createdAt: "2026-08-15T13:20:00Z",
|
createdAt: "2026-08-15T13:20:00Z",
|
||||||
updatedAt: "2026-09-30T09:00:00Z",
|
updatedAt: "2026-09-30T09:00:00Z",
|
||||||
@@ -233,6 +247,8 @@ export const issues: Issue[] = [
|
|||||||
projectId: "p1",
|
projectId: "p1",
|
||||||
cycleId: "c4",
|
cycleId: "c4",
|
||||||
parentId: null,
|
parentId: null,
|
||||||
|
needsTriage: false,
|
||||||
|
estimate: null,
|
||||||
labelIds: ["l5"],
|
labelIds: ["l5"],
|
||||||
createdAt: "2026-10-09T08:00:00Z",
|
createdAt: "2026-10-09T08:00:00Z",
|
||||||
updatedAt: "2026-10-09T08:00:00Z",
|
updatedAt: "2026-10-09T08:00:00Z",
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ export interface Issue {
|
|||||||
cycleId: ID | null;
|
cycleId: ID | null;
|
||||||
/** Id del issue padre (sub-issues, Fase 3 en sam). Null/ausente = raíz. */
|
/** Id del issue padre (sub-issues, Fase 3 en sam). Null/ausente = raíz. */
|
||||||
parentId: ID | null;
|
parentId: ID | null;
|
||||||
|
/** TRUE = pendiente de triage (Fase 4 en sam). */
|
||||||
|
needsTriage: boolean;
|
||||||
|
/** Puntos de historia, null = sin estimar (Fase 4 en sam). */
|
||||||
|
estimate: number | null;
|
||||||
labelIds: ID[];
|
labelIds: ID[];
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
@@ -128,6 +132,8 @@ export interface Issue {
|
|||||||
|
|
||||||
/** Filtros soportados por `listIssues`. */
|
/** Filtros soportados por `listIssues`. */
|
||||||
export interface IssueFilters {
|
export interface IssueFilters {
|
||||||
|
needsTriage?: boolean;
|
||||||
|
estimated?: boolean;
|
||||||
status?: IssueStatus[];
|
status?: IssueStatus[];
|
||||||
priority?: IssuePriority[];
|
priority?: IssuePriority[];
|
||||||
assigneeId?: ID[];
|
assigneeId?: ID[];
|
||||||
@@ -152,6 +158,8 @@ export interface IssueInput {
|
|||||||
projectId?: ID | null;
|
projectId?: ID | null;
|
||||||
cycleId?: ID | null;
|
cycleId?: ID | null;
|
||||||
parentId?: ID | null;
|
parentId?: ID | null;
|
||||||
|
needsTriage?: boolean;
|
||||||
|
estimate?: number | null;
|
||||||
labelIds?: ID[];
|
labelIds?: ID[];
|
||||||
actorId?: ID;
|
actorId?: ID;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user