import { type CommonReservedFields, type NoReservedFields, defaultGqlPermission, defaultPermission, } from "@tailor-platform/erp-kit/core"; import { db, type TailorAnyDBField, type TailorAnyDBType } from "@tailor-platform/sdk"; import { pipelineLifecycle } from "./pipeline.lifecycle.generated"; const builtins = (params: { userType?: TailorAnyDBType }) => { const createdByUserId = db .uuid() .description( "Foreign key to the User who created the pipeline. Immutable creation-time audit metadata; " + "ownership/responsibility semantics are a consumer concern (add via extension fields if needed).", ); return { name: db.string().description("Pipeline name"), description: db .string({ optional: true }) .description("Supplementary description or operational notes for the pipeline"), pipelineType: db .string() .description( "Pipeline kind. A free-form identifier defined by the consuming module/app (e.g. 'TASK_BOARD', 'SUPPORT_QUEUE'). " + "The set of allowed values can be injected via the pipelineTypes parameter of defineModule.", ), createdByUserId: params.userType ? createdByUserId.relation({ type: "n-1", toward: { type: params.userType, as: "createdByUser" }, backward: "createdPipelines", }) : createdByUserId, status: db .enum(pipelineLifecycle.states) .description("ACTIVE = in operation / ARCHIVED = archived"), locked: db .bool() .description( "When true, the stage configuration (lanes) is fixed to the template used at creation time and stage-mutating commands are rejected. " + "It also skips the name uniqueness constraint (for managed boards whose identity is owned by the consuming module).", ), itemNumberSeq: db .int({ optional: true }) .description( "The last itemNumber issued for this pipeline. A monotonically increasing counter. " + "Used instead of MAX(PipelineItem.itemNumber) to prevent number reuse after deletions.", ), }; }; type ReservedFields = keyof ReturnType | CommonReservedFields; export interface CreatePipelineTypeParams> { fields?: NoReservedFields; /** Related User type (user-management module). The instance wired by the consuming app is injected here. */ userType?: TailorAnyDBType; } export function createPipelineType>( params: CreatePipelineTypeParams, ) { return db .table("Pipeline", { ...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( "Pipeline. A kanban board unit that manages items. Multiple pipelines can be created per app; scoping (company, project, etc.) is a consumer concern expressed via extension fields.", ) .permission(defaultPermission) .gqlPermission(defaultGqlPermission); } export const pipeline = createPipelineType({});