feat(views): sort tasks by their position relative to the view they're in
This commit is contained in:
parent
2502776460
commit
d1d07f462c
@ -99,6 +99,7 @@ This document describes the different errors Vikunja can return.
|
||||
| 4023 | 409 | Tried to create a task relation which would create a cycle. |
|
||||
| 4024 | 400 | The provided filter expression is invalid. |
|
||||
| 4025 | 400 | The reaction kind is invalid. |
|
||||
| 4026 | 400 | You must provide a project view ID when sorting by position. |
|
||||
|
||||
## Team
|
||||
|
||||
|
@ -1114,6 +1114,25 @@ func (err ErrInvalidReactionEntityKind) HTTPError() web.HTTPError {
|
||||
}
|
||||
}
|
||||
|
||||
// ErrMustHaveProjectViewToSortByPosition represents an error where no project view id was supplied
|
||||
type ErrMustHaveProjectViewToSortByPosition struct{}
|
||||
|
||||
func (err ErrMustHaveProjectViewToSortByPosition) Error() string {
|
||||
return "You must provide a project view ID when sorting by position"
|
||||
}
|
||||
|
||||
// ErrCodeMustHaveProjectViewToSortByPosition holds the unique world-error code of this error
|
||||
const ErrCodeMustHaveProjectViewToSortByPosition = 4026
|
||||
|
||||
// HTTPError holds the http error description
|
||||
func (err ErrMustHaveProjectViewToSortByPosition) HTTPError() web.HTTPError {
|
||||
return web.HTTPError{
|
||||
HTTPCode: http.StatusBadRequest,
|
||||
Code: ErrCodeMustHaveProjectViewToSortByPosition,
|
||||
Message: "You must provide a project view ID when sorting by position",
|
||||
}
|
||||
}
|
||||
|
||||
// ============
|
||||
// Team errors
|
||||
// ============
|
||||
|
@ -200,6 +200,7 @@ func GetTasksInBucketsForView(s *xorm.Session, view *ProjectView, opts *taskSear
|
||||
|
||||
opts.sortby = []*sortParam{
|
||||
{
|
||||
projectViewID: view.ProjectID,
|
||||
orderBy: orderAscending,
|
||||
sortBy: taskPropertyPosition,
|
||||
},
|
||||
|
@ -193,6 +193,14 @@ func (tf *TaskCollection) ReadAll(s *xorm.Session, a web.Auth, search string, pa
|
||||
opts.page = page
|
||||
opts.perPage = perPage
|
||||
|
||||
if view != nil {
|
||||
opts.sortby = append(opts.sortby, &sortParam{
|
||||
projectViewID: view.ID,
|
||||
sortBy: taskPropertyPosition,
|
||||
orderBy: orderAscending,
|
||||
})
|
||||
}
|
||||
|
||||
shareAuth, is := a.(*LinkSharing)
|
||||
if is {
|
||||
project, err := GetProjectSimpleByID(s, shareAuth.ProjectID)
|
||||
|
@ -20,6 +20,7 @@ type (
|
||||
sortParam struct {
|
||||
sortBy string
|
||||
orderBy sortOrder // asc or desc
|
||||
projectViewID int64
|
||||
}
|
||||
|
||||
sortOrder string
|
||||
@ -72,5 +73,10 @@ func (sp *sortParam) validate() error {
|
||||
if sp.orderBy != orderDescending && sp.orderBy != orderAscending {
|
||||
return ErrInvalidSortOrder{OrderBy: sp.orderBy}
|
||||
}
|
||||
|
||||
if sp.sortBy == taskPropertyPosition && sp.projectViewID == 0 {
|
||||
return ErrMustHaveProjectViewToSortByPosition{}
|
||||
}
|
||||
|
||||
return validateTaskField(sp.sortBy)
|
||||
}
|
||||
|
@ -53,14 +53,19 @@ func getOrderByDBStatement(opts *taskSearchOptions) (orderby string, err error)
|
||||
return "", err
|
||||
}
|
||||
|
||||
var prefix string
|
||||
if param.sortBy == taskPropertyPosition {
|
||||
prefix = "task_positions."
|
||||
}
|
||||
|
||||
// Mysql sorts columns with null values before ones without null value.
|
||||
// Because it does not have support for NULLS FIRST or NULLS LAST we work around this by
|
||||
// first sorting for null (or not null) values and then the order we actually want to.
|
||||
if db.Type() == schemas.MYSQL {
|
||||
orderby += "`" + param.sortBy + "` IS NULL, "
|
||||
orderby += prefix + "`" + param.sortBy + "` IS NULL, "
|
||||
}
|
||||
|
||||
orderby += "`" + param.sortBy + "` " + param.orderBy.String()
|
||||
orderby += prefix + "`" + param.sortBy + "` " + param.orderBy.String()
|
||||
|
||||
// Postgres and sqlite allow us to control how columns with null values are sorted.
|
||||
// To make that consistent with the sort order we have and other dbms, we're adding a separate clause here.
|
||||
@ -253,6 +258,13 @@ func (d *dbTaskSearcher) Search(opts *taskSearchOptions) (tasks []*Task, totalCo
|
||||
query = query.Limit(limit, start)
|
||||
}
|
||||
|
||||
for _, param := range opts.sortby {
|
||||
if param.sortBy == taskPropertyPosition {
|
||||
query = query.Join("LEFT", "task_positions", "task_positions.task_id = tasks.id AND task_positions.project_view_id = ?", param.projectViewID)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
tasks = []*Task{}
|
||||
err = query.OrderBy(orderby).Find(&tasks)
|
||||
if err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user