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..38984ee --- /dev/null +++ b/Backend/app/routes/collaboration.py @@ -0,0 +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 + +router = APIRouter(prefix="/collaboration", tags=["Collaboration"]) + +@router.put("/update-status/{collab_id}") +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") + + query = update(Collaboration).where(Collaboration.id == collab_id).values(status=status) + 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/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 diff --git a/Frontend/src/components/collaboration-hub/CollabRequests.tsx b/Frontend/src/components/collaboration-hub/CollabRequests.tsx index 57cfb2c..2908e3e 100644 --- a/Frontend/src/components/collaboration-hub/CollabRequests.tsx +++ b/Frontend/src/components/collaboration-hub/CollabRequests.tsx @@ -6,10 +6,11 @@ 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 +const BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000"; + const mockRequests = [ { - id: 1, + id: "1", sender: { name: "TechSavvy", avatar: "https://randomuser.me/api/portraits/men/32.jpg", @@ -51,7 +52,7 @@ const mockRequests = [ } }, { - id: 2, + id: "2", sender: { name: "EcoChic", avatar: "https://randomuser.me/api/portraits/women/44.jpg", @@ -96,22 +97,48 @@ const mockRequests = [ const CollabRequests: React.FC = () => { const [requests, setRequests] = useState(mockRequests); + // Switch to Set to track multiple in-flight requests + const [loadingIds, setLoadingIds] = useState>(new Set()); - const handleAccept = (id: number) => { - setRequests(prev => prev.filter(req => req.id !== id)); - // TODO: Integrate with backend to accept request + // Shared helper to keep code DRY + const handleStatusUpdate = async (id: string, status: 'accepted' | 'denied', verb: string) => { + if (loadingIds.has(id)) return; + + setLoadingIds(prev => new Set(prev).add(id)); + try { + const response = await fetch(`${BASE_URL}/collaboration/update-status/${id}?status=${status}`, { + method: 'PUT', + }); + if (response.ok) { + setRequests(prev => prev.filter(req => req.id !== id)); + } else { + const err = await response.json().catch(() => ({})); + alert(`Failed to ${verb}: ${err.detail || "Server error"}`); + } + } catch (error) { + console.error(`Failed to ${verb} request:`, error); + alert(`Network error: Could not ${verb} request. Please check your connection.`); + } finally { + setLoadingIds(prev => { + const next = new Set(prev); + next.delete(id); + return next; + }); + } }; - const handleDeny = (id: number) => { - setRequests(prev => prev.filter(req => req.id !== id)); - // TODO: Integrate with backend to deny request - }; + const handleAccept = (id: string) => handleStatusUpdate(id, 'accepted', 'accept'); + const handleDeny = (id: string) => handleStatusUpdate(id, 'denied', 'deny'); - const handleMessage = (id: number) => { - // TODO: Open message modal or redirect to chat + const handleMessage = (_id: string) => { alert("Open chat with sender (not implemented)"); }; + // Wired up the Email button + const handleEmail = (_id: string) => { + alert("Email functionality is coming soon!"); + }; + return (
{requests.length === 0 ? ( @@ -140,7 +167,6 @@ const CollabRequests: React.FC = () => {
Request: {req.summary}
- {/* Initial Collaboration Proposal Section */}
📝 Initial Collaboration Proposal @@ -154,21 +180,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
    @@ -183,16 +206,28 @@ const CollabRequests: React.FC = () => { Avg Views: {req.stats.avgViews}
- - -
@@ -204,4 +239,4 @@ const CollabRequests: React.FC = () => { ); }; -export default CollabRequests; \ No newline at end of file +export default CollabRequests; \ No newline at end of file