Skip to content

Commit fee2fe5

Browse files
Merge pull request #1335 from Code-A2Z/avdhesh/migrate-modules
Migrate 4 modules
2 parents 6ed5411 + 5c49542 commit fee2fe5

File tree

18 files changed

+1517
-1115
lines changed

18 files changed

+1517
-1115
lines changed

client/.vercelignore

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1 @@
1-
projects/*
21
server/*
3-
client/src/modules/edit-profile/*
4-
client/src/modules/manage-projects/*
5-
client/src/modules/notification/*
6-
src/modules/edit-profile/*
7-
src/modules/manage-projects/*
8-
src/modules/notification/*

client/src/App.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import Search from './modules/search';
1212
import Profile from './modules/profile';
1313
import Project from './modules/project';
1414
import Sidebar from './shared/components/organisms/sidebar';
15-
// import ChangePassword from "./modules/change-password";
16-
// import ManageProjects from "./modules/manage-projects";
17-
// import EditProfile from "./modules/edit-profile";
18-
// import Notifications from "./modules/notification";
15+
import ChangePassword from './modules/change-password';
16+
import ManageProjects from './modules/manage-projects';
17+
import EditProfile from './modules/edit-profile';
18+
import Notifications from './modules/notification';
1919

2020
function App() {
2121
useEffect(() => {
@@ -35,12 +35,12 @@ function App() {
3535
<Route path="project/:project_id" element={<Project />} />
3636

3737
<Route path="dashboard" element={<Sidebar />}>
38-
{/* <Route path="projects" element={<ManageProjects />} /> */}
39-
{/* <Route path="notifications" element={<Notifications />} /> */}
38+
<Route path="projects" element={<ManageProjects />} />
39+
<Route path="notifications" element={<Notifications />} />
4040
</Route>
4141
<Route path="settings" element={<Sidebar />}>
42-
{/* <Route path="edit-profile" element={<EditProfile />} /> */}
43-
{/* <Route path="change-password" element={<ChangePassword />} /> */}
42+
<Route path="edit-profile" element={<EditProfile />} />
43+
<Route path="change-password" element={<ChangePassword />} />
4444
</Route>
4545
</Route>
4646

