- {githubData ? (
-
- ) : (
-
- )}
-
-
- {profileData ? (
- profileData.name
+
+
+
+
+
+
+
+ {profileData?.profileBanner ? (
+
) : (
-
+
+ )}
+
+
+
+ {profileData?.profileImage ? (
+
+
+ {profileData.name}
+
+
+ ) : (
+
+
+ {profileData?.username}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {profileData ? `@${profileData.username}` : }
+
+
+
+ Add verification badge
+
+
+
+ {!isOwnProfile && (
+
+ {isFriend ? 'Disconnect' : 'Connect'}
+
)}
+
+
+ {profileData ? profileData.name : }
-
+
+ {profileData ? profileData.bio : }
+
+
{profileData ? (
- `@${profileData.username}`
+ <>
+
+
+ {profileData.email}
+
+ {profileData.location ? (
+
+
+ {profileData.location}
+
+ ) : (
+ <>>
+ )}
+ {profileData.githubUsername ? (
+ window.open(`https://github.com/${profileData.githubUsername}`, '_blank')}
+ >
+
+ GitHub
+
+ ) : (
+ <>>
+ )}
+ {profileData.leetcodeUsername ? (
+ window.open(`https://leetcode.com/${profileData.leetcodeUsername}`, '_blank')}
+ >
+
+ LeetCode
+
+ ) : (
+ <>>
+ )}
+
+ >
) : (
-
+ <>
+
+
+
+ >
)}
-
-
-
-
-
-
Bio
- {profileData ? (
-
- {profileData.bio}
-
- ) : (
-
- )}
-
-
-
-
Email
- {profileData ? (
-
- {profileData.email}
-
- ) : (
-
- )}
+
-
-
-
-
-
Top Languages
- {languages.length > 0 ? (
-
- {languages.map((lang, index) => (
-
- {lang.language}: {lang.percentage}
-
- ))}
+
+
+ {/* Analytics */}
+
+
+ Analytics
+
+
+
+
+
+
+
97 profile views
+
Discover who's viewed your profile
+
- ) : (
-
- {[1, 2, 3].map((i) => (
-
- ))}
+
+
+
+
90 post impressions
+
Check out who's engaging with your posts
+
- )}
-
+
+
+
+
13 search appearances
+
See how often you appear in search results
+
+
+
+
- {!isOwnProfile && (
-
- {isFriend ? 'Disconnect' : 'Connect'}
-
- )}
-
-
-
Friends
- {friends.length > 0 ? (
- friends.map((friend, index) => (
-
{friend}
- ))
- ) : (
-
No friends
- )}
-
-
+ {
+ githubData ? (
+ <>
+ {/* Top Languages */}
+
+
+ Top Languages
+
+
+ {languages.length > 0 ? (
+
+ {languages.map((lang, index) => (
+
+ {lang.language}: {lang.percentage}
+
+ ))}
+
+ ) : (
+
+ {[1, 2, 3].map((i) => (
+
+ ))}
+
+ )}
+
+
+ {/* GitHub Stats */}
+
+
+ GitHub Stats
+
+
+
+
GitHub Streak Stats
+ {streakStat ? (
+
+ ) : (
+ <>>
+ )}
+
+
+
GitHub Streak
+ {githubStreakSvg ? (
+
+ ) : (
+ <>>
+ )}
+
+
+
-
- {/*
Projects
-
- {profileData ? (
- profileData.projects.map((project, index) => (
-
-
- {project.title}
-
- @{profileData.username}
-
+ {/* Pinned Repositories */}
+
+
+ Pinned Repositories
- {project.description}
+ {pinnedRepos.length > 0 ? (
+
+ {pinnedRepos.map((repo, index) => (
+
+
+ {repo.repo.trim()}
+ {repo.owner}
+
+
+
+ {repo.description}
+
+
+ {repo.language && (
+
+
+ {repo.language}
+
+ )}
+
+ {repo.stars}
+
+
+ {repo.forks}
+
+
+
+
+
+ ))}
+
+ ) : (
+
+ )}
- ))
- ) : (
- <>
-
-
>
- )}
- */}
-
- {/* GitHub Stats */}
-
-
GitHub Streak Stats
- {streakStat ? (
-
- ) : (
-
- )}
-
-
GitHub Streak
- {githubStreakSvg ? (
-
- ) : (
-
- )}
-
- {/*
LeetCode Stats
- {leetcodeSvg ? (
-
- ) : (
-
- )} */}
-
+ ) : (<>>)
+ }
-
-
Pinned Repositories
- {pinnedRepos.length > 0 ? (
-
- {pinnedRepos.map((repo, index) => (
-
- {/*
*/}
-
-
- {repo.repo.trim()}
-
-
- {repo.owner}
-
-
-
-
- {repo.description}
-
-
- {repo.language && (
-
-
- {repo.language}
-
- )}
-
- {repo.stars}
-
-
- {repo.forks}
-
-
-
-
-
- ))}
-
- ) : (
-
- )}
-
+
+
-
-
+ {/* Right Sidebar */}
+
+ {/*
+
+ Profile language
+
+
+
+
+
+ English
+
+ */}
+
+
+
+ Public profile & URL
+
+ {copied ? : }
+
+
+
+
+ {`https://www.devhub.page/user/${profileData?.username}`}
+
+
+
+
+
+ {/* Friends */}
+
+
+ Friends
+
+
+ {friends.length > 0 ? (
+ friends.map((friend, index) => (
+
+ ))
+ ) : (
+ No friends yet
+ )}
+
+
+
+
+
);
}
-export default Profile;
\ No newline at end of file
+export default Profile;
+
+
diff --git a/client/src/pages/Projects.tsx b/client/src/pages/Projects.tsx
index b2ed9ad..cae834b 100644
--- a/client/src/pages/Projects.tsx
+++ b/client/src/pages/Projects.tsx
@@ -1,14 +1,10 @@
-import {
- SidebarInset,
- SidebarProvider,
- SidebarTrigger,
-} from "@/components/ui/sidebar"
-import { SidebarLeft } from '@/components/Sidebar/Sidebar'
import { useEffect, useState } from "react";
-import { ProjectCard } from "@/components/Projects/ProjectCard";
import { useParams } from "react-router-dom";
-import AddProject from "../components/Projects/AddProject";
-import { Skeleton } from '@/components/ui/skeleton';
+import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
+import { SidebarLeft } from "@/components/Sidebar/Sidebar";
+import { ProjectCard } from "@/components/Projects/ProjectCard";
+import AddProject from "@/components/Projects/AddProject";
+import { Skeleton } from "@/components/ui/skeleton";
const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000';
@@ -28,31 +24,25 @@ const Projects = () => {
const [refresh, setRefresh] = useState(false); // Refresh trigger
const { username } = useParams<{ username: string }>();
- // Fetch user projects
- const fetchUserProjects = async () => {
- try {
- const response = await fetch(`${backendUrl}/profile/${username}/projects`);
- if (!response.ok) {
- throw new Error("Failed to fetch projects");
+ useEffect(() => {
+ // Fetch user projects
+ const fetchUserProjects = async () => {
+ try {
+ const response = await fetch(`${backendUrl}/profile/${username}/projects`);
+ const data = await response.json();
+ setProjects(data.projects);
+ } catch (error) {
+ console.error('Failed to fetch projects:', error);
+ } finally {
+ setLoading(false);
}
- const data = await response.json();
- setProjects(data.projects || []);
- } catch (error) {
- console.error("Error fetching projects:", error);
- } finally {
- setLoading(false);
- }
- };
+ };
- useEffect(() => {
- if (username) {
- fetchUserProjects();
- }
- }, [username, refresh]); // Re-fetch projects on refresh trigger
+ fetchUserProjects();
+ }, [username, refresh]);
- // Callback to trigger refresh
const handleRefresh = () => {
- setRefresh(!refresh); // Toggle refresh state to re-trigger useEffect
+ setRefresh((prev) => !prev);
};
return (
@@ -68,23 +58,22 @@ const Projects = () => {
Projects
- {!projects.length ? (
+ {!projects ? (
No Projects yet.
- ) :
- loading ? (
+ ) : loading ? (
- {Array.from({ length: 3 }).map((_, index) => (
-
- ))}
+ {Array.from({ length: 3 }).map((_, index) => (
+
+ ))}
) : (
-
- {projects.map((project) => (
-
- ))}
-
+
+ {projects.map((project) => (
+
+ ))}
+
)}
diff --git a/server/api/handlers/user/profile.py b/server/api/handlers/user/profile.py
index 8e14927..37dd6eb 100644
--- a/server/api/handlers/user/profile.py
+++ b/server/api/handlers/user/profile.py
@@ -20,7 +20,9 @@ def get_profile(username):
'githubUsername': user["github_username"],
'leetcodeUsername': user["leetcode_username"],
'email': user["email"],
- 'profileImage' : user["profile_image"]
+ 'profileImage' : user["profile_image"],
+ 'profileBanner' : user["profile_banner"],
+ 'location' : user["location"]
}
projects_query = """
@@ -57,10 +59,26 @@ def upload_image_to_cloudinary(image):
except Exception as e:
logging.error(f"Error uploading image: {str(e)}")
return None
+
+def update_banner(username):
+ data = request.form
+ profile_banner = request.files.get('profile_banner')
+ query = "MATCH (u:User {username: $username}) SET u += $properties RETURN u"
+ properties = {}
+ if profile_banner:
+ banner_url = upload_image_to_cloudinary(profile_banner)
+ if banner_url:
+ properties['profile_banner'] = banner_url
+ with neo4j_db.driver.session() as session:
+ result = session.run(query, username=username, properties=properties)
+ if result.single():
+ return jsonify({'message': 'Profile updated successfully'}), 200
+ return jsonify({'message': 'User not found'}), 404
def update_profile(username):
data = request.form
- profile_image = request.files.get('profileImage')
+
+ profile_image = request.files.get('profileImage')
query = "MATCH (u:User {username: $username}) SET u += $properties RETURN u"
properties = {}
@@ -68,7 +86,7 @@ def update_profile(username):
image_url = upload_image_to_cloudinary(profile_image)
if image_url:
properties['profile_image'] = image_url
-
+
if 'name' in data:
properties['name'] = data['name']
if 'bio' in data:
diff --git a/server/api/urls.py b/server/api/urls.py
index 93867db..cc5e2f4 100644
--- a/server/api/urls.py
+++ b/server/api/urls.py
@@ -1,5 +1,5 @@
from api.handlers.auth.userauth import signup, login, check_auth, home, logout, index, check_username
-from api.handlers.user.profile import get_profile, update_profile, delete_account
+from api.handlers.user.profile import get_profile, update_profile, delete_account, update_banner
from api.handlers.projects.projects import add_project, update_project, delete_project, get_projects, increment_star, project_details, upload_image
from api.handlers.analyze.githubdata import github_data, top_languages, streak_stats, pinned_repos, streak_chart
from api.handlers.analyze.leetcodedata import leetcode_data, leetcode_card
@@ -25,6 +25,7 @@ def register_routes(app):
# Profile routes
app.add_url_rule('/profile/
', 'get_profile', get_profile, methods=['GET'])
app.add_url_rule('/profile/', 'update_profile', update_profile, methods=['PUT'])
+ app.add_url_rule('/profile/update_banner/', 'update_banner', update_banner, methods=['PUT'])
app.add_url_rule('/profile/delete/', 'delete_account', delete_account, methods=['DELETE'])
# Friends routes
diff --git a/server/models.py b/server/models.py
index 058098b..fb4aae8 100644
--- a/server/models.py
+++ b/server/models.py
@@ -28,6 +28,7 @@ class User(Base):
bio = Column(Text, nullable=True)
location = Column(String, nullable=True)
profile_image = Column(String, nullable=True)
+ profile_banner = Column(String, nullable=True)
github_username = Column(String, nullable=True)
leetcode_username = Column(String, nullable=True)
@@ -46,7 +47,7 @@ class User(Base):
skillset = Column(ARRAY(String), default=[])
suggestions = Column(ARRAY(String), default=[])
- def __init__(self, username, email, password, name=None, bio=None,location=None, github_username=None, leetcode_username=None, profile_image=None):
+ def __init__(self, username, email, password, name=None, bio=None,location=None, github_username=None, leetcode_username=None, profile_image=None, profile_banner=None):
self.username = username
self.email = email
self.password = password
@@ -54,6 +55,7 @@ def __init__(self, username, email, password, name=None, bio=None,location=None,
self.bio = bio
self.location = location
self.profile_image = profile_image
+ self.profile_banner = profile_banner
self.github_username = github_username
self.leetcode_username = leetcode_username
self.skillset = []