156 lines
4.5 KiB
TypeScript
156 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import Router from "@/components/Router";
|
|
import AppSidebar, { SidebarTrigger } from "@/components/sidebar/AppSidebar";
|
|
import { useWorkspace } from "@/hooks/workspace";
|
|
import { ChevronRight } from "lucide-react";
|
|
import Cookies from "js-cookie";
|
|
import { useEffect, useState } from "react";
|
|
import { Route } from "@/lib/types";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
createDefaultWorkspace,
|
|
saveWorkspaceToLocalStorage,
|
|
} from "@/lib/workspace";
|
|
|
|
export default function Workspace() {
|
|
const {
|
|
workspace,
|
|
loadWorkspace,
|
|
updateEntity,
|
|
setWorkspace,
|
|
addEntity,
|
|
addGroup,
|
|
renameEntity,
|
|
deleteEntity,
|
|
removeNode,
|
|
renameGroup,
|
|
} = useWorkspace();
|
|
|
|
const [route, setRoute] = useState<Route>({});
|
|
|
|
/**
|
|
* Initialize workspace from cookie on mount
|
|
*/
|
|
useEffect(() => {
|
|
const routeString = Cookies.get("route");
|
|
if (!routeString) return;
|
|
const route = JSON.parse(routeString) as Route;
|
|
setRoute(route);
|
|
}, []);
|
|
|
|
/**
|
|
* Load workspace when currentWorkspace changes.
|
|
*
|
|
* `loadWorkspace` is intentionally omitted from the dependency array
|
|
* because it is recreated on every render by the `useWorkspace` hook.
|
|
* Including it would cause this effect to run repeatedly and trigger
|
|
* the "maximum update depth exceeded" error. ESLint warning is disabled
|
|
* for this case.
|
|
*/
|
|
useEffect(() => {
|
|
loadWorkspace(route.workspace);
|
|
if (route.workspace) {
|
|
Cookies.set("route", JSON.stringify(route));
|
|
} else {
|
|
Cookies.remove("route");
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [route]);
|
|
|
|
/**
|
|
* Load route (entity) from localStorage when workspace loads
|
|
*/
|
|
useEffect(() => {
|
|
if (!workspace) return;
|
|
|
|
try {
|
|
const savedRoute = localStorage.getItem(`route_${workspace.id}`);
|
|
|
|
if (savedRoute) {
|
|
const parsedRoute: Route = JSON.parse(savedRoute);
|
|
setRoute(parsedRoute);
|
|
} else {
|
|
// Default to first entity if no route saved
|
|
const firstEntityId = Object.keys(workspace.entities)[0];
|
|
if (firstEntityId) {
|
|
setRoute({ workspace: workspace.id, entity: firstEntityId });
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error("Failed to load route:", err);
|
|
const firstEntityId = Object.keys(workspace.entities)[0];
|
|
if (firstEntityId) {
|
|
setRoute({ workspace: workspace.id, entity: firstEntityId });
|
|
}
|
|
}
|
|
}, [workspace?.id]);
|
|
|
|
/**
|
|
* Save route whenever it changes
|
|
*/
|
|
useEffect(() => {
|
|
if (!workspace || !route.entity) return;
|
|
|
|
try {
|
|
localStorage.setItem(
|
|
`route_${workspace.id}`,
|
|
JSON.stringify({ workspace: workspace.id, entity: route.entity }),
|
|
);
|
|
} catch (err) {
|
|
console.error("Failed to save route:", err);
|
|
}
|
|
}, [route, workspace?.id]);
|
|
|
|
if (!workspace) {
|
|
return (
|
|
<main
|
|
className="flex h-svh items-center justify-center bg-linear-to-br from-blue-50 to-indigo-100 dark:from-slate-900 dark:to-slate-800"
|
|
onClick={() => {
|
|
const w = createDefaultWorkspace();
|
|
saveWorkspaceToLocalStorage(w);
|
|
setRoute({ workspace: w.id });
|
|
}}
|
|
>
|
|
<div className="text-center space-y-6">
|
|
<h1 className="text-4xl font-bold">Welcome to Drift</h1>
|
|
<p className="text-lg text-muted-foreground">
|
|
Your personal productivity workspace
|
|
</p>
|
|
<Button size="lg" className="text-base">
|
|
Create a new workspace
|
|
</Button>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<AppSidebar
|
|
workspace={workspace}
|
|
setRoute={setRoute}
|
|
setWorkspace={setWorkspace}
|
|
addEntity={addEntity}
|
|
addGroup={addGroup}
|
|
renameEntity={renameEntity}
|
|
deleteEntity={deleteEntity}
|
|
removeNode={removeNode}
|
|
renameGroup={renameGroup}
|
|
>
|
|
<SidebarTrigger className="w-full px-4">
|
|
<div className="px-4">
|
|
<header className="w-full border-b pb-3 pt-2 hover:bg-accent/20 flex items-center text-foreground/90 gap-2">
|
|
<span className="font-semibold">{workspace.name}</span>
|
|
<ChevronRight className="h-5 text-muted-foreground" />
|
|
<span className="text-muted-foreground">
|
|
{workspace.entities[route.entity || ""]?.name || "Select an item"}
|
|
</span>
|
|
</header>
|
|
</div>
|
|
</SidebarTrigger>
|
|
|
|
<Router workspace={workspace} route={route} updateEntity={updateEntity} />
|
|
</AppSidebar>
|
|
);
|
|
}
|