{#if project != null}
@@ -96,6 +107,13 @@
width: 32ch;
}
+ h2 {
+ font-weight: 200;
+ margin: 0;
+ padding-bottom: 0.2em;
+ text-align: center;
+ }
+
div.body {
flex-grow: 1;
margin: 1em 1ch;
diff --git a/svelte-ui/src/components/StatusColor.svelte b/svelte-ui/src/components/StatusColor.svelte
index 3dd2609..97c75f2 100644
--- a/svelte-ui/src/components/StatusColor.svelte
+++ b/svelte-ui/src/components/StatusColor.svelte
@@ -1,6 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/svelte-ui/src/models/project.ts b/svelte-ui/src/models/project.ts
index f7b5e52..06fb350 100644
--- a/svelte-ui/src/models/project.ts
+++ b/svelte-ui/src/models/project.ts
@@ -3,6 +3,7 @@ import type { TaskResult } from "./task";
export default interface Project {
id: string
+ groupId: string | null
name: string
description: string
icon: IconName
@@ -29,6 +30,7 @@ export interface ProjectFilter {
export interface ProjectInput {
name: string
+ groupId?: string
description: string
icon: IconName
active: boolean
@@ -43,6 +45,7 @@ export interface ProjectInput {
export interface ProjectUpdate {
name?: string
description?: string
+ groupId?: string
icon?: string
active?: boolean
startTime?: string | Date
diff --git a/svelte-ui/src/models/projectgroup.ts b/svelte-ui/src/models/projectgroup.ts
index 027fbe2..032bc81 100644
--- a/svelte-ui/src/models/projectgroup.ts
+++ b/svelte-ui/src/models/projectgroup.ts
@@ -1,4 +1,4 @@
-import type Project from "./project";
+import type { ProjectResult } from "./project";
export default interface ProjectGroup {
id: string
@@ -8,12 +8,28 @@ export default interface ProjectGroup {
categoryNames: ProjectGroupCategoryMap
}
+export interface ProjectGroupInput {
+ name: string
+ abbreviation: string
+ description?: string
+ categoryNames?: ProjectGroupCategoryMap
+}
+
+export interface ProjectGroupUpdate {
+ name?: string
+ abbreviation?: string
+ description?: string
+ setCategoryNames?: ProjectGroupCategoryMap
+}
+
export interface ProjectGroupResult extends ProjectGroup {
+ projects: ProjectResult[]
projectCounts: ProjectGroupCountMap
taskCounts: ProjectGroupCountMap
}
export interface ProjectGroupCategoryMap {
+ "deadlines"?: string
"completed"?: string
"failed"?: string
"on hold"?: string
diff --git a/svelte-ui/src/pages/ProjectPage.svelte b/svelte-ui/src/pages/ProjectPage.svelte
index fdc3d7a..4c898e8 100644
--- a/svelte-ui/src/pages/ProjectPage.svelte
+++ b/svelte-ui/src/pages/ProjectPage.svelte
@@ -7,7 +7,7 @@
import type { ModalData } from "../stores/modal";
import projectStore from "../stores/project";
- const mdProjectAdd: ModalData = {name: "project.add"};
+ const mdProjectAdd: ModalData = {name: "project.add", groupId: null};
let showInactive = ($projectStore.filter.active === null);
$: {
diff --git a/svelte-ui/src/pages/QLPage.svelte b/svelte-ui/src/pages/QLPage.svelte
index 4b826be..b5a4544 100644
--- a/svelte-ui/src/pages/QLPage.svelte
+++ b/svelte-ui/src/pages/QLPage.svelte
@@ -1,17 +1,45 @@
-
+
diff --git a/svelte-ui/src/stores/markStale.ts b/svelte-ui/src/stores/markStale.ts
index c4b11ce..a8f8e7b 100644
--- a/svelte-ui/src/stores/markStale.ts
+++ b/svelte-ui/src/stores/markStale.ts
@@ -3,6 +3,7 @@ import groupStore from "./group";
import logStore, { fpLogStore } from "./logs";
import projectStore, { fpProjectStore, fpProjectStore2 } from "./project";
import taskStore, { fpTaskStore } from "./tasks";
+import projectGroupStore from "./projectGroup";
type ModelName = "goal" | "project" | "task" | "group" | "item" | "log" | "*"
@@ -17,6 +18,7 @@ export default function markStale(...models: ModelName[]) {
projectStore.markStale();
fpProjectStore.markStale();
fpProjectStore2.markStale();
+ projectGroupStore.markStale();
}
if (markAll || models.includes("task")) {
taskStore.markStale();
diff --git a/svelte-ui/src/stores/modal.ts b/svelte-ui/src/stores/modal.ts
index 0aa62e9..4e5d2d0 100644
--- a/svelte-ui/src/stores/modal.ts
+++ b/svelte-ui/src/stores/modal.ts
@@ -7,6 +7,7 @@ import type {ProjectResult} from "../models/project";
import type {TaskResult} from "../models/task";
import type Project from "../models/project";
import type Task from "../models/task";
+import type ProjectGroup from "../models/projectgroup";
export type ModalData =
| { name: "none" }
@@ -16,9 +17,12 @@ export type ModalData =
| { name: "task.add", project: ProjectResult }
| { name: "task.edit", task: TaskResult }
| { name: "task.delete", task: TaskResult }
- | { name: "project.add" }
+ | { name: "project.add", groupId: string | null }
| { name: "project.edit", project: ProjectResult }
| { name: "project.delete", project: ProjectResult }
+ | { name: "projectgroup.add" }
+ | { name: "projectgroup.edit", projectGroup: ProjectGroup }
+ | { name: "projectgroup.delete", projectGroup: ProjectGroup }
| { name: "group.add" }
| { name: "group.edit", group: GroupResult }
| { name: "group.delete", group: GroupResult }
diff --git a/svelte-ui/src/stores/projectGroup.ts b/svelte-ui/src/stores/projectGroup.ts
new file mode 100644
index 0000000..4113a17
--- /dev/null
+++ b/svelte-ui/src/stores/projectGroup.ts
@@ -0,0 +1,48 @@
+import { writable } from "svelte/store";
+import stuffLogClient from "../clients/stufflog";
+import type { ProjectGroupResult } from "../models/projectgroup";
+
+interface ProjectStoreData {
+ loading: boolean
+ stale: boolean
+ groups: ProjectGroupResult[]
+}
+
+function createProjectGroupStore() {
+ const {update, subscribe} = writable
({
+ loading: false,
+ stale: true,
+ groups: [],
+ });
+
+ return {
+ subscribe,
+
+ markStale() {
+ update(v => ({...v, stale: true}));
+ },
+
+ async loadOne(id: string) {
+ update(v => ({...v, loading: true}));
+ const group = await stuffLogClient.findProjectGroup(id);
+ update(v => ({...v, loading: false, groups: [
+ ...v.groups.filter(g => g.id === id),
+ group,
+ ]}));
+ },
+
+ fakeRefresh() {
+ update(v => ({...v, groups: [...v.groups]}))
+ },
+
+ async load() {
+ update(v => ({...v, loading: true, stale: false}));
+ const groups = await stuffLogClient.listProjectGroups();
+ update(v => ({...v, loading: false, groups: groups}));
+ },
+ }
+}
+
+const projectGroupStore = createProjectGroupStore();
+
+export default projectGroupStore;
diff --git a/svelte-ui/src/utils/sorters.ts b/svelte-ui/src/utils/sorters.ts
new file mode 100644
index 0000000..01dec58
--- /dev/null
+++ b/svelte-ui/src/utils/sorters.ts
@@ -0,0 +1,28 @@
+import type { ProjectResult } from "../models/project";
+
+const STATUS_ORDER: (string | undefined | null)[] = [
+ "deadlines",
+ null,
+ void(0),
+ "active",
+ "background",
+ "progress",
+ "to do",
+ "on hold",
+ "completed",
+ "failed",
+ "declined",
+]
+
+export function sortProjects(a: ProjectResult, b: ProjectResult) {
+ const as = STATUS_ORDER.indexOf(a.statusTag);
+ const bs = STATUS_ORDER.indexOf(b.statusTag);
+ if (as !== bs) {
+ return as - bs;
+ }
+
+ const aName = `${a.tags.slice(0, 1).map(t => t+":").join("")} ${a.name}`.trim();
+ const bName = `${b.tags.slice(0, 1).map(t => t+":").join("")} ${b.name}`.trim();
+
+ return aName.localeCompare(bName);
+}
\ No newline at end of file