Improve individual tasks on the front page, compact icon selector, and add annotaions.
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import type { IconName } from "../external/icons";
|
||||||
import type { GoalResult } from "../models/goal";
|
import type { GoalResult } from "../models/goal";
|
||||||
import type { ModalData } from "../stores/modal";
|
import type { ModalData } from "../stores/modal";
|
||||||
import Composition from "./Composition.svelte";
|
import Composition from "./Composition.svelte";
|
||||||
import EveryMinute from "./EveryMinute.svelte";
|
|
||||||
import Option from "./Option.svelte";
|
import Option from "./Option.svelte";
|
||||||
import OptionRow from "./OptionRow.svelte";
|
import OptionRow from "./OptionRow.svelte";
|
||||||
import ParentEntry from "./ParentEntry.svelte";
|
import ParentEntry from "./ParentEntry.svelte";
|
||||||
import Progress from "./Progress.svelte";
|
import Progress from "./Progress.svelte";
|
||||||
import TimeProgress from "./TimeProgress.svelte";
|
import TimeProgress from "./TimeProgress.svelte";
|
||||||
|
|
||||||
export let goal: GoalResult = null;
|
export let goal: GoalResult = null;
|
||||||
export let showAllOptions = false;
|
export let showAllOptions = false;
|
||||||
@@ -15,15 +15,29 @@ import TimeProgress from "./TimeProgress.svelte";
|
|||||||
|
|
||||||
let mdGoalEdit: ModalData;
|
let mdGoalEdit: ModalData;
|
||||||
let mdGoalDelete: ModalData;
|
let mdGoalDelete: ModalData;
|
||||||
|
let annotations: IconName[];
|
||||||
|
|
||||||
$: mdGoalEdit = {name:"goal.edit", goal};
|
$: mdGoalEdit = {name:"goal.edit", goal};
|
||||||
$: mdGoalDelete = {name:"goal.delete", goal};
|
$: mdGoalDelete = {name:"goal.delete", goal};
|
||||||
|
$: {
|
||||||
|
annotations = [];
|
||||||
|
if (goal.unweighted) {
|
||||||
|
annotations.push("balance_scale")
|
||||||
|
}
|
||||||
|
if (goal.taskFilter) {
|
||||||
|
annotations.push("filter")
|
||||||
|
}
|
||||||
|
if (goal.itemFilter) {
|
||||||
|
annotations.push("filter")
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ParentEntry
|
<ParentEntry
|
||||||
full={showAllOptions}
|
full={showAllOptions}
|
||||||
entry={goal}
|
entry={goal}
|
||||||
headerLink={linkGoal ? "/goals#"+goal.id : ""}
|
headerLink={linkGoal ? "/goals#"+goal.id : ""}
|
||||||
|
annotations={annotations}
|
||||||
>
|
>
|
||||||
{#if showAllOptions}
|
{#if showAllOptions}
|
||||||
<OptionRow>
|
<OptionRow>
|
||||||
|
|||||||
@@ -1,18 +1,34 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { commonIcons, uncommonIcons } from "../external/icons";
|
||||||
import type { IconName } from "../external/icons";
|
import type { IconName } from "../external/icons";
|
||||||
import { iconNames } from "../external/icons";
|
|
||||||
import Icon from "./Icon.svelte";
|
import Icon from "./Icon.svelte";
|
||||||
|
|
||||||
export let value: IconName;
|
export let value: IconName;
|
||||||
export let disabled: boolean;
|
export let disabled: boolean;
|
||||||
|
|
||||||
|
let showAll = !commonIcons.includes(value);
|
||||||
|
|
||||||
|
function toggleShowAll() {
|
||||||
|
showAll = !showAll;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class:disabled class="icon-select">
|
<div class:disabled class="icon-select">
|
||||||
{#each iconNames as iconName (iconName)}
|
{#each commonIcons as iconName (iconName)}
|
||||||
<div class="icon-item" class:selected={value===iconName} on:click={() => {if (!disabled) { value = iconName }}}>
|
<div class="icon-item common" class:selected={value===iconName} on:click={() => {if (!disabled) { value = iconName }}}>
|
||||||
<Icon name={iconName} />
|
<Icon name={iconName} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
<div class="show-all" on:click={toggleShowAll}>
|
||||||
|
{ showAll ? "Hide uncommon icons" : "Show uncommon icons" }
|
||||||
|
</div>
|
||||||
|
{#if showAll}
|
||||||
|
{#each uncommonIcons as iconName (iconName)}
|
||||||
|
<div class="icon-item" class:selected={value===iconName} on:click={() => {if (!disabled) { value = iconName }}}>
|
||||||
|
<Icon name={iconName} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -37,6 +53,9 @@ div.icon-item {
|
|||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
div.icon-item.uncommon {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
div.icon-item:hover {
|
div.icon-item:hover {
|
||||||
background-color: #292929;
|
background-color: #292929;
|
||||||
}
|
}
|
||||||
@@ -53,4 +72,15 @@ div.icon-select.disabled > div.icon-item {
|
|||||||
div.icon-select.disabled > div.icon-item.selected {
|
div.icon-select.disabled > div.icon-item.selected {
|
||||||
background-color: #555;
|
background-color: #555;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.show-all {
|
||||||
|
color: #777;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.25em 0;
|
||||||
|
}
|
||||||
|
div.show-all:hover {
|
||||||
|
color: #ccc;
|
||||||
|
background-color: #2c2c2c;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -34,6 +34,7 @@ import TimeProgress from "./TimeProgress.svelte";
|
|||||||
export let hideProgress: boolean = false;
|
export let hideProgress: boolean = false;
|
||||||
export let hideIcon: boolean = false;
|
export let hideIcon: boolean = false;
|
||||||
export let showTimeProgress: boolean = false;
|
export let showTimeProgress: boolean = false;
|
||||||
|
export let annotations: IconName[] = [];
|
||||||
|
|
||||||
let iconName: IconName;
|
let iconName: IconName;
|
||||||
|
|
||||||
@@ -68,6 +69,11 @@ import TimeProgress from "./TimeProgress.svelte";
|
|||||||
{entry.name}
|
{entry.name}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
{#each annotations as annotation (annotation)}
|
||||||
|
<div class="annotation">
|
||||||
|
<Icon block name={annotation} />
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
{#if entry.endTime != null}
|
{#if entry.endTime != null}
|
||||||
<div class="days-left">
|
<div class="days-left">
|
||||||
<DaysLeft startTime={entry.startTime || entry.createdTime} endTime={entry.endTime} />
|
<DaysLeft startTime={entry.startTime || entry.createdTime} endTime={entry.endTime} />
|
||||||
@@ -122,6 +128,15 @@ import TimeProgress from "./TimeProgress.svelte";
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
}
|
}
|
||||||
|
div.annotation {
|
||||||
|
margin: auto 0;
|
||||||
|
padding: 0 0.5ch;
|
||||||
|
line-height: 0em;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
div.annotation + div.annotation {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
div.days-left {
|
div.days-left {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: 0.25ch;
|
margin-right: 0.25ch;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
export let hideProgress: boolean = false;
|
export let hideProgress: boolean = false;
|
||||||
export let linkProject: boolean = false;
|
export let linkProject: boolean = false;
|
||||||
export let hideIcon: boolean = false;
|
export let hideIcon: boolean = false;
|
||||||
|
export let isFake: boolean = false;
|
||||||
|
|
||||||
let mdAddTask: ModalData;
|
let mdAddTask: ModalData;
|
||||||
let mdProjectEdit: ModalData;
|
let mdProjectEdit: ModalData;
|
||||||
@@ -58,6 +59,7 @@
|
|||||||
hideProgress={hideProgress}
|
hideProgress={hideProgress}
|
||||||
hideIcon={hideIcon}
|
hideIcon={hideIcon}
|
||||||
showTimeProgress={!hideProgress}
|
showTimeProgress={!hideProgress}
|
||||||
|
annotations={isFake ? ["list"] : []}
|
||||||
>
|
>
|
||||||
{#if showAllOptions}
|
{#if showAllOptions}
|
||||||
<OptionRow>
|
<OptionRow>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
let inactiveProjects: ProjectResult[];
|
let inactiveProjects: ProjectResult[];
|
||||||
let completedProjects: ProjectResult[];
|
let completedProjects: ProjectResult[];
|
||||||
let failedProjects: ProjectResult[];
|
let failedProjects: ProjectResult[];
|
||||||
let posponedProjects: ProjectResult[];
|
let onholdProjects: ProjectResult[];
|
||||||
let ideaProjects: ProjectResult[];
|
let ideaProjects: ProjectResult[];
|
||||||
let project: ProjectResult = null;
|
let project: ProjectResult = null;
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
$: inactiveProjects = projects.filter(p => !p.active).sort((a,b) => a.name.localeCompare(b.name));
|
$: inactiveProjects = projects.filter(p => !p.active).sort((a,b) => a.name.localeCompare(b.name));
|
||||||
$: completedProjects = inactiveProjects.filter(p => p.statusTag === "completed" || p.statusTag == null);
|
$: completedProjects = inactiveProjects.filter(p => p.statusTag === "completed" || p.statusTag == null);
|
||||||
$: failedProjects = inactiveProjects.filter(p => p.statusTag === "failed" || p.statusTag === "declined");
|
$: failedProjects = inactiveProjects.filter(p => p.statusTag === "failed" || p.statusTag === "declined");
|
||||||
$: postponedProjects = inactiveProjects.filter(p => p.statusTag === "on hold" || p.statusTag === "postponed");
|
$: onholdProjects = inactiveProjects.filter(p => p.statusTag === "on hold" || p.statusTag === "onhold");
|
||||||
$: ideaProjects = inactiveProjects.filter(p => p.statusTag === "to do" || p.statusTag === "idea");
|
$: ideaProjects = inactiveProjects.filter(p => p.statusTag === "to do" || p.statusTag === "idea");
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
<QlList label="Deadlines" projects={expiringProjects} />
|
<QlList label="Deadlines" projects={expiringProjects} />
|
||||||
<QlList label="Active" projects={activeProjects} />
|
<QlList label="Active" projects={activeProjects} />
|
||||||
<QlList label="To Do" projects={ideaProjects} />
|
<QlList label="To Do" projects={ideaProjects} />
|
||||||
<QlList label="On Hold" projects={postponedProjects} />
|
<QlList label="On Hold" projects={onholdProjects} />
|
||||||
<QlList label="Completed" projects={completedProjects} />
|
<QlList label="Completed" projects={completedProjects} />
|
||||||
<QlList label="Failed" projects={failedProjects} />
|
<QlList label="Failed" projects={failedProjects} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Vendored
+29
@@ -194,9 +194,38 @@ const icons = {
|
|||||||
"bottle_opener": faSearch,
|
"bottle_opener": faSearch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const commonIcons: IconName[] = [
|
||||||
|
"book",
|
||||||
|
"book_open",
|
||||||
|
"calendar",
|
||||||
|
"code",
|
||||||
|
"code_branch",
|
||||||
|
"crosshairs",
|
||||||
|
"cube",
|
||||||
|
"cubes",
|
||||||
|
"database",
|
||||||
|
"dice_d20",
|
||||||
|
"dice_d6",
|
||||||
|
"gamepad",
|
||||||
|
"guitar",
|
||||||
|
"headphones",
|
||||||
|
"home",
|
||||||
|
"language",
|
||||||
|
"music",
|
||||||
|
"pen",
|
||||||
|
"pencil_alt",
|
||||||
|
"question",
|
||||||
|
"server",
|
||||||
|
"star",
|
||||||
|
"terminal",
|
||||||
|
"wrench",
|
||||||
|
];
|
||||||
|
|
||||||
export type IconName = keyof typeof icons;
|
export type IconName = keyof typeof icons;
|
||||||
|
|
||||||
export const iconNames = Object.keys(icons).sort() as IconName[];
|
export const iconNames = Object.keys(icons).sort() as IconName[];
|
||||||
export const DEFAULT_ICON = iconNames[0] as IconName;
|
export const DEFAULT_ICON = iconNames[0] as IconName;
|
||||||
|
|
||||||
|
export const uncommonIcons = iconNames.filter(n => !commonIcons.includes(n));
|
||||||
|
|
||||||
export default icons;
|
export default icons;
|
||||||
|
|||||||
@@ -5,10 +5,11 @@
|
|||||||
import RefreshSelection from "../components/RefreshSelection.svelte";
|
import RefreshSelection from "../components/RefreshSelection.svelte";
|
||||||
import type { ProjectResult } from "../models/project";
|
import type { ProjectResult } from "../models/project";
|
||||||
import { fpGoalStore } from "../stores/goal";
|
import { fpGoalStore } from "../stores/goal";
|
||||||
import { fpProjectStore } from "../stores/project";
|
import projectStore, { fpProjectStore } from "../stores/project";
|
||||||
import { fpTaskStore } from "../stores/tasks";
|
import { fpTaskStore } from "../stores/tasks";
|
||||||
|
|
||||||
let fakeProject: ProjectResult
|
let fakeMap: {[projectId: string]: boolean} = {}
|
||||||
|
let fakeProjects: ProjectResult[]
|
||||||
let sortedProjects: ProjectResult[]
|
let sortedProjects: ProjectResult[]
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
@@ -39,27 +40,38 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
fakeProject = {
|
const individualTasks = $fpTaskStore.tasks
|
||||||
id: "P_fakeProject",
|
.filter(t => $fpProjectStore.projects.find(p => p.id === t.projectId) == null)
|
||||||
active: true,
|
.sort((a,b) => Date.parse(a.endTime) - Date.parse(b.endTime));
|
||||||
createdTime: "1970-01-01T00:00:00Z",
|
|
||||||
description: "",
|
fakeProjects = [];
|
||||||
icon: "list",
|
fakeMap = {};
|
||||||
name: "Individual Tasks",
|
for (let task of individualTasks) {
|
||||||
tasks: $fpTaskStore.tasks.filter(t => $fpProjectStore.projects.find(p => p.id === t.id) == null)
|
if (!task.project.active) {
|
||||||
.sort((a,b) => Date.parse(a.endTime) - Date.parse(b.endTime)),
|
continue;
|
||||||
endTime: null,
|
}
|
||||||
|
|
||||||
|
let fakeProject = fakeProjects.find(p => p.id === task.id);
|
||||||
|
if (fakeProject == null) {
|
||||||
|
fakeMap[task.projectId] = true;
|
||||||
|
fakeProjects.push({
|
||||||
|
...task.project,
|
||||||
|
tasks: [task],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fakeProject.tasks.push(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fakeProject.tasks.length > 0) {
|
for (const fakeProject of fakeProjects) {
|
||||||
fakeProject.createdTime = fakeProject.tasks.map(t => t.createdTime).sort()[0];
|
fakeProject.createdTime = fakeProject.tasks.map(t => t.createdTime).sort()[0];
|
||||||
fakeProject.endTime = fakeProject.tasks[0].endTime;
|
fakeProject.endTime = fakeProject.tasks[0].endTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
const projects = fakeProject.tasks.length > 0 ? [...$fpProjectStore.projects, fakeProject] : [...$fpProjectStore.projects];
|
sortedProjects = [...fakeProjects, ...$fpProjectStore.projects]
|
||||||
sortedProjects = projects.sort((a,b) => Date.parse(a.endTime) - Date.parse(b.endTime));
|
.sort((a,b) => Date.parse(a.endTime) - Date.parse(b.endTime));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -80,9 +92,9 @@
|
|||||||
<h1>Upcoming Deadlines</h1>
|
<h1>Upcoming Deadlines</h1>
|
||||||
{/if}
|
{/if}
|
||||||
{#each sortedProjects as project (project.id)}
|
{#each sortedProjects as project (project.id)}
|
||||||
<ProjectEntry linkProject={project != fakeProject} hideInactive project={project} />
|
<ProjectEntry isFake={fakeMap[project.id]} linkProject hideInactive project={project} />
|
||||||
{/each}
|
{/each}
|
||||||
{#if fakeProject.tasks.length === 0 && !$fpProjectStore.loading && $fpProjectStore.projects.length === 0}
|
{#if !$fpProjectStore.loading && $fpProjectStore.projects.length === 0}
|
||||||
<EmptyList icon="check" text="All good!" />
|
<EmptyList icon="check" text="All good!" />
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user