Updating workspace structure

This commit is contained in:
2026-02-27 21:16:02 +01:00
parent 629f7d7a6d
commit 1a51dc8467
3 changed files with 153 additions and 100 deletions
+22 -91
View File
@@ -1,98 +1,29 @@
import { SidebarEntity } from "@/components/sidebar/Entity";
import { SetState, Task } from "@/lib/types";
import { OutputData } from "@editorjs/editorjs";
import { Entity, EntityId, Workspace } from "@/lib/types";
import {
loadWorkspaceFromLocalStorage,
saveWorkspaceToLocalStorage,
} from "@/lib/workspace";
import { useEffect, useState } from "react";
export type Entity =
| {
name: string;
kind: "todo";
tasks: Task[];
setTasks: SetState<Task[]>;
}
| {
name: string;
kind: "doc";
doc: OutputData;
setDoc: SetState<OutputData>;
};
export function useWorkspace(workspaceId: string) {
const [workspace, setWorkspace] = useState<Workspace>(() =>
loadWorkspaceFromLocalStorage(workspaceId),
);
export interface Workspace {
name: string;
entityLayout: SidebarEntity[];
entities: Record<string, Entity>;
route: string;
setRoute: SetState<string>;
}
export default function useWorkspace(): Workspace {
const [route, setRoute] = useState("alpha_tasks");
const [tasks, setTasks] = useState<Task[]>([]);
const [doc, setDoc] = useState<OutputData>({
blocks: [],
});
const [doc2, setDoc2] = useState<OutputData>({
blocks: [],
});
const updateEntity = (entityId: EntityId, updater: (e: Entity) => Entity) => {
setWorkspace((prev) => ({
...prev,
entities: {
...prev.entities,
[entityId]: updater(prev.entities[entityId]),
},
updatedAt: Date.now(),
}));
};
useEffect(() => {
setTasks([
{ id: "1", title: "Buy groceries", status: "todo" },
{ id: "2", title: "Plan sprint", status: "todo" },
{
id: "3",
title: "Build App",
description: "A simple description",
tags: ["website", "api", "app"],
status: "inprogress",
},
]);
}, []);
saveWorkspaceToLocalStorage(workspace);
}, [workspace]);
return {
name: "Personal Workspace",
entities: {
alpha_tasks: {
name: "Alpha tasks",
kind: "todo",
tasks,
setTasks,
},
beta_tasks: {
name: "Beta tasks",
kind: "todo",
tasks,
setTasks,
},
alpha_doc: {
name: "Alpha docs",
kind: "doc",
doc: doc,
setDoc: setDoc,
},
beta_doc: {
name: "Beta docs",
kind: "doc",
doc: doc2,
setDoc: setDoc2,
},
},
entityLayout: [
{
kind: "group",
name: "Project alpha",
children: [{ id: "alpha_tasks" }, { id: "alpha_doc" }],
},
{
kind: "group",
name: "Project beta",
children: [{ id: "beta_tasks" }, { id: "beta_doc" }],
},
],
route,
setRoute,
};
return { workspace, updateEntity };
}
+81 -9
View File
@@ -1,11 +1,83 @@
import { Color } from "./ui/color";
export type Task = {
id: string;
title: string;
status: string;
description?: string;
tags?: string[];
};
import { OutputData } from "@editorjs/editorjs";
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
export type EntityId = string;
export type TagId = string;
export type TaskId = string;
export interface Workspace {
id: string;
name: string;
entities: Record<EntityId, Entity>;
layout: LayoutNode[];
createdAt: number;
updatedAt: number;
}
export type LayoutNode = {
type: "group";
id: string;
name: string;
children: LayoutItem[];
};
export type LayoutItem = {
entityId: EntityId;
};
export type Entity = TodoEntity | DocEntity;
export interface TodoEntity {
id: EntityId;
type: "todo";
name: string;
config: {
statuses: Status[];
};
tags: Record<TagId, Tag>;
tasks: Record<TaskId, Task>;
createdAt: number;
updatedAt: number;
}
export interface Status {
id: string;
name: string;
color?: string;
order: number;
}
export interface Tag {
id: TagId;
name: string;
color?: string;
}
export interface Task {
id: TaskId;
title: string;
description?: string;
statusId: string;
tagIds: TagId[];
createdAt: number;
updatedAt: number;
}
export interface DocEntity {
id: EntityId;
type: "doc";
name: string;
content: OutputData;
createdAt: number;
updatedAt: number;
}
+50
View File
@@ -0,0 +1,50 @@
import { Workspace } from "./types";
export function createDefaultWorkspace(): Workspace {
const now = Date.now();
return {
id: crypto.randomUUID(),
name: "New Workspace",
entities: {},
layout: [],
createdAt: now,
updatedAt: now,
};
}
export function loadWorkspaceFromLocalStorage(id: string): Workspace {
try {
const raw = localStorage.getItem(id);
if (!raw) {
return createDefaultWorkspace();
}
const parsed = JSON.parse(raw);
// Basic shape validation
if (
typeof parsed !== "object" ||
parsed === null ||
typeof parsed.version !== "number"
) {
console.warn("Invalid workspace shape. Resetting.");
return createDefaultWorkspace();
}
return parsed as Workspace;
} catch (err) {
console.error("Failed to load workspace:", err);
return createDefaultWorkspace();
}
}
export function saveWorkspaceToLocalStorage(workspace: Workspace): void {
try {
const serialized = JSON.stringify(workspace);
localStorage.setItem(workspace.id, serialized);
} catch (err) {
console.error("Failed to save workspace:", err);
}
}