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">
|
||||
import type { IconName } from "../external/icons";
|
||||
import type { GoalResult } from "../models/goal";
|
||||
import type { ModalData } from "../stores/modal";
|
||||
import Composition from "./Composition.svelte";
|
||||
import EveryMinute from "./EveryMinute.svelte";
|
||||
import Composition from "./Composition.svelte";
|
||||
import Option from "./Option.svelte";
|
||||
import OptionRow from "./OptionRow.svelte";
|
||||
import ParentEntry from "./ParentEntry.svelte";
|
||||
import Progress from "./Progress.svelte";
|
||||
import TimeProgress from "./TimeProgress.svelte";
|
||||
import TimeProgress from "./TimeProgress.svelte";
|
||||
|
||||
export let goal: GoalResult = null;
|
||||
export let showAllOptions = false;
|
||||
@@ -15,15 +15,29 @@ import TimeProgress from "./TimeProgress.svelte";
|
||||
|
||||
let mdGoalEdit: ModalData;
|
||||
let mdGoalDelete: ModalData;
|
||||
let annotations: IconName[];
|
||||
|
||||
$: mdGoalEdit = {name:"goal.edit", 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>
|
||||
|
||||
<ParentEntry
|
||||
full={showAllOptions}
|
||||
entry={goal}
|
||||
headerLink={linkGoal ? "/goals#"+goal.id : ""}
|
||||
annotations={annotations}
|
||||
>
|
||||
{#if showAllOptions}
|
||||
<OptionRow>
|
||||
|
||||
@@ -1,18 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { commonIcons, uncommonIcons } from "../external/icons";
|
||||
import type { IconName } from "../external/icons";
|
||||
import { iconNames } from "../external/icons";
|
||||
import Icon from "./Icon.svelte";
|
||||
|
||||
export let value: IconName;
|
||||
export let disabled: boolean;
|
||||
|
||||
let showAll = !commonIcons.includes(value);
|
||||
|
||||
function toggleShowAll() {
|
||||
showAll = !showAll;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class:disabled class="icon-select">
|
||||
{#each iconNames as iconName (iconName)}
|
||||
<div class="icon-item" class:selected={value===iconName} on:click={() => {if (!disabled) { value = iconName }}}>
|
||||
{#each commonIcons as iconName (iconName)}
|
||||
<div class="icon-item common" class:selected={value===iconName} on:click={() => {if (!disabled) { value = iconName }}}>
|
||||
<Icon name={iconName} />
|
||||
</div>
|
||||
{/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>
|
||||
|
||||
<style>
|
||||
@@ -37,6 +53,9 @@ div.icon-item {
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
div.icon-item.uncommon {
|
||||
color: #888;
|
||||
}
|
||||
div.icon-item:hover {
|
||||
background-color: #292929;
|
||||
}
|
||||
@@ -53,4 +72,15 @@ div.icon-select.disabled > div.icon-item {
|
||||
div.icon-select.disabled > div.icon-item.selected {
|
||||
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>
|
||||
@@ -34,6 +34,7 @@ import TimeProgress from "./TimeProgress.svelte";
|
||||
export let hideProgress: boolean = false;
|
||||
export let hideIcon: boolean = false;
|
||||
export let showTimeProgress: boolean = false;
|
||||
export let annotations: IconName[] = [];
|
||||
|
||||
let iconName: IconName;
|
||||
|
||||
@@ -68,6 +69,11 @@ import TimeProgress from "./TimeProgress.svelte";
|
||||
{entry.name}
|
||||
{/if}
|
||||
</div>
|
||||
{#each annotations as annotation (annotation)}
|
||||
<div class="annotation">
|
||||
<Icon block name={annotation} />
|
||||
</div>
|
||||
{/each}
|
||||
{#if entry.endTime != null}
|
||||
<div class="days-left">
|
||||
<DaysLeft startTime={entry.startTime || entry.createdTime} endTime={entry.endTime} />
|
||||
@@ -122,6 +128,15 @@ import TimeProgress from "./TimeProgress.svelte";
|
||||
vertical-align: middle;
|
||||
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 {
|
||||
margin-left: auto;
|
||||
margin-right: 0.25ch;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
export let hideProgress: boolean = false;
|
||||
export let linkProject: boolean = false;
|
||||
export let hideIcon: boolean = false;
|
||||
export let isFake: boolean = false;
|
||||
|
||||
let mdAddTask: ModalData;
|
||||
let mdProjectEdit: ModalData;
|
||||
@@ -58,6 +59,7 @@
|
||||
hideProgress={hideProgress}
|
||||
hideIcon={hideIcon}
|
||||
showTimeProgress={!hideProgress}
|
||||
annotations={isFake ? ["list"] : []}
|
||||
>
|
||||
{#if showAllOptions}
|
||||
<OptionRow>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
let inactiveProjects: ProjectResult[];
|
||||
let completedProjects: ProjectResult[];
|
||||
let failedProjects: ProjectResult[];
|
||||
let posponedProjects: ProjectResult[];
|
||||
let onholdProjects: ProjectResult[];
|
||||
let ideaProjects: ProjectResult[];
|
||||
let project: ProjectResult = null;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
$: inactiveProjects = projects.filter(p => !p.active).sort((a,b) => a.name.localeCompare(b.name));
|
||||
$: completedProjects = inactiveProjects.filter(p => p.statusTag === "completed" || p.statusTag == null);
|
||||
$: 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");
|
||||
|
||||
$: {
|
||||
@@ -39,7 +39,7 @@
|
||||
<QlList label="Deadlines" projects={expiringProjects} />
|
||||
<QlList label="Active" projects={activeProjects} />
|
||||
<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="Failed" projects={failedProjects} />
|
||||
</div>
|
||||
|
||||
Vendored
+29
@@ -194,9 +194,38 @@ const icons = {
|
||||
"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 const iconNames = Object.keys(icons).sort() as IconName[];
|
||||
export const DEFAULT_ICON = iconNames[0] as IconName;
|
||||
|
||||
export const uncommonIcons = iconNames.filter(n => !commonIcons.includes(n));
|
||||
|
||||
export default icons;
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
import RefreshSelection from "../components/RefreshSelection.svelte";
|
||||
import type { ProjectResult } from "../models/project";
|
||||
import { fpGoalStore } from "../stores/goal";
|
||||
import { fpProjectStore } from "../stores/project";
|
||||
import projectStore, { fpProjectStore } from "../stores/project";
|
||||
import { fpTaskStore } from "../stores/tasks";
|
||||
|
||||
let fakeProject: ProjectResult
|
||||
let fakeMap: {[projectId: string]: boolean} = {}
|
||||
let fakeProjects: ProjectResult[]
|
||||
let sortedProjects: ProjectResult[]
|
||||
|
||||
$: {
|
||||
@@ -39,27 +40,38 @@
|
||||
}
|
||||
|
||||
$: {
|
||||
fakeProject = {
|
||||
id: "P_fakeProject",
|
||||
active: true,
|
||||
createdTime: "1970-01-01T00:00:00Z",
|
||||
description: "",
|
||||
icon: "list",
|
||||
name: "Individual Tasks",
|
||||
tasks: $fpTaskStore.tasks.filter(t => $fpProjectStore.projects.find(p => p.id === t.id) == null)
|
||||
.sort((a,b) => Date.parse(a.endTime) - Date.parse(b.endTime)),
|
||||
endTime: null,
|
||||
const individualTasks = $fpTaskStore.tasks
|
||||
.filter(t => $fpProjectStore.projects.find(p => p.id === t.projectId) == null)
|
||||
.sort((a,b) => Date.parse(a.endTime) - Date.parse(b.endTime));
|
||||
|
||||
fakeProjects = [];
|
||||
fakeMap = {};
|
||||
for (let task of individualTasks) {
|
||||
if (!task.project.active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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.endTime = fakeProject.tasks[0].endTime;
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
const projects = fakeProject.tasks.length > 0 ? [...$fpProjectStore.projects, fakeProject] : [...$fpProjectStore.projects];
|
||||
sortedProjects = projects.sort((a,b) => Date.parse(a.endTime) - Date.parse(b.endTime));
|
||||
sortedProjects = [...fakeProjects, ...$fpProjectStore.projects]
|
||||
.sort((a,b) => Date.parse(a.endTime) - Date.parse(b.endTime));
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -80,9 +92,9 @@
|
||||
<h1>Upcoming Deadlines</h1>
|
||||
{/if}
|
||||
{#each sortedProjects as project (project.id)}
|
||||
<ProjectEntry linkProject={project != fakeProject} hideInactive project={project} />
|
||||
<ProjectEntry isFake={fakeMap[project.id]} linkProject hideInactive project={project} />
|
||||
{/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!" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user