fix task sorting and individual task deadline issues on front page.
continuous-integration/drone/push Build is passing

This commit is contained in:
2021-12-04 12:00:12 +01:00
parent 27c2c85b89
commit 531ae46f5b
11 changed files with 164 additions and 12 deletions
+15 -1
View File
@@ -8,6 +8,8 @@ import (
"github.com/gissleh/stufflog/internal/slerrors"
"github.com/gissleh/stufflog/models"
"github.com/gissleh/stufflog/services"
"sort"
"strings"
"time"
)
@@ -25,7 +27,19 @@ func Task(g *gin.RouterGroup, db database.Database) {
filter.Expiring = &expiring
}
return l.ListTasks(c, filter)
tasks, err := l.ListTasks(c, filter)
if err != nil {
return nil, err
}
sort.Sort(
models.TaskSorter{
Fields: strings.Split(c.Query("sort"), ","),
Data: tasks,
},
)
return tasks, nil
}))
g.GET("/:id", handler("task", func(c *gin.Context) (interface{}, error) {
+15 -1
View File
@@ -41,7 +41,21 @@ func (r *taskRepository) ListWithLinks(ctx context.Context, filter models.TaskFi
sq := squirrel.Select("task.*", "tl.project_id as tl_project_id", "p.icon").From("task").PlaceholderFormat(squirrel.Dollar)
sq = sq.Where(squirrel.Eq{"task.user_id": filter.UserID})
if filter.Active != nil {
sq = sq.Where(squirrel.Eq{"task.active": *filter.Active})
if *filter.Active {
sq = sq.Where(squirrel.Or{
squirrel.Eq{"task.active": true},
squirrel.Eq{"task.status_tag": []string{
"on hold", "to do",
}},
})
} else {
sq = sq.Where(squirrel.And{
squirrel.Eq{"task.active": false},
squirrel.Eq{"task.status_tag": []string{
"failed", "completed", "declined",
}},
})
}
}
if filter.Expiring != nil {
if *filter.Expiring {
@@ -0,0 +1,11 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE project
ADD COLUMN task_sort_fields TEXT[] DEFAULT ARRAY[]::TEXT[];
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
ALTER TABLE project
DROP COLUMN task_sort_fields;
-- +goose StatementEnd
+19 -1
View File
@@ -2,6 +2,7 @@ package models
import (
"context"
"sort"
"time"
)
@@ -20,6 +21,7 @@ type Project struct {
StatusTag *string `json:"statusTag" db:"status_tag"`
Favorite bool `json:"favorite" db:"favorite"`
Tags []string `json:"tags" db:"tags"`
TaskSortFields []string `json:"taskSortFields" db:"task_sort_fields"`
}
func (project *Project) Update(update ProjectUpdate) {
@@ -74,6 +76,9 @@ func (project *Project) Update(update ProjectUpdate) {
if update.SetTags != nil {
project.Tags = update.SetTags
}
if update.TaskSortFields != nil {
project.TaskSortFields = append(update.TaskSortFields[:0:0], update.TaskSortFields...)
}
if project.StatusTag != nil && project.Active {
project.StatusTag = nil
@@ -95,14 +100,27 @@ type ProjectUpdate struct {
ClearStatusTag bool `json:"clearStatusTag"`
SetTags []string `json:"setTags"`
Favorite *bool `json:"favorite"`
TaskSortFields []string `json:"taskSortFields"`
}
type ProjectResult struct {
Project
Tasks []*TaskResult `json:"tasks"`
Tasks []*TaskResult `json:"tasks"`
Subtractions []ProjectSubtraction `json:"subtractions"`
}
func (result *ProjectResult) SortTasks() {
sorter := TaskSorter{
Data: result.Tasks,
Fields: result.Project.TaskSortFields,
}
if len(sorter.Fields) == 0 {
sorter.Fields = []string{"status"}
}
sort.Sort(sorter)
}
type ProjectSubtraction struct {
Item Item `json:"item"`
Amount int `json:"amount"`
+88
View File
@@ -94,6 +94,94 @@ type TaskFilter struct {
ProjectIDs []string
}
var taskStatusOrder = []string{"", "to do", "on hold", "completed", "failed", "declined"}
type TaskSorter struct {
Data []*TaskResult
Fields []string
}
func (s *TaskSorter) Valid() bool {
for _, field := range s.Fields {
switch field {
case "name", "-name", "createdTime", "-createdTime", "amount", "-amount", "status", "-status":
default:
return false
}
}
return true
}
func (s TaskSorter) Len() int {
return len(s.Data)
}
func (s TaskSorter) Less(i, j int) bool {
a := s.Data[i]
b := s.Data[j]
for _, field := range s.Fields {
switch field {
case "status", "-status":
as := ""
if a.StatusTag != nil {
as = *a.StatusTag
}
bs := ""
if b.StatusTag != nil {
bs = *b.StatusTag
}
if as != bs {
asi := 1000
bsi := 1000
for i, sn := range taskStatusOrder {
if sn == as {
asi = i
}
if sn == bs {
bsi = i
}
}
if field == "-status" {
return asi > bsi
} else {
return asi < bsi
}
}
case "amount":
if a.ItemAmount != b.ItemAmount {
return a.ItemAmount < b.ItemAmount
}
case "-amount":
if a.ItemAmount != b.ItemAmount {
return a.ItemAmount > b.ItemAmount
}
case "name":
if a.Name != b.Name {
return a.Name < b.Name
}
case "-name":
if a.Name != b.Name {
return a.Name > b.Name
}
case "-time":
return a.CreatedTime.After(b.CreatedTime)
case "time":
return a.CreatedTime.Before(b.CreatedTime)
}
}
return a.CreatedTime.Before(b.CreatedTime)
}
func (s TaskSorter) Swap(i, j int) {
s.Data[i], s.Data[j] = s.Data[j], s.Data[i]
}
type TaskRepository interface {
Find(ctx context.Context, id string) (*Task, error)
List(ctx context.Context, filter TaskFilter) ([]*Task, error)
+4
View File
@@ -317,6 +317,8 @@ func (l *Loader) FindProject(ctx context.Context, id string) (*models.ProjectRes
}
}
result.SortTasks()
return result, nil
}
@@ -433,6 +435,8 @@ func (l *Loader) ListProjects(ctx context.Context, filter models.ProjectFilter)
}
results[i].Tasks = append(results[i].Tasks, taskResult)
}
results[i].SortTasks()
}
return results, nil
+4 -1
View File
@@ -189,7 +189,7 @@ export class StufflogClient {
return data.task;
}
async listTasks({active, expiring}: TaskFilter): Promise<TaskResult[]> {
async listTasks({active, expiring, sort}: TaskFilter): Promise<TaskResult[]> {
let queries = [];
if (active != null) {
queries.push(`active=${active}`);
@@ -197,6 +197,9 @@ export class StufflogClient {
if (expiring != null) {
queries.push(`expiring=${expiring}`);
}
if (sort != null) {
queries.push(`sort=${sort.join(",")}`)
}
const query = queries.length > 0 ? `?${queries.join("&")}` : "";
+2 -2
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import stuffLogClient from "../clients/stufflog";
import stuffLogClient from "../clients/stufflog";
import type { ProjectResult, ProjectUpdate } from "../models/project";
import type { TaskResult } from "../models/task";
@@ -7,7 +7,7 @@ import stuffLogClient from "../clients/stufflog";
import type { ModalData } from "../stores/modal";
import IS_MOBILE from "../utils/phone-check";
import Icon from "./Icon.svelte";
import ItemProgress from "./ItemProgress.svelte";
import ItemProgress from "./ItemProgress.svelte";
import Option from "./Option.svelte";
import OptionRow from "./OptionRow.svelte";
import ParentEntry from "./ParentEntry.svelte";
+1 -5
View File
@@ -8,15 +8,11 @@
export let tasks: TaskResult[];
export let showAllOptions: boolean;
export let header: string;
let sortedTasks: TaskResult[] = [];
$: sortedTasks = tasks.sort((a,b) => a.createdTime.localeCompare(b.createdTime))
</script>
{#if tasks.length > 0}
<h3>{header}</h3>
{#each sortedTasks as task (task.id)}
{#each tasks as task (task.id)}
<TaskEntry
showAllOptions={showAllOptions}
task={task} project={project}
+1
View File
@@ -37,6 +37,7 @@ export interface TaskLink {
export interface TaskFilter {
active?: boolean
expiring?: boolean
sort?: string[]
}
export interface TaskInput {
+4 -1
View File
@@ -38,6 +38,7 @@ import ParentEntry from "../components/ParentEntry.svelte";
fpTaskStore.load({
active: true,
expiring: true,
sort: ["status"],
})
}
}
@@ -77,17 +78,19 @@ import ParentEntry from "../components/ParentEntry.svelte";
fakeProjects = [];
fakeMap = {};
console.log(individualTasks, $fpTaskStore.tasks);
for (let task of individualTasks) {
if (!task.project.active) {
continue;
}
let fakeProject = fakeProjects.find(p => p.id === task.id);
let fakeProject = fakeProjects.find(p => p.id === task.projectId);
if (fakeProject == null) {
fakeMap[task.projectId] = true;
fakeProjects.push({
...task.project,
tasks: [task],
subtractions: [],
});
} else {
fakeProject.tasks.push(task);