1
0

fix(views): return tasks in their buckets

This commit is contained in:
kolaente 2024-03-15 23:31:28 +01:00
parent ca0550acea
commit 398c9f1056
No known key found for this signature in database
GPG Key ID: F40E70337AB24C9B
4 changed files with 52 additions and 39 deletions

View File

@ -1527,27 +1527,27 @@ func (err ErrBucketDoesNotExist) HTTPError() web.HTTPError {
} }
} }
// ErrBucketDoesNotBelongToProject represents an error where a kanban bucket does not belong to a project // ErrBucketDoesNotBelongToProjectView represents an error where a kanban bucket does not belong to a project
type ErrBucketDoesNotBelongToProject struct { type ErrBucketDoesNotBelongToProjectView struct {
BucketID int64 BucketID int64
ProjectID int64 ProjectViewID int64
} }
// IsErrBucketDoesNotBelongToProject checks if an error is ErrBucketDoesNotBelongToProject. // IsErrBucketDoesNotBelongToProject checks if an error is ErrBucketDoesNotBelongToProjectView.
func IsErrBucketDoesNotBelongToProject(err error) bool { func IsErrBucketDoesNotBelongToProject(err error) bool {
_, ok := err.(ErrBucketDoesNotBelongToProject) _, ok := err.(ErrBucketDoesNotBelongToProjectView)
return ok return ok
} }
func (err ErrBucketDoesNotBelongToProject) Error() string { func (err ErrBucketDoesNotBelongToProjectView) Error() string {
return fmt.Sprintf("Bucket does not not belong to project [BucketID: %d, ProjectID: %d]", err.BucketID, err.ProjectID) return fmt.Sprintf("Bucket does not not belong to project view [BucketID: %d, ProjectViewID: %d]", err.BucketID, err.ProjectViewID)
} }
// ErrCodeBucketDoesNotBelongToProject holds the unique world-error code of this error // ErrCodeBucketDoesNotBelongToProject holds the unique world-error code of this error
const ErrCodeBucketDoesNotBelongToProject = 10002 const ErrCodeBucketDoesNotBelongToProject = 10002
// HTTPError holds the http error description // HTTPError holds the http error description
func (err ErrBucketDoesNotBelongToProject) HTTPError() web.HTTPError { func (err ErrBucketDoesNotBelongToProjectView) HTTPError() web.HTTPError {
return web.HTTPError{ return web.HTTPError{
HTTPCode: http.StatusBadRequest, HTTPCode: http.StatusBadRequest,
Code: ErrCodeBucketDoesNotBelongToProject, Code: ErrCodeBucketDoesNotBelongToProject,

View File

@ -213,7 +213,7 @@ func GetTasksInBucketsForView(s *xorm.Session, view *ProjectView, opts *taskSear
opts.sortby = []*sortParam{ opts.sortby = []*sortParam{
{ {
projectViewID: view.ProjectID, projectViewID: view.ID,
orderBy: orderAscending, orderBy: orderAscending,
sortBy: taskPropertyPosition, sortBy: taskPropertyPosition,
}, },
@ -260,6 +260,10 @@ func GetTasksInBucketsForView(s *xorm.Session, view *ProjectView, opts *taskSear
return nil, err return nil, err
} }
for _, t := range ts {
t.BucketID = bucket.ID
}
bucket.Count = total bucket.Count = total
tasks = append(tasks, ts...) tasks = append(tasks, ts...)

View File

@ -209,6 +209,14 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
return nil, 0, err return nil, 0, err
} }
var joinTaskBuckets bool
for _, filter := range opts.parsedFilters {
if filter.field == taskPropertyBucketID {
joinTaskBuckets = true
break
}
}
filterCond, err := convertFiltersToDBFilterCond(opts.parsedFilters, opts.filterIncludeNulls) filterCond, err := convertFiltersToDBFilterCond(opts.parsedFilters, opts.filterIncludeNulls)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -265,20 +273,24 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
} }
} }
if joinTaskBuckets {
query = query.Join("LEFT", "task_buckets", "task_buckets.task_id = tasks.id")
}
tasks = []*Task{} tasks = []*Task{}
err = query.OrderBy(orderby).Find(&tasks) err = query.
OrderBy(orderby).
Find(&tasks)
if err != nil { if err != nil {
return nil, totalCount, err return nil, totalCount, err
} }
queryCount := d.s.Where(cond) queryCount := d.s.Where(cond)
if joinTaskBuckets {
queryCount = queryCount.Join("LEFT", "task_buckets", "task_buckets.task_id = tasks.id")
}
totalCount, err = queryCount. totalCount, err = queryCount.
Count(&Task{}) Count(&Task{})
if err != nil {
return nil, totalCount, err
}
return return
} }

