diff --git a/client/src/modules/edit-profile/index.tsx b/client/src/modules/edit-profile/index.tsx index 76a9e7fd..f880a065 100644 --- a/client/src/modules/edit-profile/index.tsx +++ b/client/src/modules/edit-profile/index.tsx @@ -39,21 +39,53 @@ const EditProfile = () => { const [charactersLeft, setCharactersLeft] = useState(bioLimit); const [updatedProfileImg, setUpdatedProfileImg] = useState(null); const [previewImg, setPreviewImg] = useState(null); + + // Form state + const [formData, setFormData] = useState({ + username: '', + bio: '', + youtube: '', + facebook: '', + twitter: '', + github: '', + instagram: '', + website: '', + }); useEffect(() => { if (isAuthenticated()) { fetchProfile().finally(() => setLoading(false)); } - }, [isAuthenticated, fetchProfile]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); useEffect(() => { - if (profile?.personal_info?.bio) { - setCharactersLeft(bioLimit - profile.personal_info.bio.length); + if (profile) { + setFormData({ + username: profile.personal_info.username || '', + bio: profile.personal_info.bio || '', + youtube: profile.social_links.youtube || '', + facebook: profile.social_links.facebook || '', + twitter: profile.social_links.x || '', + github: profile.social_links.github || '', + instagram: profile.social_links.instagram || '', + website: profile.social_links.website || '', + }); + if (profile.personal_info?.bio) { + setCharactersLeft(bioLimit - profile.personal_info.bio.length); + } } }, [profile]); const handleCharacterChange = (e: React.ChangeEvent) => { - setCharactersLeft(bioLimit - e.currentTarget.value.length); + const value = e.currentTarget.value; + setFormData(prev => ({ ...prev, bio: value })); + setCharactersLeft(bioLimit - value.length); + }; + + const handleInputChange = (e: React.ChangeEvent) => { + const { name, value } = e.currentTarget; + setFormData(prev => ({ ...prev, [name]: value })); }; const handleImagePreview = (e: React.ChangeEvent) => { @@ -93,33 +125,16 @@ const EditProfile = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (!editProfileForm.current) return; - const form = new FormData(editProfileForm.current); - const formData: { [key: string]: FormDataEntryValue } = {}; + const { username, bio, youtube, facebook, twitter, github, instagram, website } = formData; - for (const [key, value] of form.entries()) { - formData[key] = value; - } - - const { - username, - bio, - youtube, - facebook, - twitter, - github, - instagram, - website, - } = formData; - - if (typeof username !== 'string' || username.length < 3) { + if (username.length < 3) { return addNotification({ message: 'Username should be atleast 3 characters long', type: 'error', }); } - if (typeof bio === 'string' && bio.length > bioLimit) { + if (bio.length > bioLimit) { return addNotification({ message: `Bio should be less than ${bioLimit} characters`, type: 'error', @@ -131,15 +146,15 @@ const EditProfile = () => { try { await updateUserProfile({ username, - bio: (bio as string) || '', + bio: bio || '', social_links: { - youtube: (youtube as string) || '', - facebook: (facebook as string) || '', - x: (twitter as string) || '', - github: (github as string) || '', - instagram: (instagram as string) || '', - linkedin: '', - website: (website as string) || '', + youtube: youtube || '', + facebook: facebook || '', + x: twitter || '', + github: github || '', + instagram: instagram || '', + linkedin: profile?.social_links.linkedin || '', + website: website || '', }, }); addNotification({ @@ -278,9 +293,14 @@ const EditProfile = () => { id="edit-profile-username" type="text" name="username" - defaultValue={username} + value={formData.username} placeholder="Username" icon={} + slotProps={{ + htmlInput: { + onChange: handleInputChange, + }, + }} /> { name="bio" multiline rows={4} - defaultValue={bio} + value={formData.bio} placeholder="Bio" fullWidth inputProps={{ maxLength: bioLimit }} @@ -328,24 +348,25 @@ const EditProfile = () => { gap: 2, }} > - {( - Object.keys(social_links) as Array< - keyof typeof social_links - > - ).map(key => { - const link = social_links[key] || ''; + {['youtube', 'facebook', 'twitter', 'github', 'instagram', 'website'].map(key => { + const fieldName = key as keyof typeof formData; return ( } sx={{ flex: { xs: '1 1 100%', sm: '1 1 calc(50% - 8px)' }, }} + slotProps={{ + htmlInput: { + onChange: handleInputChange, + }, + }} /> ); })} diff --git a/client/src/modules/manage-projects/hooks/index.ts b/client/src/modules/manage-projects/hooks/index.ts index df1607e2..c5dc2671 100644 --- a/client/src/modules/manage-projects/hooks/index.ts +++ b/client/src/modules/manage-projects/hooks/index.ts @@ -36,36 +36,49 @@ const useManageProjects = () => { ]); if (projectsResponse.data && countResponse.data) { - const currentState = is_draft ? draftProjects : publishedProjects; - const existingResults = currentState?.results || []; - - const formattedData: ManageProjectsPaginationState = { - results: - page === 1 - ? projectsResponse.data - : [...existingResults, ...projectsResponse.data], - page, - totalDocs: countResponse.data.totalDocs || 0, - deletedDocCount, - }; + const totalDocs = countResponse.data.totalDocs || 0; if (is_draft) { - setDraftProjects(formattedData); + setDraftProjects((prevState: ManageProjectsPaginationState | undefined) => { + const previousResults = prevState?.results || []; + + const results = + page === 1 || !prevState + ? projectsResponse.data + : [...previousResults, ...projectsResponse.data]; + + return { + results, + page, + totalDocs, + deletedDocCount, + }; + }); } else { - setPublishedProjects(formattedData); + setPublishedProjects( + (prevState: ManageProjectsPaginationState | undefined) => { + const previousResults = prevState?.results || []; + + const results = + page === 1 || !prevState + ? projectsResponse.data + : [...previousResults, ...projectsResponse.data]; + + return { + results, + page, + totalDocs, + deletedDocCount, + }; + } + ); } } } catch (error) { console.error('Error fetching projects:', error); } }, - [ - isAuthenticated, - setPublishedProjects, - setDraftProjects, - publishedProjects, - draftProjects, - ] + [isAuthenticated, setPublishedProjects, setDraftProjects] ); return { diff --git a/client/src/modules/manage-projects/index.tsx b/client/src/modules/manage-projects/index.tsx index 228c777e..39ebf739 100644 --- a/client/src/modules/manage-projects/index.tsx +++ b/client/src/modules/manage-projects/index.tsx @@ -29,10 +29,12 @@ const ManageProjects = () => { if (draftProjects === null) { fetchProjects({ page: 1, is_draft: true, query }); } - }, [publishedProjects, draftProjects, fetchProjects, query]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [publishedProjects, draftProjects, query]); const handleSearch = (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && query.length) { + const value = e.currentTarget.value; + if (e.key === 'Enter' && value.length) { setPublishedProjects(null); setDraftProjects(null); } diff --git a/client/src/modules/notification/components/notificationCard.tsx b/client/src/modules/notification/components/notificationCard.tsx index 959e929f..d0503b11 100644 --- a/client/src/modules/notification/components/notificationCard.tsx +++ b/client/src/modules/notification/components/notificationCard.tsx @@ -112,7 +112,7 @@ const NotificationCard = ({ borderLeftColor: `${getNotificationColor()}.main`, position: 'relative', '&:hover': { - elevation: 4, + boxShadow: theme => theme.shadows[4], transform: 'scale(1.01)', transition: 'all 0.2s ease-in-out', }, @@ -318,8 +318,7 @@ const NotificationCard = ({ void; notification_id: string; } const NotificationCommentField = ({ - _id, - project_author, + project_id, replyingTo, setReplying, notification_id, @@ -27,8 +23,6 @@ const NotificationCommentField = ({ const user = useAtomValue(UserAtom); const { isAuthenticated } = useAuth(); - const { _id: user_id } = project_author; - const handleComment = () => { if (!comment.length) { console.error('Write something to leave a comment...'); @@ -42,11 +36,10 @@ const NotificationCommentField = ({ axios .post( - import.meta.env.VITE_SERVER_DOMAIN + '/api/notification/comment', + import.meta.env.VITE_SERVER_DOMAIN + '/api/comment', { - _id, + project_id, comment, - project_author: user_id, replying_to: replyingTo, notification_id, }, diff --git a/client/src/modules/notification/hooks/index.ts b/client/src/modules/notification/hooks/index.ts index cea6578c..f177213d 100644 --- a/client/src/modules/notification/hooks/index.ts +++ b/client/src/modules/notification/hooks/index.ts @@ -30,26 +30,27 @@ const useNotifications = () => { ]); if (notificationsResponse.data && countResponse.data) { - const currentState = notifications; - const existingResults = currentState?.results || []; - - const formattedData: NotificationPaginationState = { - results: - page === 1 - ? notificationsResponse.data - : [...existingResults, ...notificationsResponse.data], - page, - totalDocs: countResponse.data.totalDocs || 0, - deleteDocCount: deletedDocCount, - }; - - setNotifications(formattedData); + setNotifications((currentState) => { + const existingResults = currentState?.results || []; + + const formattedData: NotificationPaginationState = { + results: + page === 1 + ? notificationsResponse.data + : [...existingResults, ...notificationsResponse.data], + page, + totalDocs: countResponse.data.totalDocs || 0, + deleteDocCount: deletedDocCount, + }; + + return formattedData; + }); } } catch (error) { console.error('Error fetching notifications:', error); } }, - [isAuthenticated, setNotifications, notifications] + [isAuthenticated, setNotifications] ); return { diff --git a/client/src/modules/notification/index.tsx b/client/src/modules/notification/index.tsx index 15bfc057..26a3f266 100644 --- a/client/src/modules/notification/index.tsx +++ b/client/src/modules/notification/index.tsx @@ -35,7 +35,8 @@ const Notifications = () => { setNotifications(null); fetchNotifications({ page: 1, filter, deletedDocCount: 0 }); } - }, [isAuthenticated, filter, fetchNotifications, setNotifications]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [filter]); const handleFilter = (filterName: string) => { setFilter(filterName as NOTIFICATION_FILTER_TYPE);