Files
orca/src/shared/github/projects/project-board-columns.ts
T

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
}