View File

@ -114,7 +114,7 @@ type Task struct {
// The bucket id. Will only be populated when the task is accessed via a view with buckets. // The bucket id. Will only be populated when the task is accessed via a view with buckets.
// Can be used to move a task between buckets. In that case, the new bucket must be in the same view as the old one. // Can be used to move a task between buckets. In that case, the new bucket must be in the same view as the old one.
BucketID int64 `xorm:"bigint null" json:"bucket_id"` BucketID int64 `xorm:"<-" json:"bucket_id"`
// The position of the task - any task project can be sorted as usual by this parameter. // The position of the task - any task project can be sorted as usual by this parameter.
// When accessing tasks via views with buckets, this is primarily used to sort them based on a range. // When accessing tasks via views with buckets, this is primarily used to sort them based on a range.
@ -204,6 +204,9 @@ func (t *Task) ReadAll(_ *xorm.Session, _ web.Auth, _ string, _ int, _ int) (res
func getFilterCond(f *taskFilter, includeNulls bool) (cond builder.Cond, err error) { func getFilterCond(f *taskFilter, includeNulls bool) (cond builder.Cond, err error) {
field := "`" + f.field + "`" field := "`" + f.field + "`"
if f.field == taskPropertyBucketID {
field = "task_buckets.`bucket_id`"
}
switch f.comparator { switch f.comparator {
case taskFilterComparatorEquals: case taskFilterComparatorEquals:
cond = &builder.Eq{field: f.value} cond = &builder.Eq{field: f.value}
@ -633,15 +636,16 @@ func checkBucketLimit(s *xorm.Session, t *Task, bucket *Bucket) (err error) {
} }
// Contains all the task logic to figure out what bucket to use for this task. // Contains all the task logic to figure out what bucket to use for this task.
func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, view *ProjectView, targetBucket *TaskBucket) (err error) { func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, view *ProjectView, targetBucketID int64) (err error) {
if view.BucketConfigurationMode == BucketConfigurationModeNone {
return
}
var shouldChangeBucket = true var shouldChangeBucket = true
if targetBucket == nil { targetBucket := &TaskBucket{
targetBucket = &TaskBucket{ BucketID: targetBucketID,
BucketID: 0, TaskID: task.ID,
TaskID: task.ID, ProjectViewID: view.ID,
ProjectViewID: view.ID,
}
} }
oldTaskBucket := &TaskBucket{} oldTaskBucket := &TaskBucket{}
@ -678,15 +682,12 @@ func setTaskBucket(s *xorm.Session, task *Task, originalTask *Task, view *Projec
} }
// If there is a bucket set, make sure they belong to the same project as the task // If there is a bucket set, make sure they belong to the same project as the task
if task.ProjectID != bucket.ProjectID { if view.ID != bucket.ProjectViewID {
return ErrBucketDoesNotBelongToProject{ return ErrBucketDoesNotBelongToProjectView{
ProjectID: task.ProjectID, ProjectViewID: view.ID,
BucketID: bucket.ID, BucketID: bucket.ID,
} }
} }
if err != nil {
return
}
// Check the bucket limit // Check the bucket limit
// Only check the bucket limit if the task is being moved between buckets, allow reordering the task within a bucket // Only check the bucket limit if the task is being moved between buckets, allow reordering the task within a bucket
@ -811,7 +812,7 @@ func createTask(s *xorm.Session, t *Task, a web.Auth, updateAssignees bool) (err
for _, view := range views { for _, view := range views {
// Get the default bucket and move the task there // Get the default bucket and move the task there
err = setTaskBucket(s, t, nil, view, &TaskBucket{}) err = setTaskBucket(s, t, nil, view, 0)
if err != nil { if err != nil {
return return
} }
@ -913,20 +914,16 @@ func (t *Task) Update(s *xorm.Session, a web.Auth) (err error) {
Find(&buckets) Find(&buckets)
for _, view := range views { for _, view := range views {
taskBucket := &TaskBucket{
TaskID: t.ID,
ProjectViewID: view.ID,
}
// Only update the bucket when the current view // Only update the bucket when the current view
var targetBucketID int64
if t.BucketID != 0 { if t.BucketID != 0 {
bucket, has := buckets[t.BucketID] bucket, has := buckets[t.BucketID]
if has && bucket.ProjectViewID == view.ID { if has && bucket.ProjectViewID == view.ID {
taskBucket.BucketID = t.BucketID targetBucketID = t.BucketID
} }
} }
err = setTaskBucket(s, t, &ot, view, taskBucket) err = setTaskBucket(s, t, &ot, view, targetBucketID)
if err != nil { if err != nil {
return err return err
} }