diff --git a/Backend/app/schemas/schema.py b/Backend/app/schemas/schema.py index 7389488..63df1cb 100644 --- a/Backend/app/schemas/schema.py +++ b/Backend/app/schemas/schema.py @@ -1,10 +1,22 @@ -from pydantic import BaseModel -from typing import Optional, Dict +from pydantic import BaseModel, EmailStr +from typing import Optional, Dict, Any, List from datetime import datetime + +class LoginResponse(BaseModel): + message: str + user_id: str + email: EmailStr + name: Optional[str] = None + role: Optional[str] = None + onboarding_completed: bool = False + # The session object from Supabase contains access_token, refresh_token, etc. + session: Optional[Dict[str, Any]] = None +# --- FIX FOR ISSUE #258 END --- + class UserCreate(BaseModel): username: str - email: str + email: EmailStr # Uses EmailStr for better validation role: str profile_image: Optional[str] = None bio: Optional[str] = None @@ -50,4 +62,4 @@ class SponsorshipPaymentCreate(BaseModel): class CollaborationCreate(BaseModel): creator_1_id: str creator_2_id: str - collaboration_details: str + collaboration_details: str \ No newline at end of file diff --git a/Frontend/src/components/ui/EmptyState.tsx b/Frontend/src/components/ui/EmptyState.tsx new file mode 100644 index 0000000..6d532d4 --- /dev/null +++ b/Frontend/src/components/ui/EmptyState.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { LucideIcon } from 'lucide-react'; + +interface EmptyStateProps { + icon?: LucideIcon; + title: string; + description: string; + actionText?: string; + onAction?: () => void; +} + +const EmptyState: React.FC = ({ + icon: Icon, + title, + description, + actionText, + onAction +}) => { + return ( +
+ {Icon && } +

{title}

+

+ {description} +

+ {actionText && onAction && ( + + )} +
+ ); +}; + +export default EmptyState; \ No newline at end of file diff --git a/Frontend/src/components/ui/Skeleton.tsx b/Frontend/src/components/ui/Skeleton.tsx new file mode 100644 index 0000000..63fbc9f --- /dev/null +++ b/Frontend/src/components/ui/Skeleton.tsx @@ -0,0 +1,29 @@ +import React from "react"; + +interface SkeletonProps extends React.HTMLAttributes +{ + className?: string; + variant?: "rect" | "circle"; +} + + +const Skeleton: React.FC = ({ + className, + variant = "rect", + ...props +}) => { + // Base classes for the pulse animation and color + const baseClasses = "animate-pulse bg-gray-200 dark:bg-gray-700"; + + // Shape variants + const variantClasses = variant === "circle" ? "rounded-full" : "rounded-md"; + + return ( +
+ ); +}; + +export default Skeleton; \ No newline at end of file diff --git a/Frontend/src/pages/DashboardPage.tsx b/Frontend/src/pages/DashboardPage.tsx index e5a8fc2..1311079 100644 --- a/Frontend/src/pages/DashboardPage.tsx +++ b/Frontend/src/pages/DashboardPage.tsx @@ -1,3 +1,4 @@ +import React, { useState, useEffect } from "react" import { Link } from "react-router-dom" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card" import { Tabs, TabsContent, TabsList, TabsTrigger } from "../components/ui/tabs" @@ -10,184 +11,229 @@ import { Briefcase, DollarSign, FileText, - Icon, LayoutDashboard, LogOut, MessageSquare, Rocket, Search, Users, + PlusCircle, + TrendingUp, } from "lucide-react" 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"; +import CollaborationsPage from "./Collaborations" + +import { useNavigate } from "react-router-dom" + +// New UX Component Imports +import Skeleton from "../components/ui/Skeleton" +import EmptyState from "../components/ui/EmptyState" export default function DashboardPage() { - const {logout, user} = useAuth(); + const { logout, user } = useAuth() + const [isLoading, setIsLoading] = useState(true) + const [metrics, setMetrics] = useState(null) + const navigate = useNavigate() + + + + useEffect(() => { + const fetchDashboardData = async () => { + try { + setIsLoading(true) + + const response = await fetch('/api/dashboard/metrics') + const data = await response.json() + setMetrics(data) + + setIsLoading(false) + } catch (error) { + console.error('Failed to fetch dashboard data:', error) + setIsLoading(false) + } + } + fetchDashboardData() + }, []) + + 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 }) => ( - - ))} -
-
-
- - -
- - - -
-
+ {[ + { 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 }) => ( + + ))} +
+
+
+ + +
+ + + +
+
+

Dashboard

-
+ - - - Overview - - - Sponsorships - - - Collaborations - - - Analytics - + + Overview + Sponsorships + Collaborations + Analytics + + {/* --- METRICS SECTION WITH SKELETONS --- */}
- - - Total Revenue - - - -
$45,231.89
-

+20.1% from last month

-
-
- - - Active Sponsorships - - - -
12
-

+3 from last month

-
-
- - - Collaborations - - - -
8
-

+2 from last month

-
-
- - - Audience Growth - - - -
+12.5%
-

+2.1% from last month

-
-
+ {isLoading ? ( + Array.from({ length: 4 }).map((_, i) => ( + + + + + + + + + + + )) + ) : ( + <> + + + Total Revenue + + + +
$45,231.89
+

+20.1% from last month

+
+
+ + + Active Sponsorships + + + +
12
+

+3 from last month

+
+
+ + + Collaborations + + + +
8
+

+2 from last month

+
+
+ + + Audience Growth + + + +
+12.5%
+

+2.1% from last month

+
+
+ + )}
+ + {/* --- ACTIVITY & METRICS --- */}
- + - Performance Metrics + Performance Metrics - + {isLoading ? : } - + - Recent Activity - Your latest interactions and updates + Recent Activity + Your latest interactions and updates - + {isLoading ? ( +
+ + + +
+ ) : ( + + )}
+ + {/* --- AI MATCHES & COLLABORATIONS --- */}
- + - AI-Matched Sponsorships - Brands that match your audience and content + AI-Matched Sponsorships + Brands that match your audience and content - + {isLoading ? : } - + - Creator Collaborations - Creators with complementary audiences + Creator Collaborations + Creators with complementary audiences @@ -195,43 +241,34 @@ export default function DashboardPage() {
+ + {/* --- SPONSORSHIPS TAB WITH EMPTY STATE --- */} - - - AI-Driven Sponsorship Matchmaking - Discover brands that align with your audience and content style - - -
-

Coming Soon

-

- The full sponsorship matchmaking interface will be available here. -

-
-
-
-
- - - - - - - Performance Analytics & ROI Tracking - Track sponsorship performance and campaign success - - -
-

Coming Soon

-

- The full analytics dashboard will be available here. -

-
-
-
-
-
-
-
- ) - } \ No newline at end of file + navigate("/profile")} + /> + + + + + + + + + navigate("/settings/connections")} + /> + + + + + ) +} \ No newline at end of file