diff --git a/src/hooks/workspace.ts b/src/hooks/workspace.ts index 112a109..8dd9510 100644 --- a/src/hooks/workspace.ts +++ b/src/hooks/workspace.ts @@ -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; - } - | { - name: string; - kind: "doc"; - doc: OutputData; - setDoc: SetState; - }; +export function useWorkspace(workspaceId: string) { + const [workspace, setWorkspace] = useState(() => + loadWorkspaceFromLocalStorage(workspaceId), + ); -export interface Workspace { - name: string; - entityLayout: SidebarEntity[]; - entities: Record; - - route: string; - setRoute: SetState; -} - -export default function useWorkspace(): Workspace { - const [route, setRoute] = useState("alpha_tasks"); - const [tasks, setTasks] = useState([]); - const [doc, setDoc] = useState({ - blocks: [], - }); - const [doc2, setDoc2] = useState({ - 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 }; } diff --git a/src/lib/types.ts b/src/lib/types.ts index 6d7c086..0aa6331 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -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 = React.Dispatch>; + +export type EntityId = string; +export type TagId = string; +export type TaskId = string; + +export interface Workspace { + id: string; + name: string; + + entities: Record; + 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; + tasks: Record; + + 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; +} diff --git a/src/lib/workspace.ts b/src/lib/workspace.ts new file mode 100644 index 0000000..44d6e4e --- /dev/null +++ b/src/lib/workspace.ts @@ -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); + } +}