add support for project inactivity, added new checkbox component.
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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} />
|
<Checkbox bind:checked={markInactive} label="Mark task inactive/completed." />
|
||||||
<label for="markInactive">Complete Task</label>
|
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
|||||||
@@ -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 />
|
||||||
|
|
||||||
|
|||||||
@@ -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} />
|
<Checkbox disabled bind:checked={completed} label="Task is completed." />
|
||||||
<label for="active">Task is active/incomplete</label>
|
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
|||||||
@@ -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} />
|
<Checkbox bind:checked={completed} label="Task is completed." />
|
||||||
<label for="active">Task is active/incomplete</label>
|
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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()}));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user