From 83db13bed05761c47cc2c4708b71e6b5ce72d69c Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Tue, 19 May 2026 04:15:18 -0700 Subject: [PATCH] Honor optional sort_order on module create under a course. Accept sort_order in CreateModuleInput, shift siblings when set, and default to max+1 when omitted. Co-authored-by: Cursor --- db/query/lms_modules.sql | 23 +++++++++++----------- gen/db/lms_modules.sql.go | 15 +++++++++------ internal/domain/module.go | 2 ++ internal/repository/lms_modules.go | 31 ++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/db/query/lms_modules.sql b/db/query/lms_modules.sql index ae5d0b9..917f014 100644 --- a/db/query/lms_modules.sql +++ b/db/query/lms_modules.sql @@ -1,17 +1,18 @@ -- name: CreateModule :one INSERT INTO modules (program_id, course_id, name, description, icon, sort_order) SELECT - $1, - $2, - $3, - $4, - $5, - coalesce(( - SELECT - max(m.sort_order) - FROM modules m - WHERE - m.course_id = $2), 0) + 1 + sqlc.arg('program_id'), + sqlc.arg('course_id'), + sqlc.arg('name'), + sqlc.arg('description'), + sqlc.arg('icon'), + COALESCE(sqlc.narg('sort_order')::int, + COALESCE(( + SELECT + max(m.sort_order) + FROM modules m + WHERE + m.course_id = sqlc.arg('course_id')), 0) + 1) RETURNING *; diff --git a/gen/db/lms_modules.sql.go b/gen/db/lms_modules.sql.go index d7ba147..b0d80fb 100644 --- a/gen/db/lms_modules.sql.go +++ b/gen/db/lms_modules.sql.go @@ -19,12 +19,13 @@ SELECT $3, $4, $5, - coalesce(( - SELECT - max(m.sort_order) - FROM modules m - WHERE - m.course_id = $2), 0) + 1 + COALESCE($6::int, + COALESCE(( + SELECT + max(m.sort_order) + FROM modules m + WHERE + m.course_id = $2), 0) + 1) RETURNING id, program_id, course_id, name, description, icon, created_at, updated_at, sort_order ` @@ -35,6 +36,7 @@ type CreateModuleParams struct { Name string `json:"name"` Description pgtype.Text `json:"description"` Icon pgtype.Text `json:"icon"` + SortOrder pgtype.Int4 `json:"sort_order"` } func (q *Queries) CreateModule(ctx context.Context, arg CreateModuleParams) (Module, error) { @@ -44,6 +46,7 @@ func (q *Queries) CreateModule(ctx context.Context, arg CreateModuleParams) (Mod arg.Name, arg.Description, arg.Icon, + arg.SortOrder, ) var i Module err := row.Scan( diff --git a/internal/domain/module.go b/internal/domain/module.go index 769fa64..1874600 100644 --- a/internal/domain/module.go +++ b/internal/domain/module.go @@ -21,6 +21,8 @@ type CreateModuleInput struct { Name string `json:"name" validate:"required"` Description *string `json:"description,omitempty"` Icon *string `json:"icon,omitempty"` + // SortOrder within the course when set; omit to append after current max within course_id (uniqueness is per-course). + SortOrder *int `json:"sort_order,omitempty" validate:"omitempty,min=0"` } type UpdateModuleInput struct { diff --git a/internal/repository/lms_modules.go b/internal/repository/lms_modules.go index 8ee1f12..09d0f19 100644 --- a/internal/repository/lms_modules.go +++ b/internal/repository/lms_modules.go @@ -30,12 +30,43 @@ func moduleToDomain(m dbgen.Module) domain.Module { } func (s *Store) CreateModule(ctx context.Context, programID, courseID int64, input domain.CreateModuleInput) (domain.Module, error) { + if input.SortOrder != nil { + q, tx, err := s.BeginTx(ctx) + if err != nil { + return domain.Module{}, err + } + defer func() { _ = tx.Rollback(ctx) }() + target := int32(*input.SortOrder) + if _, err := tx.Exec(ctx, + `UPDATE modules SET sort_order = sort_order + 1 WHERE course_id = $1 AND sort_order >= $2`, + courseID, target, + ); err != nil { + return domain.Module{}, err + } + m, err := q.CreateModule(ctx, dbgen.CreateModuleParams{ + ProgramID: programID, + CourseID: courseID, + Name: input.Name, + Description: toPgText(input.Description), + Icon: toPgText(input.Icon), + SortOrder: pgtype.Int4{Int32: target, Valid: true}, + }) + if err != nil { + return domain.Module{}, err + } + if err := tx.Commit(ctx); err != nil { + return domain.Module{}, err + } + return moduleToDomain(m), nil + } + m, err := s.queries.CreateModule(ctx, dbgen.CreateModuleParams{ ProgramID: programID, CourseID: courseID, Name: input.Name, Description: toPgText(input.Description), Icon: toPgText(input.Icon), + SortOrder: pgtype.Int4{Valid: false}, }) if err != nil { return domain.Module{}, err