Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions client/src/components/ProfilePicture.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'

import { HOST } from '../util/apiRoutes'
import { fetchWithAuth } from '../util/fetchUtils'

export const ProfilePicture = ({ profile, setPfp }) => {
const { id } = useParams()

const [fetchedPfp, setFetchedPfp] = useState(null)

const fetchPfp = async () => {
try {
const data = await fetchWithAuth({
url: `${HOST}/api/pfp/${id}`,
url: `${HOST}/api/pfp/${profile._id}`,
method: 'GET',
})

Expand Down
174 changes: 43 additions & 131 deletions client/src/pages/Profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,36 +19,24 @@ function Profile() {
const { id } = useParams()
const [profile, setProfile] = useState({})

// for searching through profiles that have
// id as a username instead of id
const [profiles, setProfiles] = useState([])

const { user } = useAuthContext()
const [loading, setLoading] = useState(false)

const [saveable, setSaveable] = useState(false)

const [pfp, setPfp] = useState('')
const [username, setUsername] = useState('')
const [usernameErrorMessage, setUsernameErrorMessage] = useState('')
const [errorMessage, setErrorMessage] = useState('')
const [linkedin, setLinkedin] = useState('')
const [linkedinErrorMessage, setLinkedinErrorMessage] = useState('')
const [school, setSchool] = useState('')

const [location, setLocation] = useState('')

const hasError =
usernameErrorMessage.length !== 0 && linkedinErrorMessage.length !== 0
const hasError = errorMessage.length !== 0

const fetchProfile = async () => {
try {
setLoading(true)
const isValidId = await isMongoDBId(id)

if (!isValidId) {
setProfile(null)
throw new Error('Invalid MongoDB ID')
}

const data = await fetchWithAuth({
url: `${HOST}/api/profile/get/${id}`,
Expand All @@ -71,33 +59,40 @@ function Profile() {
}
}

const fetchSchool = async (id) => {
const fetchProfileByUsername = async () => {
try {
setLoading(true)

const data = await fetchWithAuth({
url: `${HOST}/api/school/get/${id}`,
url: `${HOST}/api/profile/getBy?username=${id}`,
method: 'GET',
})
data ? setSchool(data) : setSchool(null)
} catch (err) {
console.error('Error: ', err)

if (data.school) fetchSchool(data.school)

// Successful fetch and data extraction
setProfile(data)
setUsername(data.username)
setLinkedin(extractLinkedinUsername(data.linkedin))
setLocation(data.location)
setPfp(data.pfp)
} catch (error) {
console.error('Error:', error.message)
setProfile(null)
} finally {
setLoading(false)
}
}

const fetchProfiles = async () => {
setLoading(true)

const fetchSchool = async (id) => {
try {
const data = await fetchWithAuth({
url: `${HOST}/api/profile/`,
url: `${HOST}/api/school/get/${id}`,
method: 'GET',
})

// If fetchWithAuth doesn't throw, it means the response was ok
setProfiles(data)
} catch (error) {
console.error('Error:', error.message)
} finally {
setLoading(false)
data ? setSchool(data) : setSchool(null)
} catch (err) {
console.error('Error: ', err)
}
}

Expand Down Expand Up @@ -151,67 +146,6 @@ function Profile() {
}
}

const validateUsername = async (username) => {
const isValidUsername = async (username) => {
const mongodbConflict = await isMongoDBId(username)
const usernameRegex =
/^[a-zA-Z0-9_](?!.*[._]{2})[a-zA-Z0-9_.]{1,30}[a-zA-Z0-9_]$/
return !mongodbConflict && usernameRegex.test(username)
}

const isAvailable = (username) => {
const filteredProfiles = profiles.filter(
(profile) =>
profile.username.toLowerCase() === username.toLowerCase()
)

// check if the one profile there is the current user's
if (filteredProfiles.length === 1) {
const filteredProfile = filteredProfiles[0]

return profile.username === filteredProfile.username
}

return filteredProfiles.length === 0
}

// blank username
if (username.length === 0) {
setUsernameErrorMessage('Invalid username.')
return false
} else if (username.indexOf('/') !== -1) {
// contains '/'
setUsernameErrorMessage('Invalid username.')
return false
} else if (await isValidUsername(username)) {
// invalid regex
setUsernameErrorMessage('Invalid username.')
return false
} else if (!isAvailable(username)) {
// taken username
setUsernameErrorMessage('Username already taken.')
return false
} else {
// valid username
setUsernameErrorMessage('')
return true
}
}

const validateLinkedin = async (linkedin) => {
// Regular expression for a basic LinkedIn username check
const regex = /^[a-z0-9-]+$/i

// Check if the username matches the pattern
if (!regex.test(linkedin)) {
setLinkedinErrorMessage('Invalid Linkedin username.')
return false
} else {
setLinkedinErrorMessage('')
return true
}
}

const extractLinkedinUsername = (linkedin) => {
const regex = /https:\/\/linkedin\.com\/in\/([^/]+)/
const match = linkedin.match(regex)
Expand All @@ -225,17 +159,12 @@ function Profile() {
return null
}

const buildLinkedinUrl = (username) => {
const baseLinkedinUrl = 'https://linkedin.com/in/'
return `${baseLinkedinUrl}${username}`
}

const handleUsernameChange = async (e) => {
// change -> saveable progress
setSaveable(true)

// remove previous errors
setUsernameErrorMessage('')
setErrorMessage('')

const value = e.target.value
setUsername(value)
Expand All @@ -252,58 +181,41 @@ function Profile() {
const handleEditProfile = async () => {
const updatedProfile = {
username,
linkedin: buildLinkedinUrl(linkedin),
location,
}

// check all fields are filled out
if (!username || username.length === 0) {
setUsernameErrorMessage('All fields must be filled.')
return
}

if (!linkedin || linkedin.length === 0) {
// clear linkedin if user left blank / deleted
updatedProfile.linkedin = ''
}

if (!location || location.length === 0) {
// clear location if user left blank / deleted
updatedProfile.location = ''
}

// field validation
if (!validateUsername(username)) {
return
}

if (!validateLinkedin(linkedin)) {
return
}

try {
await fetchWithAuth({
url: `${HOST}/api/profile/get/${user.profileId}`,
url: `${HOST}/api/profile/${user.profileId}`,
method: 'PATCH',
data: updatedProfile,
})
} catch (error) {
console.error('Error:', error.message)
const message = error.message
console.error('Error:', message)
setErrorMessage(message)
}

setSaveable(false)
}

useEffect(() => {
const fetchInfo = async () => {
await fetchProfile()
await fetchProfiles()
const isValidId = await isMongoDBId(id)

if (!isValidId.response) {
await fetchProfileByUsername()
} else {
await fetchProfile()
}
}

fetchInfo()
}, [id])

const admin = user && (user.profileId === id || user.username === id)
// establish if the user viewing the profile
// has admin privileges of the viewed profile
const admin = user && profile && user.profileId === profile._id

const currentExperienceInfo = // {'Incoming / Currently / Previously', Work Title, Company Name}
profile && profile.pipeline && profile.pipeline.length > 0
Expand Down Expand Up @@ -349,9 +261,9 @@ function Profile() {
value={username}
onChange={handleUsernameChange}
/>
{usernameErrorMessage && (
{errorMessage && (
<h1 className="text-red-400">
{usernameErrorMessage}
{errorMessage}
</h1>
)}
</div>
Expand All @@ -376,7 +288,7 @@ function Profile() {
<h1 className="text-white">Anonymous</h1>
) : (
<Link
to={buildLinkedinUrl(linkedin)}
to={`https://linkedin.com/in/${linkedin}`}
target="_blank"
>
<h1 className="text-white hover:underline">
Expand Down
Loading