Browse Source

fix task sorting and individual task deadline issues on front page.

main
Gisle Aune 2 years ago
parent
commit
531ae46f5b
  1. 16
      api/task.go
  2. 16
      database/postgres/tasks.go
  3. 11
      migrations/postgres/20211204105833_add_project_column_task_sort_fields.sql
  4. 20
      models/project.go
  5. 88
      models/task.go
  6. 4
      services/loader.go
  7. 5
      svelte-ui/src/clients/stufflog.ts
  8. 4
      svelte-ui/src/components/ProjectEntry.svelte
  9. 6
      svelte-ui/src/components/TaskList.svelte
  10. 1
      svelte-ui/src/models/task.ts
  11. 5
      svelte-ui/src/pages/FrontPage.svelte

16
api/task.go

@ -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) {

16
database/postgres/tasks.go

@ -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 {

11
migrations/postgres/20211204105833_add_project_column_task_sort_fields.sql

@ -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

20
models/project.go

@ -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
models/task.go

@ -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
services/loader.go

@ -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

5
svelte-ui/src/clients/stufflog.ts

@ -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("&")}` : "";

4
svelte-ui/src/components/ProjectEntry.svelte

@ -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";

6
svelte-ui/src/components/TaskList.svelte

@ -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
svelte-ui/src/models/task.ts

@ -37,6 +37,7 @@ export interface TaskLink {
export interface TaskFilter {
active?: boolean
expiring?: boolean
sort?: string[]
}
export interface TaskInput {

5
svelte-ui/src/pages/FrontPage.svelte

@ -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);

Loading…
Cancel
Save