Skip to content

Commit a1aae4f

Browse files
Merge pull request #1336 from Code-A2Z/copilot/sub-pr-1335
[WIP] Migrate four modules including edit-profile and notifications
2 parents 1828c3b + 74577b3 commit a1aae4f

7 files changed

Lines changed: 124 additions & 94 deletions

File tree

client/src/modules/edit-profile/index.tsx

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,53 @@ const EditProfile = () => {
3939
const [charactersLeft, setCharactersLeft] = useState(bioLimit);
4040
const [updatedProfileImg, setUpdatedProfileImg] = useState<File | null>(null);
4141
const [previewImg, setPreviewImg] = useState<string | null>(null);
42+
43+
// Form state
44+
const [formData, setFormData] = useState({
45+
username: '',
46+
bio: '',
47+
youtube: '',
48+
facebook: '',
49+
twitter: '',
50+
github: '',
51+
instagram: '',
52+
website: '',
53+
});
4254

4355
useEffect(() => {
4456
if (isAuthenticated()) {
4557
fetchProfile().finally(() => setLoading(false));
4658
}
47-
}, [isAuthenticated, fetchProfile]);
59+
// eslint-disable-next-line react-hooks/exhaustive-deps
60+
}, []);
4861

4962
useEffect(() => {
50-
if (profile?.personal_info?.bio) {
51-
setCharactersLeft(bioLimit - profile.personal_info.bio.length);
63+
if (profile) {
64+
setFormData({
65+
username: profile.personal_info.username || '',
66+
bio: profile.personal_info.bio || '',
67+
youtube: profile.social_links.youtube || '',
68+
facebook: profile.social_links.facebook || '',
69+
twitter: profile.social_links.x || '',
70+
github: profile.social_links.github || '',
71+
instagram: profile.social_links.instagram || '',
72+
website: profile.social_links.website || '',
73+
});
74+
if (profile.personal_info?.bio) {
75+
setCharactersLeft(bioLimit - profile.personal_info.bio.length);
76+
}
5277
}
5378
}, [profile]);
5479

5580
const handleCharacterChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
56-
setCharactersLeft(bioLimit - e.currentTarget.value.length);
81+
const value = e.currentTarget.value;
82+
setFormData(prev => ({ ...prev, bio: value }));
83+
setCharactersLeft(bioLimit - value.length);
84+
};
85+
86+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
87+
const { name, value } = e.currentTarget;
88+
setFormData(prev => ({ ...prev, [name]: value }));
5789
};
5890

