Updating workspace structure
This commit is contained in:
+22
-91
@@ -1,98 +1,29 @@
|
|||||||
import { SidebarEntity } from "@/components/sidebar/Entity";
|
import { Entity, EntityId, Workspace } from "@/lib/types";
|
||||||
import { SetState, Task } from "@/lib/types";
|
import {
|
||||||
import { OutputData } from "@editorjs/editorjs";
|
loadWorkspaceFromLocalStorage,
|
||||||
|
saveWorkspaceToLocalStorage,
|
||||||
|
} from "@/lib/workspace";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
export type Entity =
|
export function useWorkspace(workspaceId: string) {
|
||||||
| {
|
const [workspace, setWorkspace] = useState<Workspace>(() =>
|
||||||
name: string;
|
loadWorkspaceFromLocalStorage(workspaceId),
|
||||||
kind: "todo";
|
);
|
||||||
tasks: Task[];
|
|
||||||
setTasks: SetState<Task[]>;
|
const updateEntity = (entityId: EntityId, updater: (e: Entity) => Entity) => {
|
||||||
}
|
setWorkspace((prev) => ({
|
||||||
| {
|
...prev,
|
||||||
name: string;
|
entities: {
|
||||||
kind: "doc";
|
...prev.entities,
|
||||||
doc: OutputData;
|
[entityId]: updater(prev.entities[entityId]),
|
||||||
setDoc: SetState<OutputData>;
|
},
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
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: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTasks([
|
saveWorkspaceToLocalStorage(workspace);
|
||||||
{ id: "1", title: "Buy groceries", status: "todo" },
|
}, [workspace]);
|
||||||
{ id: "2", title: "Plan sprint", status: "todo" },
|
|
||||||
{
|
|
||||||
id: "3",
|
|
||||||
title: "Build App",
|
|
||||||
description: "A simple description",
|
|
||||||
tags: ["website", "api", "app"],
|
|
||||||
status: "inprogress",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
return { workspace, updateEntity };
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
+81
-9
@@ -1,11 +1,83 @@
|
|||||||
import { Color } from "./ui/color";
|
import { OutputData } from "@editorjs/editorjs";
|
||||||
|
|
||||||
export type Task = {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
status: string;
|
|
||||||
description?: string;
|
|
||||||
tags?: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user