Skip to content
Closed
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
9 changes: 5 additions & 4 deletions backend/projects/urls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from django.urls import path
from .views import ProjectCRUDView, get_projects, toggle_follow, toggle_like, project_member_hander
from .views import ProjectCRUDView, get_projects, toggle_follow, toggle_like, project_member_hander, check_like_status

urlpatterns = [
path('', get_projects, name='get_projects'), # Fetch all projects
path('create/', ProjectCRUDView.as_view(), name='project-create'), # Create project
path('<str:pk>/', ProjectCRUDView.as_view(), name='project-detail'), # Read, Update, Delete project by ID
path('like/<str:project_id>', toggle_like, name='toggle_like'),
path('follow/<str:project_id>', toggle_follow, name='toggle_follow'),
path('member/manage/<str:project_id>', project_member_hander, name='project-member-handler'),
path('like/<str:project_id>/', toggle_like, name='toggle_like'),
path('follow/<str:project_id>/', toggle_follow, name='toggle_follow'),
path('member/manage/<str:project_id>/', project_member_hander, name='project-member-handler'),
path('likes/check/<str:user_id>/<str:project_id>/', check_like_status, name='check_like_status')
]
11 changes: 11 additions & 0 deletions backend/projects/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,14 @@ def project_member_hander(request, project_id):
except User.DoesNotExist:
return Response({'error': 'User not found'}, status=404)

@api_view(['GET'])
@permission_classes([AllowAny])
def check_like_status(request, user_id, project_id):
try:
project = get_object_or_404(Project, id=project_id)
user = get_object_or_404(User, id=user_id)

liked = Like.objects.filter(project=project, user=user).exists()
return Response({'liked': liked}, status=status.HTTP_200_OK)
except Exception as e:
return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
4 changes: 4 additions & 0 deletions backend/userauth/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ class UserAdmin(admin.ModelAdmin):
)
search_fields = ('username', 'email', 'first_name', 'last_name')
list_filter = ('location',)
<<<<<<< HEAD
filter_horizontal = ('projects', 'owned')
=======
filter_horizontal = ('projects',)
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
4 changes: 4 additions & 0 deletions backend/userauth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ class User(models.Model):
email = models.EmailField(unique=True, blank=True, null=True)
first_name = models.CharField(max_length=255, default="")
last_name = models.CharField(max_length=255, default="")
<<<<<<< HEAD
colleges = ArrayField(models.CharField(max_length=225), blank=True, null=True)
=======
college = models.CharField(max_length=255, default="")
major = models.CharField(max_length=255, default="")
grad_date = models.CharField(max_length=255, default="")
about_me = models.TextField(max_length=500, blank=True, null=True)
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
followers = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
roles = models.JSONField(default=list)
Expand Down
8 changes: 8 additions & 0 deletions backend/userauth/urls.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
from django.urls import path
<<<<<<< HEAD
from .views import UsersCRUDView, check_if_onboarded
=======
from .views import UsersCRUDView, check_if_onboarded, fetch_by_uuid
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84

urlpatterns = [
path('onboard/', UsersCRUDView.as_view(), name='onboard-user'), # Create user
path('onboard/check/<str:clerk_id>', check_if_onboarded, name="onboard-check"), # Check if user onboarded
<<<<<<< HEAD
path('<str:pk>/', UsersCRUDView.as_view(), name='user'), # Read, Update, Delete user by ID
=======
path('<str:clerk_id>/', UsersCRUDView.as_view(), name='user'), # Read, Update, Delete user by ID
path('fetch/<str:uuid>/', fetch_by_uuid, name='uuid-fetch'),
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
]
55 changes: 55 additions & 0 deletions backend/userauth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,60 @@
from .serializers import UserSerializer
from rest_framework.permissions import AllowAny
from rest_framework.decorators import api_view, permission_classes
<<<<<<< HEAD

@api_view(['GET'])
@permission_classes([AllowAny])
def check_if_onboarded(request, clerk_id):
if User.objects.filter(auth_id=clerk_id).exists():
return Response({"message": "User already onboarded"})
else:
return Response({"message": "User not onboarded yet!"})

