import { type CommonReservedFields, type NoReservedFields, defaultGqlPermission, defaultPermission, } from "@tailor-platform/erp-kit/core"; import { db, type TailorAnyDBField, type TailorAnyDBType } from "@tailor-platform/sdk"; import { pipelineItem } from "./pipelineItem"; export const PIPELINE_ITEM_CHANGE_TYPES = ["CONTENT", "FIELD", "LIFECYCLE", "LABEL"] as const; export const PIPELINE_ITEM_CHANGE_FIELD_NAMES = [ "TITLE", "DESCRIPTION", "ASSIGNEE", "PRIORITY", "DUE_DATE", "LIFECYCLE", "LABEL", ] as const; const builtins = (params: { userType?: TailorAnyDBType }) => { const changedByUserId = db.uuid().description("Foreign key to User who made the change"); return { itemId: db .uuid() .relation({ type: "n-1", toward: { type: pipelineItem, as: "item" }, backward: "changes", }) .description("ID of the item that changed"), changedByUserId: params.userType ? changedByUserId.relation({ type: "n-1", toward: { type: params.userType, as: "changedByUser" }, backward: "pipelineItemChanges", }) : changedByUserId, changeType: db .enum(PIPELINE_ITEM_CHANGE_TYPES) .description( "Change category. CONTENT = title/description / FIELD = assignee/priority/dueDate / LIFECYCLE = DRAFT→OPEN→CLOSED / LABEL = label attach/detach", ), fieldName: db .enum(PIPELINE_ITEM_CHANGE_FIELD_NAMES) .description( "Name of the changed field. TITLE / DESCRIPTION / ASSIGNEE / PRIORITY / DUE_DATE / LIFECYCLE / LABEL", ), prevValue: db .string({ optional: true }) .description( "Value before the change (one of uuid / enum / ISO date / string depending on the field type; null if it was previously unset).", ), newValue: db .string({ optional: true }) .description( "Value after the change (one of uuid / enum / ISO date / string depending on the field type; null if it was cleared).", ), }; }; type ReservedFields = keyof ReturnType | CommonReservedFields; export interface CreatePipelineItemChangeTypeParams> { fields?: NoReservedFields; /** Related User type (user-management module). The instance wired by the consuming app is injected here. */ userType?: TailorAnyDBType; } export function createPipelineItemChangeType>( params: CreatePipelineItemChangeTypeParams, ) { return db .table("PipelineItemChange", { ...builtins(params), // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- mirrors erp-kit's createXxxType factories; field bag is constrained but TS can't prove F is concrete ...((params.fields ?? {}) as F), ...db.fields.timestamps(), }) .description( "PipelineItem change history (other than PipelineStageTransition). An append-only log where one row is added per field that changed in a single operation.", ) .permission(defaultPermission) .gqlPermission(defaultGqlPermission); } export const pipelineItemChange = createPipelineItemChangeType({});