aethex-forge/client/pages/Network.tsx
2025-09-27 21:44:20 +00:00

151 lines
7.1 KiB
TypeScript

import Layout from "@/components/Layout";
import { useAuth } from "@/contexts/AuthContext";
import { useEffect, useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import LoadingScreen from "@/components/LoadingScreen";
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 { aethexSocialService } from "@/lib/aethex-social-service";
import { UserPlus, UserCheck } from "lucide-react";
export default function Network() {
const { user, profile, loading } = useAuth();
const navigate = useNavigate();
const [isLoading, setIsLoading] = useState(true);
const [recommended, setRecommended] = useState<any[]>([]);
const [following, setFollowing] = useState<string[]>([]);
useEffect(() => {
if (!loading && !user) {
navigate("/login", { replace: true });
return;
}
if (!user) return;
const load = async () => {
setIsLoading(true);
try {
const recs = await aethexSocialService.listRecommended(user.id, 12);
setRecommended(recs);
const flw = await aethexSocialService.getFollowing(user.id);
setFollowing(flw);
} finally {
setIsLoading(false);
}
};
load();
}, [user, loading, navigate]);
const isFollowing = (id: string) => following.includes(id);
const toggleFollow = async (targetId: string) => {
if (!user) return;
if (isFollowing(targetId)) {
await aethexSocialService.unfollowUser(user.id, targetId);
setFollowing((s) => s.filter((x) => x !== targetId));
} else {
await aethexSocialService.followUser(user.id, targetId);
setFollowing((s) => Array.from(new Set([...s, targetId])));
}
};
if (loading || isLoading) {
return <LoadingScreen message="Loading your network..." showProgress duration={1000} />;
}
if (!user) return null;
return (
<Layout>
<div className="min-h-screen bg-aethex-gradient py-8">
<div className="container mx-auto px-4 max-w-7xl grid grid-cols-1 lg:grid-cols-12 gap-6">
{/* Public Profile */}
<div className="lg:col-span-4 space-y-6">
<Card className="bg-card/50 border-border/50">
<CardContent className="p-6 space-y-4">
<div className="flex items-center gap-4">
<Avatar className="h-16 w-16">
<AvatarImage src={profile?.avatar_url} />
<AvatarFallback>{profile?.full_name?.[0] || user.email?.[0]?.toUpperCase()}</AvatarFallback>
</Avatar>
<div>
<h2 className="text-xl font-semibold">{profile?.full_name || user.email?.split("@")[0]}</h2>
<p className="text-sm text-muted-foreground">{profile?.role || "Member"}</p>
<div className="mt-2 flex gap-2">
<Badge variant="outline" className="border-aethex-400/50 text-aethex-400">Level {profile?.level || 1}</Badge>
<Badge variant="outline">{(profile as any)?.experience_level || "beginner"}</Badge>
</div>
</div>
</div>
{profile?.bio && (
<p className="text-sm text-muted-foreground">{profile.bio}</p>
)}
<div className="flex gap-2">
<Button variant="outline" onClick={() => navigate("/dashboard")}>Edit Profile</Button>
<Button variant="outline" onClick={() => navigate("/onboarding")}>Improve Profile</Button>
</div>
</CardContent>
</Card>
<Card className="bg-card/50 border-border/50">
<CardHeader>
<CardTitle>Recommendations</CardTitle>
<CardDescription>People who align with your interests</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{recommended.slice(0, 3).map((r) => (
<div key={r.id} className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Avatar className="h-10 w-10"><AvatarImage src={r.avatar_url} /><AvatarFallback>{(r.full_name || r.username || "U")[0]}</AvatarFallback></Avatar>
<div>
<div className="font-medium">{r.full_name || r.username}</div>
<div className="text-xs text-muted-foreground">{r.bio?.slice(0, 40) || "Member"}</div>
</div>
</div>
<Button size="sm" variant={isFollowing(r.id) ? "outline" : "default"} onClick={() => toggleFollow(r.id)}>
{isFollowing(r.id) ? (<span className="flex items-center gap-1"><UserCheck className="h-4 w-4" /> Following</span>) : (<span className="flex items-center gap-1"><UserPlus className="h-4 w-4" /> Follow</span>)}
</Button>
</div>
))}
{recommended.length === 0 && (
<div className="text-sm text-muted-foreground">No recommendations yet.</div>
)}
</CardContent>
</Card>
</div>
{/* Discover People */}
<div className="lg:col-span-8">
<Card className="bg-card/50 border-border/50">
<CardHeader>
<CardTitle>Discover People</CardTitle>
<CardDescription>Connect with creators, clients, and members</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{recommended.map((r) => (
<div key={r.id} className="flex items-center justify-between p-3 rounded-lg border border-border/50 hover:border-aethex-400/50 transition-all">
<div className="flex items-center gap-3">
<Avatar className="h-12 w-12"><AvatarImage src={r.avatar_url} /><AvatarFallback>{(r.full_name || r.username || "U")[0]}</AvatarFallback></Avatar>
<div>
<div className="font-semibold">{r.full_name || r.username}</div>
<div className="text-xs text-muted-foreground">{r.bio?.slice(0, 80) || "Member"}</div>
</div>
</div>
<Button size="sm" variant={isFollowing(r.id) ? "outline" : "default"} onClick={() => toggleFollow(r.id)}>
{isFollowing(r.id) ? (<span className="flex items-center gap-1"><UserCheck className="h-4 w-4" /> Following</span>) : (<span className="flex items-center gap-1"><UserPlus className="h-4 w-4" /> Follow</span>)}
</Button>
</div>
))}
{recommended.length === 0 && (
<div className="text-sm text-muted-foreground">No people found yet.</div>
)}
</CardContent>
</Card>
</div>
</div>
</div>
</Layout>
);
}