# DRF CRUD views for Users model
class UsersCRUDView(APIView):
permission_classes = [AllowAny]
# Onboard a new user (POST)
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({
"message": "User successfully onboarded",
"data": serializer.data
}, status=status.HTTP_201_CREATED)
else:
print(serializer.errors)
error_details = {
"message": "Invalid user data provided.",
"errors": serializer.errors,
}
return Response(error_details, status=status.HTTP_400_BAD_REQUEST)

# Get a single user by ID (GET)
def get(self, request, pk):
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)

# Update an existing user by ID (PUT)
def put(self, request, pk):
user = get_object_or_404(User, pk=pk)
serializer = UserSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response({
"message": "User successfully updated!",
"data": serializer.data
}, status=status.HTTP_201_CREATED)
else:
print(serializer.errors)
error_details = {
"message": "Invalid user data provided.",
"errors": serializer.errors,
}
return Response(error_details, status=status.HTTP_400_BAD_REQUEST)
=======

@api_view(['GET'])
@permission_classes([AllowAny])
Expand Down Expand Up @@ -80,3 +134,4 @@ def put(self, request, clerk_id):
return Response(error_details, status=status.HTTP_400_BAD_REQUEST)


>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
3 changes: 3 additions & 0 deletions frontend/bitmatch/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions frontend/bitmatch/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
},
"dependencies": {
"@clerk/clerk-react": "^5.24.0",
<<<<<<< HEAD
=======
"@google/genai": "^0.10.0",
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-label": "^2.1.2",
Expand Down
25 changes: 25 additions & 0 deletions frontend/bitmatch/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import ProjectListPage from "./views/ProjectListPage";
import ProjectDetailPage from "./views/IndividualProjectPage";
import AddProjectPage from "./views/AddProjectPage";
import ProfilePage from "./views/ProfilePage";
<<<<<<< HEAD
=======
import AboutPage from "./views/AboutPage";
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84

import OnboardPage from "./views/OnboardPage";
import InterestPage from "./components/onboarding/Interest";
Expand All @@ -34,9 +37,14 @@ function AppRoutes() {
const pathname = location.pathname;
const isLanding = pathname === "/" && !isSignedIn;
const isOnboard = pathname.startsWith("/onboard");
<<<<<<< HEAD

const shouldUseContainer = !isLanding && !isOnboard;
=======
const isAbout = pathname.startsWith("/about");

const shouldUseContainer = !isLanding && !isOnboard && !isAbout;
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84

const layoutClass = shouldUseContainer
? "container mx-auto px-4 py-16 pb-6 min-h-screen"
Expand Down Expand Up @@ -89,7 +97,11 @@ function AppRoutes() {
}
/>
<Route
<<<<<<< HEAD
path="/my-profile"
=======
path="/profile/:id"
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
element={
<SignedIn>
<ProfilePage />
Expand All @@ -98,6 +110,18 @@ function AppRoutes() {
/>

{/* Onboarding */}
<<<<<<< HEAD
<Route path="/onboard" element={<OnboardPage />}>
<Route path="interests" element={<InterestPage />} />
<Route path="location" element={<LocationPage />} />
<Route path="positions" element={<PositionPage />} />
<Route path="skills" element={<SkillsPage />} />
<Route path="user" element={<UserPage />} />
</Route>
</Routes>
</main>

=======
<Route
path="/onboard"
element={
Expand Down Expand Up @@ -161,6 +185,7 @@ function AppRoutes() {
</Routes>
</main>

>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
<MainFooter />
</>
);
Expand Down
6 changes: 6 additions & 0 deletions frontend/bitmatch/src/components/navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ export default function Navbar({ links = [] }) {
const { user } = useUser();
const defaultLinks = [
{ href: "/about", label: "About" },
<<<<<<< HEAD
{ href: "/browse", label: "Browse" },
{ href: "/contact", label: "Contact" },
{ href: "/my-profile", label: "My Profile" },
=======
...(user?.id
? [
{ href: "/project-list", label: "Browse" },
{ href: `/profile/${user.id}`, label: "My Profile" },
]
: []),
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
];

const [showModal, setShowModal] = useState(false);
Expand Down
108 changes: 108 additions & 0 deletions frontend/bitmatch/src/components/onboarding/CreateProfile.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,56 @@
<<<<<<< HEAD
'use client'

import {
forwardRef,
useEffect,
useState,
useImperativeHandle,
} from 'react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Badge } from '@/components/ui/badge'
import { X } from 'lucide-react'

// ✅ Component with forwardRef
const CreateProfile = forwardRef(({ onDataChange, formData }, ref) => {
const [firstName, setFirstName] = useState(formData?.first_name || '')
const [lastName, setLastName] = useState(formData?.last_name || '')
const [colleges, setColleges] = useState(formData?.colleges || [])
const [newCollege, setNewCollege] = useState('')
const [errors, setErrors] = useState({})

// Send data to parent on change
useEffect(() => {
onDataChange({ first_name: firstName, last_name: lastName, colleges })
}, [firstName, lastName, colleges])

// Validation logic exposed via ref
useImperativeHandle(ref, () => ({
validate: () => {
const newErrors = {}
if (!firstName.trim()) newErrors.firstName = 'First name is required.'
if (!lastName.trim()) newErrors.lastName = 'Last name is required.'
if (colleges.length === 0) newErrors.colleges = 'At least one college is required.'
setErrors(newErrors)
return Object.keys(newErrors).length === 0
},
}))

const handleAddCollege = () => {
const trimmed = newCollege.trim()
if (trimmed && !colleges.includes(trimmed)) {
const updated = [...colleges, trimmed].sort()
setColleges(updated)
setNewCollege('')
}
}

const handleRemoveCollege = (college) => {
const updated = colleges.filter((c) => c !== college).sort()
setColleges(updated)
}
=======
"use client";

import { forwardRef, useEffect, useState, useImperativeHandle } from "react";
Expand Down Expand Up @@ -47,6 +100,7 @@ const CreateProfile = forwardRef(({ onDataChange, formData }, ref) => {
return Object.keys(newErrors).length === 0;
},
}));
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84

return (
<div className="space-y-6">
Expand All @@ -61,9 +115,13 @@ const CreateProfile = forwardRef(({ onDataChange, formData }, ref) => {
onChange={(e) => setFirstName(e.target.value)}
placeholder="Enter your first name"
/>
<<<<<<< HEAD
{errors.firstName && <p className="text-red-500 text-sm">{errors.firstName}</p>}
=======
{errors.firstName && (
<p className="text-red-500 text-sm">{errors.firstName}</p>
)}
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
</div>

{/* Last Name */}
Expand All @@ -77,6 +135,55 @@ const CreateProfile = forwardRef(({ onDataChange, formData }, ref) => {
onChange={(e) => setLastName(e.target.value)}
placeholder="Enter your last name"
/>
<<<<<<< HEAD
{errors.lastName && <p className="text-red-500 text-sm">{errors.lastName}</p>}
</div>

{/* Colleges */}
<div className="space-y-2">
<label className="text-sm font-medium">
Colleges/Universities <span className="text-red-500">*</span>
</label>
<div className="flex gap-2">
<Input
value={newCollege}
onChange={(e) => setNewCollege(e.target.value)}
placeholder="Add college or university"
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault()
handleAddCollege()
}
}}
/>
<Button onClick={handleAddCollege} className="bg-blue-600 hover:bg-blue-700">
Add
</Button>
</div>

<div className="flex flex-wrap gap-2 py-2">
{colleges.map((college) => (
<Badge key={college} className="px-4 py-2 bg-blue-600 text-white">
{college}
<button
onClick={() => handleRemoveCollege(college)}
className="ml-1 hover:text-gray-200"
>
<X className="h-4 w-4" />
</button>
</Badge>
))}
</div>

{errors.colleges && <p className="text-red-500 text-sm">{errors.colleges}</p>}
</div>
</div>
)
})

// ✅ Export wrapped with forwardRef
export default CreateProfile
=======
{errors.lastName && (
<p className="text-red-500 text-sm">{errors.lastName}</p>
)}
Expand Down Expand Up @@ -158,3 +265,4 @@ const CreateProfile = forwardRef(({ onDataChange, formData }, ref) => {
});

export default CreateProfile;
>>>>>>> d273cc17c82f58fa65fb0d6a01dc2146af9a0b84
Loading
Loading