CreatorProfile page - view individual creator's profile
cgen-44d16224e7e047cf8d1954b27ce93904
This commit is contained in:
parent
8c0e463419
commit
b322efa514
1 changed files with 244 additions and 0 deletions
244
client/pages/creators/CreatorProfile.tsx
Normal file
244
client/pages/creators/CreatorProfile.tsx
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
|
import Layout from "@/components/Layout";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Loader2, ArrowLeft, ExternalLink, MessageSquare } from "lucide-react";
|
||||||
|
import { getCreatorByUsername } from "@/api/creators";
|
||||||
|
import { ArmBadge } from "@/components/creator-network/ArmBadge";
|
||||||
|
import type { Creator } from "@/api/creators";
|
||||||
|
|
||||||
|
export default function CreatorProfile() {
|
||||||
|
const { username } = useParams<{ username: string }>();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [creator, setCreator] = useState<Creator | null>(null);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchCreator = async () => {
|
||||||
|
if (!username) return;
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const data = await getCreatorByUsername(username);
|
||||||
|
setCreator(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch creator:", error);
|
||||||
|
setCreator(null);
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchCreator();
|
||||||
|
}, [username]);
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
|
<Loader2 className="h-8 w-8 animate-spin text-purple-400" />
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!creator) {
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className="min-h-screen bg-black text-white flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<h1 className="text-3xl font-bold mb-4">Creator Not Found</h1>
|
||||||
|
<p className="text-gray-400 mb-6">The creator you're looking for doesn't exist.</p>
|
||||||
|
<Button onClick={() => navigate("/creators")}>
|
||||||
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||||
|
Back to Creators
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className="relative min-h-screen bg-black text-white overflow-hidden">
|
||||||
|
{/* Background */}
|
||||||
|
<div className="pointer-events-none absolute inset-0 opacity-[0.12] [background-image:radial-gradient(circle_at_top,#8b5cf6_0,rgba(0,0,0,0.45)_55%,rgba(0,0,0,0.9)_100%)]" />
|
||||||
|
<div className="pointer-events-none absolute inset-0 bg-[linear-gradient(transparent_0,transparent_calc(100%-1px),rgba(139,92,246,0.05)_calc(100%-1px))] bg-[length:100%_32px]" />
|
||||||
|
|
||||||
|
<main className="relative z-10">
|
||||||
|
<div className="container mx-auto max-w-4xl px-4 py-12">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<Button
|
||||||
|
onClick={() => navigate("/creators")}
|
||||||
|
variant="ghost"
|
||||||
|
className="mb-4"
|
||||||
|
>
|
||||||
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||||
|
Back to Creators
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Profile Card */}
|
||||||
|
<Card className="bg-slate-800/50 border-slate-700 mb-8">
|
||||||
|
<CardContent className="p-8">
|
||||||
|
<div className="flex flex-col md:flex-row items-start md:items-center gap-6 mb-6">
|
||||||
|
<Avatar className="h-24 w-24">
|
||||||
|
<AvatarImage src={creator.avatar_url} alt={creator.username} />
|
||||||
|
<AvatarFallback>
|
||||||
|
{creator.username.charAt(0).toUpperCase()}
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<h1 className="text-3xl font-bold">@{creator.username}</h1>
|
||||||
|
{creator.devconnect_linked && (
|
||||||
|
<Badge className="bg-cyan-500/10 text-cyan-300 border-cyan-500/20">
|
||||||
|
<ExternalLink className="h-3 w-3 mr-1" />
|
||||||
|
On DevConnect
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-300 text-lg mb-4">{creator.bio}</p>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-2 mb-4">
|
||||||
|
{creator.primary_arm && (
|
||||||
|
<ArmBadge arm={creator.primary_arm} />
|
||||||
|
)}
|
||||||
|
{creator.arm_affiliations &&
|
||||||
|
creator.arm_affiliations
|
||||||
|
.filter((arm) => arm !== creator.primary_arm)
|
||||||
|
.map((arm) => (
|
||||||
|
<ArmBadge key={arm} arm={arm} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-3">
|
||||||
|
{creator.devconnect_link && (
|
||||||
|
<Button asChild>
|
||||||
|
<a
|
||||||
|
href={creator.devconnect_link.devconnect_profile_url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<ExternalLink className="h-4 w-4 mr-2" />
|
||||||
|
View on DevConnect
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button variant="outline">
|
||||||
|
<MessageSquare className="h-4 w-4 mr-2" />
|
||||||
|
Message
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{/* Skills Section */}
|
||||||
|
{creator.skills && creator.skills.length > 0 && (
|
||||||
|
<Card className="bg-slate-800/50 border-slate-700 mb-8">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Skills & Expertise</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{creator.skills.map((skill) => (
|
||||||
|
<Badge
|
||||||
|
key={skill}
|
||||||
|
className="bg-slate-700/50 text-gray-300 border-0 px-3 py-1.5 text-sm"
|
||||||
|
>
|
||||||
|
{skill}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Projects Section */}
|
||||||
|
{creator.aethex_projects && creator.aethex_projects.length > 0 && (
|
||||||
|
<Card className="bg-slate-800/50 border-slate-700 mb-8">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Portfolio Projects</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{creator.aethex_projects.map((project) => (
|
||||||
|
<Card
|
||||||
|
key={project.id}
|
||||||
|
className="bg-slate-700/30 border-slate-600"
|
||||||
|
>
|
||||||
|
<CardContent className="p-4">
|
||||||
|
{project.image_url && (
|
||||||
|
<img
|
||||||
|
src={project.image_url}
|
||||||
|
alt={project.title}
|
||||||
|
className="w-full h-40 object-cover rounded-lg mb-3"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<h3 className="font-bold text-white mb-1">
|
||||||
|
{project.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-gray-400 mb-3">
|
||||||
|
{project.description}
|
||||||
|
</p>
|
||||||
|
{project.url && (
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="w-full"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href={project.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
View Project
|
||||||
|
<ExternalLink className="h-3 w-3 ml-2" />
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{project.tags && project.tags.length > 0 && (
|
||||||
|
<div className="flex flex-wrap gap-1 mt-3">
|
||||||
|
{project.tags.map((tag) => (
|
||||||
|
<span
|
||||||
|
key={tag}
|
||||||
|
className="text-xs bg-slate-600/50 text-gray-300 px-2 py-1 rounded"
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Experience Level */}
|
||||||
|
<Card className="bg-slate-800/50 border-slate-700">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Experience Level</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Badge className="bg-slate-700/50 text-gray-300 border-0 px-3 py-1.5 text-sm">
|
||||||
|
{creator.experience_level || "Not specified"}
|
||||||
|
</Badge>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue