Add Network section to Connections tab with following/followers/connections and follow/unfollow actions
cgen-c997ca199cef4c59bfc63c95d2657cbd
This commit is contained in:
parent
ed2cd6065b
commit
57efc506e4
1 changed files with 147 additions and 0 deletions
|
|
@ -86,6 +86,9 @@ export default function Dashboard() {
|
||||||
const [achievements, setAchievements] = useState<any[]>([]); // earned achievements
|
const [achievements, setAchievements] = useState<any[]>([]); // earned achievements
|
||||||
const [allAchievements, setAllAchievements] = useState<any[]>([]);
|
const [allAchievements, setAllAchievements] = useState<any[]>([]);
|
||||||
const [achievementFilter, setAchievementFilter] = useState<"all" | "earned" | "locked">("earned");
|
const [achievementFilter, setAchievementFilter] = useState<"all" | "earned" | "locked">("earned");
|
||||||
|
const [followingIds, setFollowingIds] = useState<string[]>([]);
|
||||||
|
const [followerIds, setFollowerIds] = useState<string[]>([]);
|
||||||
|
const [connectionsList, setConnectionsList] = useState<any[]>([]);
|
||||||
const [profileCompletion, setProfileCompletion] = useState(0);
|
const [profileCompletion, setProfileCompletion] = useState(0);
|
||||||
const [stats, setStats] = useState({
|
const [stats, setStats] = useState({
|
||||||
activeProjects: 0,
|
activeProjects: 0,
|
||||||
|
|
@ -362,6 +365,22 @@ export default function Dashboard() {
|
||||||
setInvites([]);
|
setInvites([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load network: following, followers, connections
|
||||||
|
try {
|
||||||
|
const [flw, fol, conns] = await Promise.all([
|
||||||
|
aethexSocialService.getFollowing(user!.id),
|
||||||
|
aethexSocialService.getFollowers(user!.id),
|
||||||
|
aethexSocialService.getConnections(user!.id),
|
||||||
|
]);
|
||||||
|
setFollowingIds(Array.isArray(flw) ? flw : []);
|
||||||
|
setFollowerIds(Array.isArray(fol) ? fol : []);
|
||||||
|
setConnectionsList(Array.isArray(conns) ? conns : []);
|
||||||
|
} catch (e) {
|
||||||
|
setFollowingIds([]);
|
||||||
|
setFollowerIds([]);
|
||||||
|
setConnectionsList([]);
|
||||||
|
}
|
||||||
|
|
||||||
// Load project applications (if table exists)
|
// Load project applications (if table exists)
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
|
|
@ -1121,6 +1140,134 @@ export default function Dashboard() {
|
||||||
onLink={handleLinkProvider}
|
onLink={handleLinkProvider}
|
||||||
onUnlink={handleUnlinkProvider}
|
onUnlink={handleUnlinkProvider}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Separator className="my-6" />
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h3 className="text-lg font-semibold text-foreground">
|
||||||
|
Your network
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
People you follow, your followers, and direct connections.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
<Card className="bg-card/50 border-border/50">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-base">Following</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{followingIds.length} people
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
{followingIds.length === 0 ? (
|
||||||
|
<div className="text-sm text-muted-foreground">You're not following anyone yet.</div>
|
||||||
|
) : (
|
||||||
|
followingIds.slice(0, 6).map((id) => (
|
||||||
|
<div key={id} className="flex items-center justify-between p-2 rounded border border-border/40">
|
||||||
|
<div className="text-xs text-muted-foreground truncate">{id}</div>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={async () => {
|
||||||
|
if (!user) return;
|
||||||
|
try {
|
||||||
|
await aethexSocialService.unfollowUser(user.id, id);
|
||||||
|
setFollowingIds((s) => s.filter((x) => x !== id));
|
||||||
|
} catch {}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Unfollow
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="bg-card/50 border-border/50">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-base">Followers</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{followerIds.length} people
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
{followerIds.length === 0 ? (
|
||||||
|
<div className="text-sm text-muted-foreground">No followers yet.</div>
|
||||||
|
) : (
|
||||||
|
followerIds.slice(0, 6).map((id) => (
|
||||||
|
<div key={id} className="flex items-center justify-between p-2 rounded border border-border/40">
|
||||||
|
<div className="text-xs text-muted-foreground truncate">{id}</div>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={async () => {
|
||||||
|
if (!user) return;
|
||||||
|
try {
|
||||||
|
await aethexSocialService.followUser(user.id, id);
|
||||||
|
setFollowingIds((s) => Array.from(new Set([...s, id])));
|
||||||
|
} catch {}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Follow back
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card className="bg-card/50 border-border/50">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-base">Connections</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{connectionsList.length} people
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
{connectionsList.length === 0 ? (
|
||||||
|
<div className="text-sm text-muted-foreground">No direct connections yet.</div>
|
||||||
|
) : (
|
||||||
|
connectionsList.slice(0, 6).map((row: any) => {
|
||||||
|
const up = row.user_profiles || row.profile || null;
|
||||||
|
const label = up?.full_name || up?.username || row.connection_id || "User";
|
||||||
|
const id = row.connection_id || up?.id;
|
||||||
|
const isFollowing = followingIds.includes(id);
|
||||||
|
return (
|
||||||
|
<div key={id} className="flex items-center justify-between p-2 rounded border border-border/40">
|
||||||
|
<div className="text-xs truncate">
|
||||||
|
<span className="font-medium">{label}</span>
|
||||||
|
<span className="text-muted-foreground ml-2">{id}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={async () => {
|
||||||
|
if (!user || !id) return;
|
||||||
|
try {
|
||||||
|
if (isFollowing) {
|
||||||
|
await aethexSocialService.unfollowUser(user.id, id);
|
||||||
|
setFollowingIds((s) => s.filter((x) => x !== id));
|
||||||
|
} else {
|
||||||
|
await aethexSocialService.followUser(user.id, id);
|
||||||
|
setFollowingIds((s) => Array.from(new Set([...s, id])));
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isFollowing ? "Unfollow" : "Follow"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="notifications" className="space-y-4">
|
<TabsContent value="notifications" className="space-y-4">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue