You've already forked agentic-coding-workflow
feat(web): api client for project/requirement/issue
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
import { request } from './client'
|
||||
|
||||
export type Phase = 'planning' | 'auditing' | 'implementing' | 'reviewing' | 'done'
|
||||
export type Status = 'open' | 'closed'
|
||||
export type Visibility = 'private' | 'internal'
|
||||
|
||||
export interface Project {
|
||||
id: string
|
||||
slug: string
|
||||
name: string
|
||||
description: string
|
||||
visibility: Visibility
|
||||
owner_id: string
|
||||
archived_at?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface Requirement {
|
||||
id: string
|
||||
project_id: string
|
||||
number: number
|
||||
title: string
|
||||
description: string
|
||||
phase: Phase
|
||||
status: Status
|
||||
owner_id: string
|
||||
closed_at?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface Issue {
|
||||
id: string
|
||||
project_id: string
|
||||
requirement_id: string | null
|
||||
number: number
|
||||
title: string
|
||||
description: string
|
||||
status: Status
|
||||
assignee_id: string | null
|
||||
created_by: string
|
||||
closed_at?: string | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface ListWrapper<T> {
|
||||
items: T[]
|
||||
}
|
||||
|
||||
export interface RequirementDetail {
|
||||
requirement: Requirement
|
||||
issues: Issue[]
|
||||
}
|
||||
|
||||
const enc = (v: string) => encodeURIComponent(v)
|
||||
|
||||
export const projectsApi = {
|
||||
// ===== Project =====
|
||||
list: (includeArchived = false) =>
|
||||
request<ListWrapper<Project>>(`/api/v1/projects/?archived=${includeArchived}`),
|
||||
create: (body: { slug: string; name: string; description?: string; visibility?: Visibility }) =>
|
||||
request<Project>('/api/v1/projects/', { method: 'POST', body }),
|
||||
get: (slug: string) => request<Project>(`/api/v1/projects/${enc(slug)}`),
|
||||
update: (
|
||||
slug: string,
|
||||
body: Partial<{ name: string; description: string; visibility: Visibility }>,
|
||||
) => request<Project>(`/api/v1/projects/${enc(slug)}`, { method: 'PATCH', body }),
|
||||
archive: (slug: string) =>
|
||||
request<void>(`/api/v1/projects/${enc(slug)}/archive`, { method: 'POST' }),
|
||||
unarchive: (slug: string) =>
|
||||
request<void>(`/api/v1/projects/${enc(slug)}/unarchive`, { method: 'POST' }),
|
||||
|
||||
// ===== Requirement =====
|
||||
listRequirements: (slug: string, params: { phase?: Phase; status?: Status } = {}) => {
|
||||
const q = new URLSearchParams()
|
||||
if (params.phase) q.set('phase', params.phase)
|
||||
if (params.status) q.set('status', params.status)
|
||||
const qs = q.toString()
|
||||
return request<ListWrapper<Requirement>>(
|
||||
`/api/v1/projects/${enc(slug)}/requirements${qs ? '?' + qs : ''}`,
|
||||
)
|
||||
},
|
||||
createRequirement: (slug: string, body: { title: string; description?: string; owner_id?: string }) =>
|
||||
request<Requirement>(`/api/v1/projects/${enc(slug)}/requirements`, {
|
||||
method: 'POST',
|
||||
body,
|
||||
}),
|
||||
getRequirement: (slug: string, number: number) =>
|
||||
request<RequirementDetail>(`/api/v1/projects/${enc(slug)}/requirements/${number}`),
|
||||
updateRequirement: (
|
||||
slug: string,
|
||||
number: number,
|
||||
body: Partial<{ title: string; description: string; owner_id: string }>,
|
||||
) =>
|
||||
request<Requirement>(`/api/v1/projects/${enc(slug)}/requirements/${number}`, {
|
||||
method: 'PATCH',
|
||||
body,
|
||||
}),
|
||||
changeRequirementPhase: (slug: string, number: number, phase: Phase) =>
|
||||
request<Requirement>(`/api/v1/projects/${enc(slug)}/requirements/${number}/phase`, {
|
||||
method: 'POST',
|
||||
body: { phase },
|
||||
}),
|
||||
closeRequirement: (slug: string, number: number) =>
|
||||
request<void>(`/api/v1/projects/${enc(slug)}/requirements/${number}/close`, { method: 'POST' }),
|
||||
reopenRequirement: (slug: string, number: number) =>
|
||||
request<void>(`/api/v1/projects/${enc(slug)}/requirements/${number}/reopen`, { method: 'POST' }),
|
||||
|
||||
// ===== Issue =====
|
||||
listIssues: (
|
||||
slug: string,
|
||||
params: { requirement?: number | 'none'; status?: Status; assignee?: string } = {},
|
||||
) => {
|
||||
const q = new URLSearchParams()
|
||||
if (params.requirement !== undefined) q.set('requirement', String(params.requirement))
|
||||
if (params.status) q.set('status', params.status)
|
||||
if (params.assignee) q.set('assignee', params.assignee)
|
||||
const qs = q.toString()
|
||||
return request<ListWrapper<Issue>>(
|
||||
`/api/v1/projects/${enc(slug)}/issues${qs ? '?' + qs : ''}`,
|
||||
)
|
||||
},
|
||||
createIssue: (
|
||||
slug: string,
|
||||
body: {
|
||||
title: string
|
||||
description?: string
|
||||
requirement_number?: number
|
||||
assignee_id?: string
|
||||
},
|
||||
) => request<Issue>(`/api/v1/projects/${enc(slug)}/issues`, { method: 'POST', body }),
|
||||
getIssue: (slug: string, number: number) =>
|
||||
request<Issue>(`/api/v1/projects/${enc(slug)}/issues/${number}`),
|
||||
updateIssue: (
|
||||
slug: string,
|
||||
number: number,
|
||||
body: Partial<{
|
||||
title: string
|
||||
description: string
|
||||
// 三态:未传字段 / 显式 null(detach) / number(attach/move)
|
||||
requirement_number: number | null
|
||||
}>,
|
||||
) =>
|
||||
request<Issue>(`/api/v1/projects/${enc(slug)}/issues/${number}`, {
|
||||
method: 'PATCH',
|
||||
body,
|
||||
}),
|
||||
assignIssue: (slug: string, number: number, assignee_id: string | null) =>
|
||||
request<Issue>(`/api/v1/projects/${enc(slug)}/issues/${number}/assign`, {
|
||||
method: 'POST',
|
||||
body: { assignee_id },
|
||||
}),
|
||||
closeIssue: (slug: string, number: number) =>
|
||||
request<void>(`/api/v1/projects/${enc(slug)}/issues/${number}/close`, { method: 'POST' }),
|
||||
reopenIssue: (slug: string, number: number) =>
|
||||
request<void>(`/api/v1/projects/${enc(slug)}/issues/${number}/reopen`, { method: 'POST' }),
|
||||
}
|
||||
Reference in New Issue
Block a user