mirror of
https://github.com/stablyai/orca.git
synced 2026-11-14 01:47:24 +00:00
83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
import { EMPTY_PROJECT_GROUP_KEY, groupRowsByField } from './project-group-sort'
|
|
import type {
|
|
GitHubProjectField,
|
|
GitHubProjectFieldMutationValue,
|
|
GitHubProjectRow,
|
|
GitHubProjectView
|
|
} from './project-types'
|
|
|
|
export type ProjectBoardColumn = {
|
|
key: string
|
|
label: string
|
|
/** GitHub single-select color token ('GREEN', …) when the column is one. */
|
|
color: string | null
|
|
/** undefined is read-only; null clears the field. */
|
|
dropValue: GitHubProjectFieldMutationValue | null | undefined
|
|
rows: GitHubProjectRow[]
|
|
}
|
|
|
|
export function resolveBoardColumnField(view: GitHubProjectView): GitHubProjectField | null {
|
|
const vertical = view.verticalGroupByFields?.[0]
|
|
// Non-select fields must never expose clear-only drop targets.
|
|
if (vertical && (vertical.kind === 'single-select' || vertical.kind === 'iteration')) {
|
|
return vertical
|
|
}
|
|
// Older hosts omit vertical grouping; Status is GitHub's default.
|
|
const singleSelects = view.fields.filter((field) => field.kind === 'single-select')
|
|
return singleSelects.find((field) => /^status$/i.test(field.name)) ?? singleSelects[0] ?? null
|
|
}
|
|
|
|
/** Includes empty options, deleted-option buckets, and a final unset bucket. */
|
|
export function buildBoardColumns(
|
|
field: GitHubProjectField,
|
|
rowsInOrder: GitHubProjectRow[]
|
|
): ProjectBoardColumn[] {
|
|
const buckets = groupRowsByField(field, rowsInOrder)
|
|
const bucketsByKey = new Map(buckets.map((bucket) => [bucket.key, bucket]))
|
|
const columns: ProjectBoardColumn[] = []
|
|
if (field.kind === 'single-select') {
|
|
for (const option of field.options) {
|
|
columns.push({
|
|
key: option.id,
|
|
label: option.name,
|
|
color: option.color || null,
|
|
dropValue: { kind: 'single-select', optionId: option.id },
|
|
rows: bucketsByKey.get(option.id)?.rows ?? []
|
|
})
|
|
bucketsByKey.delete(option.id)
|
|
}
|
|
} else if (field.kind === 'iteration') {
|
|
for (const iteration of field.iterations) {
|
|
columns.push({
|
|
key: iteration.id,
|
|
label: iteration.title,
|
|
color: null,
|
|
dropValue: { kind: 'iteration', iterationId: iteration.id },
|
|
rows: bucketsByKey.get(iteration.id)?.rows ?? []
|
|
})
|
|
bucketsByKey.delete(iteration.id)
|
|
}
|
|
}
|
|
// Preserve deleted options as read-only columns.
|
|
for (const bucket of buckets) {
|
|
if (!bucketsByKey.has(bucket.key) || bucket.key === EMPTY_PROJECT_GROUP_KEY) {
|
|
continue
|
|
}
|
|
columns.push({
|
|
key: bucket.key,
|
|
label: bucket.label,
|
|
color: null,
|
|
dropValue: undefined,
|
|
rows: bucket.rows
|
|
})
|
|
}
|
|
columns.push({
|
|
key: EMPTY_PROJECT_GROUP_KEY,
|
|
label: `No ${field.name}`,
|
|
color: null,
|
|
dropValue: field.kind === 'single-select' || field.kind === 'iteration' ? null : undefined,
|
|
rows: bucketsByKey.get(EMPTY_PROJECT_GROUP_KEY)?.rows ?? []
|
|
})
|
|
return columns
|
|
}
|