Browse Source

add support for project inactivity, added new checkbox component.

main
Gisle Aune 4 years ago
parent
commit
c3e589aa3e
  1. 4
      api/project.go
  2. 2
      database/postgres/project.go
  3. 63
      svelte-ui/src/components/Checkbox.svelte
  4. 14
      svelte-ui/src/components/ProjectEntry.svelte
  5. 4
      svelte-ui/src/forms/LogAddForm.svelte
  6. 6
      svelte-ui/src/forms/ProjectEditForm.svelte
  7. 6
      svelte-ui/src/forms/TaskDeleteForm.svelte
  8. 9
      svelte-ui/src/forms/TaskEditForm.svelte
  9. 2
      svelte-ui/src/models/project.ts
  10. 21
      svelte-ui/src/pages/ProjectPage.svelte
  11. 7
      svelte-ui/src/stores/project.ts

4
api/project.go

@ -14,15 +14,11 @@ import (
func Project(g *gin.RouterGroup, db database.Database) { func Project(g *gin.RouterGroup, db database.Database) {
l := services.Loader{DB: db} l := services.Loader{DB: db}
defaultActive := true
g.GET("/", handler("projects", func(c *gin.Context) (interface{}, error) { g.GET("/", handler("projects", func(c *gin.Context) (interface{}, error) {
filter := models.ProjectFilter{} filter := models.ProjectFilter{}
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 != "" { if setting := c.Query("expiring"); setting != "" {
filter.Expiring = setting == "true" filter.Expiring = setting == "true"

2
database/postgres/project.go

@ -40,7 +40,7 @@ func (r *projectRepository) List(ctx context.Context, filter models.ProjectFilte
sq = sq.Where("end_time IS NOT NULL") sq = sq.Where("end_time IS NOT NULL")
} }
sq = sq.OrderBy("created_time DESC")
sq = sq.OrderBy("active", "created_time DESC")
query, args, err := sq.ToSql() query, args, err := sq.ToSql()
if err != nil { if err != nil {

63
svelte-ui/src/components/Checkbox.svelte

@ -0,0 +1,63 @@
<script lang="ts">
import Icon from "./Icon.svelte";
export let checked = false;
export let centered = false;
export let disabled = false;
export let label = "(Missing label property)";
function handleClick() {
if (!disabled) {
checked = !checked;
}
}
</script>
<div class="checkbox" class:centered on:click={handleClick}>
<div class="box" class:disabled class:checked class:unchecked={!checked}>
<Icon name="check" />
</div>
<div class="label">{label}</div>
</div>
<style>
div.checkbox {
display: flex;
flex-direction: row;
flex-shrink: 0;
margin-top: 0.5em;
margin-bottom: 1em;
}
div.checkbox.centered {
margin: auto
}
div.box {
border: 0.5px solid;
padding: 0.025em 0.5ch;
padding-top: 0.25em;
background-color: #111;
color: #555;
font-size: 1.25em;
}
div.box.checked {
color: #fC1;
background-color: #5c4d20;
}
div.box.disabled {
color: #777;
background-color: #444;
}
div.box.unchecked :global(*) {
opacity: 0;
}
div.label {
margin: auto 0;
margin-left: 1ch;
-webkit-user-select: none;
-moz-user-select: none;
}
</style>

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

@ -25,7 +25,7 @@
</script> </script>
<div class="project"> <div class="project">
<div class="icon"><Icon block name={iconName} /></div>
<div class="icon" class:inactive={!project.active}><Icon block name={iconName} /></div>
<div class="body"> <div class="body">
<div class="header"> <div class="header">
<div class="name">{project.name}</div> <div class="name">{project.name}</div>
@ -66,6 +66,9 @@
padding-top: 0.125em; padding-top: 0.125em;
color: #333; color: #333;
} }
div.icon.inactive {
color: #484;
}
div.body { div.body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -91,4 +94,13 @@
padding: 0; padding: 0;
margin: 0.25em 0; margin: 0.25em 0;
} }
@media screen and (max-width: 550px) {
div.icon {
color: #777;
}
div.icon.inactive {
color: #78ff78;
}
}
</style> </style>

4
svelte-ui/src/forms/LogAddForm.svelte

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import stuffLogClient from "../clients/stufflog"; import stuffLogClient from "../clients/stufflog";
import Checkbox from "../components/Checkbox.svelte";
import Modal from "../components/Modal.svelte"; import Modal from "../components/Modal.svelte";
import goalStore, { fpGoalStore } from "../stores/goal"; import goalStore, { fpGoalStore } from "../stores/goal";
import logStore from "../stores/logs"; import logStore from "../stores/logs";
@ -54,8 +55,7 @@ import logStore from "../stores/logs";
<input name="loggedTime" type="datetime-local" bind:value={loggedTime} /> <input name="loggedTime" type="datetime-local" bind:value={loggedTime} />
<label for="description">Description</label> <label for="description">Description</label>
<textarea name="description" bind:value={description} /> <textarea name="description" bind:value={description} />
<input id="markInactive" type="checkbox" bind:checked={markInactive} />
<label for="markInactive">Complete Task</label>
<Checkbox bind:checked={markInactive} label="Mark task inactive/completed." />
<hr /> <hr />

6
svelte-ui/src/forms/ProjectEditForm.svelte

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import stuffLogClient from "../clients/stufflog"; import stuffLogClient from "../clients/stufflog";
import Checkbox from "../components/Checkbox.svelte";
import IconSelect from "../components/IconSelect.svelte"; import IconSelect from "../components/IconSelect.svelte";
import Modal from "../components/Modal.svelte"; import Modal from "../components/Modal.svelte";
import type { IconName } from "../external/icons"; import type { IconName } from "../external/icons";
@ -14,6 +15,7 @@
const project = md.project; const project = md.project;
let endTime = project.endTime ? formatFormTime(project.endTime) : ""; let endTime = project.endTime ? formatFormTime(project.endTime) : "";
let active = project.active;
let name = project.name; let name = project.name;
let description = project.description; let description = project.description;
let icon = project.icon as IconName; let icon = project.icon as IconName;
@ -21,11 +23,10 @@
function onSubmit() { function onSubmit() {
stuffLogClient.updateProject(project.id, { stuffLogClient.updateProject(project.id, {
active: true,
endTime: ( endTime == "" ) ? null : new Date(endTime), endTime: ( endTime == "" ) ? null : new Date(endTime),
clearEndTime: ( endTime == "" ), clearEndTime: ( endTime == "" ),
name, description, icon,
name, active, description, icon,
}).then(() => { }).then(() => {
projectStore.markStale(); projectStore.markStale();
fpProjectStore.markStale(); fpProjectStore.markStale();
@ -52,6 +53,7 @@
<IconSelect bind:value={icon} /> <IconSelect bind:value={icon} />
<label for="endTime">Deadline (Optional)</label> <label for="endTime">Deadline (Optional)</label>
<input name="endTime" type="datetime-local" bind:value={endTime} /> <input name="endTime" type="datetime-local" bind:value={endTime} />
<Checkbox bind:checked={active} label="Project is active/incomplete." />
<hr /> <hr />

6
svelte-ui/src/forms/TaskDeleteForm.svelte

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import stuffLogClient from "../clients/stufflog"; import stuffLogClient from "../clients/stufflog";
import Checkbox from "../components/Checkbox.svelte";
import Modal from "../components/Modal.svelte"; import Modal from "../components/Modal.svelte";
import goalStore, { fpGoalStore } from "../stores/goal"; import goalStore, { fpGoalStore } from "../stores/goal";
import modalStore from "../stores/modal"; import modalStore from "../stores/modal";
@ -15,7 +16,7 @@
let name = task.name; let name = task.name;
let description = task.description; let description = task.description;
let itemAmount = task.itemAmount; let itemAmount = task.itemAmount;
let active = task.active;
let completed = !task.active;
let endTime = task.endTime ? formatFormTime(task.endTime) : ""; let endTime = task.endTime ? formatFormTime(task.endTime) : "";
let error = null; let error = null;
@ -50,8 +51,7 @@
<input disabled name="itemAmount" type="number" value={itemAmount} /> <input disabled name="itemAmount" type="number" value={itemAmount} />
<label for="endTime">Deadline (Optional)</label> <label for="endTime">Deadline (Optional)</label>
<input disabled name="endTime" type="datetime-local" value={endTime} /> <input disabled name="endTime" type="datetime-local" value={endTime} />
<input id="active" type="checkbox" checked={active} />
<label for="active">Task is active/incomplete</label>
<Checkbox disabled bind:checked={completed} label="Task is completed." />
<hr /> <hr />

9
svelte-ui/src/forms/TaskEditForm.svelte

@ -1,5 +1,6 @@
<script lang="ts"> <script lang="ts">
import stuffLogClient from "../clients/stufflog"; import stuffLogClient from "../clients/stufflog";
import Checkbox from "../components/Checkbox.svelte";
import Modal from "../components/Modal.svelte"; import Modal from "../components/Modal.svelte";
import goalStore, { fpGoalStore } from "../stores/goal"; import goalStore, { fpGoalStore } from "../stores/goal";
import modalStore from "../stores/modal"; import modalStore from "../stores/modal";
@ -15,7 +16,7 @@
let name = task.name; let name = task.name;
let description = task.description; let description = task.description;
let itemAmount = task.itemAmount; let itemAmount = task.itemAmount;
let active = task.active;
let completed = !task.active;
let endTime = task.endTime ? formatFormTime(task.endTime) : ""; let endTime = task.endTime ? formatFormTime(task.endTime) : "";
let error = null; let error = null;
@ -23,8 +24,9 @@
stuffLogClient.updateTask(task.id, { stuffLogClient.updateTask(task.id, {
endTime: (endTime == "") ? null : new Date(endTime), endTime: (endTime == "") ? null : new Date(endTime),
clearEndTime: endTime == "", clearEndTime: endTime == "",
active: !completed,
name, description, itemAmount, active,
name, description, itemAmount,
}).then(() => { }).then(() => {
projectStore.markStale(); projectStore.markStale();
fpProjectStore.markStale(); fpProjectStore.markStale();
@ -53,8 +55,7 @@
<input name="itemAmount" type="number" bind:value={itemAmount} /> <input name="itemAmount" type="number" bind:value={itemAmount} />
<label for="endTime">Deadline (Optional)</label> <label for="endTime">Deadline (Optional)</label>
<input name="endTime" type="datetime-local" bind:value={endTime} /> <input name="endTime" type="datetime-local" bind:value={endTime} />
<input id="active" type="checkbox" bind:checked={active} />
<label for="active">Task is active/incomplete</label>
<Checkbox bind:checked={completed} label="Task is completed." />
<hr /> <hr />

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

@ -5,7 +5,7 @@ export default interface Project {
name: string name: string
description: string description: string
icon: string icon: string
active: string
active: boolean
createdTime: string createdTime: string
endTime?: string endTime?: string
} }

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

@ -1,21 +1,35 @@
<script lang="ts"> <script lang="ts">
import Boi from "../components/Boi.svelte"; import Boi from "../components/Boi.svelte";
import Checkbox from "../components/Checkbox.svelte";
import ProjectEntry from "../components/ProjectEntry.svelte"; import ProjectEntry from "../components/ProjectEntry.svelte";
import type { ModalData } from "../stores/modal"; import type { ModalData } from "../stores/modal";
import projectStore from "../stores/project"; import projectStore from "../stores/project";
const mdProjectAdd: ModalData = {name: "project.add"}; const mdProjectAdd: ModalData = {name: "project.add"};
let showInactive = ($projectStore.filter.active === null);
$: {
if (showInactive && $projectStore.filter.active === true) {
projectStore.markStale();
}
if (!showInactive && $projectStore.filter.active == null) {
projectStore.markStale();
}
}
$: { $: {
if ($projectStore.stale && !$projectStore.loading) { if ($projectStore.stale && !$projectStore.loading) {
projectStore.load({ projectStore.load({
active: true,
active: showInactive ? null : true,
}); });
} }
} }
</script> </script>
<div class="page"> <div class="page">
<div class="options">
<Checkbox centered bind:checked={showInactive} label="Show inactive projects." />
</div>
{#each $projectStore.projects as project (project.id)} {#each $projectStore.projects as project (project.id)}
<ProjectEntry showAllOptions project={project} /> <ProjectEntry showAllOptions project={project} />
{/each} {/each}
@ -31,4 +45,9 @@
margin-top: 0; margin-top: 0;
box-sizing: border-box; box-sizing: border-box;
} }
div.options {
display: flex;
margin: 1em auto;
}
</style> </style>

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

@ -5,6 +5,7 @@ import type { ProjectFilter, ProjectResult } from "../models/project";
interface ProjectStoreData { interface ProjectStoreData {
loading: boolean loading: boolean
stale: boolean stale: boolean
filter: ProjectFilter
projects: ProjectResult[] projects: ProjectResult[]
} }
@ -12,6 +13,7 @@ function createProjectStore() {
const {update, subscribe} = writable<ProjectStoreData>({ const {update, subscribe} = writable<ProjectStoreData>({
loading: false, loading: false,
stale: true, stale: true,
filter: {},
projects: [], projects: [],
}); });
@ -23,9 +25,10 @@ function createProjectStore() {
}, },
async load(filter: ProjectFilter) { async load(filter: ProjectFilter) {
update(v => ({...v, loading: true, filter}));
console.log(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, stale: false, projects: projects.reverse()}));
update(v => ({...v, loading: false, projects: projects.reverse()}));
}, },
} }
} }

Loading…
Cancel
Save