Skip to content

Commit 26a8d4e

Browse files
committed
Fixes for state mangement, small refactors, and new workspaces
1 parent 581634e commit 26a8d4e

File tree

12 files changed

+212
-96
lines changed

12 files changed

+212
-96
lines changed

packages/app/src/components/AppSidebar.tsx renamed to packages/app/src/components/Sidebar/AppSidebar.tsx

Lines changed: 5 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,21 @@
1-
import { useContext, useEffect, useState } from "react";
1+
import { useContext, useEffect } from "react";
22
import { useLocation, useNavigate } from "react-router";
3-
import { StoreContext } from "../contexts/StoreContext";
3+
import { StoreContext } from "../../contexts/StoreContext";
4+
import WorkspaceDetails from "./WorkspaceDetails"
45
import WorkspaceMenu from "./WorkspaceMenu";
5-
import { Workspace } from "../types";
66

77
export default function AppSidebar() {
88
const { state } = useContext(StoreContext);
99
const location = useLocation();
1010
const navigate = useNavigate();
1111

12-
const [workspace, setWorkspace] = useState<Workspace>({
13-
name: "Default",
14-
users: [] ,
15-
id: "0",
16-
projects: [],
17-
settings: {},
18-
createdAt: new Date().toISOString(),
19-
updatedAt: new Date().toISOString(),
20-
});
21-
2212
useEffect(() => {
2313
const userData = state.user;
2414
console.log("userData", userData);
2515
if (!userData) {
2616
return;
2717
}
28-
29-
console.log("activeWorkspace", state.activeWorkspace);
30-
31-
let activeWorkspace;
32-
if (!state.activeWorkspace) {
33-
if (!localStorage.getItem("activeWorkspace")) {
34-
return;
35-
}
36-
activeWorkspace = parseInt(localStorage.getItem("activeWorkspace") as string);
37-
} else {
38-
activeWorkspace = state.activeWorkspace;
39-
}
40-
41-
const workspaces = userData.workspaces;
42-
const found = workspaces.find(
43-
(ws: any) => ws.id === activeWorkspace
44-
);
45-
if (!found) {
46-
return;
47-
}
48-
console.log("found", found);
49-
setWorkspace(found);
50-
}, [state.activeWorkspace, state.user]);
18+
}, [state.user]);
5119