client/src/infra/rest/apis/project/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export const userProjects = async ({
7575
query,
7676
deletedDocCount,
7777
}: userProjectsPayload) => {
78-
return get<undefined, ApiResponse<userProjectsResponse>>(
78+
return get<undefined, ApiResponse<userProjectsResponse[]>>(
7979
`/api/project/user?is_draft=${is_draft}&query=${query}&page=${page}&deletedDocCount=${deletedDocCount}`
8080
);
8181
};

client/src/modules/change-password/index.tsx

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1-
import { useRef } from 'react';
1+
import { useRef, useState } from 'react';
22
import { useNotifications } from '../../shared/hooks/use-notification';
33
import { passwordRegex } from '../../shared/utils/regex';
44
import { changePassword } from '../../infra/rest/apis/auth';
55
import InputBox from '../../shared/components/atoms/input-box';
6-
import { Box, Button, Typography, Stack } from '@mui/material';
6+
import { Box, Button, Stack, CircularProgress } from '@mui/material';
77
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
88
import VpnKeyOutlinedIcon from '@mui/icons-material/VpnKeyOutlined';
9+
import A2ZTypography from '../../shared/components/atoms/typography';
910

1011
const ChangePassword = () => {
1112
const changePasswordForm = useRef<HTMLFormElement>(null);
1213
const { addNotification } = useNotifications();
14+
const [loading, setLoading] = useState(false);
1315

14-
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
16+
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
1517
e.preventDefault();
1618

1719
if (!changePasswordForm.current) return;
@@ -44,26 +46,29 @@ const ChangePassword = () => {
4446
return;
4547
}
4648

47-
e.currentTarget.setAttribute('disabled', 'true');
49+
setLoading(true);
4850

49-
changePassword({
50-
current_password: currentPassword,
51-
new_password: newPassword,
52-
})
53-
.then(() => {
54-
e.currentTarget.removeAttribute('disabled');
55-
return addNotification({
56-
message: 'Password Updated!',
57-
type: 'success',
58-
});
59-
})
60-
.catch(({ response }) => {
61-
e.currentTarget.removeAttribute('disabled');
62-
return addNotification({
63-
message: response.data.error,
64-
type: 'error',
65-
});
51+
try {
52+
await changePassword({
53+
current_password: currentPassword,
54+
new_password: newPassword,
6655
});
56+
addNotification({
57+
message: 'Password Updated!',
58+
type: 'success',
59+
});
60+
if (changePasswordForm.current) {
61+
changePasswordForm.current.reset();
62+
}
63+
} catch (error: unknown) {
64+
const err = error as { response?: { data?: { error?: string } } };
65+
addNotification({
66+
message: err.response?.data?.error || 'Failed to update password',
67+
type: 'error',
68+
});
69+
} finally {
70+
setLoading(false);
71+
}
6772
};
6873

6974
return (
@@ -75,14 +80,16 @@ const ChangePassword = () => {
7580
display: 'flex',
7681
flexDirection: 'column',
7782
gap: 4,
78-
maxWidth: 400,
83+
maxWidth: 500,
7984
width: '100%',
80-
py: 6,
85+
py: 4,
8186
}}
8287
>
83-
<Typography variant="h5" fontWeight={600}>
84-
Change Password
85-
</Typography>
88+
<A2ZTypography
89+
variant="h5"
90+
text="Change Password"
91+
props={{ fontWeight: 600 }}
92+
/>
8693

8794
<Stack spacing={3}>
8895
<InputBox
@@ -91,19 +98,22 @@ const ChangePassword = () => {
9198
type="password"
9299
placeholder="Current Password"
93100
icon={<LockOutlinedIcon />}
101+
disabled={loading}
94102
/>
95103
<InputBox
96104
id="change-password--new"
97105
name="newPassword"
98106
type="password"
99107
placeholder="New Password"
100108
icon={<VpnKeyOutlinedIcon />}
109+
disabled={loading}
101110
/>
102111
</Stack>
103112
<Button
104113
type="submit"
105114
variant="contained"
106115
color="primary"
116+
disabled={loading}
107117
sx={{
108118
mt: 2,
109119
py: 1.2,
@@ -113,7 +123,7 @@ const ChangePassword = () => {
113123
fontWeight: 500,
114124
}}
115125
>
116-
Change Password
126+
{loading ? <CircularProgress size={24} /> : 'Change Password'}
117127
</Button>
118128
</Box>
119129
);
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { useCallback } from 'react';
2+
import { useSetAtom, useAtomValue } from 'jotai';
3+
import {
4+
userProfile,
5+
updateProfile,
6+
updateProfileImg,
7+
} from '../../../infra/rest/apis/user';
8+
import { uploadImage } from '../../../infra/rest/apis/media';
9+
import { EditProfileAtom } from '../states';
10+
import { UserAtom } from '../../../infra/states/user';
11+
12+
const useEditProfile = () => {
13+
const setProfile = useSetAtom(EditProfileAtom);
14+
const setUser = useSetAtom(UserAtom);
15+
const user = useAtomValue(UserAtom);
16+
const profile = useAtomValue(EditProfileAtom);
17+
18+
const fetchProfile = useCallback(async () => {
19+
if (!user?.personal_info?.username) return;
20+
21+
try {
22+
const response = await userProfile(user.personal_info.username);
23+
if (response.data) {
24+
setProfile(response.data);
25+
}
26+
} catch (error) {
27+
console.error('Error fetching profile:', error);
28+
}
29+
}, [user?.personal_info?.username, setProfile]);
30+
31+
const updateProfileImage = useCallback(
32+
async (imageFile: File) => {
33+
const uploadResponse = await uploadImage(imageFile);
34+
if (uploadResponse.data?.upload_url) {
35+
const response = await updateProfileImg(uploadResponse.data.upload_url);
36+
if (response.data && user) {
37+
const updatedUser = {
38+
...user,
39+
personal_info: {
40+
...user.personal_info,
41+
profile_img: response.data.profile_img,
42+
},
43+
};
44+
setUser(updatedUser);
45+
return response.data.profile_img;
46+
}
47+
}
48+
},
49+
[user, setUser]
50+
);
51+
52+
const updateUserProfile = useCallback(
53+
async (profileData: {
54+
username: string;
55+
bio: string;
56+
social_links: {
57+
youtube: string;
58+
instagram: string;
59+
facebook: string;
60+
x: string;
61+
github: string;
62+
linkedin: string;
63+
website: string;
64+
};
65+
}) => {
66+
const response = await updateProfile(profileData);
67+
if (response.data && user) {
68+
const updatedUser = {
69+
...user,
70+
personal_info: {
71+
...user.personal_info,
72+
username: response.data.username,
73+
},
74+
};
75+
setUser(updatedUser);
76+
}
77+
return response;
78+
},
79+
[user, setUser]
80+
);
81+
82+
return {
83+
fetchProfile,
84+
updateProfileImage,
85+
updateUserProfile,
86+
profile,
87+
};
88+
};
89+
90+
export default useEditProfile;

0 commit comments

Comments
 (0)