5991
const handleImagePreview = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -93,33 +125,16 @@ const EditProfile = () => {
93125
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
94126
e.preventDefault();
95127

96-
if (!editProfileForm.current) return;
97-
const form = new FormData(editProfileForm.current);
98-
const formData: { [key: string]: FormDataEntryValue } = {};
128+
const { username, bio, youtube, facebook, twitter, github, instagram, website } = formData;
99129

100-
for (const [key, value] of form.entries()) {
101-
formData[key] = value;
102-
}
103-
104-
const {
105-
username,
106-
bio,
107-
youtube,
108-
facebook,
109-
twitter,
110-
github,
111-
instagram,
112-
website,
113-
} = formData;
114-
115-
if (typeof username !== 'string' || username.length < 3) {
130+
if (username.length < 3) {
116131
return addNotification({
117132
message: 'Username should be atleast 3 characters long',
118133
type: 'error',
119134
});
120135
}
121136

122-
if (typeof bio === 'string' && bio.length > bioLimit) {
137+
if (bio.length > bioLimit) {
123138
return addNotification({
124139
message: `Bio should be less than ${bioLimit} characters`,
125140
type: 'error',
@@ -131,15 +146,15 @@ const EditProfile = () => {
131146
try {
132147
await updateUserProfile({
133148
username,
134-
bio: (bio as string) || '',
149+
bio: bio || '',
135150
social_links: {
136-
youtube: (youtube as string) || '',
137-
facebook: (facebook as string) || '',
138-
x: (twitter as string) || '',
139-
github: (github as string) || '',
140-
instagram: (instagram as string) || '',
141-
linkedin: '',
142-
website: (website as string) || '',
151+
youtube: youtube || '',
152+
facebook: facebook || '',
153+
x: twitter || '',
154+
github: github || '',
155+
instagram: instagram || '',
156+
linkedin: profile?.social_links.linkedin || '',
157+
website: website || '',
143158
},
144159
});
145160
addNotification({
@@ -278,9 +293,14 @@ const EditProfile = () => {
278293
id="edit-profile-username"
279294
type="text"
280295
name="username"
281-
defaultValue={username}
296+
value={formData.username}
282297
placeholder="Username"
283298
icon={<AlternateEmailIcon />}
299+
slotProps={{
300+
htmlInput: {
301+
onChange: handleInputChange,
302+
},
303+
}}
284304
/>
285305
<Typography
286306
variant="caption"
@@ -297,7 +317,7 @@ const EditProfile = () => {
297317
name="bio"
298318
multiline
299319
rows={4}
300-
defaultValue={bio}
320+
value={formData.bio}
301321
placeholder="Bio"
302322
fullWidth
303323
inputProps={{ maxLength: bioLimit }}
@@ -328,24 +348,25 @@ const EditProfile = () => {
328348
gap: 2,
329349
}}
330350
>
331-
{(
332-
Object.keys(social_links) as Array<
333-
keyof typeof social_links
334-
>
335-
).map(key => {
336-
const link = social_links[key] || '';
351+
{['youtube', 'facebook', 'twitter', 'github', 'instagram', 'website'].map(key => {
352+
const fieldName = key as keyof typeof formData;
337353
return (
338354
<InputBox
339355
key={key}
340356
id={`edit-profile-${key}`}
341357
name={key}
342358
type="text"
343-
defaultValue={link}
359+
value={formData[fieldName]}
344360
placeholder="https://"
345361
icon={socialIcons[key] || <LanguageIcon />}
346362
sx={{
347363
flex: { xs: '1 1 100%', sm: '1 1 calc(50% - 8px)' },
348364
}}
365+
slotProps={{
366+
htmlInput: {
367+
onChange: handleInputChange,
368+
},
369+
}}
349370
/>
350371
);
351372
})}

client/src/modules/manage-projects/hooks/index.ts

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,36 +36,49 @@ const useManageProjects = () => {
3636
]);
3737

3838
if (projectsResponse.data && countResponse.data) {
39-
const currentState = is_draft ? draftProjects : publishedProjects;
40-
const existingResults = currentState?.results || [];
41-
42-
const formattedData: ManageProjectsPaginationState = {
43-
results:
44-
page === 1
45-
? projectsResponse.data
46-
: [...existingResults, ...projectsResponse.data],
47-
page,
48-
totalDocs: countResponse.data.totalDocs || 0,
49-
deletedDocCount,
50-
};
39+
const totalDocs = countResponse.data.totalDocs || 0;
5140

5241
if (is_draft) {
53-
setDraftProjects(formattedData);
42+
setDraftProjects((prevState: ManageProjectsPaginationState | undefined) => {
43+
const previousResults = prevState?.results || [];
44+
45+
const results =
46+
page === 1 || !prevState
47+
? projectsResponse.data
48+
: [...previousResults, ...projectsResponse.data];
49+
50+
return {
51+
results,
52+
page,
53+
totalDocs,
54+
deletedDocCount,
55+
};
56+
});
5457
} else {
55-
setPublishedProjects(formattedData);
58+
setPublishedProjects(
59+
(prevState: ManageProjectsPaginationState | undefined) => {
60+
const previousResults = prevState?.results || [];
61+
62+
const results =
63+
page === 1 || !prevState
64+
? projectsResponse.data
65+
: [...previousResults, ...projectsResponse.data];
66+
67+
return {
68+
results,
69+
page,
70+
totalDocs,
71+
deletedDocCount,
72+
};
73+
}
74+
);
5675
}
5776
}
5877
} catch (error) {
5978
console.error('Error fetching projects:', error);
6079
}
6180
},
62-
[
63-
isAuthenticated,
64-
setPublishedProjects,
65-
setDraftProjects,
66-
publishedProjects,
67-
draftProjects,
68-
]
81+
[isAuthenticated, setPublishedProjects, setDraftProjects]
6982
);
7083

7184
return {

client/src/modules/manage-projects/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ const ManageProjects = () => {
2929
if (draftProjects === null) {
3030
fetchProjects({ page: 1, is_draft: true, query });
3131
}
32-
}, [publishedProjects, draftProjects, fetchProjects, query]);
32+
// eslint-disable-next-line react-hooks/exhaustive-deps
33+
}, [publishedProjects, draftProjects, query]);
3334

3435
const handleSearch = (e: React.KeyboardEvent<HTMLInputElement>) => {
35-
if (e.key === 'Enter' && query.length) {
36+
const value = e.currentTarget.value;
37+
if (e.key === 'Enter' && value.length) {
3638
setPublishedProjects(null);
3739
setDraftProjects(null);
3840
}

client/src/modules/notification/components/notificationCard.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ const NotificationCard = ({
112112
borderLeftColor: `${getNotificationColor()}.main`,
113113
position: 'relative',
114114
'&:hover': {
115-
elevation: 4,
115+
boxShadow: theme => theme.shadows[4],
116116
transform: 'scale(1.01)',
117117
transition: 'all 0.2s ease-in-out',
118118
},
@@ -318,8 +318,7 @@ const NotificationCard = ({
318318
<Collapse in={isReplying} timeout="auto" unmountOnExit>
319319
<Box sx={{ p: 2, bgcolor: 'grey.50' }}>
320320
<NotificationCommentField
321-
_id={project_id}
322-
project_author={{ _id: userAuth?.personal_info?.username || '' }}
321+
project_id={project_id}
323322
replyingTo={comment_id?._id}
324323
setReplying={setIsReplying}
325324
notification_id={notification_id}

client/src/modules/notification/components/notificationCommentField.tsx

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,14 @@ import { TextField, Button, Box, Typography } from '@mui/material';
77
import { Reply } from '@mui/icons-material';
88

99
interface NotificationCommentFieldProps {
10-
_id: string;
11-
project_author: {
12-
_id: string;
13-
};
10+
project_id: string;
1411
replyingTo?: string;
1512
setReplying: (value: boolean) => void;
1613
notification_id: string;
1714
}
1815

1916
const NotificationCommentField = ({
20-
_id,
21-
project_author,
17+
project_id,
2218
replyingTo,
2319
setReplying,
2420
notification_id,
@@ -27,8 +23,6 @@ const NotificationCommentField = ({
2723
const user = useAtomValue(UserAtom);
2824
const { isAuthenticated } = useAuth();
2925

30-
const { _id: user_id } = project_author;
31-
3226
const handleComment = () => {
3327
if (!comment.length) {
3428
console.error('Write something to leave a comment...');
@@ -42,11 +36,10 @@ const NotificationCommentField = ({
4236

4337
axios
4438
.post(
45-
import.meta.env.VITE_SERVER_DOMAIN + '/api/notification/comment',
39+
import.meta.env.VITE_SERVER_DOMAIN + '/api/comment',
4640
{
47-
_id,
41+
project_id,
4842
comment,
49-
project_author: user_id,
5043
replying_to: replyingTo,
5144
notification_id,
5245
},

client/src/modules/notification/hooks/index.ts

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,27 @@ const useNotifications = () => {
3030
]);
3131

3232
if (notificationsResponse.data && countResponse.data) {
33-
const currentState = notifications;
34-
const existingResults = currentState?.results || [];
35-
36-
const formattedData: NotificationPaginationState = {
37-
results:
38-
page === 1
39-
? notificationsResponse.data
40-
: [...existingResults, ...notificationsResponse.data],
41-
page,
42-
totalDocs: countResponse.data.totalDocs || 0,
43-
deleteDocCount: deletedDocCount,
44-
};
45-
46-
setNotifications(formattedData);
33+
setNotifications((currentState) => {
34+
const existingResults = currentState?.results || [];
35+
36+
const formattedData: NotificationPaginationState = {
37+
results:
38+
page === 1
39+
? notificationsResponse.data
40+
: [...existingResults, ...notificationsResponse.data],
41+
page,
42+
totalDocs: countResponse.data.totalDocs || 0,
43+
deleteDocCount: deletedDocCount,
44+
};
45+
46+
return formattedData;
47+
});
4748
}
4849
} catch (error) {
4950
console.error('Error fetching notifications:', error);
5051
}
5152
},
52-
[isAuthenticated, setNotifications, notifications]
53+
[isAuthenticated, setNotifications]
5354
);
5455

5556
return {

client/src/modules/notification/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ const Notifications = () => {
3535
setNotifications(null);
3636
fetchNotifications({ page: 1, filter, deletedDocCount: 0 });
3737
}
38-
}, [isAuthenticated, filter, fetchNotifications, setNotifications]);
38+
// eslint-disable-next-line react-hooks/exhaustive-deps
39+
}, [filter]);
3940

4041
const handleFilter = (filterName: string) => {
4142
setFilter(filterName as NOTIFICATION_FILTER_TYPE);

0 commit comments

Comments
 (0)