5220
return (
5321
<div className="bg-secondaryBackground-light dark:bg-secondaryBackground-dark w-80 px-4 py-2 rounded-xl flex flex-col">
@@ -66,28 +34,7 @@ export default function AppSidebar() {
6634
</div>
6735

6836
{/* Workspace info */}
69-
<div className="px-4 py-4 bg-[#212047] rounded-lg mt-4 border-[1px] border-[#35345B]">
70-
<h2 className="font-bold">{workspace.name} Workspace</h2>
71-
<div className="flex gap-x-2 border-b-[1px] border-[#2B2A51] pt-2 pb-3">
72-
{/* IMG */}
73-
<div className="flex">
74-
{workspace.users[0] && <img className="w-6 h-6 rounded-full border-[1px] border-foreground-light dark:border-foreground-dark" src={workspace.users[0].avatar} alt="Profile" />}
75-
{workspace.users[1] && <img className="w-6 h-6 ml-[-6px] rounded-full border-[1px] border-foreground-light dark:border-foreground-dark" src={workspace.users[1].avatar} alt="Profile" />}
76-
{workspace.users[2] && <img className="w-6 h-6 ml-[-6px] rounded-full border-[1px] border-foreground-light dark:border-foreground-dark" src={workspace.users[2].avatar} alt="Profile" />}
77-
</div>
78-
<p className="text-text-lightInfo dark:text-text-darkInfo text-sm">{workspace.users.length} {workspace.users.length === 1 ? "member" : "members"}</p>
79-
</div>
80-
81-
{/* Invite teammates */}
82-
<button className="flex text-text-gray w-full mt-2 p-2 gap-x-2 rounded-lg hover:bg-hover-light dark:hover:bg-hover-dark transition-all">
83-
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
84-
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.35861 12.7691C4.92484 11.6727 4 9.94433 4 8C4 4.68629 6.68629 2 10 2C13.3137 2 16 4.68629 16 8C16 9.94433 15.0752 11.6727 13.6414 12.7691C14.479 13.1397 15.2595 13.6379 15.9535 14.25C16.3677 14.6153 16.4073 15.2473 16.0419 15.6615C15.6766 16.0756 15.0447 16.1152 14.6305 15.7499C13.6202 14.8587 12.3741 14.278 11.0419 14.0775C10.7144 14.0282 10.3851 14.0024 10.0564 13.9997L10 14L9.94361 13.9997C8.97355 14.0076 8.01151 14.217 7.12002 14.6194C5.89211 15.1737 4.85024 16.0705 4.11943 17.2023C3.38862 18.3341 2.99993 19.6527 3 20.9999C3.00003 21.5522 2.55234 21.9999 2.00005 22C1.44777 22 1.00003 21.5523 1 21C0.999905 19.2679 1.49965 17.5725 2.43926 16.1174C3.37888 14.6622 4.71843 13.5092 6.29717 12.7965L6.35861 12.7691ZM6 8C6 5.79086 7.79086 4 10 4C12.2091 4 14 5.79086 14 8C14 10.1917 12.2374 11.9716 10.0524 11.9997C10.0175 11.9995 9.98255 11.9995 9.94766 11.9997C7.76264 11.9716 6 10.1917 6 8Z" fill="currentColor"/>
85-
<path d="M19 15C19.5523 15 20 15.4477 20 16V18H22C22.5523 18 23 18.4477 23 19C23 19.5523 22.5523 20 22 20H20V22C20 22.5523 19.5523 23 19 23C18.4477 23 18 22.5523 18 22V20H16C15.4477 20 15 19.5523 15 19C15 18.4477 15.4477 18 16 18H18V16C18 15.4477 18.4477 15 19 15Z" fill="currentColor"/>
86-
</svg>
87-
<p className="text-md">Invite teammates</p>
88-
89-
</button>
90-
</div>
37+
<WorkspaceDetails />
9138

9239
{/* Navigation */}
9340
<div className="grow flex flex-col mt-8 gap-y-2">
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { useContext, useEffect, useState } from "react";
2+
import { StoreContext } from "../../contexts/StoreContext";
3+
import { Workspace } from "../../types";
4+
5+
export default function WorkspaceDetails() {
6+
const { state } = useContext(StoreContext);
7+
8+
const [workspace, setWorkspace] = useState<Workspace>({
9+
name: "Default",
10+
users: [] ,
11+
id: 0,
12+
projects: [],
13+
settings: {},
14+
createdAt: new Date().toISOString(),
15+
updatedAt: new Date().toISOString(),
16+
});
17+
18+
useEffect(() => {
19+
const userData = state.user;
20+
if (!userData) {
21+
return;
22+
}
23+
24+
if (state.activeWorkspace) {
25+
setWorkspace(state.activeWorkspace);
26+
}
27+
}, [state.activeWorkspace, state.user]);
28+
29+
return (
30+
<div className="px-4 py-4 bg-[#212047] rounded-lg mt-4 border-[1px] border-[#35345B]">
31+
<h2 className="font-bold">{workspace.name} Workspace</h2>
32+
<div className="flex gap-x-2 border-b-[1px] border-[#2B2A51] pt-2 pb-3">
33+
{/* IMG */}
34+
<div className="flex">
35+
{workspace.users[0] && <img className="w-6 h-6 rounded-full border-[1px] border-foreground-light dark:border-foreground-dark" src={workspace.users[0].avatar} alt="Profile" />}
36+
{workspace.users[1] && <img className="w-6 h-6 ml-[-6px] rounded-full border-[1px] border-foreground-light dark:border-foreground-dark" src={workspace.users[1].avatar} alt="Profile" />}
37+
{workspace.users[2] && <img className="w-6 h-6 ml-[-6px] rounded-full border-[1px] border-foreground-light dark:border-foreground-dark" src={workspace.users[2].avatar} alt="Profile" />}
38+
</div>
39+
<p className="text-text-lightInfo dark:text-text-darkInfo text-sm">{workspace.users.length} {workspace.users.length === 1 ? "member" : "members"}</p>
40+
</div>
41+
42+
{/* Invite teammates */}
43+
<button className="flex text-text-gray w-full mt-2 p-2 gap-x-2 rounded-lg hover:bg-hover-light dark:hover:bg-hover-dark transition-all">
44+
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
45+
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.35861 12.7691C4.92484 11.6727 4 9.94433 4 8C4 4.68629 6.68629 2 10 2C13.3137 2 16 4.68629 16 8C16 9.94433 15.0752 11.6727 13.6414 12.7691C14.479 13.1397 15.2595 13.6379 15.9535 14.25C16.3677 14.6153 16.4073 15.2473 16.0419 15.6615C15.6766 16.0756 15.0447 16.1152 14.6305 15.7499C13.6202 14.8587 12.3741 14.278 11.0419 14.0775C10.7144 14.0282 10.3851 14.0024 10.0564 13.9997L10 14L9.94361 13.9997C8.97355 14.0076 8.01151 14.217 7.12002 14.6194C5.89211 15.1737 4.85024 16.0705 4.11943 17.2023C3.38862 18.3341 2.99993 19.6527 3 20.9999C3.00003 21.5522 2.55234 21.9999 2.00005 22C1.44777 22 1.00003 21.5523 1 21C0.999905 19.2679 1.49965 17.5725 2.43926 16.1174C3.37888 14.6622 4.71843 13.5092 6.29717 12.7965L6.35861 12.7691ZM6 8C6 5.79086 7.79086 4 10 4C12.2091 4 14 5.79086 14 8C14 10.1917 12.2374 11.9716 10.0524 11.9997C10.0175 11.9995 9.98255 11.9995 9.94766 11.9997C7.76264 11.9716 6 10.1917 6 8Z" fill="currentColor"/>
46+
<path d="M19 15C19.5523 15 20 15.4477 20 16V18H22C22.5523 18 23 18.4477 23 19C23 19.5523 22.5523 20 22 20H20V22C20 22.5523 19.5523 23 19 23C18.4477 23 18 22.5523 18 22V20H16C15.4477 20 15 19.5523 15 19C15 18.4477 15.4477 18 16 18H18V16C18 15.4477 18.4477 15 19 15Z" fill="currentColor"/>
47+
</svg>
48+
<p className="text-md">Invite teammates</p>
49+
50+
</button>
51+
</div>
52+
)
53+
}

packages/app/src/components/WorkspaceMenu.tsx renamed to packages/app/src/components/Sidebar/WorkspaceMenu.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DropdownMenu } from '@radix-ui/themes';
22
import { useEffect, useState, useContext } from 'react';
3-
import { StoreContext } from '../contexts/StoreContext';
3+
import { StoreContext } from '../../contexts/StoreContext';
44
import { useNavigate } from 'react-router';
55
// import { NewWorkspaceDialog } from './NewWorkspaceDialog';
66

@@ -12,12 +12,12 @@ export default function WorkspaceMenu() {
1212
const isActiveWorkspace = (workspace: any) => {
1313
const activeWorkspace = stateContext.state.activeWorkspace;
1414
console.log(activeWorkspace, workspace.id);
15-
return activeWorkspace && workspace.id === activeWorkspace;
15+
return activeWorkspace && workspace.id === activeWorkspace.id;
1616
}
1717

1818
const setActiveWorkspace = (workspace: any) => {
19-
stateContext.changeState({ activeWorkspace: workspace.id });
20-
localStorage.setItem('activeWorkspace', workspace.id);
19+
stateContext.changeState({ activeWorkspace: workspace });
20+
localStorage.setItem('activeWorkspaceId', workspace.id);
2121
navigate('/projects');
2222
}
2323

packages/app/src/contexts/StoreContext.tsx

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { createContext, useState, useEffect } from "react";
2+
import { Workspace } from "../types";
23

34
export type State = {
4-
activeWorkspace: string | null;
5+
activeWorkspace: Workspace | null;
56
user: any;
67
};
78

@@ -26,6 +27,7 @@ export function StoreProvider({ children }: { children: React.ReactNode }) {
2627
if (response.ok) {
2728
const user = await response.json();
2829
changeState({ user });
30+
return user;
2931
} else {
3032
console.error("Failed to fetch user");
3133
}
@@ -36,19 +38,51 @@ export function StoreProvider({ children }: { children: React.ReactNode }) {
3638

3739
useEffect(() => {
3840
if (localStorage.getItem("jwt")) {
39-
loadUser();
41+
loadUser()
42+
.then((user: any) => {
43+
if (!user) {
44+
localStorage.removeItem("jwt");
45+
localStorage.removeItem("activeWorkspace");
46+
window.location.reload();
47+
}
4048

41-
const activeWorkspace = localStorage.getItem("activeWorkspace");
42-
if (activeWorkspace) {
43-
changeState({ activeWorkspace });
44-
}
49+
let activeWorkspaceId: number;
50+
if (!state.activeWorkspace) {
51+
// If we don't already have the active workspace in the state, we'll try to get it from the local storage. In the case it's not there, we'll set the workspace to the one named "Default".
52+
if (!localStorage.getItem("activeWorkspace")) {
53+
const defaultWorkspace = user.workspaces.find((workspace: Workspace) => workspace.name === "Default");
54+
localStorage.setItem("activeWorkspace", defaultWorkspace.id);
55+
changeState({ activeWorkspace: defaultWorkspace });
56+
return;
57+
}
58+
59+
activeWorkspaceId = parseInt(localStorage.getItem("activeWorkspace") as string);
60+
} else {
61+
activeWorkspaceId = state.activeWorkspace.id;
62+
}
63+
64+
if (activeWorkspaceId) {
65+
const activeWorkspace = user.workspaces.find((workspace: Workspace) => workspace.id === activeWorkspaceId);
66+
changeState({ activeWorkspace });
67+
}
68+
});
4569
}
4670
}, []);
4771

4872
function getInitialState() {
73+
if (!localStorage.getItem("jwt")) {
74+
return {
75+
activeWorkspace: null,
76+
user: null,
77+
};
78+
}
79+
80+
const user = JSON.parse(localStorage.getItem("user") as string);
81+
const activeWorkspaceId = localStorage.getItem("activeWorkspace");
82+
4983
return {
50-
activeWorkspace: null,
51-
user: null,
84+
activeWorkspace: user.workspaces.find((workspace: Workspace) => workspace.id === parseInt(activeWorkspaceId as string)),
85+
user,
5286
};
5387
}
5488

packages/app/src/pages/auth/index.tsx

Lines changed: 84 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,26 +54,27 @@ export default function Auth(props: {
5454
}
5555

5656
const user = await userResponse.json();
57-
console.log('User data:', user);
5857

5958
// Store the user data (e.g., in localStorage)
6059
localStorage.setItem('user', JSON.stringify(user));
6160
const defaultWorkspace = user.workspaces.find((workspace: any) => workspace.name === "Default");
6261

6362
// Check if we've set an active workspace before
64-
const localStoreWorkspace = localStorage.getItem('activeWorkspace');
63+
const localStoreWorkspace = localStorage.getItem('activeWorkspaceId');
6564
if (localStoreWorkspace) {
65+
const workspace = user.workspaces.find((workspace: any) => workspace.id === parseInt(localStoreWorkspace));
6666

6767
changeState({
68-
activeWorkspace: localStoreWorkspace,
68+
activeWorkspace: workspace,
6969
user,
7070
});
7171
navigate('/projects');
7272
return;
7373
}
7474

75+
localStorage.setItem('activeWorkspaceId', defaultWorkspace.id);
7576
changeState({
76-
activeWorkspace: defaultWorkspace.id,
77+
activeWorkspace: defaultWorkspace,
7778
user,
7879
})
7980

@@ -114,6 +115,46 @@ export default function Auth(props: {
114115
// Store the token (e.g., in localStorage)
115116
localStorage.setItem("jwt", token);
116117

118+
const userResponse = await fetch(
119+
`${process.env.REACT_APP_BACKEND_API_URL}/api/v1/users/self`,
120+
{
121+
headers: {
122+
Authorization: `Bearer ${token}`,
123+
'Content-Type': 'application/json',
124+
},
125+
}
126+
);
127+
128+
if (!userResponse.ok) {
129+
throw new Error('Failed to fetch user data');
130+
}
131+
132+
const user = await userResponse.json();
133+
134+
// Store the user data (e.g., in localStorage)
135+
localStorage.setItem('user', JSON.stringify(user));
136+
const defaultWorkspace = user.workspaces.find((workspace: any) => workspace.name === "Default");
137+
138+
// Check if we've set an active workspace before
139+
const localStoreWorkspace = localStorage.getItem('activeWorkspaceId');
140+
if (localStoreWorkspace) {
141+
const workspace = user.workspaces.find((workspace: any) => workspace.id === parseInt(localStoreWorkspace));
142+
143+
changeState({
144+
activeWorkspace: workspace,
145+
user,
146+
});
147+
navigate('/projects');
148+
return;
149+
}
150+
151+
localStorage.setItem('activeWorkspaceId', defaultWorkspace.id);
152+
changeState({
153+
activeWorkspace: defaultWorkspace,
154+
user,
155+
})
156+
157+
117158
// Redirect to the dashboard or a protected route
118159
navigate("/projects");
119160
} catch (error) {
@@ -151,6 +192,45 @@ export default function Auth(props: {
151192
// Store the token (e.g., in localStorage)
152193
localStorage.setItem("jwt", token);
153194

195+
const userResponse = await fetch(
196+
`${process.env.REACT_APP_BACKEND_API_URL}/api/v1/users/self`,
197+
{
198+
headers: {
199+
Authorization: `Bearer ${token}`,
200+
'Content-Type': 'application/json',
201+
},
202+
}
203+
);
204+
205+
if (!userResponse.ok) {
206+
throw new Error('Failed to fetch user data');
207+
}
208+
209+
const user = await userResponse.json();
210+
211+
// Store the user data (e.g., in localStorage)
212+
localStorage.setItem('user', JSON.stringify(user));
213+
const defaultWorkspace = user.workspaces.find((workspace: any) => workspace.name === "Default");
214+
215+
// Check if we've set an active workspace before
216+
const localStoreWorkspace = localStorage.getItem('activeWorkspaceId');
217+
if (localStoreWorkspace) {
218+
const workspace = user.workspaces.find((workspace: any) => workspace.id === parseInt(localStoreWorkspace));
219+
220+
changeState({
221+
activeWorkspace: workspace,
222+
user,
223+
});
224+
navigate('/projects');
225+
return;
226+
}
227+
228+
localStorage.setItem('activeWorkspaceId', defaultWorkspace.id);
229+
changeState({
230+
activeWorkspace: defaultWorkspace,
231+
user,
232+
})
233+
154234
// Redirect to the dashboard or a protected route
155235
navigate("/projects");
156236
} catch (error) {

packages/app/src/components/NewProjectDialog.tsx renamed to packages/app/src/pages/projects/NewProjectDialog.tsx

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

99
export default function NewProjectDialog() {
1010
const { state } = useContext(StoreContext);

0 commit comments

Comments
 (0)