// Copyright 2026 Moko Consulting // SPDX-License-Identifier: GPL-3.0-or-later package issues import ( "context" "code.mokoconsulting.tech/MokoConsulting/MokoGitea/models/db" "code.mokoconsulting.tech/MokoConsulting/MokoGitea/modules/timeutil" ) func init() { db.RegisterModel(new(IssuePriorityDef)) } // IssuePriorityDef defines a custom issue priority at the org level. type IssuePriorityDef struct { ID int64 `xorm:"pk autoincr"` OrgID int64 `xorm:"INDEX NOT NULL DEFAULT 0 'org_id'"` Name string `xorm:"NOT NULL"` Color string `xorm:"VARCHAR(7)"` Description string `xorm:"TEXT"` SortOrder int `xorm:"NOT NULL DEFAULT 0 'sort_order'"` IsDefault bool `xorm:"NOT NULL DEFAULT false 'is_default'"` IsActive bool `xorm:"NOT NULL DEFAULT true 'is_active'"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED 'created_unix'"` UpdatedUnix timeutil.TimeStamp `xorm:"UPDATED 'updated_unix'"` } func (IssuePriorityDef) TableName() string { return "issue_priority_def" } // GetIssuePriorityDefsByOrg returns active priority definitions for an org. // If none exist, seeds the org with default priorities automatically. func GetIssuePriorityDefsByOrg(ctx context.Context, orgID int64) ([]*IssuePriorityDef, error) { defs := make([]*IssuePriorityDef, 0, 10) if err := db.GetEngine(ctx). Where("org_id = ? AND is_active = ?", orgID, true). OrderBy("sort_order ASC, id ASC"). Find(&defs); err != nil { return nil, err } if len(defs) == 0 && orgID > 0 { if err := seedDefaultIssuePriorities(ctx, orgID); err != nil { return defs, nil // non-fatal } return GetIssuePriorityDefsByOrg(ctx, orgID) } return defs, nil } // seedDefaultIssuePriorities creates the standard priority presets for an org. func seedDefaultIssuePriorities(ctx context.Context, orgID int64) error { defaults := []*IssuePriorityDef{ {OrgID: orgID, Name: "Critical", Color: "#dc2626", Description: "Requires immediate attention", SortOrder: 1, IsActive: true}, {OrgID: orgID, Name: "High", Color: "#f97316", Description: "Should be addressed soon", SortOrder: 2, IsActive: true}, {OrgID: orgID, Name: "Medium", Color: "#eab308", Description: "Normal priority", SortOrder: 3, IsDefault: true, IsActive: true}, {OrgID: orgID, Name: "Low", Color: "#2563eb", Description: "Can wait", SortOrder: 4, IsActive: true}, } for _, d := range defaults { if _, err := db.GetEngine(ctx).Insert(d); err != nil { return err } } return nil } // GetAllIssuePriorityDefsByOrg returns all priority definitions (including inactive). func GetAllIssuePriorityDefsByOrg(ctx context.Context, orgID int64) ([]*IssuePriorityDef, error) { defs := make([]*IssuePriorityDef, 0, 10) return defs, db.GetEngine(ctx). Where("org_id = ?", orgID). OrderBy("sort_order ASC, id ASC"). Find(&defs) } // GetIssuePriorityDefByID returns a single priority definition. func GetIssuePriorityDefByID(ctx context.Context, id int64) (*IssuePriorityDef, error) { def := new(IssuePriorityDef) has, err := db.GetEngine(ctx).ID(id).Get(def) if err != nil { return nil, err } if !has { return nil, db.ErrNotExist{Resource: "IssuePriorityDef", ID: id} } return def, nil } // CreateIssuePriorityDef creates a new priority definition. func CreateIssuePriorityDef(ctx context.Context, def *IssuePriorityDef) error { _, err := db.GetEngine(ctx).Insert(def) return err } // UpdateIssuePriorityDef updates a priority definition. func UpdateIssuePriorityDef(ctx context.Context, def *IssuePriorityDef) error { _, err := db.GetEngine(ctx).ID(def.ID).AllCols().Update(def) return err } // DeleteIssuePriorityDef deletes a priority definition and clears references on issues. func DeleteIssuePriorityDef(ctx context.Context, id int64) error { if _, err := db.GetEngine(ctx).Exec("UPDATE issue SET priority_id = 0 WHERE priority_id = ?", id); err != nil { return err } _, err := db.GetEngine(ctx).ID(id).Delete(new(IssuePriorityDef)) return err } // SetIssuePriorityID updates the priority_id on an issue. func SetIssuePriorityID(ctx context.Context, issueID, priorityID int64) error { _, err := db.GetEngine(ctx).Exec("UPDATE issue SET priority_id = ? WHERE id = ?", priorityID, issueID) return err }