Browse Source

hide completed tasks in projects, and add individual deadline tasks to front page.

main
Gisle Aune 4 years ago
parent
commit
48ba7c8693
  1. 8
      api/task.go
  2. 7
      database/postgres/tasks.go
  3. 1
      models/task.go
  4. 14
      svelte-ui/src/clients/stufflog.ts
  5. 8
      svelte-ui/src/components/ProjectEntry.svelte
  6. 3
      svelte-ui/src/models/group.ts
  7. 3
      svelte-ui/src/models/item.ts
  8. 5
      svelte-ui/src/models/project.ts
  9. 9
      svelte-ui/src/models/task.ts
  10. 37
      svelte-ui/src/pages/FrontPage.svelte
  11. 4
      svelte-ui/src/pages/ProjectPage.svelte
  12. 1
      svelte-ui/src/stores/project.ts
  13. 38
      svelte-ui/src/stores/tasks.ts

8
api/task.go

@ -14,15 +14,15 @@ import (
func Task(g *gin.RouterGroup, db database.Database) { func Task(g *gin.RouterGroup, db database.Database) {
l := services.Loader{DB: db} l := services.Loader{DB: db}
defaultActive := true
g.GET("/", handler("tasks", func(c *gin.Context) (interface{}, error) { g.GET("/", handler("tasks", func(c *gin.Context) (interface{}, error) {
filter := models.TaskFilter{} filter := models.TaskFilter{}
if setting := c.Query("active"); setting != "" { if setting := c.Query("active"); setting != "" {
active := setting == "true" active := setting == "true"
filter.Active = &active filter.Active = &active
} else {
filter.Active = &defaultActive
}
if setting := c.Query("expiring"); setting != "" {
expiring := setting == "true"
filter.Expiring = &expiring
} }
return l.ListTasks(c, filter) return l.ListTasks(c, filter)

7
database/postgres/tasks.go

@ -33,6 +33,13 @@ func (r *taskRepository) List(ctx context.Context, filter models.TaskFilter) ([]
if filter.Active != nil { if filter.Active != nil {
sq = sq.Where(squirrel.Eq{"task.active": *filter.Active}) sq = sq.Where(squirrel.Eq{"task.active": *filter.Active})
} }
if filter.Expiring != nil {
if *filter.Expiring {
sq = sq.Where("task.end_time IS NOT NULL")
} else {
sq = sq.Where("task.end_time IS NULL")
}
}
if filter.IDs != nil { if filter.IDs != nil {
sq = sq.Where(squirrel.Eq{"task.task_id": filter.IDs}) sq = sq.Where(squirrel.Eq{"task.task_id": filter.IDs})
} }

1
models/task.go

@ -60,6 +60,7 @@ type TaskResult struct {
type TaskFilter struct { type TaskFilter struct {
UserID string UserID string
Active *bool Active *bool
Expiring *bool
IDs []string IDs []string
ItemIDs []string ItemIDs []string
ProjectIDs []string ProjectIDs []string

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

@ -1,7 +1,7 @@
import { getJwt } from "./amplify"; import { getJwt } from "./amplify";
import type { GoalFilter, GoalInput, GoalResult, GoalUpdate } from "../models/goal"; import type { GoalFilter, GoalInput, GoalResult, GoalUpdate } from "../models/goal";
import type { ProjectFilter, ProjectInput, ProjectResult, ProjectUpdate } from "../models/project"; import type { ProjectFilter, ProjectInput, ProjectResult, ProjectUpdate } from "../models/project";
import type { TaskInput, TaskResult, TaskUpdate } from "../models/task";
import type { TaskFilter, TaskInput, TaskResult, TaskUpdate } from "../models/task";
import type { LogFilter, LogInput, LogResult, LogUpdate } from "../models/log"; import type { LogFilter, LogInput, LogResult, LogUpdate } from "../models/log";
import type { GroupInput, GroupResult, GroupUpdate } from "../models/group"; import type { GroupInput, GroupResult, GroupUpdate } from "../models/group";
import type { ItemInput, ItemResult, ItemUpdate } from "../models/item"; import type { ItemInput, ItemResult, ItemUpdate } from "../models/item";
@ -134,8 +134,16 @@ export class StufflogClient {
return data.task; return data.task;
} }
async listTasks(active?: boolean): Promise<TaskResult[]> {
let query = (active != null) ? `?active=${active}` : "";
async listTasks({active, expiring}: TaskFilter): Promise<TaskResult[]> {
let queries = [];
if (active != null) {
queries.push(`active=${active}`);
}
if (expiring != null) {
queries.push(`expiring=${expiring}`);
}
const query = queries.length > 0 ? `?${queries.join("&")}` : "";
const data = await this.fetch("GET", `/api/task/${query}`); const data = await this.fetch("GET", `/api/task/${query}`);
return data.tasks; return data.tasks;

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

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { IconName } from "../external/icons"; import type { IconName } from "../external/icons";
import TaskAddForm from "../forms/TaskAddForm.svelte";
import type { ProjectResult } from "../models/project"; import type { ProjectResult } from "../models/project";
import type { ModalData } from "../stores/modal"; import type { ModalData } from "../stores/modal";
import DaysLeft from "./DaysLeft.svelte"; import DaysLeft from "./DaysLeft.svelte";
@ -10,16 +11,15 @@
export let project: ProjectResult = null; export let project: ProjectResult = null;
export let showAllOptions: boolean = false; export let showAllOptions: boolean = false;
export let hideInactive: boolean = false;
let iconName: IconName = "question"; let iconName: IconName = "question";
let mdAddTask: ModalData; let mdAddTask: ModalData;
let mdProjectAdd: ModalData;
let mdProjectEdit: ModalData; let mdProjectEdit: ModalData;
let mdProjectDelete: ModalData; let mdProjectDelete: ModalData;
$: iconName = project.icon as IconName; $: iconName = project.icon as IconName;
$: mdAddTask = {name:"task.add", project}; $: mdAddTask = {name:"task.add", project};
$: mdProjectAdd = {name:"project.add"};
$: mdProjectEdit = {name:"project.edit", project}; $: mdProjectEdit = {name:"project.edit", project};
$: mdProjectDelete = {name:"project.delete", project}; $: mdProjectDelete = {name:"project.delete", project};
</script> </script>
@ -47,7 +47,9 @@
{/if} {/if}
<div class="list" class:full={showAllOptions}> <div class="list" class:full={showAllOptions}>
{#each project.tasks as task (task.id)} {#each project.tasks as task (task.id)}
<TaskEntry showAllOptions={showAllOptions} task={task} />
{#if !hideInactive || task.active}
<TaskEntry showAllOptions={showAllOptions} task={task} />
{/if}
{/each} {/each}
</div> </div>
</div> </div>

3
svelte-ui/src/models/group.ts

@ -1,9 +1,10 @@
import type { IconName } from "../external/icons";
import type Item from "./item"; import type Item from "./item";
export default interface Group { export default interface Group {
id: string id: string
name: string name: string
icon: string
icon: IconName
description: string description: string
} }

3
svelte-ui/src/models/item.ts

@ -1,10 +1,11 @@
import type { IconName } from "../external/icons";
import type Group from "./group"; import type Group from "./group";
export default interface Item { export default interface Item {
id: string id: string
groupId: string groupId: string
groupWeight: number groupWeight: number
icon: string
icon: IconName
name: string name: string
description: string description: string
} }

5
svelte-ui/src/models/project.ts

@ -1,10 +1,11 @@
import type { IconName } from "../external/icons";
import type { TaskResult } from "./task"; import type { TaskResult } from "./task";
export default interface Project { export default interface Project {
id: string id: string
name: string name: string
description: string description: string
icon: string
icon: IconName
active: boolean active: boolean
createdTime: string createdTime: string
endTime?: string endTime?: string
@ -22,7 +23,7 @@ export interface ProjectFilter {
export interface ProjectInput { export interface ProjectInput {
name: string name: string
description: string description: string
icon: string
icon: IconName
active: boolean active: boolean
endTime?: string | Date endTime?: string | Date
} }

9
svelte-ui/src/models/task.ts

@ -1,3 +1,4 @@
import type { IconName } from "../external/icons";
import type Item from "./item"; import type Item from "./item";
import type Log from "./log"; import type Log from "./log";
@ -8,18 +9,22 @@ export default interface Task {
itemAmount: number itemAmount: number
name: string name: string
description: string description: string
icon: string
icon: IconName
active: boolean active: boolean
createdTime: string createdTime: string
endTime?: string endTime?: string
} }
export interface TaskResult extends Task { export interface TaskResult extends Task {
item: Item item: Item
logs: Log[] logs: Log[]
completedAmount: number completedAmount: number
} }
export interface TaskFilter {
active?: boolean
expiring?: boolean
}
export interface TaskInput { export interface TaskInput {
itemId: string itemId: string
projectId: string projectId: string

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

@ -1,9 +1,13 @@
<script>
<script lang="ts">
import EmptyList from "../components/EmptyList.svelte"; import EmptyList from "../components/EmptyList.svelte";
import GoalEntry from "../components/GoalEntry.svelte"; import GoalEntry from "../components/GoalEntry.svelte";
import ProjectEntry from "../components/ProjectEntry.svelte"; import ProjectEntry from "../components/ProjectEntry.svelte";
import type { ProjectResult } from "../models/project";
import { fpGoalStore } from "../stores/goal"; import { fpGoalStore } from "../stores/goal";
import { fpProjectStore } from "../stores/project"; import { fpProjectStore } from "../stores/project";
import { fpTaskStore } from "../stores/tasks";
let fakeProject: ProjectResult
$: { $: {
if ($fpGoalStore.stale && !$fpGoalStore.loading) { if ($fpGoalStore.stale && !$fpGoalStore.loading) {
@ -14,6 +18,15 @@
} }
} }
$: {
if ($fpTaskStore.stale && !$fpTaskStore.loading) {
fpTaskStore.load({
active: true,
expiring: true,
})
}
}
$: { $: {
if ($fpProjectStore.stale && !$fpProjectStore.loading) { if ($fpProjectStore.stale && !$fpProjectStore.loading) {
fpProjectStore.load({ fpProjectStore.load({
@ -22,6 +35,21 @@
}); });
} }
} }
$: {
fakeProject = {
id: "P_fakeProject",
active: true,
createdTime: new Date().toISOString(),
description: "",
icon: "list",
name: "Individual Tasks",
tasks: $fpTaskStore.tasks.filter(t => $fpProjectStore.projects.find(p => p.id === t.id) == null),
endTime: null,
}
console.log(fakeProject, $fpTaskStore);
}
</script> </script>
<div class="page"> <div class="page">
@ -41,9 +69,12 @@
<h1>Upcoming Deadlines</h1> <h1>Upcoming Deadlines</h1>
{/if} {/if}
{#each $fpProjectStore.projects as project (project.id)} {#each $fpProjectStore.projects as project (project.id)}
<ProjectEntry project={project} />
<ProjectEntry hideInactive project={project} />
{/each} {/each}
{#if !$fpProjectStore.loading && $fpProjectStore.projects.length === 0}
{#if fakeProject.tasks.length > 0}
<ProjectEntry project={fakeProject} />
{/if}
{#if fakeProject.tasks.length === 0 && !$fpProjectStore.loading && $fpProjectStore.projects.length === 0}
<EmptyList icon="check" text="All good!" /> <EmptyList icon="check" text="All good!" />
{/if} {/if}
</div> </div>

4
svelte-ui/src/pages/ProjectPage.svelte

@ -28,10 +28,10 @@
<div class="page"> <div class="page">
<div class="options"> <div class="options">
<Checkbox centered bind:checked={showInactive} label="Show inactive projects." />
<Checkbox centered bind:checked={showInactive} label="Show completed tasks and projects." />
</div> </div>
{#each $projectStore.projects as project (project.id)} {#each $projectStore.projects as project (project.id)}
<ProjectEntry showAllOptions project={project} />
<ProjectEntry hideInactive={!showInactive} showAllOptions project={project} />
{/each} {/each}
<Boi open={mdProjectAdd}>Add Project</Boi> <Boi open={mdProjectAdd}>Add Project</Boi>
</div> </div>

1
svelte-ui/src/stores/project.ts

@ -25,7 +25,6 @@ function createProjectStore() {
}, },
async load(filter: ProjectFilter) { async load(filter: ProjectFilter) {
console.log(filter);
update(v => ({...v, loading: true, stale: false, filter})); update(v => ({...v, loading: true, stale: false, filter}));
const projects = await stuffLogClient.listProjects(filter); const projects = await stuffLogClient.listProjects(filter);
update(v => ({...v, loading: false, projects: projects.reverse()})); update(v => ({...v, loading: false, projects: projects.reverse()}));

38
svelte-ui/src/stores/tasks.ts

@ -0,0 +1,38 @@
import { writable } from "svelte/store";
import stuffLogClient from "../clients/stufflog";
import type { TaskFilter, TaskResult } from "../models/task";
interface TaskStoreData {
loading: boolean
stale: boolean
filter: TaskFilter
tasks: TaskResult[]
}
function createTaskStore() {
const {update, subscribe} = writable<TaskStoreData>({
loading: false,
stale: true,
filter: {},
tasks: [],
});
return {
subscribe,
markStale() {
update(v => ({...v, stale: true}));
},
async load(filter: TaskFilter) {
update(v => ({...v, loading: true, stale: false, filter}));
const tasks = await stuffLogClient.listTasks(filter);
update(v => ({...v, loading: false, tasks: [...tasks]}));
},
}
}
const taskStore = createTaskStore();
export default taskStore;
export const fpTaskStore = createTaskStore();
Loading…
Cancel
Save