import Layout from "@/components/Layout"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { supabase } from "@/lib/supabase"; import { devconnect, hasDevConnect } from "@/lib/supabase-devconnect"; import { useEffect, useMemo, useState } from "react"; function initials(name?: string | null) { const parts = (name || "").trim().split(/\s+/); return ((parts[0] || "").charAt(0) + (parts[1] || "").charAt(0)).toUpperCase() || "A"; } export default function Directory() { const [query, setQuery] = useState(""); const [hideAeThex, setHideAeThex] = useState(true); const source = devconnect ? "DevConnect" : "AeThex"; type BasicDev = { id: string; name: string; avatar_url?: string | null; location?: string | null; user_type?: string | null; experience_level?: string | null }; const [devs, setDevs] = useState([]); type Studio = { id: string; name: string; description?: string | null; type?: string | null; is_recruiting?: boolean | null; recruiting_roles?: string[] | null; tags?: string[] | null; slug?: string | null; visibility?: string | null; members_count?: number }; const [studios, setStudios] = useState([]); useEffect(() => { const client = devconnect || supabase; const userTable = client === devconnect ? "profiles" : "user_profiles"; const normalize = (u: any): BasicDev => ({ id: String(u.id), name: u.full_name || u.display_name || u.username || "Developer", avatar_url: u.avatar_url || u.image_url || u.photo_url || null, location: u.location || u.city || u.country || null, user_type: u.user_type || u.role || null, experience_level: u.experience_level || u.seniority || null, }); client .from(userTable as any) .select("*") .limit(200) .then(({ data, error }) => { if (!error && Array.isArray(data)) setDevs(data.map(normalize)); else if (client !== supabase) { supabase .from("user_profiles" as any) .select("*") .limit(200) .then(({ data: d2 }) => setDevs((d2 || []).map(normalize))); } }); const studiosTable = client === devconnect ? "collectives" : "teams"; const mapStudio = (r: any): Studio => ({ id: String(r.id), name: r.name, description: r.description || null, type: r.type || (r.visibility || null), is_recruiting: r.is_recruiting ?? null, recruiting_roles: r.recruiting_roles ?? null, tags: r.tags ?? null, slug: r.slug || null, visibility: r.visibility || null, members_count: Array.isArray(r.collective_members) && r.collective_members.length ? Number(r.collective_members[0]?.count ?? 0) : Array.isArray(r.team_memberships) && r.team_memberships.length ? Number(r.team_memberships[0]?.count ?? 0) : undefined, }); client .from(studiosTable as any) .select( client === devconnect ? "id,name,description,type,is_recruiting,recruiting_roles,tags,slug,created_at, collective_members:collective_members(count)" : "id,name,description,visibility,created_at, team_memberships:team_memberships(count)" ) .limit(200) .then(({ data, error }) => { if (!error && Array.isArray(data)) setStudios(data.map(mapStudio)); else if (client !== supabase) { supabase .from("teams" as any) .select("id,name,description,visibility,created_at, team_memberships:team_memberships(count)") .limit(200) .then(({ data: d2 }) => setStudios((d2 || []).map(mapStudio))); } }); }, []); const filteredDevs = useMemo(() => { const q = query.trim().toLowerCase(); return devs.filter((u) => { if (hideAeThex && u.user_type === "staff") return false; if (!q) return true; return ( (u.name || "").toLowerCase().includes(q) || (u.location || "").toLowerCase().includes(q) ); }); }, [devs, query, hideAeThex]); const filteredStudios = useMemo(() => { const q = query.trim().toLowerCase(); return studios.filter((t) => { if (!q) return true; return ( (t.name || "").toLowerCase().includes(q) || (t.description || "").toLowerCase().includes(q) || (t.tags || []).join(" ").toLowerCase().includes(q) ); }); }, [studios, query]); return (
Directory

Creators & Studios

Browse non‑AeThex creators and studios. Opt‑in visibility; public info only.

Source: {source}
setQuery(e.target.value)} />
Developers Studios
{filteredDevs.map((u) => ( {initials(u.name)}
{u.name || "Developer"}
{u.location || "Global"}
{u.user_type} {u.experience_level && {u.experience_level}}
))}
{filteredStudios.map((t) => (
{t.name} {t.is_recruiting && ( Recruiting )}
{(t.type || t.visibility) && ( {t.type || t.visibility} )}

{t.description || ""}

{typeof t.members_count === "number" && ( {t.members_count} members )} {(t.recruiting_roles && t.recruiting_roles.length > 0) && ( Roles: {t.recruiting_roles!.join(", ")} )}
{(t.tags && t.tags.length > 0) && (
{t.tags!.map((tag) => ( {tag} ))}
)}
))}
); }