diff --git a/client/src/App.tsx b/client/src/App.tsx index 420dc14..44a448c 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -8,6 +8,7 @@ import Profile from './pages/Profile'; import { Toaster } from "@/components/ui/sonner"; import EditProfileForm from './pages/EditProfileForm'; import { MessagePage } from './pages/MessagePage'; +import Projects from './components/Projects/Projects'; const App = () => { @@ -21,6 +22,7 @@ const App = () => { } /> } /> } /> + } /> } /> 404} /> diff --git a/client/src/components/Projects/Projects.tsx b/client/src/components/Projects/Projects.tsx new file mode 100644 index 0000000..7ca44ec --- /dev/null +++ b/client/src/components/Projects/Projects.tsx @@ -0,0 +1,34 @@ +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/components/ui/sidebar" +import { SidebarLeft } from '@/components/Sidebar/Sidebar' + +const Projects = () => { + return ( + + + +
+
+ +
+
+
+ +
+
+
+ ) +} + +const Dashboard = () => { + return( + <> + project + + ) +} + +export default Projects \ No newline at end of file diff --git a/client/src/components/Sidebar/Sidebar.tsx b/client/src/components/Sidebar/Sidebar.tsx index 008b7f0..2f33bb5 100644 --- a/client/src/components/Sidebar/Sidebar.tsx +++ b/client/src/components/Sidebar/Sidebar.tsx @@ -6,6 +6,7 @@ import { CircleUser, Trash2, LogOut, + MessagesSquare, type LucideIcon, } from "lucide-react" @@ -68,6 +69,11 @@ export function SidebarLeft({ ...props }: React.ComponentProps) { title: "Message", url: "/message", + icon: MessagesSquare, + }, + { + title: "Projects", + url: "/projects", icon: Inbox, }, ], @@ -155,7 +161,7 @@ function NavSecondary({ url: string icon: LucideIcon badge?: React.ReactNode - onClick?: React.MouseEventHandler // Add onClick type + onClick?: React.MouseEventHandler }[] } & React.ComponentPropsWithoutRef) { return ( @@ -165,7 +171,6 @@ function NavSecondary({ {items.map((item) => ( - {/* Handle click if provided */} {item.title} diff --git a/client/src/pages/Profile.tsx b/client/src/pages/Profile.tsx index 45ae956..f32a460 100644 --- a/client/src/pages/Profile.tsx +++ b/client/src/pages/Profile.tsx @@ -125,9 +125,8 @@ const Dashboard = () => { setProfileData(profileResponse.data); const friendsResponse = await axios.get(`${backendUrl}/profile/${username}/friends`); - setFriendsList(friendsResponse.data.friends); // Set the visited user's friends list + setFriendsList(friendsResponse.data.friends); - // Check if logged-in user is a friend of the profile user const loggedInFriendsResponse = await axios.get(`${backendUrl}/profile/${loggedInUsername}/friends`); const isFriendOfProfileUser = loggedInFriendsResponse.data.friends.includes(username); setIsFriend(isFriendOfProfileUser); @@ -171,22 +170,19 @@ const Dashboard = () => { const handleFriendRequest = async () => { try { if (isFriend) { - // Disconnect if already a friend await axios.delete(`${backendUrl}/profile/${profileData?.username}/friends`, { data: { friend_username: loggedInUsername } }); setIsFriend(false); - if (username) { // Ensure username is defined + if (username) { dispatch(setFriendStatus({ username, isFriend: false })); } } else { - // Connect if not a friend await axios.post(`${backendUrl}/profile/${profileData?.username}/friends`, { friend_username: loggedInUsername }); setIsFriend(true); - if (username) { // Ensure username is defined + if (username) { dispatch(setFriendStatus({ username, isFriend: true })); } } - // Fetch the updated friends list for the visited user const friendsResponse = await axios.get(`${backendUrl}/profile/${username}/friends`); setFriendsList(friendsResponse.data.friends); } catch (error) { @@ -203,7 +199,6 @@ const Dashboard = () => { return (
- {/* Sidebar */}
{githubData ? ( @@ -234,7 +229,6 @@ const Dashboard = () => {

- {/* Profile Info Section */}

Bio

@@ -258,7 +252,6 @@ const Dashboard = () => { )}
- {/* GitHub and LeetCode Links */}
{profileData ? ( <> @@ -310,14 +303,13 @@ const Dashboard = () => {
)}
- {/* Friend Button */} + {!isOwnProfile && ( )} - {/* Friends List */}

Friends

{friends.length > 0 ? ( @@ -330,10 +322,7 @@ const Dashboard = () => {
- {/* Main Content Section */}
- {/* Friends Section */} -

Projects

{profileData ? ( diff --git a/server/api/handlers/projects/projects.py b/server/api/handlers/projects/projects.py new file mode 100644 index 0000000..204332d --- /dev/null +++ b/server/api/handlers/projects/projects.py @@ -0,0 +1,96 @@ +from flask import request, jsonify, current_app +from models import Project +from extensions import neo4j_db + +def add_project(username): + data = request.get_json() + + # Get project details from request + title = data.get('title') + description = data.get('description', '') + repo_link = data.get('repo_link', '') + tags = data.get('tags', '') + + # Define a query to create a project without tags initially + create_project_query = """ + MATCH (u:User {username: $username}) + CREATE (p:Project {title: $title, description: $description, repo_link: $repo_link, tags: $tags}) + CREATE (u)-[:OWNS]->(p) + RETURN p + """ + + try: + with neo4j_db.driver.session() as session: + # Create the project + result = session.run(create_project_query, username=username, title=title, description=description, repo_link=repo_link, tags=tags) + project_record = result.single() + + if project_record: + project = project_record["p"] + + domain_tags = [tag for tag in tags.split(',') if tag.strip() in tags] + + # Update project with tags + update_project_query = """ + MATCH (p:Project {title: $title, description: $description, repo_link: $repo_link}) + WITH p, $tags AS tags + UNWIND tags AS tagName + MERGE (t:Tag {name: tagName}) + CREATE (p)-[:TAGGED_WITH]->(t) + RETURN p + """ + + # Update the project with tags + session.run(update_project_query, title=title, description=description, repo_link=repo_link, tags=domain_tags) + + return jsonify({'message': 'Project added and categorized successfully', 'project': { + 'title': project["title"], + 'description': project.get("description", ""), + 'repoLink': project.get("repo_link", ""), + 'tags': domain_tags + }}), 201 + else: + return jsonify({'message': 'User not found'}), 404 + except Exception as e: + current_app.logger.error(f"Error adding project: {e}") + return jsonify({'message': 'An error occurred while adding the project'}), 500 + + + +def update_project(username, project_id): + data = request.get_json() + title = data.get('title') + description = data.get('description') + repo_link = data.get('repo_link') + tags = data.get('tags', '') + + query = """ + MATCH (u:User {username: $username})-[:OWNS]->(p:Project {id: $project_id}) + SET p.title = $title, p.description = $description, p.repo_link = $repo_link + WITH p + OPTIONAL MATCH (p)-[r:TAGGED_WITH]->(t:Tag) + DELETE r + WITH p + UNWIND $tags AS tagName + MERGE (t:Tag {name: tagName}) + CREATE (p)-[:TAGGED_WITH]->(t) + RETURN p + """ + with neo4j_db.driver.session() as session: + result = session.run(query, username=username, project_id=project_id, title=title, description=description, repo_link=repo_link, tags=tags.split(', ') if tags else []) + if result.single(): + return jsonify({'message': 'Project updated successfully'}), 200 + return jsonify({'message': 'Project or user not found'}), 404 + +def delete_project(username, project_title): + query = """ + MATCH (u:User {username: $username})-[:OWNS]->(p:Project {title: $project_title}) + OPTIONAL MATCH (p)-[r]-() + DELETE r, p + RETURN u + """ + with neo4j_db.driver.session() as session: + result = session.run(query, username=username, project_title=project_title) + if result.single(): + return jsonify({'message': 'Project deleted successfully'}), 200 + return jsonify({'message': 'Project or user not found'}), 404 \ No newline at end of file diff --git a/server/api/handlers/user/profile.py b/server/api/handlers/user/profile.py index aa8721c..06ddd02 100644 --- a/server/api/handlers/user/profile.py +++ b/server/api/handlers/user/profile.py @@ -1,8 +1,5 @@ -from flask import request, jsonify, current_app +from flask import request, jsonify from extensions import neo4j_db -from models import User, Project, Tag -from sqlalchemy.orm import sessionmaker -import os def get_profile(username): logged_in_user = request.args.get('logged_in_user') @@ -41,7 +38,6 @@ def get_profile(username): profile_data['projects'] = projects if logged_in_user: - # Check if the logged-in user is friends with the profile user friendship_query = """ MATCH (u1:User {username: $logged_in_user})-[:FRIEND]->(u2:User {username: $username}) RETURN COUNT(u1) > 0 AS isFriend @@ -71,95 +67,3 @@ def update_profile(username): return jsonify({'message': 'Profile updated successfully'}), 200 return jsonify({'message': 'User not found'}), 404 -def add_project(username): - data = request.get_json() - - # Get project details from request - title = data.get('title') - description = data.get('description', '') - repo_link = data.get('repo_link', '') - tags = data.get('tags', '') - - # Define a query to create a project without tags initially - create_project_query = """ - MATCH (u:User {username: $username}) - CREATE (p:Project {title: $title, description: $description, repo_link: $repo_link, tags: $tags}) - CREATE (u)-[:OWNS]->(p) - RETURN p - """ - - try: - with neo4j_db.driver.session() as session: - # Create the project - result = session.run(create_project_query, username=username, title=title, description=description, repo_link=repo_link, tags=tags) - project_record = result.single() - - if project_record: - project = project_record["p"] - - domain_tags = [tag for tag in tags.split(',') if tag.strip() in tags] - - # Update project with tags - update_project_query = """ - MATCH (p:Project {title: $title, description: $description, repo_link: $repo_link}) - WITH p, $tags AS tags - UNWIND tags AS tagName - MERGE (t:Tag {name: tagName}) - CREATE (p)-[:TAGGED_WITH]->(t) - RETURN p - """ - - # Update the project with tags - session.run(update_project_query, title=title, description=description, repo_link=repo_link, tags=domain_tags) - - return jsonify({'message': 'Project added and categorized successfully', 'project': { - 'title': project["title"], - 'description': project.get("description", ""), - 'repoLink': project.get("repo_link", ""), - 'tags': domain_tags - }}), 201 - else: - return jsonify({'message': 'User not found'}), 404 - except Exception as e: - current_app.logger.error(f"Error adding project: {e}") - return jsonify({'message': 'An error occurred while adding the project'}), 500 - - - -def update_project(username, project_id): - data = request.get_json() - title = data.get('title') - description = data.get('description') - repo_link = data.get('repo_link') - tags = data.get('tags', '') - - query = """ - MATCH (u:User {username: $username})-[:OWNS]->(p:Project {id: $project_id}) - SET p.title = $title, p.description = $description, p.repo_link = $repo_link - WITH p - OPTIONAL MATCH (p)-[r:TAGGED_WITH]->(t:Tag) - DELETE r - WITH p - UNWIND $tags AS tagName - MERGE (t:Tag {name: tagName}) - CREATE (p)-[:TAGGED_WITH]->(t) - RETURN p - """ - with neo4j_db.driver.session() as session: - result = session.run(query, username=username, project_id=project_id, title=title, description=description, repo_link=repo_link, tags=tags.split(', ') if tags else []) - if result.single(): - return jsonify({'message': 'Project updated successfully'}), 200 - return jsonify({'message': 'Project or user not found'}), 404 - -def delete_project(username, project_title): - query = """ - MATCH (u:User {username: $username})-[:OWNS]->(p:Project {title: $project_title}) - OPTIONAL MATCH (p)-[r]-() - DELETE r, p - RETURN u - """ - with neo4j_db.driver.session() as session: - result = session.run(query, username=username, project_title=project_title) - if result.single(): - return jsonify({'message': 'Project deleted successfully'}), 200 - return jsonify({'message': 'Project or user not found'}), 404 \ No newline at end of file diff --git a/server/api/urls.py b/server/api/urls.py index cee3510..ce72174 100644 --- a/server/api/urls.py +++ b/server/api/urls.py @@ -1,5 +1,6 @@ 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, add_project, update_project, delete_project +from api.handlers.user.profile import get_profile, update_profile +from api.handlers.projects.projects import add_project, update_project, delete_project 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 from api.handlers.query.querymodel import chat,chat_history diff --git a/server/app.py b/server/app.py index 0366907..5b3a6ed 100644 --- a/server/app.py +++ b/server/app.py @@ -23,7 +23,8 @@ def create_app(): } }) - logging.basicConfig(level=logging.DEBUG) + logging.getLogger('neo4j').setLevel(logging.WARNING) + logging.getLogger('pymongo').setLevel(logging.WARNING) with app.app_context(): from api.urls import register_routes diff --git a/server/extensions.py b/server/extensions.py index 3598611..a612d5e 100644 --- a/server/extensions.py +++ b/server/extensions.py @@ -3,7 +3,6 @@ from neo4j import GraphDatabase from config import Config from pymongo import MongoClient -import logging bcrypt = Bcrypt() cors = CORS() @@ -24,6 +23,4 @@ def close(self): mongo_client = MongoClient(Config.MONGODB_URI) mongo_db = mongo_client['devhub'] users_chat = mongo_db['users'] -chat_collection = mongo_db['chats'] - -logging.getLogger('pymongo').setLevel(logging.WARNING) \ No newline at end of file +chat_collection = mongo_db['chats'] \ No newline at end of file diff --git a/server/requirements.txt b/server/requirements.txt index 6e73322..8f04375 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -13,4 +13,4 @@ beautifulsoup4==4.12.3 langchain==0.2.1 langchain-community==0.2.1 langchain-google-genai==1.0.5 -pymongo \ No newline at end of file +pymongo==4.10.1 \ No newline at end of file