From 642418da0a38fb6ee7d6298e13c4fa9b0b73b6f6 Mon Sep 17 00:00:00 2001 From: Mahi Date: Mon, 30 Mar 2026 20:05:10 +0530 Subject: [PATCH] removed ggl auth --- backend/controllers/auth.controller.js | 27 ++ backend/routes/auth.route.js | 48 +-- frontend/src/api/auth.api.js | 84 ++--- frontend/src/components/auth/LoginForm.jsx | 32 +- frontend/src/components/auth/SignupForm.jsx | 26 +- .../src/pages/candidate/CandidateProfile.jsx | 328 ++++++++++-------- frontend/src/pages/company/CompanyProfile.jsx | 326 +++++++++-------- frontend/src/store/authStore.js | 20 +- 8 files changed, 492 insertions(+), 399 deletions(-) diff --git a/backend/controllers/auth.controller.js b/backend/controllers/auth.controller.js index 1912ddc..b64acf5 100644 --- a/backend/controllers/auth.controller.js +++ b/backend/controllers/auth.controller.js @@ -144,6 +144,33 @@ export const updateProfile = async (req, res) => { } }; +// NEW: Remove profile picture — sets profile_pic to null +export const removeProfilePic = async (req, res) => { + try { + if (!req.user) { + return res.status(401).json({ message: "Unauthorized" }); + } + + const userId = req.user.id; + + const { data, error } = await supabase + .from('profiles') + .update({ profile_pic: null }) + .eq('id', userId) + .select() + .single(); + + if (error) { + return res.status(400).json({ message: error.message }); + } + + res.status(200).json(data); + } catch (error) { + console.error("Error in removeProfilePic:", error); + res.status(500).json({ message: "Server error" }); + } +}; + export const checkAuth = async (req, res) => { try { // Get profile data diff --git a/backend/routes/auth.route.js b/backend/routes/auth.route.js index 252b1ee..5a6fe80 100644 --- a/backend/routes/auth.route.js +++ b/backend/routes/auth.route.js @@ -1,24 +1,26 @@ -import express from 'express'; -import { - signup, - login, - logout, - updateProfile, - checkAuth, - getProfile -} from '../controllers/auth.controller.js'; -import { protectRoute } from '../middleware/auth.middleware.js'; - -const router = express.Router(); - -// Public routes -router.post('/signup', signup); -router.post('/login', login); -router.post('/logout', logout); - -// Protected routes -router.get('/check', protectRoute, checkAuth); -router.put('/update-profile', protectRoute, updateProfile); -router.get('/profile/:userId', protectRoute, getProfile); - +import express from 'express'; +import { + signup, + login, + logout, + updateProfile, + removeProfilePic, + checkAuth, + getProfile +} from '../controllers/auth.controller.js'; +import { protectRoute } from '../middleware/auth.middleware.js'; + +const router = express.Router(); + +// Public routes +router.post('/signup', signup); +router.post('/login', login); +router.post('/logout', logout); + +// Protected routes +router.get('/check', protectRoute, checkAuth); +router.put('/update-profile', protectRoute, updateProfile); +router.delete('/remove-profile-pic', protectRoute, removeProfilePic); // NEW +router.get('/profile/:userId', protectRoute, getProfile); + export default router; \ No newline at end of file diff --git a/frontend/src/api/auth.api.js b/frontend/src/api/auth.api.js index f9c0f0d..7d6023a 100644 --- a/frontend/src/api/auth.api.js +++ b/frontend/src/api/auth.api.js @@ -1,40 +1,46 @@ -import api from './axios'; - -export const authAPI = { - signup: async (data) => { - const response = await api.post('/auth/signup', data); - return response.data; - }, - - login: async (credentials) => { - const response = await api.post('/auth/login', credentials); - if (response.data.session?.access_token) { - localStorage.setItem('token', response.data.session.access_token); - localStorage.setItem('user', JSON.stringify(response.data.user)); - localStorage.setItem('profile', JSON.stringify(response.data.profile)); - } - return response.data; - }, - - logout: async () => { - await api.post('/auth/logout'); - localStorage.removeItem('token'); - localStorage.removeItem('user'); - localStorage.removeItem('profile'); - }, - - checkAuth: async () => { - const response = await api.get('/auth/check'); - return response.data; - }, - - updateProfile: async (profilePic) => { - const response = await api.put('/auth/update-profile', { profilePic }); - return response.data; - }, - - getProfile: async (userId) => { - const response = await api.get(`/auth/profile/${userId}`); - return response.data; - }, +import api from './axios'; + +export const authAPI = { + signup: async (data) => { + const response = await api.post('/auth/signup', data); + return response.data; + }, + + login: async (credentials) => { + const response = await api.post('/auth/login', credentials); + if (response.data.session?.access_token) { + localStorage.setItem('token', response.data.session.access_token); + localStorage.setItem('user', JSON.stringify(response.data.user)); + localStorage.setItem('profile', JSON.stringify(response.data.profile)); + } + return response.data; + }, + + logout: async () => { + await api.post('/auth/logout'); + localStorage.removeItem('token'); + localStorage.removeItem('user'); + localStorage.removeItem('profile'); + }, + + checkAuth: async () => { + const response = await api.get('/auth/check'); + return response.data; + }, + + updateProfile: async (profilePic) => { + const response = await api.put('/auth/update-profile', { profilePic }); + return response.data; + }, + + // NEW: remove profile picture + removeProfilePic: async () => { + const response = await api.delete('/auth/remove-profile-pic'); + return response.data; + }, + + getProfile: async (userId) => { + const response = await api.get(`/auth/profile/${userId}`); + return response.data; + }, }; \ No newline at end of file diff --git a/frontend/src/components/auth/LoginForm.jsx b/frontend/src/components/auth/LoginForm.jsx index cbed1b7..5c69b98 100644 --- a/frontend/src/components/auth/LoginForm.jsx +++ b/frontend/src/components/auth/LoginForm.jsx @@ -30,10 +30,6 @@ const LoginForm = () => { } }; - const handleGoogleSignIn = () => { - alert('Google Sign-In coming soon!'); - }; - return (
{/* Error Message */} @@ -43,32 +39,6 @@ const LoginForm = () => {
)} - {/* Google Sign In Button */} - - -
-
-
-
-
- - Or continue with email - -
-
- {/* Email/Password Form */}
@@ -163,4 +133,4 @@ const LoginForm = () => { ); }; -export default LoginForm; +export default LoginForm; \ No newline at end of file diff --git a/frontend/src/components/auth/SignupForm.jsx b/frontend/src/components/auth/SignupForm.jsx index 214a81b..06d921d 100644 --- a/frontend/src/components/auth/SignupForm.jsx +++ b/frontend/src/components/auth/SignupForm.jsx @@ -42,10 +42,6 @@ const SignupForm = () => { } }; - const handleGoogleSignUp = () => { - alert('Google Sign-Up coming soon!'); - }; - return (
@@ -56,26 +52,6 @@ const SignupForm = () => {
)} - {/* Google Sign Up */} - - -
-
-
-
-
- - Or continue with email - -
-
- {/* Account Type */}
{['candidate', 'company'].map((type) => ( @@ -262,4 +238,4 @@ const SignupForm = () => { ); }; -export default SignupForm; +export default SignupForm; \ No newline at end of file diff --git a/frontend/src/pages/candidate/CandidateProfile.jsx b/frontend/src/pages/candidate/CandidateProfile.jsx index abcc83b..6bdbf17 100644 --- a/frontend/src/pages/candidate/CandidateProfile.jsx +++ b/frontend/src/pages/candidate/CandidateProfile.jsx @@ -1,140 +1,190 @@ -import { useState } from 'react'; -import { User, Mail, Camera } from 'lucide-react'; -import { useAuth } from '../../hooks/useAuth'; -import { useAuthStore } from '../../store/authStore'; -import { useFileUpload } from '../../hooks/useFileUpload'; -import { validateImageFile } from '../../utils/validators'; -import toast from 'react-hot-toast'; - -const CandidateProfile = () => { - const { user, profile } = useAuth(); - const { updateProfile } = useAuthStore(); - const { uploadFile, isUploading } = useFileUpload(); - const [selectedImage, setSelectedImage] = useState(null); - - const handleImageSelect = async (e) => { - const file = e.target.files[0]; - if (!file) return; - - const errors = validateImageFile(file); - if (errors.length > 0) { - toast.error(errors.join(', ')); - return; - } - - setSelectedImage(file); - }; - - const handleUploadProfilePic = async () => { - if (!selectedImage) return; - - try { - await uploadFile(selectedImage, async (base64) => { - await updateProfile(base64); - }); - setSelectedImage(null); - } catch (error) { - console.error('Upload failed:', error); - } - }; - - return ( -
-
-

My Profile

-

Manage your profile information

-
- -
-
-
-
-
- {profile?.profile_pic ? ( - Profile - ) : ( -
- -
- )} -
-
- -
- - {selectedImage && ( - - )} -
-
- -
- -
-
- - -
- -
- - -
- -
- - -
-
-
-
-
- ); -}; - +import { useState } from 'react'; +import { User, Mail, Camera, Trash2 } from 'lucide-react'; +import { useAuth } from '../../hooks/useAuth'; +import { useAuthStore } from '../../store/authStore'; +import { useFileUpload } from '../../hooks/useFileUpload'; +import { validateImageFile } from '../../utils/validators'; +import toast from 'react-hot-toast'; + +const CandidateProfile = () => { + const { user, profile } = useAuth(); + const { updateProfile, removeProfilePic } = useAuthStore(); + const { uploadFile, isUploading } = useFileUpload(); + const [selectedImage, setSelectedImage] = useState(null); + const [isRemoving, setIsRemoving] = useState(false); + + const handleImageSelect = async (e) => { + const file = e.target.files[0]; + if (!file) return; + + const errors = validateImageFile(file); + if (errors.length > 0) { + toast.error(errors.join(', ')); + return; + } + + setSelectedImage(file); + }; + + const handleUploadProfilePic = async () => { + if (!selectedImage) return; + + try { + await uploadFile(selectedImage, async (base64) => { + await updateProfile(base64); + }); + setSelectedImage(null); + // Reset file input + const input = document.getElementById('profile-pic-input'); + if (input) input.value = ''; + } catch (error) { + console.error('Upload failed:', error); + } + }; + + const handleRemoveProfilePic = async () => { + if (!window.confirm('Are you sure you want to remove your profile picture?')) return; + + setIsRemoving(true); + try { + await removeProfilePic(); + // Clear any pending file selection too + setSelectedImage(null); + const input = document.getElementById('profile-pic-input'); + if (input) input.value = ''; + } catch (error) { + console.error('Remove failed:', error); + } finally { + setIsRemoving(false); + } + }; + + return ( +
+
+

My Profile

+

Manage your profile information

+
+ +
+
+
+ {/* Avatar */} +
+
+ {profile?.profile_pic ? ( + Profile + ) : ( +
+ +
+ )} +
+
+ + {/* Upload controls */} +
+ + +
+ {/* Upload button — only shown when a file is selected */} + {selectedImage && ( + + )} + + {/* Remove button — only shown when a profile pic exists */} + {profile?.profile_pic && !selectedImage && ( + + )} +
+ +

+ JPG, PNG or WebP · Max 2MB +

+
+
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+ ); +}; + export default CandidateProfile; \ No newline at end of file diff --git a/frontend/src/pages/company/CompanyProfile.jsx b/frontend/src/pages/company/CompanyProfile.jsx index 0108c94..0da90e6 100644 --- a/frontend/src/pages/company/CompanyProfile.jsx +++ b/frontend/src/pages/company/CompanyProfile.jsx @@ -1,140 +1,188 @@ -import { useState } from 'react'; -import { Building, Mail, Camera } from 'lucide-react'; -import { useAuth } from '../../hooks/useAuth'; -import { useAuthStore } from '../../store/authStore'; -import { useFileUpload } from '../../hooks/useFileUpload'; -import { validateImageFile } from '../../utils/validators'; -import toast from 'react-hot-toast'; - -const CompanyProfile = () => { - const { user, profile } = useAuth(); - const { updateProfile } = useAuthStore(); - const { uploadFile, isUploading } = useFileUpload(); - const [selectedImage, setSelectedImage] = useState(null); - - const handleImageSelect = async (e) => { - const file = e.target.files[0]; - if (!file) return; - - const errors = validateImageFile(file); - if (errors.length > 0) { - toast.error(errors.join(', ')); - return; - } - - setSelectedImage(file); - }; - - const handleUploadLogo = async () => { - if (!selectedImage) return; - - try { - await uploadFile(selectedImage, async (base64) => { - await updateProfile(base64); - }); - setSelectedImage(null); - } catch (error) { - console.error('Upload failed:', error); - } - }; - - return ( -
-
-

Company Profile

-

Manage your company information

-
- -
-
-
-
-
- {profile?.profile_pic ? ( - Company Logo - ) : ( -
- -
- )} -
-
- -
- - {selectedImage && ( - - )} -
-
- -
- -
-
- - -
- -
- - -
- -
- - -
-
-
-
-
- ); -}; - +import { useState } from 'react'; +import { Building, Mail, Camera, Trash2 } from 'lucide-react'; +import { useAuth } from '../../hooks/useAuth'; +import { useAuthStore } from '../../store/authStore'; +import { useFileUpload } from '../../hooks/useFileUpload'; +import { validateImageFile } from '../../utils/validators'; +import toast from 'react-hot-toast'; + +const CompanyProfile = () => { + const { user, profile } = useAuth(); + const { updateProfile, removeProfilePic } = useAuthStore(); + const { uploadFile, isUploading } = useFileUpload(); + const [selectedImage, setSelectedImage] = useState(null); + const [isRemoving, setIsRemoving] = useState(false); + + const handleImageSelect = async (e) => { + const file = e.target.files[0]; + if (!file) return; + + const errors = validateImageFile(file); + if (errors.length > 0) { + toast.error(errors.join(', ')); + return; + } + + setSelectedImage(file); + }; + + const handleUploadLogo = async () => { + if (!selectedImage) return; + + try { + await uploadFile(selectedImage, async (base64) => { + await updateProfile(base64); + }); + setSelectedImage(null); + const input = document.getElementById('company-logo-input'); + if (input) input.value = ''; + } catch (error) { + console.error('Upload failed:', error); + } + }; + + const handleRemoveLogo = async () => { + if (!window.confirm('Are you sure you want to remove your company logo?')) return; + + setIsRemoving(true); + try { + await removeProfilePic(); + setSelectedImage(null); + const input = document.getElementById('company-logo-input'); + if (input) input.value = ''; + } catch (error) { + console.error('Remove failed:', error); + } finally { + setIsRemoving(false); + } + }; + + return ( +
+
+

Company Profile

+

Manage your company information

+
+ +
+
+
+ {/* Avatar / Logo */} +
+
+ {profile?.profile_pic ? ( + Company Logo + ) : ( +
+ +
+ )} +
+
+ + {/* Upload controls */} +
+ + +
+ {/* Upload button — only shown when a file is selected */} + {selectedImage && ( + + )} + + {/* Remove button — only shown when a logo exists */} + {profile?.profile_pic && !selectedImage && ( + + )} +
+ +

+ JPG, PNG or WebP · Max 2MB +

+
+
+ +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+
+ ); +}; + export default CompanyProfile; \ No newline at end of file diff --git a/frontend/src/store/authStore.js b/frontend/src/store/authStore.js index fd1b02c..437bb7e 100644 --- a/frontend/src/store/authStore.js +++ b/frontend/src/store/authStore.js @@ -71,11 +71,25 @@ export const useAuthStore = create((set, get) => ({ const updatedProfile = await authAPI.updateProfile(profilePic); set({ profile: updatedProfile }); localStorage.setItem('profile', JSON.stringify(updatedProfile)); - toast.success('Profile updated successfully'); + toast.success('Profile picture updated successfully'); return updatedProfile; } catch (error) { - toast.error('Failed to update profile'); + toast.error('Failed to update profile picture'); throw error; } }, -})); + + // NEW: remove profile picture + removeProfilePic: async () => { + try { + const updatedProfile = await authAPI.removeProfilePic(); + set({ profile: updatedProfile }); + localStorage.setItem('profile', JSON.stringify(updatedProfile)); + toast.success('Profile picture removed'); + return updatedProfile; + } catch (error) { + toast.error('Failed to remove profile picture'); + throw error; + } + }, +})); \ No newline at end of file