From b52b5284b25bd119b62f00f331fa3f3676da4406 Mon Sep 17 00:00:00 2001 From: shivansh023023 Date: Wed, 18 Feb 2026 00:29:33 +0530 Subject: [PATCH 1/5] feat: add Dockerfile for backend environment --- Dockerfile | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..64329c5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Use a stable Python base image +FROM python:3.10-slim + +# Set the working directory +WORKDIR /app + +# Install system-level dependencies for building Python packages +RUN apt-get update && apt-get install -y \ + build-essential \ + && rm -rf /var/lib/apt/lists/* + +# Copy requirements from the Backend folder and install +COPY Backend/requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the entire project into the container +COPY . . + +# Set environment variables (standard for AI platforms) +ENV PYTHONUNBUFFERED=1 + +# Change to the Backend directory to run the application +WORKDIR /app/Backend \ No newline at end of file From 52b9261193a8b5854ba15e716208ce2a67e00b13 Mon Sep 17 00:00:00 2001 From: shivansh023023 Date: Wed, 18 Feb 2026 12:12:35 +0530 Subject: [PATCH 2/5] feat: implement API and frontend logic for collaboration requests --- Backend/app/main.py | 3 +- Backend/app/routes/collaboration.py | 18 ++++++++++++ .../collaboration-hub/CollabRequests.tsx | 29 ++++++++++++++----- 3 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 Backend/app/routes/collaboration.py diff --git a/Backend/app/main.py b/Backend/app/main.py index 86d892a..0ecd8e0 100644 --- a/Backend/app/main.py +++ b/Backend/app/main.py @@ -7,6 +7,7 @@ from .routes.chat import router as chat_router from .routes.match import router as match_router from sqlalchemy.exc import SQLAlchemyError +from .routes.collaboration import router as collaboration_router import logging import os from dotenv import load_dotenv @@ -56,7 +57,7 @@ async def lifespan(app: FastAPI): app.include_router(match_router) app.include_router(ai.router) app.include_router(ai.youtube_router) - +app.include_router(collaboration_router) @app.get("/") async def home(): diff --git a/Backend/app/routes/collaboration.py b/Backend/app/routes/collaboration.py new file mode 100644 index 0000000..926df11 --- /dev/null +++ b/Backend/app/routes/collaboration.py @@ -0,0 +1,18 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import update +from ..db.db import get_db +from ..models.models import Collaboration # Verify this name in models/models.py + +router = APIRouter(prefix="/collaboration", tags=["Collaboration"]) + +@router.put("/update-status/{collab_id}") +async def update_collab_status(collab_id: str, status: str, db: AsyncSession = Depends(get_db)): + if status not in ["accepted", "denied"]: + raise HTTPException(status_code=400, detail="Invalid status") + + # This matches the 'status' column in your sql.txt collaborations table + query = update(Collaboration).where(Collaboration.id == collab_id).values(status=status) + await db.execute(query) + await db.commit() + return {"message": f"Collaboration {status} successfully"} \ No newline at end of file diff --git a/Frontend/src/components/collaboration-hub/CollabRequests.tsx b/Frontend/src/components/collaboration-hub/CollabRequests.tsx index 57cfb2c..d539960 100644 --- a/Frontend/src/components/collaboration-hub/CollabRequests.tsx +++ b/Frontend/src/components/collaboration-hub/CollabRequests.tsx @@ -97,16 +97,31 @@ const mockRequests = [ const CollabRequests: React.FC = () => { const [requests, setRequests] = useState(mockRequests); - const handleAccept = (id: number) => { - setRequests(prev => prev.filter(req => req.id !== id)); - // TODO: Integrate with backend to accept request +const handleAccept = async (id: number) => { + try { + const response = await fetch(`http://localhost:8000/collaboration/update-status/${id}?status=accepted`, { + method: 'PUT', + }); + if (response.ok) { + setRequests(prev => prev.filter(req => req.id !== id)); + } + } catch (error) { + console.error("Failed to accept request:", error); + } }; - const handleDeny = (id: number) => { - setRequests(prev => prev.filter(req => req.id !== id)); - // TODO: Integrate with backend to deny request + const handleDeny = async (id: number) => { + try { + const response = await fetch(`http://localhost:8000/collaboration/update-status/${id}?status=denied`, { + method: 'PUT', + }); + if (response.ok) { + setRequests(prev => prev.filter(req => req.id !== id)); + } + } catch (error) { + console.error("Failed to deny request:", error); + } }; - const handleMessage = (id: number) => { // TODO: Open message modal or redirect to chat alert("Open chat with sender (not implemented)"); From 23564dd770e8d30481b8655ba077a16879138a25 Mon Sep 17 00:00:00 2001 From: shivansh023023 Date: Wed, 18 Feb 2026 12:24:58 +0530 Subject: [PATCH 3/5] refactor: address CodeRabbit review (security, type-safety, and env vars) --- Backend/app/routes/collaboration.py | 21 ++++++++++++--- .../collaboration-hub/CollabRequests.tsx | 27 +++++++++++++------ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/Backend/app/routes/collaboration.py b/Backend/app/routes/collaboration.py index 926df11..38984ee 100644 --- a/Backend/app/routes/collaboration.py +++ b/Backend/app/routes/collaboration.py @@ -1,18 +1,31 @@ +from typing import Annotated from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import update from ..db.db import get_db -from ..models.models import Collaboration # Verify this name in models/models.py +from ..models.models import Collaboration router = APIRouter(prefix="/collaboration", tags=["Collaboration"]) @router.put("/update-status/{collab_id}") -async def update_collab_status(collab_id: str, status: str, db: AsyncSession = Depends(get_db)): +async def update_collab_status( + collab_id: str, + status: str, + db: Annotated[AsyncSession, Depends(get_db)] +): + """ + Updates the status of a collaboration request. + Includes validation to ensure the record exists. + """ if status not in ["accepted", "denied"]: raise HTTPException(status_code=400, detail="Invalid status") - # This matches the 'status' column in your sql.txt collaborations table query = update(Collaboration).where(Collaboration.id == collab_id).values(status=status) - await db.execute(query) + result = await db.execute(query) await db.commit() + + # Major fix: Check if the row actually existed + if result.rowcount == 0: + raise HTTPException(status_code=404, detail=f"Collaboration {collab_id} not found") + return {"message": f"Collaboration {status} successfully"} \ No newline at end of file diff --git a/Frontend/src/components/collaboration-hub/CollabRequests.tsx b/Frontend/src/components/collaboration-hub/CollabRequests.tsx index d539960..60b4bfb 100644 --- a/Frontend/src/components/collaboration-hub/CollabRequests.tsx +++ b/Frontend/src/components/collaboration-hub/CollabRequests.tsx @@ -9,7 +9,7 @@ import { MessageSquare, CheckCircle, XCircle, Lightbulb, TrendingUp, Users, Star // Mock data for incoming requests const mockRequests = [ { - id: 1, + id: "1", // Updated to string for UUID compatibility sender: { name: "TechSavvy", avatar: "https://randomuser.me/api/portraits/men/32.jpg", @@ -51,7 +51,7 @@ const mockRequests = [ } }, { - id: 2, + id: "2", // Updated to string for UUID compatibility sender: { name: "EcoChic", avatar: "https://randomuser.me/api/portraits/women/44.jpg", @@ -97,32 +97,43 @@ const mockRequests = [ const CollabRequests: React.FC = () => { const [requests, setRequests] = useState(mockRequests); -const handleAccept = async (id: number) => { + // Use environment variable for the API base URL + const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000"; + + const handleAccept = async (id: string) => { try { - const response = await fetch(`http://localhost:8000/collaboration/update-status/${id}?status=accepted`, { + const response = await fetch(`${BASE_URL}/collaboration/update-status/${id}?status=accepted`, { method: 'PUT', }); if (response.ok) { setRequests(prev => prev.filter(req => req.id !== id)); + } else { + // Feedback for non-OK responses + const err = await response.json().catch(() => ({})); + alert(`Failed to accept: ${err.detail || "Server error"}`); } } catch (error) { console.error("Failed to accept request:", error); } }; - const handleDeny = async (id: number) => { + const handleDeny = async (id: string) => { try { - const response = await fetch(`http://localhost:8000/collaboration/update-status/${id}?status=denied`, { + const response = await fetch(`${BASE_URL}/collaboration/update-status/${id}?status=denied`, { method: 'PUT', }); if (response.ok) { setRequests(prev => prev.filter(req => req.id !== id)); + } else { + // Feedback for non-OK responses + alert("Failed to deny request. Please try again."); } } catch (error) { console.error("Failed to deny request:", error); } }; - const handleMessage = (id: number) => { + + const handleMessage = (id: string) => { // TODO: Open message modal or redirect to chat alert("Open chat with sender (not implemented)"); }; @@ -219,4 +230,4 @@ const handleAccept = async (id: number) => { ); }; -export default CollabRequests; \ No newline at end of file +export default CollabRequests; \ No newline at end of file From 10453f0e2ee0abc0ed11e3a184070e65a0b58828 Mon Sep 17 00:00:00 2001 From: shivansh023023 Date: Wed, 18 Feb 2026 12:43:03 +0530 Subject: [PATCH 4/5] refactor: optimize architecture and UX based on peer review --- .../collaboration-hub/CollabRequests.tsx | 52 +++++++++++++------ 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/Frontend/src/components/collaboration-hub/CollabRequests.tsx b/Frontend/src/components/collaboration-hub/CollabRequests.tsx index 60b4bfb..f440b80 100644 --- a/Frontend/src/components/collaboration-hub/CollabRequests.tsx +++ b/Frontend/src/components/collaboration-hub/CollabRequests.tsx @@ -6,10 +6,13 @@ import { Badge } from "../ui/badge"; import { Separator } from "../ui/separator"; import { MessageSquare, CheckCircle, XCircle, Lightbulb, TrendingUp, Users, Star, Mail } from "lucide-react"; -// Mock data for incoming requests +// 1. Move BASE_URL to module scope to avoid evaluation on every render +const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000"; + +// Mock data for incoming requests with string IDs for UUID compatibility const mockRequests = [ { - id: "1", // Updated to string for UUID compatibility + id: "1", sender: { name: "TechSavvy", avatar: "https://randomuser.me/api/portraits/men/32.jpg", @@ -51,7 +54,7 @@ const mockRequests = [ } }, { - id: "2", // Updated to string for UUID compatibility + id: "2", sender: { name: "EcoChic", avatar: "https://randomuser.me/api/portraits/women/44.jpg", @@ -96,11 +99,12 @@ const mockRequests = [ const CollabRequests: React.FC = () => { const [requests, setRequests] = useState(mockRequests); - - // Use environment variable for the API base URL - const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000"; + // 2. Add loadingId state to track and disable in-flight requests + const [loadingId, setLoadingId] = useState(null); const handleAccept = async (id: string) => { + if (loadingId) return; // Prevent concurrent requests + setLoadingId(id); try { const response = await fetch(`${BASE_URL}/collaboration/update-status/${id}?status=accepted`, { method: 'PUT', @@ -108,16 +112,19 @@ const CollabRequests: React.FC = () => { if (response.ok) { setRequests(prev => prev.filter(req => req.id !== id)); } else { - // Feedback for non-OK responses const err = await response.json().catch(() => ({})); alert(`Failed to accept: ${err.detail || "Server error"}`); } } catch (error) { console.error("Failed to accept request:", error); + } finally { + setLoadingId(null); } }; const handleDeny = async (id: string) => { + if (loadingId) return; // Prevent concurrent requests + setLoadingId(id); try { const response = await fetch(`${BASE_URL}/collaboration/update-status/${id}?status=denied`, { method: 'PUT', @@ -125,15 +132,19 @@ const CollabRequests: React.FC = () => { if (response.ok) { setRequests(prev => prev.filter(req => req.id !== id)); } else { - // Feedback for non-OK responses - alert("Failed to deny request. Please try again."); + // 3. Extract real server error instead of hardcoded string + const err = await response.json().catch(() => ({})); + alert(`Failed to deny: ${err.detail || "Server error"}`); } } catch (error) { console.error("Failed to deny request:", error); + } finally { + setLoadingId(null); } }; - const handleMessage = (id: string) => { + // 4. Mark unused parameter with underscore to silence linter + const handleMessage = (_id: string) => { // TODO: Open message modal or redirect to chat alert("Open chat with sender (not implemented)"); }; @@ -166,7 +177,6 @@ const CollabRequests: React.FC = () => {
Request: {req.summary}
- {/* Initial Collaboration Proposal Section */}
📝 Initial Collaboration Proposal @@ -180,21 +190,18 @@ const CollabRequests: React.FC = () => {
- {/* AI Advantages */}
Advantages
    {req.ai.advantages.map((adv, i) =>
  • {adv}
  • )}
- {/* AI Ideas */}
Collaboration Ideas
    {req.ai.ideas.map((idea, i) =>
  • {idea}
  • )}
- {/* AI Recommendations */}
Recommendations
    @@ -209,10 +216,23 @@ const CollabRequests: React.FC = () => { Avg Views: {req.stats.avgViews}
- - -