Skip to content
Clone
main
168 lines 5.0 KB
/**
* Issue-board (问题集) row shapes.
*
* Types are derived from the generated response envelopes wherever the backend
* exposes one, so a schema change in `bun run gen:api` shows up here as a type
* error instead of a runtime surprise. Paged payloads are typed as the generic
* `Page` schema by the generator, so their item rows are declared structurally
* (they mirror `IssueListItemDto` / `EventDto` in `lib/issues`).
*/
import type {
EnvelopeBoardDto,
EnvelopeCommentDto,
EnvelopeIssueAttachmentDto,
EnvelopeIssueDto,
EnvelopeIssueUploadIntentDto,
EnvelopeLabelDto,
EnvelopeMilestoneDto,
EnvelopeVecCommentTreeNode,
IssuePriority,
IssueState,
UserDto,
WatchMode,
} from '@app/services/api'
export type { IssuePriority, IssueState, UserDto, WatchMode }
/** One issue board (`GET /issues/{space}/boards`). */
export type Board = NonNullable<EnvelopeBoardDto['data']>
/** Full issue payload (`GET …/issues/{issue_id}`). */
export type IssueDetail = NonNullable<EnvelopeIssueDto['data']>
/** Board label (`GET …/labels`). */
export type IssueLabel = NonNullable<EnvelopeLabelDto['data']>
/** Milestone (`GET …/milestones`). */
export type Milestone = NonNullable<EnvelopeMilestoneDto['data']>
/** One issue attachment (`GET …/issues/{id}/attachments`). */
export type IssueAttachment = NonNullable<EnvelopeIssueAttachmentDto['data']>
/** Presigned upload intent (`POST /issues/attachments`). */
export type IssueUploadIntent = NonNullable<EnvelopeIssueUploadIntentDto['data']>
/** Issue comment, flat or inside a thread tree. */
export type IssueComment = NonNullable<EnvelopeCommentDto['data']>
export type CommentNode = NonNullable<NonNullable<EnvelopeVecCommentTreeNode['data']>[number]>
/** Row of `GET …/issues` — the list endpoint omits body/description. */
export interface IssueListItem {
id: string
board_id: string
number: number
author?: UserDto | null
title: string
labels: IssueLabel[]
milestone_id?: string | null
priority: IssuePriority
due_at?: string | null
assignees: UserDto[]
state: IssueState
created_at: string
updated_at?: string | null
closed_at?: string | null
}
/** Activity line of `GET …/issues/{id}/events`. */
export interface IssueEvent {
id: string
issue_id?: string | null
user?: UserDto | null
event_type: string
comment_id?: string | null
commit_sha?: string | null
old_title?: string | null
new_title?: string | null
old_ref?: string | null
new_ref?: string | null
content?: string | null
created_at: string
}
/** Space member row used by the assignee picker. */
export interface SpaceMember {
user?: UserDto | null
alias_name?: string | null
status?: string | null
}
/** Offset page as emitted by the backend (`{items,total,limit,offset}`). */
export interface IssuePage<T> {
items: T[]
total: number
limit: number
offset: number
}
/** Filters accepted by `GET …/issues` (plus paging). */
export interface IssueQuery {
state?: IssueState | 'all'
q?: string
/** Multi-select: any of these priorities matches. */
priorities?: IssuePriority[]
labelIds?: string[]
/** `none` is a UI-only token meaning "no milestone" (filtered client-side). */
milestoneIds?: string[]
assigneeIds?: string[]
authorId?: number
sort?: IssueSort
limit?: number
offset?: number
}
/** Sort keys the backend understands; `created` is its default (newest first). */
export type IssueSort = 'created' | 'updated' | 'due' | 'priority' | 'number'
/**
* The board page's view state. The route persists it (see
* `pages/workspace/model/board-location`); the feature components only ever
* receive it as a plain object, so no UI slice depends on the page layer.
*/
export interface IssueBoardView {
/** `board` = kanban grouped by priority, `list` = table. */
view: 'board' | 'list'
state: IssueState | 'all'
/** Title keyword (backend `q`). */
q: string
/** Filter panel dimensions; empty arrays mean "no constraint". */
priorities: IssuePriority[]
labelIds: string[]
milestoneIds: string[]
assigneeIds: string[]
sort: IssueSort
/** 1-based page within the filtered result set. */
page: number
/** Open issue: board-scoped number (`42`) or uuid. Empty = board/list. */
issue: string
}
/** Body of `POST …/issues`. */
export interface NewIssueCmd {
title: string
description?: string | null
content?: string
priority?: IssuePriority
due_at?: string | null
milestone_id?: string | null
assignee_ids?: number[]
label_ids?: string[]
/** Draft upload ids from `POST /issues/attachments` to link to the new issue. */
attachment_ids?: string[]
}
/**
* Body of `PATCH …/issues/{id}`. Three-state fields: absent = unchanged,
* `null` = cleared, value = set — so the caller must not send `undefined`
* for a field it wants cleared.
*/
export interface IssuePatchCmd {
title?: string
description?: string | null
content?: string
priority?: IssuePriority
due_at?: string | null
milestone_id?: string | null
assignee_ids?: number[]
label_ids?: string[]
}