Skip to content

Commit 2097c94

Browse files
committed
feat: Add workspace functionality done
1 parent 6b7cd05 commit 2097c94

File tree

5 files changed

+168
-112
lines changed

5 files changed

+168
-112
lines changed

src/app/api/file.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@ export const uploadIcon = async (
2626

2727
export const getIcon = async (authorizationToken: string, orgName: string) => {
2828
const url = BACKEND_URL + '/api/protected/file/getIcon/' + orgName;
29-
const response = await axios.get(url, {
29+
const response = await axios.get(url,
30+
31+
{
32+
responseType:'blob',
3033
headers: {
3134
Authorization: `Bearer ${authorizationToken}`,
35+
Accept: "*/*"
3236
},
3337
});
3438
return response;

src/app/api/organization.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ export interface OrgProjects{
2424
projects: Projects
2525
}
2626

27+
export interface Workspace{
28+
id: number;
29+
name: string;
30+
description: string
31+
}
32+
interface OrgMembers {
33+
members: {
34+
[username: string]: string;
35+
};
36+
}
2737

2838

2939
export const deleteOrg = async (
@@ -88,7 +98,7 @@ export const addOrgMembers = async (
8898
},
8999
}
90100
);
91-
101+
92102
return respnse;
93103
};
94104

@@ -132,6 +142,7 @@ export const changeOrgMembersStatus = async (
132142
return respnse;
133143
};
134144

145+
135146
export const setArcheiveStatus = async (
136147
authorizationToken: string,
137148
orgName: string,
@@ -178,10 +189,10 @@ export const setBookmarkStatus = async (
178189
export const getOrgMembers = async (
179190
authorizationToken: string,
180191
orgName: string
181-
) => {
192+
) : Promise<AxiosResponse<OrgMembers>> => {
182193
const url = BACKEND_URL + '/api/protected/org/getMembers/' + orgName;
183194

184-
const respnse = await axios.get(url, {
195+
const respnse = await axios.get<OrgMembers>(url, {
185196
headers: {
186197
Accept: 'application/json',
187198
Authorization: `Bearer ${authorizationToken}`,
@@ -204,9 +215,9 @@ export const getOrgProjects = async (
204215
return respnse;
205216
};
206217

207-
export const getOrg = async (authorizationToken: string, orgName: string) => {
208-
const url = BACKEND_URL + '/api/protected/org/getProjects/' + orgName;
209-
const respnse = await axios.get(url, {
218+
export const getOrg = async (authorizationToken: string, orgName: string): Promise<AxiosResponse<Workspace>> => {
219+
const url = BACKEND_URL + '/api/protected/org/getOrg/' + orgName;
220+
const respnse = await axios.get<Workspace>(url, {
210221
headers: {
211222
Accept: 'application/json',
212223
Authorization: `Bearer ${authorizationToken}`,

src/features/AddWorkspace/index.tsx

Lines changed: 84 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,31 @@
11
import { getAllUser, getUser } from 'app/api/user';
2-
import React, { ChangeEvent, useState } from 'react';
2+
import React, { ChangeEvent, useContext, useEffect, useState } from 'react';
33
import { useNavigate } from 'react-router-dom';
44
import { useQuery } from 'react-query';
55
import toast from 'react-hot-toast';
6-
import { addOrg, getAllOrgs } from 'app/api/organization';
6+
import { addOrg, addOrgMembers, getAllOrgs } from 'app/api/organization';
77
import { uploadIcon } from 'app/api/file';
8-
import { useSelector } from 'react-redux';
9-
import { Organization } from 'app/state/reducers/orgReducers';
10-
import { RootState } from 'app/state/reducers';
11-
12-
138

149
import './index.scss';
10+
import UserContext from 'app/context/user/userContext';
1511

1612
const AddWorkspace = () => {
1713
const navigate = useNavigate();
1814
const token = localStorage.getItem('token');
15+
const userContext = useContext(UserContext);
1916

2017
const [selectedFile, setSelectedFile] = useState<File | null>(null);
2118
const [name, SetName] = useState<string | null>(null);
2219
const [description, setDiscription] = useState<string | null>(null);
20+
const [validDescription, setValidDescription] = useState<boolean>(true);
2321
const [validName, setValidName] = useState<boolean>(false);
24-
22+
const [uniqueName, setUniqueName] = useState<boolean>(false);
2523
const [members, setMembers] = useState<string[]>([]);
2624
const [memberName, setMemberName] = useState<string | null>(null);
2725

2826
const [users, setUsers] = useState<string[]>([]);
2927
const [orgs, setOrgs] = useState<string[]>([]);
3028

31-
const orgState= useSelector((state:RootState)=>state.organization);
32-
33-
3429
const dataFetch = async () => {
3530
try {
3631
if (token) {
@@ -52,30 +47,9 @@ const AddWorkspace = () => {
5247
} catch (e) {}
5348
};
5449

55-
const checklogin = async () => {
56-
if (token != null) {
57-
const userData = await getUser(token);
58-
return userData.data;
59-
} else {
60-
toast.error('Session expired');
61-
navigate('/login');
62-
}
63-
};
64-
65-
const { data, isError } = useQuery({
66-
queryFn: () => checklogin(),
67-
queryKey: 'checkLogin',
68-
});
69-
70-
if (isError) {
71-
toast.error('Session expired');
72-
navigate('/login');
73-
}
74-
75-
const {} = useQuery({
76-
queryFn: () => dataFetch(),
77-
queryKey: 'allUsersAndAllOrgs',
78-
});
50+
useEffect(() => {
51+
dataFetch();
52+
}, []);
7953

8054
const allowedFieTypes = ['image/jpeg', 'image/jpg', 'image/png'];
8155

@@ -94,41 +68,36 @@ const AddWorkspace = () => {
9468

9569
function valid_name(str: string): boolean {
9670
// Define a regular expression for special characters (excluding letters, digits, and spaces)
97-
const specialCharacters = /[^a-zA-Z0-9\s]/;
71+
const specialCharacters = /^[a-zA-Z0-9_-]+$/;
9872

9973
// Check if the string contains any special characters
100-
return (
101-
specialCharacters.test(str) &&
102-
!str.endsWith('/userspace') &&
103-
!orgs.includes(str)
104-
105-
);
74+
return specialCharacters.test(str) && !str.endsWith('-userspace');
10675
}
10776

108-
const isNotOrgName= (orgName:string)=>{
109-
orgState.forEach(el=>{
110-
if(el.name==orgName){
111-
return false
112-
}
113-
})
114-
return true
77+
function isUniqueName(str: string): boolean {
78+
return !orgs.includes(str);
11579
}
11680

11781
const handleNameChange = (event: ChangeEvent<HTMLInputElement>) => {
11882
SetName(event.target.value);
119-
120-
121-
122-
setValidName(() => valid_name(event.target.value)&&isNotOrgName(event.target.value));
83+
setUniqueName(() => isUniqueName(event.target.value));
84+
setValidName(() => valid_name(event.target.value));
12385
};
12486

12587
const handleDesriptionChange = (event: ChangeEvent<HTMLInputElement>) => {
12688
setDiscription(event.target.value);
89+
if (description?.length) {
90+
setValidDescription(description.length < 200);
91+
}
12792
};
12893

12994
const addMembers = () => {
13095
if (memberName) {
131-
if (users.includes(memberName) && memberName != data?.message) {
96+
if (
97+
users.includes(memberName) &&
98+
memberName != userContext?.username &&
99+
!members.includes(memberName)
100+
) {
132101
setMembers([...members, memberName]);
133102
setMemberName(null);
134103
}
@@ -137,52 +106,60 @@ const AddWorkspace = () => {
137106

138107
const removeMembers = (member: string) => {
139108
const indexToRemove = members.indexOf(member);
140-
}
141-
const SubmitHandler=async():Promise<void>=>{
142-
143-
if(description&&token&&name){
144-
145-
const func= async():Promise<void>=>{
146-
const dataRes= await addOrg(token,{
147-
name:name,
148-
description:description
149-
})
150-
151-
try{
152-
if(selectedFile!=null){
153-
const fileRes= uploadIcon(token, name, selectedFile)
154-
}}catch(e){
155-
156-
}
157-
navigate("/workspace-view")
158-
}
159109

160-
toast.promise(
161-
func(),{
162-
loading:'Saving...',
163-
success: <b>Workspace Saved</b>,
164-
error: <b>Could not save.</b>,
165-
166-
}
167-
)
168-
169-
170-
}else{
110+
if (indexToRemove !== -1) {
111+
const updatedMembers = [
112+
...members.slice(0, indexToRemove),
113+
...members.slice(indexToRemove + 1),
114+
];
171115

116+
setMembers(updatedMembers);
117+
} else {
118+
console.warn(`Member "${member}" not found in the members array.`);
172119
}
173120
};
121+
const SubmitHandler = async (): Promise<void> => {
122+
if (
123+
description &&
124+
token &&
125+
name &&
126+
validName &&
127+
uniqueName &&
128+
validDescription
129+
) {
130+
const func = async (): Promise<void> => {
131+
const dataRes = await addOrg(token, {
132+
name: name,
133+
description: description,
134+
});
174135

136+
try {
137+
if (selectedFile != null) {
138+
const fileRes = uploadIcon(token, name, selectedFile);
139+
}
140+
} catch (e) {}
141+
if (members.length > 0) {
142+
try{
175143

176-
177-
144+
const addMmebersRes= await addOrgMembers(token, name, members);
145+
146+
}catch(e){
147+
148+
}
178149

179-
180-
toast.promise(SubmitHandler(), {
181-
loading: 'Saving Workspace',
182-
success: <b>Workspace saved</b>,
183-
error: <b>Could not save</b>,
184-
});
150+
}
151+
navigate('/workspace-view');
152+
};
185153

154+
toast.promise(func(), {
155+
loading: 'Saving Workspace',
156+
success: <b>Workspace saved</b>,
157+
error: <b>Could not save</b>,
158+
});
159+
} else {
160+
toast.error('Invalid inputs');
161+
}
162+
};
186163

187164
return (
188165
<div className='main_aworkspace_container'>
@@ -215,6 +192,13 @@ const AddWorkspace = () => {
215192
onChange={handleNameChange}
216193
placeholder='workspace name'
217194
/>
195+
{!name ? <p>Name feild should not be empty</p> : <></>}
196+
{!validName && name ? <p>Not a valid name</p> : <></>}
197+
{!uniqueName && name ? (
198+
<p>Name already taken. Try another name</p>
199+
) : (
200+
<></>
201+
)}
218202
</div>
219203
<div className='single-form-element-container'>
220204
<label className='label' htmlFor='workspace-description'>
@@ -228,6 +212,12 @@ const AddWorkspace = () => {
228212
onChange={handleDesriptionChange}
229213
placeholder='workspace description'
230214
/>
215+
{!description ? <p>Description feild should not be empty</p> : <></>}
216+
{!validDescription ? (
217+
<p>Characters length should be less than 200</p>
218+
) : (
219+
<></>
220+
)}
231221
<div className='add-member-container'>
232222
<input
233223
type='text'
@@ -244,7 +234,8 @@ const AddWorkspace = () => {
244234
className='add-member-button'
245235
disabled={
246236
memberName
247-
? !users.includes(memberName) && memberName == data?.message
237+
? !users.includes(memberName) &&
238+
memberName == userContext?.username
248239
: true
249240
}
250241
>

src/features/workspace-view/index.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,42 @@ import { workSpaceData } from 'app/utils/workspaceData';
66
import UserContext from 'app/context/user/userContext';
77
import { UserOrgDetails, getUserOrgs } from 'app/api/user';
88
import loader from '../../app/assets/gifs/loader.gif'
9+
import { Navigate, useNavigate } from 'react-router-dom';
910

1011
const WorkspaceView = () => {
1112
const userContext= useContext(UserContext);
12-
const [isLoad, setIsLoad]= useState<boolean>(true)
13+
const [isLoad, setIsLoad]= useState<boolean>(false)
1314
const [archeives, setArcheives]= useState<boolean>(false);
1415
const token= localStorage.getItem('token')
16+
const navigate= useNavigate()
1517
const fetchData=async()=>{
1618

1719
if(token&&userContext?.username&&!userContext.userOrgs){
20+
1821
setIsLoad(true)
1922
try{
2023
const userOrgs= await getUserOrgs(token, userContext?.username.toString());
2124
userContext?.setUserOrgs(userOrgs.data)
2225
}catch(e){
2326

2427
}
28+
2529
setIsLoad(false)
2630
}
2731
}
2832

2933

3034
useEffect(()=>{
3135
fetchData();
32-
},[userContext?.setUsername, userContext?.username])
36+
},[userContext?.setUsername, userContext?.username,navigate])
3337

3438
return (
3539
<div className='workspaceview-container'>
3640
<div className='workspaceview-header'>
3741
<SearchBar />
3842

3943
<button onClick={()=>setArcheives(!archeives)}>Archeives</button>
40-
<button>Create a workspace</button>
44+
<button onClick={()=>navigate("/addWorkspace")}>Create a workspace</button>
4145

4246
</div>
4347

0 commit comments

Comments
 (0)