From 012465760f69a0e3e5aab34ebfe7fb3512c994df Mon Sep 17 00:00:00 2001 From: Saahi30 Date: Sat, 12 Jul 2025 05:14:20 +0530 Subject: [PATCH 1/8] feat(collaboration-hub): implement AI matches and general collaboration dashboard UI (frontend) --- .../collaboration-hub/ConnectModal.tsx | 113 +++++++ .../collaboration-hub/CreatorMatchCard.tsx | 116 +++++++ .../collaboration-hub/CreatorMatchGrid.tsx | 46 +++ .../collaboration-hub/ViewProfileModal.tsx | 84 +++++ .../collaboration-hub/mockProfileData.ts | 59 ++++ .../dashboard/creator-collaborations.tsx | 55 ++++ Frontend/src/pages/Collaborations.tsx | 288 +++++------------- Frontend/src/pages/DashboardPage.tsx | 18 +- 8 files changed, 557 insertions(+), 222 deletions(-) create mode 100644 Frontend/src/components/collaboration-hub/ConnectModal.tsx create mode 100644 Frontend/src/components/collaboration-hub/CreatorMatchCard.tsx create mode 100644 Frontend/src/components/collaboration-hub/CreatorMatchGrid.tsx create mode 100644 Frontend/src/components/collaboration-hub/ViewProfileModal.tsx create mode 100644 Frontend/src/components/collaboration-hub/mockProfileData.ts diff --git a/Frontend/src/components/collaboration-hub/ConnectModal.tsx b/Frontend/src/components/collaboration-hub/ConnectModal.tsx new file mode 100644 index 0000000..8891ca6 --- /dev/null +++ b/Frontend/src/components/collaboration-hub/ConnectModal.tsx @@ -0,0 +1,113 @@ +import React, { useState } from "react"; +import { mockProfileDetails, mockCollabIdeas, mockRequestTexts, mockWhyMatch } from "./mockProfileData"; +import { Button } from "../ui/button"; +import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; +import { Badge } from "../ui/badge"; + +interface WhyMatchReason { + point: string; + description: string; +} + +interface ConnectModalProps { + open: boolean; + onClose: () => void; + onSend: (selectedText: string) => void; + matchPercentage?: number; + whyMatch?: WhyMatchReason[]; +} + +const defaultMatch = 98; +const IDEAS_PER_PAGE = 3; + +const ConnectModal: React.FC = ({ open, onClose, onSend, matchPercentage = defaultMatch, whyMatch = mockWhyMatch }) => { + const [ideasPage, setIdeasPage] = useState(0); + const [selectedText, setSelectedText] = useState(mockRequestTexts[0]); + if (!open) return null; + const profile = mockProfileDetails; + const totalIdeas = mockCollabIdeas.length; + const startIdx = (ideasPage * IDEAS_PER_PAGE) % totalIdeas; + const ideasToShow = Array.from({ length: IDEAS_PER_PAGE }, (_, i) => mockCollabIdeas[(startIdx + i) % totalIdeas]); + + const handleNextIdeas = () => { + setIdeasPage((prev) => prev + 1); + }; + + return ( +
+
+ + {/* Left: Profile Info */} +
+ + {matchPercentage}% Match + + + + {profile.name.slice(0,2).toUpperCase()} + +

Connect with {profile.name}

+
{profile.contentType}
+
+
Why you match
+
    + {whyMatch.map((reason, idx) => ( +
  • +
    {reason.point}
    +
    {reason.description}
    +
  • + ))} +
+
+
+ {/* Right: Ideas and Messages */} +
+
+
AI-Generated Collaboration Ideas
+
    + {ideasToShow.map((idea, idx) => ( +
  • +
    {idea.title}
    +
    {idea.description}
    +
  • + ))} +
+ {totalIdeas > IDEAS_PER_PAGE && ( +
+ +
+ )} +
+
+
Select a message to send
+
+ {mockRequestTexts.map((text, idx) => ( + + ))} +
+
+
+ + +
+
+
+
+ ); +}; + +export default ConnectModal; \ No newline at end of file diff --git a/Frontend/src/components/collaboration-hub/CreatorMatchCard.tsx b/Frontend/src/components/collaboration-hub/CreatorMatchCard.tsx new file mode 100644 index 0000000..f2d55c9 --- /dev/null +++ b/Frontend/src/components/collaboration-hub/CreatorMatchCard.tsx @@ -0,0 +1,116 @@ +import React, { useState } from "react"; +import { Button } from "../ui/button"; +import { Badge } from "../ui/badge"; +import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; +import ViewProfileModal from "./ViewProfileModal"; +import ConnectModal from "./ConnectModal"; + +export interface CreatorMatchCardProps { + name: string; + avatar: string; + contentType: string; + matchPercentage: number; + audienceMatch: string; + followers: string; + engagement: string; + content: string; + collabs: number; + whyMatch: string[]; +} + +const getAudienceMatchColor = (level: string) => { + switch (level) { + case "Very High": + return "bg-green-500"; + case "High": + return "bg-yellow-400"; + case "Good": + return "bg-blue-400"; + default: + return "bg-gray-300"; + } +}; + +export const CreatorMatchCard: React.FC = ({ + name, + avatar, + contentType, + matchPercentage, + audienceMatch, + followers, + engagement, + content, + collabs, + whyMatch, +}) => { + const [showProfile, setShowProfile] = useState(false); + const [showConnect, setShowConnect] = useState(false); + const [requestSent, setRequestSent] = useState(false); + + const handleSendRequest = () => { + setShowConnect(false); + setRequestSent(true); + setTimeout(() => setRequestSent(false), 2000); + }; + + return ( + <> +
+ + {matchPercentage}% Match + +
+ + + {name.slice(0,2).toUpperCase()} + +

{name}

+
{contentType}
+
+
+ Audience Match + {audienceMatch} +
+
+
+
+
+
Followers
{followers}
+
Engagement
{engagement}
+
Content
{content}
+
Collabs
{collabs} completed
+
+
+
Why you match
+
    + {whyMatch.map((reason, idx) => ( +
  • {reason}
  • + ))} +
+
+
+ + +
+ {requestSent && ( +
Request Sent!
+ )} +
+ setShowProfile(false)} + onConnect={() => { + setShowProfile(false); + setTimeout(() => setShowConnect(true), 200); + }} + /> + setShowConnect(false)} + onSend={handleSendRequest} + /> + + ); +}; + +export default CreatorMatchCard; \ No newline at end of file diff --git a/Frontend/src/components/collaboration-hub/CreatorMatchGrid.tsx b/Frontend/src/components/collaboration-hub/CreatorMatchGrid.tsx new file mode 100644 index 0000000..1472763 --- /dev/null +++ b/Frontend/src/components/collaboration-hub/CreatorMatchGrid.tsx @@ -0,0 +1,46 @@ +import React, { useState } from "react"; +import CreatorMatchCard, { CreatorMatchCardProps } from "./CreatorMatchCard"; + +interface CreatorMatchGridProps { + creators: CreatorMatchCardProps[]; +} + +const PAGE_SIZE = 4; + +const CreatorMatchGrid: React.FC = ({ creators }) => { + const [page, setPage] = useState(0); + const totalPages = Math.ceil(creators.length / PAGE_SIZE); + + const startIdx = page * PAGE_SIZE; + const endIdx = startIdx + PAGE_SIZE; + const currentCreators = creators.slice(startIdx, endIdx); + + return ( +
+
+ {currentCreators.map((creator, idx) => ( + + ))} +
+
+ + Page {page + 1} of {totalPages} + +
+
+ ); +}; + +export default CreatorMatchGrid; \ No newline at end of file diff --git a/Frontend/src/components/collaboration-hub/ViewProfileModal.tsx b/Frontend/src/components/collaboration-hub/ViewProfileModal.tsx new file mode 100644 index 0000000..138b242 --- /dev/null +++ b/Frontend/src/components/collaboration-hub/ViewProfileModal.tsx @@ -0,0 +1,84 @@ +import React from "react"; +import { mockProfileDetails, mockWhyMatch } from "./mockProfileData"; +import { Button } from "../ui/button"; +import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; +import { Badge } from "../ui/badge"; + +interface WhyMatchReason { + point: string; + description: string; +} + +const defaultMatch = 98; + +interface ViewProfileModalProps { + open: boolean; + onClose: () => void; + onConnect: () => void; + matchPercentage?: number; + whyMatch?: WhyMatchReason[]; +} + +const ViewProfileModal: React.FC = ({ open, onClose, onConnect, matchPercentage = defaultMatch, whyMatch = mockWhyMatch }) => { + if (!open) return null; + const profile = mockProfileDetails; + return ( +
+
+ +
+ + {matchPercentage}% Match + + + + {profile.name.slice(0,2).toUpperCase()} + +

{profile.name}

+
{profile.contentType} • {profile.location}
+
+
{profile.bio}
+ +
+
Followers
{profile.followers}
+
Engagement
{profile.engagement}
+
Content
{profile.content}
+
Collabs
{profile.collabs} completed
+
+
+
Why you match
+
    + {whyMatch.map((reason, idx) => ( +
  • +
    {reason.point}
    +
    {reason.description}
    +
  • + ))} +
+
+ +
+
+ ); +}; + +export default ViewProfileModal; \ No newline at end of file diff --git a/Frontend/src/components/collaboration-hub/mockProfileData.ts b/Frontend/src/components/collaboration-hub/mockProfileData.ts new file mode 100644 index 0000000..47212dd --- /dev/null +++ b/Frontend/src/components/collaboration-hub/mockProfileData.ts @@ -0,0 +1,59 @@ +// MOCK DATA: This will be replaced by functioning backend/AI logic later on + +export const mockProfileDetails = { + id: 1, + name: "TechReviewer", + avatar: "/placeholder.svg?height=96&width=96", + contentType: "Tech Reviews & Tutorials", + location: "San Francisco, CA", + bio: "Passionate about the latest in tech. I review gadgets, apps, and everything in between. Always looking for new collab opportunities!", + followers: "1.2M", + engagement: "4.8%", + content: "Tech Reviews", + collabs: 12, + socialLinks: [ + { platform: "Instagram", url: "https://instagram.com/techreviewer", icon: "instagram" }, + { platform: "YouTube", url: "https://youtube.com/techreviewer", icon: "youtube" }, + { platform: "Twitter", url: "https://twitter.com/techreviewer", icon: "twitter" } + ] +}; + +export const mockCollabIdeas = [ + { + title: "Gadget Showdown", + description: "Compare the latest gadgets in a head-to-head review with unique perspectives from both creators." + }, + { + title: "Tech for Good", + description: "Collaborate on a series highlighting technology that makes a positive impact on society." + }, + { + title: "Unboxing Marathon", + description: "Host a live unboxing event featuring products from both creators' favorite brands." + }, + { + title: "Ask the Experts", + description: "A Q&A session where both creators answer audience tech questions together." + } +]; + +export const mockRequestTexts = [ + "Hi! I love your content. Would you be interested in collaborating on a tech review series?", + "Hey! I think our audiences would both enjoy a joint unboxing event. Let me know if you're interested!", + "Hello! I have some ideas for a tech-for-good collaboration. Would you like to connect and discuss?" +]; + +export const mockWhyMatch = [ + { + point: "Complementary content styles", + description: "Your focus on in-depth reviews complements their quick unboxing and first impressions, offering audiences a full spectrum of tech insights." + }, + { + point: "85% audience demographic overlap", + description: "Both of your audiences are primarily tech enthusiasts aged 18-35, ensuring high engagement and relevance for collaborative content." + }, + { + point: "Similar engagement patterns", + description: "Both channels see peak engagement during product launch weeks, making joint campaigns more impactful." + } +]; \ No newline at end of file diff --git a/Frontend/src/components/dashboard/creator-collaborations.tsx b/Frontend/src/components/dashboard/creator-collaborations.tsx index f002c02..f63ecc5 100644 --- a/Frontend/src/components/dashboard/creator-collaborations.tsx +++ b/Frontend/src/components/dashboard/creator-collaborations.tsx @@ -1,5 +1,60 @@ import React from 'react' +// MOCK DATA: This will be replaced by functioning backend logic later on +export const mockCreatorMatches = [ + { + id: 1, + name: "TechReviewer", + avatar: "/placeholder.svg?height=96&width=96", + contentType: "Tech Reviews & Tutorials", + matchPercentage: 98, + audienceMatch: "Very High", + followers: "1.2M", + engagement: "4.8%", + content: "Tech Reviews", + collabs: 12, + whyMatch: [ + "Complementary content styles", + "85% audience demographic overlap", + "Similar engagement patterns" + ] + }, + { + id: 2, + name: "GadgetGuru", + avatar: "/placeholder.svg?height=96&width=96", + contentType: "Unboxing & First Impressions", + matchPercentage: 92, + audienceMatch: "High", + followers: "850K", + engagement: "5.2%", + content: "Unboxing", + collabs: 8, + whyMatch: [ + "Your reviews + their unboxings = perfect combo", + "78% audience demographic overlap", + "Different posting schedules (opportunity)" + ] + }, + { + id: 3, + name: "TechTalker", + avatar: "/placeholder.svg?height=96&width=96", + contentType: "Tech News & Commentary", + matchPercentage: 87, + audienceMatch: "Good", + followers: "1.5M", + engagement: "3.9%", + content: "Tech News", + collabs: 15, + whyMatch: [ + "Their news + your reviews = full coverage", + "65% audience demographic overlap", + "Complementary content calendars" + ] + } +]; + function CreatorCollaborations() { return (
creator-collaborations
diff --git a/Frontend/src/pages/Collaborations.tsx b/Frontend/src/pages/Collaborations.tsx index fed0781..3d552a8 100644 --- a/Frontend/src/pages/Collaborations.tsx +++ b/Frontend/src/pages/Collaborations.tsx @@ -10,65 +10,62 @@ import { Badge } from "../components/ui/badge" import { Tabs, TabsContent, TabsList, TabsTrigger } from "../components/ui/tabs" import { Label } from "../components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../components/ui/select" +import CreatorMatchGrid from "../components/collaboration-hub/CreatorMatchGrid"; +import { mockCreatorMatches } from "../components/dashboard/creator-collaborations"; +import ActiveCollabsGrid from "../components/collaboration-hub/ActiveCollabsGrid"; +import React from "react"; -export default function CollaborationsPage() { +export default function CollaborationsPage({ showHeader = true }: { showHeader?: boolean }) { return (
-
-
- - - Inpact - -
- {[ - { to: "/dashboard", icon: LayoutDashboard, label: "Dashboard" }, - { to: "/dashboard/sponsorships", icon: Briefcase, label: "Sponsorships" }, - { to: "/dashboard/collaborations", icon: Users, label: "Collaborations" }, - { to: "/dashboard/contracts", icon: FileText, label: "Contracts" }, - { to: "/dashboard/analytics", icon: BarChart3, label: "Analytics" }, - { to: "/dashboard/messages", icon: MessageSquare, label: "Messages" }, - ].map(({ to, icon: Icon, label }) => ( - - ))} -
-
-
- - -
- - -
-
-
-
-
-

Creator Collaboration Hub

-
- + {showHeader && ( +
+
+ + + Inpact + +
+ {[ + { to: "/dashboard", icon: LayoutDashboard, label: "Dashboard" }, + { to: "/dashboard/sponsorships", icon: Briefcase, label: "Sponsorships" }, + { to: "/dashboard/collaborations", icon: Users, label: "Collaborations" }, + { to: "/dashboard/contracts", icon: FileText, label: "Contracts" }, + { to: "/dashboard/analytics", icon: BarChart3, label: "Analytics" }, + { to: "/dashboard/messages", icon: MessageSquare, label: "Messages" }, + ].map(({ to, icon: Icon, label }) => ( + + ))} +
+
+
+ + +
+ + +
-
- -
+ + )} +
+
+ {/* Filter Sidebar */} Filters @@ -144,164 +141,41 @@ export default function CollaborationsPage() { - -
+ {/* Main Content */} +
+ {/* Tabs for AI Matches, Active Collabs, Requests */} - + AI Matches Active Collabs Requests - - - -
- - - SJ - -
-
-
-

Sarah Johnson

-
- Travel - Lifestyle -
-
- 95% Match -
-

- Travel content creator specializing in sustainable tourism and local experiences. Looking to - collaborate with creators who share similar values and can bring complementary perspectives. -

-
-
-

Audience

-

350K followers

-
-
-

Engagement

-

4.8%

-
-
-

Location

-

Los Angeles, CA

-
-
-

Audience Overlap

-

32%

-
-
-
- - - -
-
+ + {/* Banner */} +
+
+
+ + AI-Powered Creator Matching
- - - - - -
- - - SJ - -
-
-
-

Sarah Johnson

-
- Travel - Lifestyle -
-
- 95% Match -
-

- Travel content creator specializing in sustainable tourism and local experiences. Looking to - collaborate with creators who share similar values and can bring complementary perspectives. -

-
-
-

Audience

-

350K followers

-
-
-

Engagement

-

4.8%

-
-
-

Location

-

Los Angeles, CA

-
-
-

Audience Overlap

-

32%

-
-
-
- - - -
-
-
-
-
- - - -
- - - SJ - -
-
-
-

Sarah Johnson

-
- Travel - Lifestyle -
-
- 95% Match -
-

- Travel content creator specializing in sustainable tourism and local experiences. Looking to - collaborate with creators who share similar values and can bring complementary perspectives. -

-
-
-

Audience

-

350K followers

-
-
-

Engagement

-

4.8%

-
-
-

Location

-

Los Angeles, CA

-
-
-

Audience Overlap

-

32%

-
-
-
- - - -
-
-
-
-
+
Our AI analyzes your content style, audience demographics, and engagement patterns to find your ideal collaborators.
+
+ +
+ {/* Creator Match Grid with Pagination */} +
+ +
+ {/* View More Recommendations Button */} +
+ +
+
+ + + + +
No collaboration requests at this time.
diff --git a/Frontend/src/pages/DashboardPage.tsx b/Frontend/src/pages/DashboardPage.tsx index fba9c86..e5a8fc2 100644 --- a/Frontend/src/pages/DashboardPage.tsx +++ b/Frontend/src/pages/DashboardPage.tsx @@ -22,6 +22,7 @@ import { PerformanceMetrics } from "../components/dashboard/performance-metrics" import { RecentActivity } from "../components/dashboard/recent-activity" import { SponsorshipMatches } from "../components/dashboard/sponsorship-matches" import { useAuth } from "../context/AuthContext" +import CollaborationsPage from "./Collaborations"; export default function DashboardPage() { const {logout, user} = useAuth(); @@ -189,7 +190,7 @@ export default function DashboardPage() { Creators with complementary audiences - {/* */} +
@@ -211,20 +212,7 @@ export default function DashboardPage() { - - - Creator Collaboration Hub - Find and partner with creators who have complementary audiences - - -
-

Coming Soon

-

- The full creator collaboration interface will be available here. -

-
-
-
+
From f1f63d38899d68205713ccb809f1a07567ba9c2c Mon Sep 17 00:00:00 2001 From: Saahi30 Date: Sat, 12 Jul 2025 05:14:38 +0530 Subject: [PATCH 2/8] feat(active-collabs): add UI for active collaborations with progress bars, timeline, and latest update (frontend) --- .../collaboration-hub/ActiveCollabCard.tsx | 118 ++++++++++++++++++ .../collaboration-hub/ActiveCollabsGrid.tsx | 69 ++++++++++ .../activeCollabsMockData.ts | 52 ++++++++ 3 files changed, 239 insertions(+) create mode 100644 Frontend/src/components/collaboration-hub/ActiveCollabCard.tsx create mode 100644 Frontend/src/components/collaboration-hub/ActiveCollabsGrid.tsx create mode 100644 Frontend/src/components/collaboration-hub/activeCollabsMockData.ts diff --git a/Frontend/src/components/collaboration-hub/ActiveCollabCard.tsx b/Frontend/src/components/collaboration-hub/ActiveCollabCard.tsx new file mode 100644 index 0000000..8ce5a47 --- /dev/null +++ b/Frontend/src/components/collaboration-hub/ActiveCollabCard.tsx @@ -0,0 +1,118 @@ +import React from "react"; +import { Button } from "../ui/button"; +import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; + +export interface ActiveCollabCardProps { + collaborator: { + name: string; + avatar: string; + contentType: string; + }; + collabTitle: string; + status: string; + startDate: string; + dueDate: string; + messages: number; + deliverables: { completed: number; total: number }; + lastActivity: string; + latestUpdate: string; +} + +const statusColors: Record = { + "In Progress": "bg-blue-100 text-blue-700", + "Awaiting Response": "bg-yellow-100 text-yellow-700", + "Completed": "bg-green-100 text-green-700" +}; + +function getDaysBetween(start: string, end: string) { + const s = new Date(start); + const e = new Date(end); + return Math.ceil((e.getTime() - s.getTime()) / (1000 * 60 * 60 * 24)); +} + +function getDaysLeft(due: string) { + const now = new Date(); + const d = new Date(due); + return Math.ceil((d.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); +} + +function getTimelineProgress(start: string, due: string) { + const total = getDaysBetween(start, due); + const elapsed = getDaysBetween(start, new Date().toISOString().slice(0, 10)); + return Math.min(100, Math.max(0, Math.round((elapsed / total) * 100))); +} + +const ActiveCollabCard: React.FC = ({ + collaborator, + collabTitle, + status, + startDate, + dueDate, + messages, + deliverables, + lastActivity, + latestUpdate +}) => { + const deliverableProgress = Math.round((deliverables.completed / deliverables.total) * 100); + const timelineProgress = getTimelineProgress(startDate, dueDate); + const daysLeft = getDaysLeft(dueDate); + const overdue = daysLeft < 0 && status !== "Completed"; + + return ( +
+
+ + + {collaborator.name.slice(0,2).toUpperCase()} + +
+
{collaborator.name}
+
{collaborator.contentType}
+
+ {status} +
+
+ Collab: {collabTitle} + Start: {startDate} + Due: {dueDate} + {overdue ? `Overdue by ${Math.abs(daysLeft)} days` : daysLeft === 0 ? "Due today" : `${daysLeft} days left`} +
+ {/* Timeline Progress Bar */} +
+
+ Timeline + {timelineProgress}% +
+
+
+
+
+ {/* Deliverables Progress Bar */} +
+
+ Deliverables + {deliverables.completed}/{deliverables.total} ({deliverableProgress}%) +
+
+
+
+
+
+ Messages: {messages} + Last activity: {lastActivity} +
+
+ Latest update: {latestUpdate} +
+
+ + + {status !== "Completed" && ( + + )} +
+
+ ); +}; + +export default ActiveCollabCard; \ No newline at end of file diff --git a/Frontend/src/components/collaboration-hub/ActiveCollabsGrid.tsx b/Frontend/src/components/collaboration-hub/ActiveCollabsGrid.tsx new file mode 100644 index 0000000..cf9e302 --- /dev/null +++ b/Frontend/src/components/collaboration-hub/ActiveCollabsGrid.tsx @@ -0,0 +1,69 @@ +import React, { useState } from "react"; +import { activeCollabsMock } from "./activeCollabsMockData"; +import ActiveCollabCard from "./ActiveCollabCard"; + +const statusOptions = ["All", "In Progress", "Completed"]; +const sortOptions = ["Start Date", "Due Date", "Name"]; + +const ActiveCollabsGrid: React.FC = () => { + const [statusFilter, setStatusFilter] = useState("All"); + const [sortBy, setSortBy] = useState("Start Date"); + + // Only show In Progress and Completed + let filtered = activeCollabsMock.filter(c => c.status !== "Awaiting Response"); + if (statusFilter !== "All") { + filtered = filtered.filter(c => c.status === statusFilter); + } + if (sortBy === "Start Date") { + filtered = [...filtered].sort((a, b) => a.startDate.localeCompare(b.startDate)); + } else if (sortBy === "Due Date") { + filtered = [...filtered].sort((a, b) => a.dueDate.localeCompare(b.dueDate)); + } else if (sortBy === "Name") { + filtered = [...filtered].sort((a, b) => a.collaborator.name.localeCompare(b.collaborator.name)); + } + + return ( +
+
+
+ Status: + +
+
+ Sort by: + +
+
+ {filtered.length === 0 ? ( +
+
No active collaborations
+
Start a new collaboration to see it here!
+
+ ) : ( +
+ {filtered.map(collab => ( + + ))} +
+ )} +
+ ); +}; + +export default ActiveCollabsGrid; \ No newline at end of file diff --git a/Frontend/src/components/collaboration-hub/activeCollabsMockData.ts b/Frontend/src/components/collaboration-hub/activeCollabsMockData.ts new file mode 100644 index 0000000..95d1b06 --- /dev/null +++ b/Frontend/src/components/collaboration-hub/activeCollabsMockData.ts @@ -0,0 +1,52 @@ +// MOCK DATA: This will be replaced by functioning backend logic later on + +export const activeCollabsMock = [ + { + id: 1, + collaborator: { + name: "GadgetGuru", + avatar: "/placeholder.svg?height=96&width=96", + contentType: "Unboxing & First Impressions" + }, + collabTitle: "Unboxing Marathon", + status: "In Progress", + startDate: "2024-06-01", + dueDate: "2024-06-15", + messages: 12, + deliverables: { completed: 2, total: 3 }, + lastActivity: "2 days ago", + latestUpdate: "Finalizing thumbnail for the main video." + }, + { + id: 2, + collaborator: { + name: "TechTalker", + avatar: "/placeholder.svg?height=96&width=96", + contentType: "Tech News & Commentary" + }, + collabTitle: "Tech for Good", + status: "Awaiting Response", + startDate: "2024-06-05", + dueDate: "2024-06-20", + messages: 5, + deliverables: { completed: 0, total: 2 }, + lastActivity: "5 days ago", + latestUpdate: "Waiting for approval on the script." + }, + { + id: 3, + collaborator: { + name: "StyleStar", + avatar: "/placeholder.svg?height=96&width=96", + contentType: "Fashion & Lifestyle" + }, + collabTitle: "Style Swap Challenge", + status: "Completed", + startDate: "2024-05-10", + dueDate: "2024-05-25", + messages: 18, + deliverables: { completed: 2, total: 2 }, + lastActivity: "1 day ago", + latestUpdate: "Collab video published and shared on socials." + } +]; \ No newline at end of file From 9efa3ec17d7c385454b72b4416920065958618da Mon Sep 17 00:00:00 2001 From: Saahi30 Date: Sat, 12 Jul 2025 12:43:02 +0530 Subject: [PATCH 3/8] feat: added the ui of the view details view of the active collabs card(frontend only) --- Frontend/src/App.tsx | 9 + .../collaboration-hub/ActiveCollabCard.tsx | 12 +- Frontend/src/pages/CollaborationDetails.tsx | 1259 +++++++++++++++++ 3 files changed, 1279 insertions(+), 1 deletion(-) create mode 100644 Frontend/src/pages/CollaborationDetails.tsx diff --git a/Frontend/src/App.tsx b/Frontend/src/App.tsx index be41d2e..60f7ecd 100644 --- a/Frontend/src/App.tsx +++ b/Frontend/src/App.tsx @@ -4,6 +4,7 @@ import HomePage from "../src/pages/HomePage"; import DashboardPage from "../src/pages/DashboardPage"; import SponsorshipsPage from "../src/pages/Sponsorships"; import CollaborationsPage from "../src/pages/Collaborations"; +import CollaborationDetails from "../src/pages/CollaborationDetails"; import MessagesPage from "../src/pages/Messages"; import LoginPage from "./pages/Login"; import SignupPage from "./pages/Signup"; @@ -100,6 +101,14 @@ function App() { } /> + + + + } + /> = ({ + id, collaborator, collabTitle, status, @@ -53,6 +56,7 @@ const ActiveCollabCard: React.FC = ({ lastActivity, latestUpdate }) => { + const navigate = useNavigate(); const deliverableProgress = Math.round((deliverables.completed / deliverables.total) * 100); const timelineProgress = getTimelineProgress(startDate, dueDate); const daysLeft = getDaysLeft(dueDate); @@ -105,7 +109,13 @@ const ActiveCollabCard: React.FC = ({ Latest update: {latestUpdate}
- + {status !== "Completed" && ( diff --git a/Frontend/src/pages/CollaborationDetails.tsx b/Frontend/src/pages/CollaborationDetails.tsx new file mode 100644 index 0000000..a8dfee6 --- /dev/null +++ b/Frontend/src/pages/CollaborationDetails.tsx @@ -0,0 +1,1259 @@ +import React, { useState, useEffect } from "react"; +import { useParams, useNavigate, Link } from "react-router-dom"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card"; +import { Button } from "../components/ui/button"; +import { Avatar, AvatarFallback, AvatarImage } from "../components/ui/avatar"; +import { Badge } from "../components/ui/badge"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "../components/ui/tabs"; +import { Input } from "../components/ui/input"; +import { Textarea } from "../components/ui/textarea"; +import { Separator } from "../components/ui/separator"; +import { ModeToggle } from "../components/mode-toggle"; +import { UserNav } from "../components/user-nav"; +import { + ArrowLeft, + MessageSquare, + Calendar, + CheckCircle, + Clock, + FileText, + Users, + BarChart3, + Send, + Edit, + Download, + Eye, + MoreHorizontal, + Phone, + Mail, + MapPin, + ExternalLink, + Star, + TrendingUp, + Activity, + LayoutDashboard, + Briefcase, + Search, + Rocket, + X +} from "lucide-react"; +import { activeCollabsMock } from "../components/collaboration-hub/activeCollabsMockData"; + +interface Message { + id: number; + sender: string; + content: string; + timestamp: string; + isOwn: boolean; +} + +interface Deliverable { + id: number; + title: string; + description: string; + status: "pending" | "in-progress" | "completed" | "review"; + dueDate: string; + assignedTo: string; + files?: string[]; +} + +interface Milestone { + id: number; + title: string; + description: string; + dueDate: string; + status: "upcoming" | "in-progress" | "completed"; + progress: number; +} + +const mockMessages: Message[] = [ + { + id: 1, + sender: "GadgetGuru", + content: "Hey! I've started working on the unboxing video. Should have the first draft ready by tomorrow.", + timestamp: "2024-06-10 14:30", + isOwn: false + }, + { + id: 2, + sender: "You", + content: "Perfect! Looking forward to seeing it. Any specific angles you want to focus on?", + timestamp: "2024-06-10 15:45", + isOwn: true + }, + { + id: 3, + sender: "GadgetGuru", + content: "I'm thinking close-ups of the packaging and then a reveal shot. Also planning to include some B-roll of the setup process.", + timestamp: "2024-06-10 16:20", + isOwn: false + } +]; + +const mockDeliverables: Deliverable[] = [ + { + id: 1, + title: "Unboxing Video", + description: "Main unboxing video showcasing the product features", + status: "in-progress", + dueDate: "2024-06-12", + assignedTo: "GadgetGuru", + files: ["unboxing_draft_v1.mp4"] + }, + { + id: 2, + title: "Thumbnail Design", + description: "Eye-catching thumbnail for the video", + status: "completed", + dueDate: "2024-06-10", + assignedTo: "GadgetGuru", + files: ["thumbnail_final.png"] + }, + { + id: 3, + title: "Social Media Posts", + description: "Instagram and TikTok posts promoting the collaboration", + status: "pending", + dueDate: "2024-06-15", + assignedTo: "GadgetGuru" + } +]; + +const mockMilestones: Milestone[] = [ + { + id: 1, + title: "Project Kickoff", + description: "Initial meeting and project setup", + dueDate: "2024-06-01", + status: "completed", + progress: 100 + }, + { + id: 2, + title: "Content Creation", + description: "Video production and editing", + dueDate: "2024-06-12", + status: "in-progress", + progress: 65 + }, + { + id: 3, + title: "Review & Approval", + description: "Content review and final approval", + dueDate: "2024-06-14", + status: "upcoming", + progress: 0 + }, + { + id: 4, + title: "Publication", + description: "Video goes live on all platforms", + dueDate: "2024-06-15", + status: "upcoming", + progress: 0 + } +]; + +export default function CollaborationDetails() { + const { id } = useParams<{ id: string }>(); + const navigate = useNavigate(); + const [newMessage, setNewMessage] = useState(""); + const [activeTab, setActiveTab] = useState("overview"); + const [showContractModal, setShowContractModal] = useState(false); + const [messageStyle, setMessageStyle] = useState("professional"); + const [showStyleOptions, setShowStyleOptions] = useState(false); + const [customStyle, setCustomStyle] = useState(""); + const [isEditingUpdate, setIsEditingUpdate] = useState(false); + const [editedUpdate, setEditedUpdate] = useState(""); + const [showDeliverableModal, setShowDeliverableModal] = useState(false); + const [selectedDeliverable, setSelectedDeliverable] = useState(null); + + // Find the collaboration data + const collaboration = activeCollabsMock.find(collab => collab.id === parseInt(id || "1")); + + if (!collaboration) { + return ( +
+
+

Collaboration Not Found

+

The collaboration you're looking for doesn't exist.

+ +
+
+ ); + } + + const getStatusColor = (status: string) => { + switch (status) { + case "In Progress": return "bg-blue-100 text-blue-700"; + case "Awaiting Response": return "bg-yellow-100 text-yellow-700"; + case "Completed": return "bg-green-100 text-green-700"; + default: return "bg-gray-100 text-gray-700"; + } + }; + + const getDeliverableStatusColor = (status: string) => { + switch (status) { + case "completed": return "bg-green-100 text-green-700"; + case "in-progress": return "bg-blue-100 text-blue-700"; + case "review": return "bg-yellow-100 text-yellow-700"; + case "pending": return "bg-gray-100 text-gray-700"; + default: return "bg-gray-100 text-gray-700"; + } + }; + + const getMilestoneStatusColor = (status: string) => { + switch (status) { + case "completed": return "bg-green-100 text-green-700"; + case "in-progress": return "bg-blue-100 text-blue-700"; + case "upcoming": return "bg-gray-100 text-gray-700"; + default: return "bg-gray-100 text-gray-700"; + } + }; + + const handleSendMessage = () => { + if (newMessage.trim()) { + // In a real app, this would send the message to the backend + console.log("Sending message:", newMessage); + setNewMessage(""); + } + }; + + const handleViewContract = () => { + setShowContractModal(true); + }; + + // Mock contract URL - in a real app, this would come from the collaboration data + const contractUrl = "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf"; + + // Message style options for AI enhancement + const messageStyles = [ + { value: "professional", label: "Professional", description: "Formal and business-like" }, + { value: "casual", label: "Casual", description: "Friendly and relaxed" }, + { value: "polite", label: "Polite", description: "Courteous and respectful" }, + { value: "concise", label: "Concise", description: "Brief and to the point" }, + { value: "enthusiastic", label: "Enthusiastic", description: "Energetic and positive" }, + { value: "constructive", label: "Constructive", description: "Helpful and solution-focused" } + ]; + + /** + * AI Message Style Enhancement Feature(Not implemented yet) + * i am putting this here for future reference for contributors... + * This feature allows users to enhance their message content using AI to match different communication styles. + * + * Requirements for future development: + * 1. Integration with LLM API (OpenAI, Anthropic, etc.) to generate styled messages + * 2. Real-time message transformation as user types or selects style + * 3. Support for custom style descriptions (user-defined tone/approach) + * 4. Context awareness - consider collaboration history and relationship + * 5. Style suggestions based on message content and collaboration stage + * 6. Option to preview changes before applying + * 7. Learning from user preferences and successful communication patterns + * 8. Integration with collaboration analytics to suggest optimal communication timing + * + * Technical considerations: + * - API rate limiting and error handling + * - Caching of common style transformations + * - Privacy and data security for message content + * - Real-time collaboration features (typing indicators, etc.) + */ + const handleStyleChange = (style: string) => { + setMessageStyle(style); + setShowStyleOptions(false); + + // TODO: Implement AI message transformation + // This would call an LLM API to transform the current message + // Example API call structure: + // const transformedMessage = await transformMessageStyle(newMessage, style); + // setNewMessage(transformedMessage); + + console.log(`Applying ${style} style to message:`, newMessage); + }; + + const handleCustomStyle = () => { + if (customStyle.trim()) { + // TODO: Implement custom style transformation + // This would use the custom style description to guide the AI transformation + console.log(`Applying custom style "${customStyle}" to message:`, newMessage); + setCustomStyle(""); + setShowStyleOptions(false); + } + }; + + const handleEditUpdate = () => { + setEditedUpdate(collaboration.latestUpdate); + setIsEditingUpdate(true); + }; + + const handleSaveUpdate = () => { + // TODO: Implement API call to save the updated latest update + // This would update the collaboration's latest update in the backend + console.log("Saving updated latest update:", editedUpdate); + + // For now, we'll just close the edit mode + // In a real app, you would update the collaboration object here + setIsEditingUpdate(false); + }; + + const handleCancelEdit = () => { + setIsEditingUpdate(false); + setEditedUpdate(""); + }; + + const handleViewDeliverable = (deliverable: Deliverable) => { + setSelectedDeliverable(deliverable); + setShowDeliverableModal(true); + }; + + const handleCloseDeliverableModal = () => { + setShowDeliverableModal(false); + setSelectedDeliverable(null); + }; + + return ( +
+ {/* Main Header */} +
+
+ + + Inpact + +
+ {[ + { to: "/dashboard", icon: LayoutDashboard, label: "Dashboard" }, + { to: "/dashboard/sponsorships", icon: Briefcase, label: "Sponsorships" }, + { to: "/dashboard/collaborations", icon: Users, label: "Collaborations" }, + { to: "/dashboard/contracts", icon: FileText, label: "Contracts" }, + { to: "/dashboard/analytics", icon: BarChart3, label: "Analytics" }, + { to: "/dashboard/messages", icon: MessageSquare, label: "Messages" }, + ].map(({ to, icon: Icon, label }) => ( + + ))} +
+
+
+ + +
+ + +
+
+
+ + {/* Page Header */} +
+
+
+
+ +
+

{collaboration.collabTitle}

+

Collaboration with {collaboration.collaborator.name}

+
+
+
+ + {collaboration.status} + + +
+
+
+
+ +
+
+ {/* Main Content */} +
+ + + Overview + Messages + Deliverables + Timeline + + + {/* Overview Tab */} + + {/* Collaboration Summary */} + + + + + Collaboration Summary + + + +
+
+

Project Details

+
+
+ Start Date: + {collaboration.startDate} +
+
+ Due Date: + {collaboration.dueDate} +
+
+ Content Type: + {collaboration.collaborator.contentType} +
+
+
+
+

Progress

+
+
+ Deliverables: + {collaboration.deliverables.completed}/{collaboration.deliverables.total} +
+
+ Messages: + {collaboration.messages} +
+
+ Last Activity: + {collaboration.lastActivity} +
+
+
+
+ + + +
+
+

Latest Update

+ {!isEditingUpdate && ( + + )} +
+ + {isEditingUpdate ? ( +
+