-
Digital Portfolio
-
-
Add Work
-
+ {loading ? (
+
+ {[1, 2, 3].map(i => (
+
+ ))}
-
- {loading ? (
-
- {[1, 2, 3, 4].map(i => (
-
- ))}
-
- ) : portfolio.length === 0 ? (
-
-
No assets uploaded
-
Initialize Portfolio
-
- ) : (
-
- {portfolio.map(item => (
+ ) : portfolio.length === 0 ? (
+
+ Upload your first work to start attracting brands
+
+ ) : (
+
+ {portfolio.map(item => {
+ const firstMedia = item.items?.[0];
+ return (
setSelectedDetailProject({
+ ...item,
+ mediaUrl: firstMedia?.media_url,
+ mediaType: firstMedia?.media_type,
+ items: (item.items || []).map(i => ({
+ ...i,
+ mediaUrl: i.media_url || i.mediaUrl,
+ mediaType: i.media_type || i.mediaType
+ })),
+ author: user
+ })}
+ className="group bg-white border border-[#F0F0F0] p-4 transition-all duration-300 cursor-pointer"
>
-
- {item.media_type === 'image' ? (
-
+
+ {firstMedia ? (
+ firstMedia.media_type === 'image' ? (
+
+ ) : (
+
e.target.play()}
+ onMouseOut={e => e.target.pause()}
+ />
+ )
) : (
-
-
+
+
+
+ )}
+
+
+ {item.category}
+
+ {firstMedia?.media_type === 'video' && (
+
)}
-
-
{item.title}
-
-
{item.category}
-
{new Date(item.created_at).toLocaleDateString()}
+
+
{item.title}
+
+
{new Date(item.created_at).toLocaleDateString()}
+
-
- ))}
-
- )}
-
+ );
+ })}
+
+ )}
+
- {/* ── Inquiries Sidebar ── */}
-
-
-
-
Inquiries
- {pendingRequests.length} Pending
-
+ {/* Inquiries section */}
+
+
+
INQUIRIES
+
{pendingRequests.length} PENDING
+
- {loading ? (
-
- {[1, 2].map(i => (
-
- ))}
-
- ) : pendingRequests.length === 0 ? (
-
- ) : (
-
- {pendingRequests.map(r => (
-
+ {[1, 2].map(i => (
+
+ ))}
+
+ ) : pendingRequests.length === 0 ? (
+
+ No inquiries yet — brands will appear here once they reach out
+
+ ) : (
+
+ {pendingRequests.map(r => (
+
+
+
+
{r.project_title}
+
Client: {r.brand?.username}
+
+
+
${r.budget?.toLocaleString()}
+
Budget (USD)
+
+
+
+
+ {r.project_description}
+
+
+
+
updateStatus(r.id, 'Accepted')}
+ className="border border-[#0D1033] bg-white text-[#0D1033] px-10 py-3 text-[9px] font-black uppercase tracking-[0.2em] hover:bg-[#F9FAFB] transition-all"
>
-
-
-
{r.project_title}
-
from {r.brand?.username}
-
-
-
${r.budget?.toLocaleString()}
-
Offer
-
-
-
- "{r.project_description}"
-
-
- updateStatus(r.id, 'Accepted')}
- className="flex-1 bg-black text-white py-3 text-[9px] font-bold uppercase tracking-widest hover:bg-[#222222] transition-colors"
- >
- Accept
-
- updateStatus(r.id, 'Declined')}
- className="flex-1 border border-[#EEEEEE] text-black py-3 text-[9px] font-bold uppercase tracking-widest hover:border-black transition-colors"
- >
- Decline
-
-
-
- ))}
-
- )}
+ Accept Proposal
+
+ updateStatus(r.id, 'Declined')}
+ className="border border-[#F0F0F0] text-[#6B7280] px-10 py-3 text-[9px] font-black uppercase tracking-[0.2em] hover:bg-gray-50 transition-all"
+ >
+ Decline
+
+
+
+ ))}
-
-
-
-
Encrypted Comms
-
Access all active threads
-
-
-
-
-
-
+ )}
+
);
diff --git a/client/src/pages/CreatorPricingPage.jsx b/client/src/pages/CreatorPricingPage.jsx
new file mode 100644
index 0000000..7a50e09
--- /dev/null
+++ b/client/src/pages/CreatorPricingPage.jsx
@@ -0,0 +1,177 @@
+import { useState, useEffect } from 'react';
+import { useParams, useNavigate, useLocation } from 'react-router-dom';
+import { motion } from 'framer-motion';
+import {
+ CheckCircle,
+ ArrowRight,
+ ShieldCheck,
+ Clock,
+ CreditCard,
+ MessageSquare,
+ ChevronLeft
+} from 'lucide-react';
+import { api } from '../lib/api';
+
+const DEFAULT_PACKAGES = [
+ {
+ title: 'Social / Lifestyle',
+ price: '₹5,000–₹12,000',
+ subtitle: 'Perfect for personal branding & short reels',
+ features: ['2–4 hours shoot time', 'Professional lighting setup', 'Basic color grading', '2–3 edited short videos', 'Optimised for Instagram/TikTok', 'Web-ready delivery'],
+ },
+ {
+ title: 'Event Coverage',
+ price: '₹15,000–₹25,000',
+ subtitle: 'Best for corporate events & parties',
+ features: ['6–8 hours event coverage', 'Dual camera setup', 'Candid + staged shots', 'Advanced colour correction', '1–3 min highlight reel', 'Raw footage available'],
+ popular: true,
+ },
+ {
+ title: 'Commercial Film',
+ price: '₹35,000–₹80,000+',
+ subtitle: 'High-end visuals for brands & businesses',
+ features: ['Full-day shoot (10–12 h)', 'Pre-production & storyboarding', 'Cinema-grade cameras', 'Professional crew & sound', 'Cinematic colour grading', 'Multiple export formats'],
+ },
+];
+
+export default function CreatorPricingPage() {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const [creator, setCreator] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ const loadCreator = async () => {
+ try {
+ const res = await api.get(`/creators/${id}`);
+ setCreator(res.data.creator);
+ } catch (err) {
+ console.error('Failed to load creator:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+ loadCreator();
+ window.scrollTo(0, 0);
+ }, [id]);
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ if (!creator) return
Creator not found
;
+
+ return (
+
+ {/* ── Minimal Header ── */}
+
+
navigate(`/profile/${id}`)}
+ className="text-[11px] font-bold uppercase tracking-[0.3em] text-[#AAAAAA] hover:text-black transition-all flex items-center gap-2"
+ >
+ Back to Profile
+
+
+ Secure Checkout System
+
+
+
+
+ {/* ── Pricing Grid ── */}
+
+
+ {DEFAULT_PACKAGES.map((pkg, i) => (
+
+ {pkg.popular && (
+
+ Most Popular Choice
+
+ )}
+
+
+
{pkg.title}
+
{pkg.subtitle}
+
+
+
+
+ {pkg.price}
+
+
Est. Project Value
+
+
+
+
+ {pkg.features.map((feature, fIdx) => (
+
+
+
+ {feature}
+
+
+ ))}
+
+
+
+ navigate(`/profile/${creator.id}/checkout`, { state: { pkg, creator } })}
+ className={`w-full py-5 text-[11px] font-bold uppercase tracking-[0.3em] transition-all border-2 ${
+ pkg.popular
+ ? 'bg-black text-white border-black hover:bg-zinc-800'
+ : 'bg-white text-black border-zinc-900 hover:bg-black hover:text-white'
+ }`}
+ >
+ Select Package
+
+
+ ))}
+
+
+
+ {/* ── Trust Section ── */}
+
+
+
+
+
Secure Escrow
+
Funds are held safely in escrow and only released upon your final approval of the work.
+
+
+
+
Timeline Guarantees
+
Milestone-based tracking ensures your project stays on schedule and updates are frequent.
+
+
+
+
Split Payments
+
Flexible payment options including 50/50 splits or full upfront for priority scheduling.
+
+
+
+
Need something else?
+ navigate(`/dm/${id}`)}
+ className="mt-4 text-[11px] font-bold uppercase tracking-[0.2em] underline underline-offset-8 hover:text-blue-600 transition-all"
+ >
+ Request Custom Quote
+
+
+
+
+
+ );
+}
diff --git a/client/src/pages/CreatorProfilePage.jsx b/client/src/pages/CreatorProfilePage.jsx
index b052ba1..32100f7 100644
--- a/client/src/pages/CreatorProfilePage.jsx
+++ b/client/src/pages/CreatorProfilePage.jsx
@@ -1,103 +1,92 @@
import { useState, useEffect } from 'react';
import { useParams, Link, useNavigate } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
+import {
+ MessageSquare,
+ Video,
+ MapPin,
+ Layers,
+ Camera,
+ Quote,
+ Edit,
+ Trash2,
+ Plus,
+ X,
+ Upload,
+ Loader2,
+ Globe,
+ Instagram,
+ Eye,
+ TrendingUp,
+ BarChart2,
+ Users,
+ Bookmark,
+ MoreHorizontal,
+ Star,
+ UserPlus
+} from 'lucide-react';
import { api } from '../lib/api';
+import { useAuth } from '../context/AuthContext';
-const DEMO_CREATOR = {
- id: 'demo',
- name: 'Atharv Gadekar',
- username: 'atharvagadekar2906',
- role: 'Creative Director & Cinematographer',
- location: 'Mumbai / Global',
- img: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=500&auto=format&fit=crop',
- qualifications: ['Advanced Color Grading', 'Certified Drone Operator', '10+ Years Industry Exp.'],
- pastWork: ['Nike', 'Sony', 'RedBull'],
- brandDeals: ['Nike', 'Sony', 'RedBull', 'Apple'],
- workflow: [
- { step: 'Discovery', description: 'Deep dive into brand guidelines and visual identity.' },
- { step: 'Pre-Production', description: 'Storyboarding, scouting, and preparation.' },
- { step: 'Production', description: 'On-set execution with top-tier equipment.' },
- { step: 'Post & Delivery', description: 'Color grading and final rendering.' }
- ],
- portfolio_items: [
- { id: 1, title: 'Mandai', type: 'Cinematography', img: 'https://images.unsplash.com/photo-1518131672697-613becd4fab5?w=500&auto=format&fit=crop' },
- { id: 2, title: 'Ethnicity', type: 'Direction', img: 'https://images.unsplash.com/photo-1555680202-c86f0e12f086?w=500&auto=format&fit=crop' },
- { id: 3, title: 'Urban Flow', type: 'Photography', img: 'https://images.unsplash.com/photo-1600132806370-bf17e65e942f?w=500&auto=format&fit=crop' },
- ],
- packages: [
- {
- title: "Social / Lifestyle",
- price: "₹5,000-₹12,000",
- subtitle: "Perfect for personal branding & short reels",
- features: [
- "2-4 hours shoot time",
- "Professional lighting setup",
- "Basic color grading",
- "2-3 edited short videos",
- "Optimized for Instagram/TikTok",
- "Web-ready delivery"
- ]
- },
- {
- title: "Event Coverage",
- price: "₹15,000-₹25,000",
- subtitle: "Best choice for corporate events & parties",
- features: [
- "6-8 hours event coverage",
- "Dual camera setup for backup",
- "Candid + staged shots",
- "Advanced color correction",
- "1-3 minute highlight reel",
- "Raw footage available"
- ],
- popular: true
- },
- {
- title: "Commercial Film",
- price: "₹35,000-₹80,000+",
- subtitle: "High-end visuals for brands & businesses",
- features: [
- "Full-day shoot (10-12 hours)",
- "Pre-production & storyboarding",
- "Cinema-grade RED/ARRI cameras",
- "Professional crew & sound design",
- "Cinematic color grading",
- "Multiple export formats"
- ]
- }
- ]
-};
+
+
+/* Derive readable initials from a username / display_name */
+function initials(str = 'A') {
+ return str.split(/[\s_]/).slice(0, 2).map(w => w[0]?.toUpperCase() ?? '').join('');
+}
export default function CreatorProfilePage() {
- const { id } = useParams();
- const navigate = useNavigate();
- const [creator, setCreator] = useState(null);
- const [error, setError] = useState(null);
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const { user } = useAuth();
+
+ const [creator, setCreator] = useState(null);
+ const [error, setError] = useState(null);
const [selectedMedia, setSelectedMedia] = useState(null);
- const [showPricing, setShowPricing] = useState(false);
- useEffect(() => {
- const load = async () => {
- if (id === 'demo') {
- setCreator(DEMO_CREATOR);
- window.scrollTo(0, 0);
- return;
- }
- try {
- const data = await api.get(`/creators/${id}`);
- if (data?.data?.creator) {
- setCreator(data.data.creator);
- } else {
- setError('Creator not found');
- }
- } catch (err) {
- setError(err.message || 'Failed to load creator profile');
+ const [portfolioFilter, setPortfolioFilter] = useState('All');
+ const [editingProject, setEditingProject] = useState(null);
+ const [isSaving, setIsSaving] = useState(false);
+
+ /* Resolve 'me' → real user id, then fetch */
+ const load = async () => {
+ const resolvedId = id === 'me' ? user?.id : id;
+ if (!resolvedId) return;
+
+ try {
+ const data = await api.get(`/creators/${resolvedId}`);
+ if (data?.data?.creator) {
+ setCreator(data.data.creator);
+ } else {
+ setError('Creator not found');
}
- };
+ } catch (err) {
+ setError(err.message || 'Failed to load creator profile');
+ }
+ };
+
+ useEffect(() => {
load();
window.scrollTo(0, 0);
- }, [id]);
+ }, [id, user?.id]);
+ const isOwnProfile = user && creator && user.id === creator.id;
+
+ /* ── Portfolio filter ── */
+ const portfolioProjects = creator?.portfolio_projects || [];
+ const filteredProjects = portfolioFilter === 'All'
+ ? portfolioProjects
+ : portfolioProjects.filter(project =>
+ project.items?.some(item =>
+ portfolioFilter === 'Video'
+ ? item.media_type === 'video'
+ : item.media_type !== 'video'
+ )
+ );
+
+ const [activeTab, setActiveTab] = useState('Projects');
+
+ /* ── Error / loading states ── */
if (error) {
return (
@@ -107,281 +96,523 @@ export default function CreatorProfilePage() {
);
}
- if (!creator) return
Loading...
;
+ if (!creator) {
+ return (
+
+ );
+ }
+
+ /* Derive display values from real profile fields */
+ const displayName = creator.display_name || creator.username || 'Salih Turan';
+ const roleLabel = creator.category || 'Creator';
+ const locationLabel = creator.location || 'Bursa, Turkey';
+ const bio = creator.bio || 'A multi-disciplinary creative professional with a passion for pushing visual boundaries and capturing the essence of the moment.';
+
+ const stats = [
+ { label: 'Total Views', value: '1.2M', icon: Eye },
+ { label: 'Recent Rank', value: '#12', icon: TrendingUp },
+ { label: 'All-Time Rank', value: '#45', icon: BarChart2 },
+ { label: 'Followers', value: '8.4K', icon: Users },
+ ];
+
+
+
return (
-
- {/* Hero Section */}
-
-
-
-
-
-
← Back to Explore
+
+
+ {/* ── Minimal Top Navigation ── */}
+
+
+
← Back
+
+ {isOwnProfile && (
+
+ Settings
+
+ )}
+
+
+
+
-
-
-
- {/* Left Wing: Profile & Quick Info */}
-
+ {/* ── Profile Header Section ── */}
+
+
+
+ {/* Circular Avatar */}
-
-
-
-
+
+ {creator.avatar_url ? (
+
+ ) : (
+
+ {initials(displayName)}
+
+ )}
+
-
-
-
{creator.display_name || creator.username}
-
Verified
+ {/* Name and Bio */}
+
+
{displayName}
+
+ {bio}
+
+
+ {/* Location and Socials */}
+
+
+ {locationLabel}
+
+
-
{creator.role} • {creator.location || 'Remote'}
+
+
-
-
- Message
-
- setShowPricing(true)}
- className="flex-1 bg-black text-white py-4 rounded-none font-bold text-sm hover:bg-zinc-800 transition-all shadow-[0_4px_14px_rgba(0,0,0,0.1)]"
- >
- Hire
-
-
- ★
-
-
+ {/* Action Buttons */}
+
+ navigate(`/profile/${creator.id}/pricing`)}
+ className="flex items-center gap-2 px-8 py-2.5 bg-black text-white border border-black rounded-none shadow-md text-[11px] font-bold uppercase tracking-[0.2em] hover:bg-zinc-800 transition-all"
+ >
+ Hire Me
+
+ navigate(`/dm/${creator.id}`)}
+ className="flex items-center gap-2 px-5 py-2.5 bg-white border border-zinc-900 rounded-none shadow-sm text-sm font-semibold hover:bg-zinc-50 transition-all"
+ >
+ Message
+
+
+
+
-
-
-
Qualification
-
- {(creator.qualifications || ['Professional Creator']).map((q, i) => (
-
-
- {q}
-
- ))}
-
-
+ {/* ── Statistics Section ── */}
+
+
+ {/* Performance Card */}
+
+
+ 1.2M
+ Total Views
+
+
+ #12
+ 30-day Rank
+
+
+ #45
+ All-time Rank
+
+
-
-
Past Work
-
- {(creator.past_work || creator.pastWork || ['New Member']).map((pw, i) => (
-
- {pw}
-
- ))}
-
-
-
-
+ {/* Activity Card */}
+
+
+ 0
+ Challenges Won
+
+
+ 14
+ Shortlisted
+
+
+ 0
+ Meetups
+
+
+
+
- {/* Brand Deals Wing */}
-
+
+ {['All', 'Video', 'Photos'].map(f => (
+
setPortfolioFilter(f)}
+ className={`px-6 py-2 rounded-none text-[11px] font-bold uppercase tracking-widest transition-all ${
+ portfolioFilter === f
+ ? 'bg-zinc-900 text-white shadow-lg'
+ : 'bg-zinc-50 text-zinc-500 hover:bg-zinc-100 border border-zinc-200'
+ }`}
>
- Trusted By
-
- {(creator.brandDeals || ['Community Partner']).map((brand, i) => (
-
- {brand}
-
- ))}
-
-
+ {f}
+
+ ))}
+
+
+ {filteredProjects.length === 0 ? (
+
+
+
+ {isOwnProfile ? 'Your portfolio is currently empty.' : 'No projects found in this category.'}
+
+ {isOwnProfile && (
+
+ Add Your First Project
+
+ )}
+ ) : (
+
+ {filteredProjects.map((project, i) => {
+ const firstItem = project.items?.[0] || {};
+ const hasMedia = firstItem.media_url;
- {/* Right Wing: Main Content */}
-
-
- {/* Workflow Section */}
-
-
- Stable Workflow
-
-
-
- {(creator.workflow || [
- { step: 'Strategy', description: 'Aligning vision with brand goals.' },
- { step: 'Production', description: 'High-end content creation.' },
- { step: 'Review', description: 'Collaborative feedback loop.' },
- { step: 'Delivery', description: 'Final assets in all formats.' }
- ]).map((item, i) => (
+ return (
+
setSelectedMedia({
+ ...project,
+ mediaUrl: firstItem.media_url,
+ mediaType: firstItem.media_type,
+ items: project.items
+ })}
+ className="relative aspect-square rounded-none overflow-hidden bg-zinc-50 cursor-pointer hover:shadow-xl transition-all duration-300 border border-transparent hover:border-zinc-900"
>
- {i + 1}
-
-
{item.step}
-
{item.description}
+ {hasMedia ? (
+ <>
+ {firstItem.media_type === 'video' ? (
+
e.target.play()}
+ onMouseLeave={e => {
+ e.target.pause();
+ e.target.currentTime = 0;
+ }}
+ />
+ ) : (
+
+ )}
+ >
+ ) : (
+
+ )}
+
+ {/* Favorite Star Icon */}
+
+
+
+
-
- ))}
-
-
-
- {/* Portfolio Grid */}
-
-
-
- Portfolio
-
-
-
- {['All', 'Video', 'Photos'].map(f => (
-
- {f}
-
- ))}
-
-
-
-
- {(creator.portfolio_items || []).map((item, i) => (
-
setSelectedMedia(item)}
- className="break-inside-avoid relative group cursor-pointer overflow-hidden rounded-none bg-zinc-100"
- >
-
-
-
{item.title}
-
{item.type}
+
+ {/* More Menu Overlay */}
+
+
+
+
- ))}
-
-
+
+ );
+ })}
-
-
+ )}
- {/* Lightbox Modal */}
+ {/* ── Lightbox (Minimalist) ── */}
{selectedMedia && (
- setSelectedMedia(null)}
>
- e.stopPropagation()}
- >
- setSelectedMedia(null)}
- className="absolute top-8 right-8 text-white p-3 z-10 bg-black/50 backdrop-blur-md rounded-none hover:bg-black transition-all border border-white/10"
- >
- ✕
-
-
-
-
{selectedMedia.title}
-
{selectedMedia.media_type || selectedMedia.type}
+
+
+
+
+
+
+
{displayName}
+
{roleLabel}
+
-
+
+ navigate(`/dm/${creator.id}`)}
+ className="bg-black text-white px-6 py-2 rounded-none text-xs font-bold hover:bg-zinc-800 transition-all"
+ >
+ Hire Me
+
+ setSelectedMedia(null)}
+ className="p-2 hover:bg-zinc-100 rounded-none transition-all"
+ >
+
+
+
+
+
+
e.stopPropagation()}>
+
+
+
{selectedMedia.title}
+
{selectedMedia.description}
+
+
+
+ {selectedMedia.items?.map((item, idx) => (
+
+ {item.media_type === 'video' ? (
+
+ ) : (
+
+ )}
+
+ ))}
+
+
+
)}
- {/* Pricing Modal */}
+ {/* ── Edit Project Modal (Reusing existing logic) ── */}
- {showPricing && (
- setShowPricing(false)}
+ className="fixed inset-0 z-[110] bg-white/95 flex items-center justify-center p-4 backdrop-blur-md"
+ onClick={() => setEditingProject(null)}
>
- e.stopPropagation()}
>
-
-
setShowPricing(false)}
- className="text-white/70 text-[10px] font-bold tracking-widest uppercase hover:text-white flex items-center gap-2 bg-white/5 px-4 py-2 rounded-full border border-white/10 transition-all hover:bg-white/10"
+
+
Edit Project
+
setEditingProject(null)}
+ className="p-2 hover:bg-zinc-100 rounded-full transition-all"
>
-
- Back
+
-
Photography
-
-
- {(creator.packages || []).map((pkg, i) => (
-
- {pkg.popular && (
-
- ★ Popular
-
- )}
-
-
{pkg.title}
-
{pkg.price}
-
{pkg.subtitle}
+
+
);
}
diff --git a/client/src/pages/DirectMessagePage.jsx b/client/src/pages/DirectMessagePage.jsx
index 7987c18..40efc9f 100644
--- a/client/src/pages/DirectMessagePage.jsx
+++ b/client/src/pages/DirectMessagePage.jsx
@@ -1,19 +1,20 @@
import { useState, useRef, useEffect } from 'react';
-import { useParams, Link } from 'react-router-dom';
+import { useParams, Link, useNavigate } from 'react-router-dom';
import { motion } from 'framer-motion';
+import { api } from '../lib/api';
-const MOCK_CREATOR = {
- name: 'Atharv Gadekar',
- username: '@atharvagadekar2906',
- role: 'Cinematographer & Director',
- location: 'Mumbai, India',
- img: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=500&auto=format&fit=crop',
- bio: 'Specializing in high-end commercial videography and aesthetic brand storytelling. Let\'s create something beautiful together.',
- rate: '₹5,000/hr'
-};
+
+
+function initials(str = 'A') {
+ return str.split(/[\s_]/).slice(0, 2).map(w => w[0]?.toUpperCase() ?? '').join('');
+}
export default function DirectMessagePage() {
const { id } = useParams();
+ const navigate = useNavigate();
+ const [creator, setCreator] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
const [messages, setMessages] = useState([
{ id: 1, text: "Hey! I saw your portfolio and I love your work.", sender: 'me', time: '10:41 AM', status: 'read' },
{ id: 2, text: "Are you taking on new projects? We have a commercial shoot next month.", sender: 'me', time: '10:42 AM', status: 'read' },
@@ -22,6 +23,22 @@ export default function DirectMessagePage() {
const [newMessage, setNewMessage] = useState("");
const messagesEndRef = useRef(null);
+ useEffect(() => {
+ if (!id) return;
+ const loadCreator = async () => {
+ try {
+ const { data } = await api.get(`/creators/${id}`);
+ setCreator(data.creator);
+ } catch (err) {
+ console.error('Failed to load creator:', err);
+ setError('Creator not found');
+ } finally {
+ setLoading(false);
+ }
+ };
+ loadCreator();
+ }, [id]);
+
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
@@ -54,7 +71,7 @@ export default function DirectMessagePage() {
setTimeout(() => {
setMessages(prev => [...prev, {
id: Date.now() + 1,
- text: "That sounds interesting! Could you share the brief?",
+ text: `Hi! Thank you for reaching out. Let's discuss this further.`,
sender: 'them',
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
}]);
@@ -63,55 +80,99 @@ export default function DirectMessagePage() {
}, 1000);
};
+ if (loading) return
;
+ if (error) return
{error} Back to Messages;
+
+ const displayName = creator.display_name || creator.username;
+ const username = `@${creator.username}`;
+ const role = creator.category || 'Creator';
+ const location = creator.location || 'Remote';
+ const bio = creator.bio || 'Multi-disciplinary creative professional.';
+ const rate = creator.min_budget ? `₹${creator.min_budget}/hr` : 'Contact for Rates';
+
return (
-
+
{/* Left Sidebar - Creator Profile */}
-
-
-
- Back to Profile
+
+
+
+ Back
-
+
-
-
+
+ {creator.avatar_url ? (
+
+ ) : (
+
{initials(displayName)}
+ )}
+
+
-
{MOCK_CREATOR.name}
-
{MOCK_CREATOR.username}
-
- {MOCK_CREATOR.role}
+
{displayName}
+
{username}
+
+
+ {role}
-
- {MOCK_CREATOR.bio}
+
+ {bio}
-
+
-
Location
-
{MOCK_CREATOR.location}
+
Location
+
{location}
-
Base Rate
-
{MOCK_CREATOR.rate}
+
Base Rate
+
{rate}
+
+ {/* Action Buttons */}
+
+
+ Start Google Meet
+
+
navigate('/checkout', { state: { creator: MOCK_CREATOR, pkg: null } })}
+ className="w-full flex items-center justify-center gap-2 bg-transparent text-[#111] text-[10px] font-normal uppercase tracking-[0.15em] py-2.5 border border-[#E5E5E5] hover:bg-[#FAFAFA] transition-colors"
+ >
+ Fix a Deal
+
+
{/* Right Side - Chat Interface */}
-
+
{/* Chat Header */}
-
+
{/* Chat Messages */}
-
+
{messages.map((msg) => (
-
-
{msg.text}
-
-
{msg.time}
+
+
{msg.text}
+
+
{msg.time}
{msg.sender === 'me' && (
-
-
- {msg.status !== 'sent' && }
+
+
)}
@@ -157,26 +216,24 @@ export default function DirectMessagePage() {
{/* Chat Input */}
-
-
-
-
+
diff --git a/client/src/pages/EditProfilePage.jsx b/client/src/pages/EditProfilePage.jsx
index d5633c6..bb49d1e 100644
--- a/client/src/pages/EditProfilePage.jsx
+++ b/client/src/pages/EditProfilePage.jsx
@@ -2,6 +2,19 @@ import { useState, useRef, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import { Helmet } from 'react-helmet-async';
+import {
+ Instagram,
+ Twitter,
+ Linkedin,
+ Globe,
+ Plus,
+ Info,
+ DollarSign,
+ Users,
+ Target,
+ Hash,
+ Briefcase
+} from 'lucide-react';
import { api } from '../lib/api';
import { useAuth } from '../context/AuthContext';
@@ -44,6 +57,7 @@ export default function EditProfilePage() {
banner_url: '',
instagram: '',
twitter: '',
+ linkedin: '',
website: '',
min_budget: 0,
max_budget: 10000,
@@ -78,6 +92,7 @@ export default function EditProfilePage() {
banner_url: creator.banner_url || '',
instagram: creator.instagram || '',
twitter: creator.twitter || '',
+ linkedin: creator.linkedin || '',
website: creator.website || '',
min_budget: creator.min_budget || 0,
max_budget: creator.max_budget || 10000,
@@ -272,6 +287,7 @@ export default function EditProfilePage() {
location: formData.location,
instagram: formData.instagram,
twitter: formData.twitter,
+ linkedin: formData.linkedin,
website: formData.website,
avatar_url: avatarUrl,
banner_url: bannerUrl,
@@ -289,7 +305,7 @@ export default function EditProfilePage() {
await api.patch('/creators/profile', payload);
setMessage({ type: 'success', text: 'Profile updated successfully!' });
setTimeout(() => {
- navigate(`/dashboard/${user.role}`);
+ navigate('/profile/me');
}, 1500);
} catch (err) {
setMessage({ type: 'error', text: err.message || 'Failed to update profile' });
@@ -298,17 +314,48 @@ export default function EditProfilePage() {
}
};
- const inputClass = (field) =>
- `w-full border-b py-3 focus:outline-none transition-colors bg-transparent text-black placeholder:text-[#CCC] ${
- errors[field] ? 'border-red-400' : 'border-[#DDD] focus:border-black'
- }`;
+ const handleCancel = () => navigate('/profile/me');
+
+ const cardStyle = {
+ background: '#FFFFFF',
+ border: '1px solid #E5E7EB',
+ borderRadius: '8px',
+ padding: '32px',
+ boxShadow: '0px 1px 2px rgba(0, 0, 0, 0.05), 0px 0px 0px 1px rgba(0, 0, 0, 0.05)',
+ marginBottom: '32px'
+ };
+
+ const inputStyle = {
+ width: '100%',
+ border: '1px solid #D1D5DB',
+ borderRadius: '6px',
+ padding: '12px 16px',
+ fontSize: '14px',
+ color: '#111111',
+ background: '#FFFFFF',
+ outline: 'none',
+ transition: 'all 0.3s ease',
+ fontFamily: "'Inter', sans-serif"
+ };
+
+ const labelStyle = {
+ fontSize: '14px',
+ color: '#555555',
+ fontWeight: '500',
+ marginBottom: '8px',
+ display: 'block',
+ fontFamily: "'Inter', sans-serif"
+ };
+
+ const focusStyle = "focus:border-[#3B82F6] focus:ring-2 focus:ring-[#3B82F6] focus:ring-opacity-20";
if (initialLoading) {
return (
-
-
-
-
Loading profile...
+
);
@@ -321,342 +368,373 @@ export default function EditProfilePage() {
-
-
- Edit Profile
-
- {isCreator ? 'Showcase your work and let brands find you.' : 'Tell creators about your brand and what you\'re looking for.'}
-
-
- {/* Banner Upload */}
-
-
- Banner Image
-
-
bannerInputRef.current?.click()}
- className={`aspect-[3/1] border-2 border-dashed transition-all cursor-pointer flex flex-col items-center justify-center p-8 relative overflow-hidden ${
- dragActiveBanner ? 'border-black bg-gray-50' : 'border-[#DDD] bg-white hover:border-black'
- }`}
- >
-
- {bannerPreview ? (
-
- ) : (
- <>
-
-
-
-
Drag & Drop or Click
-
Recommended: 1200×400px
- >
- )}
-
- {errors.banner &&
{errors.banner}
}
-
+
- {/* Avatar Upload */}
-
-
- Avatar Image
-
-
avatarInputRef.current?.click()}
- className={`w-32 h-32 border-2 border-dashed transition-all cursor-pointer flex items-center justify-center relative overflow-hidden rounded-lg ${
- dragActiveAvatar ? 'border-black bg-gray-50' : 'border-[#DDD] bg-white hover:border-black'
- }`}
- >
-
- {avatarPreview ? (
-
- ) : (
- <>
-
-
-
- >
- )}
-
-
Square image recommended (500×500px)
- {errors.avatar &&
{errors.avatar}
}
-
+ {/* BODY */}
+
- {/* Form */}
-
+
+
{message.text && (
{message.text}
)}
- {/* Bio */}
-
-
- Bio {formData.bio.length > 0 && ({formData.bio.length}/500) }
-
-
- {errors.bio &&
{errors.bio}
}
-
-
- {/* Category */}
-
-
- {isCreator ? 'Specialty' : 'Industry'}
-
-
- Select a category...
- {categories.map(cat => (
- {cat}
- ))}
-
- {errors.category &&
{errors.category}
}
-
-
- {/* Location */}
-
-
- Location
-
-
- {errors.location &&
{errors.location}
}
-
-
- {/* Social Links */}
-
-
Social Links (Optional)
-
-
-
-
- Instagram Handle
-
-
-
-
-
-
- Twitter/X Handle
-
-
-
-
-
-
- Website
-
-
- {errors.website &&
{errors.website}
}
-
-
-
-
- {/* Creator Specific Settings */}
- {isCreator && (
-
-
Creator Discovery Settings
+ {/* COMBINED SECTION — SOCIAL & DISCOVERY */}
+
+
-
-
-
- Min Budget ($)
-
-
- {errors.min_budget &&
{errors.min_budget}
}
-
-
-
- Max Budget ($)
-
-
- {errors.max_budget &&
{errors.max_budget}
}
+ {/* Left Column: Social Profiles */}
+
+
+
+ Social Profiles
+
+
+ Connect your social profiles to boost visibility and help brands find your work easily.
+
-
-
-
-
- Follower Count
-
-
- {errors.follower_count &&
{errors.follower_count}
}
-
-
-
- Platforms
-
-
- {['Instagram', 'TikTok', 'YouTube', 'Twitter', 'Twitch', 'LinkedIn'].map(plt => (
-
handlePlatformToggle(plt)}
- className={`px-4 py-2 rounded-full text-xs font-bold transition-all border ${
- formData.platforms.includes(plt)
- ? 'bg-black border-black text-white shadow-lg'
- : 'bg-white border-gray-100 text-gray-400 hover:border-black hover:text-black'
- }`}
- >
- {plt}
-
- ))}
+
+ {/* Instagram */}
+
+
+
+ {!formData.instagram && (
+
Required
+ )}
+
+
+
+ @
+
+
+
+ {/* Twitter/X */}
+
+
+ {/* LinkedIn */}
+
+
+ {/* Website */}
+
+
+
+
+
+
+
Portfolio Website
+
+
+
+ {errors.website &&
{errors.website}
}
+
+
+
+
+
+
+ Pro Tip: Linked profiles are shown on your public portfolio to build trust with potential clients.
+
+
+
-
-
- Tags (Comma separated)
-
-
-
-
-
-
-
Available for Hire
-
Show "Available Now" badge on your profile
+ {/* Right Column: Discovery Details */}
+ {isCreator && (
+
+
+
+ Discovery Details
+
+
+ Set your preferences to help brands find and hire you based on their specific needs.
+
+
+
+
+
+
+
+ Total Follower Count
+
+
+
+
+
+
+ Active Platforms
+
+
+ {['Instagram', 'TikTok', 'YouTube', 'Twitter', 'Twitch', 'LinkedIn'].map(plt => {
+ const isSelected = formData.platforms.includes(plt);
+ return (
+ handlePlatformToggle(plt)}
+ className="transition-all duration-200 active:scale-95"
+ style={{
+ padding: '8px 16px',
+ borderRadius: '12px',
+ fontSize: '13px',
+ fontWeight: '600',
+ border: isSelected ? 'none' : '1px solid #E5E7EB',
+ background: isSelected ? '#3B82F6' : '#FFFFFF',
+ color: isSelected ? '#FFFFFF' : '#4B5563',
+ cursor: 'pointer',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ boxShadow: isSelected ? '0 4px 12px rgba(59, 130, 246, 0.25)' : 'none'
+ }}
+ >
+ {plt}
+
+ );
+ })}
+
+
+
+
+
+ Expertise Tags
+
+
document.getElementById('tags-input').focus()}>
+ {formData.tags.map(tag => (
+
+ {tag}
+ {
+ e.stopPropagation();
+ setFormData(p => ({ ...p, tags: p.tags.filter(t => t !== tag) }));
+ }}
+ style={{ border: 'none', background: 'none', color: '#FFFFFF', cursor: 'pointer', padding: '0', fontSize: '14px', opacity: 0.8 }}
+ >
+ ×
+
+
+ ))}
+ {
+ if (e.key === ',' || e.key === 'Enter') {
+ e.preventDefault();
+ const val = e.target.value.trim().replace(/,$/, '');
+ if (val && !formData.tags.includes(val)) {
+ setFormData(p => ({ ...p, tags: [...p.tags, val] }));
+ e.target.value = '';
+ }
+ }
+ }}
+ />
+
+
+
+
+
+
+
+
+
+
Availability
+
Allow brands to hire you for projects
+
+
+
handleInputChange({ target: { name: 'is_available', type: 'checkbox', checked: !formData.is_available } })}
+ className={`w-12 h-6 rounded-full p-1 cursor-pointer transition-all duration-300 ${formData.is_available ? 'bg-blue-600' : 'bg-gray-300'}`}
+ >
+
+
+
-
-
+ )}
- )}
+
+
- {/* Submit Button */}
-
-
+ navigate(`/dashboard/${user.role}`)}
- className="flex-1 border border-black text-black py-4 font-bold text-xs uppercase tracking-[0.2em] hover:bg-black/5 transition-all"
- >
- Cancel
-
- e.currentTarget.style.background = '#2563EB'}
+ onMouseOut={(e) => e.currentTarget.style.background = '#3B82F6'}
>
- {loading ? (
-
-
- Saving...
-
- ) : (
- 'Save Changes'
- )}
+ {loading ? 'Saving...' : 'Save Settings'}
-
-
+
+
+
>
);
diff --git a/client/src/pages/ExplorePage.jsx b/client/src/pages/ExplorePage.jsx
index 50b8884..2784413 100644
--- a/client/src/pages/ExplorePage.jsx
+++ b/client/src/pages/ExplorePage.jsx
@@ -3,6 +3,7 @@ import { motion } from 'framer-motion';
import { Link } from 'react-router-dom';
import { Helmet } from 'react-helmet-async';
import { getAllExploreContent } from '../services/externalMediaService';
+import WorkDetailCard from '../components/WorkDetailCard';
export default function ExplorePage() {
const [content, setContent] = useState([]);
@@ -112,7 +113,7 @@ export default function ExplorePage() {
{item.mediaType === 'video' ? (
e.target.play()}
@@ -122,9 +123,20 @@ export default function ExplorePage() {
)}
+
+ {/* Multi-item indicator */}
+ {item.items?.length > 1 && (
+
+
+
+
+ {item.items.length} Files
+
+ )}
+
@@ -147,7 +159,7 @@ export default function ExplorePage() {
)}
- By {item.author?.username || 'Anonymous'} • {item.category}
+ By {item.author?.username || item.author?.display_name || 'Anonymous'} • {item.category}
@@ -178,60 +190,10 @@ export default function ExplorePage() {
)}
>
)}
- {selectedProject && (
-
setSelectedProject(null)}>
-
e.stopPropagation()}
- >
- setSelectedProject(null)}
- >
-
-
-
-
- {selectedProject.mediaType === 'video' ? (
-
- ) : (
-
- )}
-
-
-
-
{selectedProject.title}
-
{selectedProject.category}
-
-
-
Creator
-
-
- {(selectedProject.author?.username || 'A')[0]}
-
-
-
{selectedProject.author?.username || 'Anonymous'}
-
Independent
-
-
-
- {selectedProject.author?.bio || "A multi-disciplinary creative professional with a proven track record of delivering high-impact visual experiences. Passionate about pushing boundaries and setting new industry standards."}
-
-
-
-
-
-
-
- )}
+
setSelectedProject(null)}
+ />
);
}
diff --git a/client/src/pages/OnboardingPage.jsx b/client/src/pages/OnboardingPage.jsx
index 0e8f3a3..8af2083 100644
--- a/client/src/pages/OnboardingPage.jsx
+++ b/client/src/pages/OnboardingPage.jsx
@@ -407,7 +407,7 @@ function Step3({ onNext, onBack }) {
/>
))}
-
q.trim()) || !data.past_work.some(p => p.trim())} />
+
);
}
@@ -494,7 +494,7 @@ function Step4({ onNext, onBack }) {
You can change all of this anytime from your dashboard.
-
+
);
}
@@ -666,7 +666,7 @@ function Step5({ onBack, onSubmit, loading }) {
/>
-
+