Skip to content

Commit 1bf3a81

Browse files
author
joeldevelops
committed
Add workspaces page and new state context
1 parent 509c4ed commit 1bf3a81

File tree

15 files changed

+412
-99
lines changed

15 files changed

+412
-99
lines changed

packages/app/src/components/AccountMenu.tsx

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,30 @@ import LoginDialog from './LoginDialog';
44

55
export default function AccountMenu() {
66
const [loggedIn, setLoggedIn] = useState(false);
7-
const [user, setUser] = useState(null);
7+
const [user, setUser] = useState<any>({});
88
const [avatar, setAvatar] = useState("https://randomuser.me/api/portraits/men/75.jpg");
99
const [name, setName] = useState("");
1010
const [username, setUsername] = useState("");
1111

1212
const logOut = (e: React.MouseEvent<HTMLButtonElement>) => {
1313
e.preventDefault();
1414
localStorage.removeItem('jwt');
15+
localStorage.removeItem('user');
1516
setLoggedIn(false);
1617
}
1718

18-
const loadUser = async () => {
19-
console.log(user);
20-
const jwt = localStorage.getItem('jwt');
21-
if (!jwt) return;
22-
23-
const res = fetch(`${process.env.REACT_APP_BACKEND_API_URL}/api/v1/users/self`, {
24-
headers: {
25-
Authorization: `Bearer ${jwt}`,
26-
'Content-Type': 'application/json',
27-
}
28-
})
29-
.then(res => res.json())
30-
.catch(err => console.error('Failed to fetch user data:', err));
31-
32-
return res;
33-
}
34-
35-
3619
useEffect(() => {
3720
// Check if the user is logged in
3821
const jwt = localStorage.getItem('jwt');
3922
setLoggedIn(!!jwt);
4023

41-
if (jwt) {
42-
// Get user data
43-
loadUser()
44-
.then((data) => {
45-
setUser(data);
46-
localStorage.setItem('user', JSON.stringify(data));
47-
setAvatar(data.avatar);
48-
setName(data.name);
49-
setUsername(data.username);
50-
})
51-
.catch(err => console.error('Failed to fetch user data:', err));
24+
// Get user data
25+
const userData: string | null = localStorage.getItem('user');
26+
if (userData) {
27+
setUser(JSON.parse(userData));
28+
setAvatar(user.avatar);
29+
setName(user.name);
30+
setUsername(user.username);
5231
}
5332
}, []);
5433

@@ -89,7 +68,7 @@ export default function AccountMenu() {
8968
</DropdownMenu.Item>
9069
<DropdownMenu.Separator />
9170
<DropdownMenu.Item className='hover:bg-primary-dark'>
92-
<button onClick={logOut}>Log out</button>
71+
<button className='w-full text-left' onClick={logOut}>Log out</button>
9372
</DropdownMenu.Item>
9473
</DropdownMenu.Content>
9574
</DropdownMenu.Root>

packages/app/src/components/AppSidebar.tsx

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,30 @@
1+
import { useContext, useEffect, useState } from "react";
12
import { useLocation } from "react-router";
3+
import { StoreContext } from "../contexts/StoreContext";
4+
import WorkspaceMenu from "./WorkspaceMenu";
25

36
export default function AppSidebar() {
7+
const stateContext = useContext(StoreContext);
48
const location = useLocation();
59

10+
const [workspace, setWorkspace] = useState({ name: "Default" });
11+
12+
useEffect(() => {
13+
const userData = localStorage.getItem("user");
14+
if (!userData) {
15+
return;
16+
}
17+
18+
const workspaces = JSON.parse(userData).workspaces;
19+
const found = workspaces.find(
20+
(ws: any) => ws.id === stateContext.state.activeWorkspace
21+
);
22+
if (!found) {
23+
return;
24+
}
25+
setWorkspace(found);
26+
}, [stateContext.state.activeWorkspace]);
27+
628
return (
729
<div className="bg-secondaryBackground-light dark:bg-secondaryBackground-dark w-80 px-4 py-2 rounded-xl flex flex-col">
830
{/* Header */}
@@ -21,7 +43,7 @@ export default function AppSidebar() {
2143

2244
{/* Workspace info */}
2345
<div className="px-4 py-4 bg-[#212047] rounded-lg mt-4 border-[1px] border-[#35345B]">
24-
<h2 className="font-bold">Alex's Workspace</h2>
46+
<h2 className="font-bold">{workspace.name} Workspace</h2>
2547
<div className="flex gap-x-2 border-b-[1px] border-[#2B2A51] pt-2 pb-3">
2648
{/* IMG */}
2749
<div className="flex">
@@ -73,16 +95,7 @@ export default function AppSidebar() {
7395
<p className="text-md">Settings</p>
7496
</button>
7597

76-
<button className={`mt-auto flex gap-x-2 p-2 rounded-lg text-gray-light dark:text-gray-dark bg-search-bgLight dark:bg-search-bgDark px-3 py-2.5 mb-2 border-[1px] border-search-bgLight dark:border-search-bgDark hover:bg-hover-dark dark:hover:bg-hover-light transition-all`}>
77-
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
78-
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 4C4.73478 4 4.48043 4.10536 4.29289 4.29289C4.10536 4.48043 4 4.73478 4 5V19C4 19.2652 4.10536 19.5196 4.29289 19.7071C4.48043 19.8946 4.73478 20 5 20H9C9.55228 20 10 20.4477 10 21C10 21.5523 9.55228 22 9 22H5C4.20435 22 3.44129 21.6839 2.87868 21.1213C2.31607 20.5587 2 19.7956 2 19V5C2 4.20435 2.31607 3.44129 2.87868 2.87868C3.44129 2.31607 4.20435 2 5 2H9C9.55228 2 10 2.44772 10 3C10 3.55228 9.55228 4 9 4H5Z" fill="currentColor"/>
79-
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2929 6.29289C15.6834 5.90237 16.3166 5.90237 16.7071 6.29289L21.7071 11.2929C22.0976 11.6834 22.0976 12.3166 21.7071 12.7071L16.7071 17.7071C16.3166 18.0976 15.6834 18.0976 15.2929 17.7071C14.9024 17.3166 14.9024 16.6834 15.2929 16.2929L19.5858 12L15.2929 7.70711C14.9024 7.31658 14.9024 6.68342 15.2929 6.29289Z" fill="currentColor"/>
80-
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 12C8 11.4477 8.44772 11 9 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H9C8.44772 13 8 12.5523 8 12Z" fill="currentColor"/>
81-
</svg>
82-
83-
84-
<p className="text-md">Switch workspace</p>
85-
</button>
98+
<WorkspaceMenu />
8699
</div>
87100
</div>
88101
);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Button } from "@radix-ui/themes";
2+
import { useContext } from "react";
3+
import { ThemeContext } from "../contexts/ThemeContext";
4+
5+
export default function ChangeThemeButton() {
6+
const themeContext = useContext(ThemeContext);
7+
8+
return (
9+
<div className="flex gap-y-4 gap-x-5 border border-secondarySurface-light dark:border-secondarySurface-dark px-3 py-2 rounded-xl">
10+
<Button
11+
variant="ghost"
12+
size="1"
13+
onClick={() => themeContext.changeTheme("light")}
14+
className={`p-2.5 rounded-md cursor-pointer ${
15+
themeContext.theme === "light"
16+
? "bg-secondarySurface-light"
17+
: "hover:bg-primary-dark"
18+
}`}
19+
>
20+
<svg
21+
width="16"
22+
height="16"
23+
viewBox="0 0 20 20"
24+
xmlns="http://www.w3.org/2000/svg"
25+
className="text-gray-light dark:text-gray-dark"
26+
fill="currentColor"
27+
>
28+
<path d="M10 14.1667C7.7 14.1667 5.83333 12.3 5.83333 10C5.83333 7.7 7.7 5.83333 10 5.83333C12.3 5.83333 14.1667 7.7 14.1667 10C14.1667 12.3 12.3 14.1667 10 14.1667ZM10 7.5C8.625 7.5 7.5 8.625 7.5 10C7.5 11.375 8.625 12.5 10 12.5C11.375 12.5 12.5 11.375 12.5 10C12.5 8.625 11.375 7.5 10 7.5ZM10.8333 3.33333V0.833333C10.8333 0.375 10.4583 0 10 0C9.54167 0 9.16667 0.375 9.16667 0.833333V3.33333C9.16667 3.79167 9.54167 4.16667 10 4.16667C10.4583 4.16667 10.8333 3.79167 10.8333 3.33333ZM10.8333 19.1667V16.6667C10.8333 16.2083 10.4583 15.8333 10 15.8333C9.54167 15.8333 9.16667 16.2083 9.16667 16.6667V19.1667C9.16667 19.625 9.54167 20 10 20C10.4583 20 10.8333 19.625 10.8333 19.1667ZM4.16667 10C4.16667 9.54167 3.79167 9.16667 3.33333 9.16667H0.833333C0.375 9.16667 0 9.54167 0 10C0 10.4583 0.375 10.8333 0.833333 10.8333H3.33333C3.79167 10.8333 4.16667 10.4583 4.16667 10ZM20 10C20 9.54167 19.625 9.16667 19.1667 9.16667H16.6667C16.2083 9.16667 15.8333 9.54167 15.8333 10C15.8333 10.4583 16.2083 10.8333 16.6667 10.8333H19.1667C19.625 10.8333 20 10.4583 20 10ZM5.59167 5.59167C5.91667 5.26667 5.91667 4.74167 5.59167 4.41667L3.925 2.75C3.6 2.425 3.075 2.425 2.75 2.75C2.425 3.075 2.425 3.6 2.75 3.925L4.41667 5.59167C4.58333 5.75833 4.79167 5.83333 5.00833 5.83333C5.225 5.83333 5.43333 5.75 5.6 5.59167H5.59167ZM17.2583 17.2583C17.5833 16.9333 17.5833 16.4083 17.2583 16.0833L15.5917 14.4167C15.2667 14.0917 14.7417 14.0917 14.4167 14.4167C14.0917 14.7417 14.0917 15.2667 14.4167 15.5917L16.0833 17.2583C16.25 17.425 16.4583 17.5 16.675 17.5C16.8917 17.5 17.1 17.4167 17.2667 17.2583H17.2583ZM3.925 17.2583L5.59167 15.5917C5.91667 15.2667 5.91667 14.7417 5.59167 14.4167C5.26667 14.0917 4.74167 14.0917 4.41667 14.4167L2.75 16.0833C2.425 16.4083 2.425 16.9333 2.75 17.2583C2.91667 17.425 3.125 17.5 3.34167 17.5C3.55833 17.5 3.76667 17.4167 3.93333 17.2583H3.925ZM15.5917 5.59167L17.2583 3.925C17.5833 3.6 17.5833 3.075 17.2583 2.75C16.9333 2.425 16.4083 2.425 16.0833 2.75L14.4167 4.41667C14.0917 4.74167 14.0917 5.26667 14.4167 5.59167C14.5833 5.75833 14.7917 5.83333 15.0083 5.83333C15.225 5.83333 15.4333 5.75 15.6 5.59167H15.5917Z" />
29+
</svg>
30+
</Button>
31+
<Button
32+
variant="ghost"
33+
size="1"
34+
onClick={() => themeContext.changeTheme("dark")}
35+
className={`p-2.5 rounded-md cursor-pointer ${
36+
themeContext.theme === "dark" ? "bg-secondarySurface-dark" : ""
37+
}`}
38+
>
39+
<svg
40+
width="16"
41+
height="16"
42+
viewBox="0 0 20 20"
43+
xmlns="http://www.w3.org/2000/svg"
44+
className="text-gray-light dark:text-gray-dark"
45+
fill="currentColor"
46+
>
47+
<path d="M10.0075 19.9999C8.60714 19.9916 7.22397 19.6908 5.94659 19.1168C4.66922 18.5429 3.5258 17.7085 2.58958 16.6671C1.65335 15.6257 0.94497 14.4002 0.509804 13.0691C0.0746374 11.7381 -0.077718 10.3308 0.0624964 8.93745C0.311639 6.7617 1.26115 4.72586 2.76796 3.13668C4.27477 1.54751 6.25724 0.491093 8.41666 0.126615C9.81172 -0.0898286 11.2356 -0.0315131 12.6083 0.298282C12.9698 0.391053 13.2997 0.579203 13.5635 0.84307C13.8274 1.10694 14.0156 1.43683 14.1083 1.79828C14.2018 2.15645 14.1969 2.53319 14.0943 2.88883C13.9916 3.24448 13.795 3.56585 13.525 3.81912C9.72583 7.29245 10.0542 12.6899 14.1975 15.8291C14.4902 16.059 14.7159 16.3632 14.8513 16.7099C14.9866 17.0566 15.0265 17.4333 14.967 17.8007C14.9074 18.1681 14.7506 18.5128 14.5126 18.7991C14.2747 19.0853 13.9645 19.3026 13.6142 19.4283C12.4498 19.8087 11.2324 20.0016 10.0075 19.9999ZM10.0692 1.66662C9.60184 1.66552 9.13515 1.7009 8.67333 1.77245C6.87666 2.07727 5.22745 2.957 3.97357 4.2794C2.71969 5.6018 1.92888 7.29544 1.72 9.10578C1.59005 10.2725 1.70988 11.4535 2.07152 12.5703C2.43317 13.6871 3.02834 14.7142 3.8175 15.5833C4.97229 16.8173 6.46055 17.6896 8.10155 18.094C9.74254 18.4984 11.4657 18.4177 13.0617 17.8616C13.1302 17.8357 13.1907 17.7921 13.237 17.7354C13.2834 17.6786 13.3139 17.6106 13.3256 17.5382C13.3372 17.4659 13.3296 17.3917 13.3035 17.3233C13.2773 17.2548 13.2336 17.1944 13.1767 17.1483C8.23583 13.4166 7.8475 6.73912 12.3875 2.60328C12.4393 2.55486 12.4767 2.49297 12.4954 2.42454C12.5141 2.35611 12.5134 2.28383 12.4933 2.21578C12.4761 2.14324 12.4393 2.07679 12.387 2.02363C12.3347 1.97046 12.2689 1.93259 12.1967 1.91412C11.5 1.74627 10.7857 1.66317 10.0692 1.66662ZM17.0833 9.99995C16.8976 9.99994 16.7171 9.93786 16.5707 9.82357C16.4242 9.70928 16.3201 9.54932 16.275 9.36912L15.9767 8.17745L14.7833 7.85662C14.6039 7.80831 14.4457 7.70139 14.334 7.55286C14.2223 7.40433 14.1634 7.22272 14.1667 7.0369C14.1701 6.85107 14.2355 6.6717 14.3525 6.5273C14.4695 6.3829 14.6314 6.28175 14.8125 6.23995L15.9792 5.96912L16.2717 4.79745C16.3168 4.61726 16.4209 4.45733 16.5673 4.34305C16.7138 4.22877 16.8942 4.1667 17.08 4.1667C17.2658 4.1667 17.4462 4.22877 17.5926 4.34305C17.7391 4.45733 17.8432 4.61726 17.8883 4.79745L18.1842 5.97912L19.3658 6.27495C19.546 6.32011 19.706 6.42418 19.8202 6.57063C19.9345 6.71708 19.9966 6.89752 19.9966 7.08328C19.9966 7.26904 19.9345 7.44948 19.8202 7.59593C19.706 7.74239 19.546 7.84646 19.3658 7.89162L18.1842 8.18745L17.8883 9.36912C17.8433 9.54877 17.7398 9.70831 17.594 9.82255C17.4482 9.93678 17.2685 9.9992 17.0833 9.99995ZM13.3333 11.6666C13.3333 11.8876 13.4211 12.0996 13.5774 12.2559C13.7337 12.4122 13.9456 12.4999 14.1667 12.4999C14.3877 12.4999 14.5996 12.4122 14.7559 12.2559C14.9122 12.0996 15 11.8876 15 11.6666C15 11.4456 14.9122 11.2336 14.7559 11.0774C14.5996 10.9211 14.3877 10.8333 14.1667 10.8333C13.9456 10.8333 13.7337 10.9211 13.5774 11.0774C13.4211 11.2336 13.3333 11.4456 13.3333 11.6666ZM18.3333 14.9999C18.3333 15.221 18.4211 15.4329 18.5774 15.5892C18.7337 15.7455 18.9457 15.8333 19.1667 15.8333C19.3877 15.8333 19.5996 15.7455 19.7559 15.5892C19.9122 15.4329 20 15.221 20 14.9999C20 14.7789 19.9122 14.567 19.7559 14.4107C19.5996 14.2544 19.3877 14.1666 19.1667 14.1666C18.9457 14.1666 18.7337 14.2544 18.5774 14.4107C18.4211 14.567 18.3333 14.7789 18.3333 14.9999Z" />
48+
</svg>
49+
</Button>
50+
</div>
51+
);
52+
}

packages/app/src/components/NewProjectDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
} from "@radix-ui/themes";
55
import { useState } from "react";
66

7-
export function NewProjectDialog() {
7+
export default function NewProjectDialog() {
88
const [open, setOpen] = useState(false);
99

1010
return (
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {
2+
Button,
3+
Dialog,
4+
} from "@radix-ui/themes";
5+
import { useState } from "react";
6+
7+
export default function NewWorkspaceDialog() {
8+
const [open, setOpen] = useState(false);
9+
10+
return (
11+
<Dialog.Root open={open} onOpenChange={setOpen}>
12+
<Dialog.Trigger>
13+
<button className="text-md font-bold flex gap-x-2 bg-primary-light dark:bg-primary-dark hover:bg-primary-hoverLight dark:hover:bg-primary-hoverDark rounded-lg px-3 py-2.5 transition-all">
14+
<svg width="20px" height="20px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="my-auto">
15+
<path d="M13 3C13 2.44772 12.5523 2 12 2C11.4477 2 11 2.44772 11 3V11H3C2.44772 11 2 11.4477 2 12C2 12.5523 2.44772 13 3 13H11V21C11 21.5523 11.4477 22 12 22C12.5523 22 13 21.5523 13 21V13H21C21.5523 13 22 12.5523 22 12C22 11.4477 21.5523 11 21 11H13V3Z" fill="currentColor"/>
16+
</svg>
17+
New Project
18+
</button>
19+
</Dialog.Trigger>
20+
<Dialog.Content className="bg-secondarySurface-light text-text-light dark:bg-secondarySurface-dark dark:text-text-dark border-0">
21+
<Dialog.Title>
22+
<div className="flex justify-end">
23+
<Dialog.Close>
24+
<Button variant="ghost" className="hover:bg-transparent cursor-pointer">
25+
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
26+
<path d="M18.7071 7.20711C19.0976 6.81658 19.0976 6.18342 18.7071 5.79289C18.3166 5.40237 17.6834 5.40237 17.2929 5.79289L12 11.0858L6.70711 5.79289C6.31658 5.40237 5.68342 5.40237 5.29289 5.79289C4.90237 6.18342 4.90237 6.81658 5.29289 7.20711L10.5858 12.5L5.29289 17.7929C4.90237 18.1834 4.90237 18.8166 5.29289 19.2071C5.68342 19.5976 6.31658 19.5976 6.70711 19.2071L12 13.9142L17.2929 19.2071C17.6834 19.5976 18.3166 19.5976 18.7071 19.2071C19.0976 18.8166 19.0976 18.1834 18.7071 17.7929L13.4142 12.5L18.7071 7.20711Z" fill="currentColor"/>
27+
</svg>
28+
</Button>
29+
</Dialog.Close>
30+
</div>
31+
</Dialog.Title>
32+
<h1 className="text-3xl font-bold text-center p-4">New Project</h1>
33+
<p className="text-lg text-text-gray text-center pb-3">Create a new project</p>
34+
<form onSubmit={(e) => e.preventDefault()}>
35+
<div className="flex flex-col gap-y-4 p-4">
36+
{/* <TextField label="Project Name" placeholder="Enter project name" />
37+
<TextField label="Description" placeholder="Enter project description" />
38+
<TextField label="Tags" placeholder="Enter tags" /> */}
39+
<Button type="submit" className="bg-primary-light dark:bg-primary-dark hover:bg-primary-hoverLight dark:hover:bg-primary-hoverDark rounded-lg px-3 py-2.5 transition-all">
40+
Create Workspace
41+
</Button>
42+
</div>
43+
</form>
44+
</Dialog.Content>
45+
</Dialog.Root>
46+
);
47+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { DropdownMenu } from '@radix-ui/themes';
2+
import { useEffect, useState } from 'react';
3+
import { NewWorkspaceDialog } from './NewWorkspaceDialog';
4+
5+
export default function WorkspaceMenu() {
6+
const [workspaces, setWorkspaces] = useState([]);
7+
8+
useEffect(() => {
9+
const userData: string | null = localStorage.getItem('user');
10+
if (userData) {
11+
const user = JSON.parse(userData);
12+
setWorkspaces(user.workspaces);
13+
}
14+
}, []);
15+
16+
return (
17+
<>
18+
<DropdownMenu.Root>
19+
<DropdownMenu.Trigger>
20+
<button className={`mt-auto flex gap-x-2 p-2 rounded-lg text-gray-light dark:text-gray-dark bg-search-bgLight dark:bg-search-bgDark px-3 py-2.5 mb-2 border-[1px] border-search-bgLight dark:border-search-bgDark hover:bg-hover-dark dark:hover:bg-hover-light transition-all`}>
21+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
22+
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 4C4.73478 4 4.48043 4.10536 4.29289 4.29289C4.10536 4.48043 4 4.73478 4 5V19C4 19.2652 4.10536 19.5196 4.29289 19.7071C4.48043 19.8946 4.73478 20 5 20H9C9.55228 20 10 20.4477 10 21C10 21.5523 9.55228 22 9 22H5C4.20435 22 3.44129 21.6839 2.87868 21.1213C2.31607 20.5587 2 19.7956 2 19V5C2 4.20435 2.31607 3.44129 2.87868 2.87868C3.44129 2.31607 4.20435 2 5 2H9C9.55228 2 10 2.44772 10 3C10 3.55228 9.55228 4 9 4H5Z" fill="currentColor"/>
23+
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2929 6.29289C15.6834 5.90237 16.3166 5.90237 16.7071 6.29289L21.7071 11.2929C22.0976 11.6834 22.0976 12.3166 21.7071 12.7071L16.7071 17.7071C16.3166 18.0976 15.6834 18.0976 15.2929 17.7071C14.9024 17.3166 14.9024 16.6834 15.2929 16.2929L19.5858 12L15.2929 7.70711C14.9024 7.31658 14.9024 6.68342 15.2929 6.29289Z" fill="currentColor"/>
24+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 12C8 11.4477 8.44772 11 9 11H21C21.5523 11 22 11.4477 22 12C22 12.5523 21.5523 13 21 13H9C8.44772 13 8 12.5523 8 12Z" fill="currentColor"/>
25+
</svg>
26+
<p className="text-md">Switch workspace</p>
27+
</button>
28+
</DropdownMenu.Trigger>
29+
<DropdownMenu.Content variant='soft' className='bg-foreground-light dark:bg-foreground-dark mt-1.5'>
30+
{workspaces.map((workspace: any) => (
31+
<DropdownMenu.Item key={workspace.id} className='hover:bg-hover-dark'>
32+
<button className='w-full text-left text-lg'>{workspace.name}</button>
33+
</DropdownMenu.Item>
34+
))}
35+
<DropdownMenu.Separator />
36+
<DropdownMenu.Item className='hover:bg-hover-dark'>
37+
<button className='w-full flex text-text-gray text-md gap-x-2' onClick={(e) => e.preventDefault()}>
38+
<svg width="16px" height="16px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="my-auto">
39+
<path d="M13 3C13 2.44772 12.5523 2 12 2C11.4477 2 11 2.44772 11 3V11H3C2.44772 11 2 11.4477 2 12C2 12.5523 2.44772 13 3 13H11V21C11 21.5523 11.4477 22 12 22C12.5523 22 13 21.5523 13 21V13H21C21.5523 13 22 12.5523 22 12C22 11.4477 21.5523 11 21 11H13V3Z" fill="currentColor"/>
40+
</svg>
41+
Manage Workspaces
42+
</button>
43+
</DropdownMenu.Item>
44+
</DropdownMenu.Content>
45+
</DropdownMenu.Root>
46+
</>
47+
)
48+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { createContext, useState } from "react";
2+
3+
export type State = {
4+
activeWorkspace: string | null;
5+
};
6+
7+
// Create a Context
8+
export const StoreContext = createContext<{
9+
state: State;
10+
changeState: (newState: State) => void;
11+
}>({
12+
state: { activeWorkspace: null },
13+
// eslint-disable-next-line @typescript-eslint/no-empty-function
14+
changeState: () => {},
15+
});
16+
17+
export function StoreProvider({ children }: { children: React.ReactNode }) {
18+
function getInitialState() {
19+
return {
20+
activeWorkspace: null,
21+
};
22+
}
23+
24+
const [state, setState] = useState<State>(
25+
getInitialState(),
26+
);
27+
28+
function changeState(newState: State) {
29+
setState(newState);
30+
}
31+
32+
return (
33+
<StoreContext.Provider value={{ state, changeState }}>
34+
{children}
35+
</StoreContext.Provider>
36+
);
37+
}
38+
39+
40+

0 commit comments

Comments
 (0)