"use client"; import { useEffect, useState, useCallback } from "react"; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Skeleton } from "@/components/ui/skeleton"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { UserPlus, Mail, Clock, RefreshCw } from "lucide-react"; import { toast } from "sonner"; import { formatDistanceToNow } from "date-fns"; interface TeamUser { id: string; name: string; email: string; role: string | null; createdAt: number; image: string | null; } interface PendingInvite { id: string; email: string; role: string; expiresAt: number; createdAt: number; } const ROLE_CONFIG: Record = { superadmin: { label: "Super Admin", color: "bg-amber-500/20 text-amber-400 border-amber-500/30" }, admin: { label: "Admin", color: "bg-blue-500/20 text-blue-400 border-blue-500/30" }, moderator: { label: "Moderator", color: "bg-violet-500/20 text-violet-400 border-violet-500/30" }, }; export default function TeamPage() { const [users, setUsers] = useState([]); const [invites, setInvites] = useState([]); const [loading, setLoading] = useState(true); const [inviteOpen, setInviteOpen] = useState(false); const [inviteEmail, setInviteEmail] = useState(""); const [inviteRole, setInviteRole] = useState<"admin" | "moderator">("moderator"); const [inviting, setInviting] = useState(false); const fetchTeam = useCallback(async () => { try { const res = await fetch("/api/team"); if (res.ok) { const data = await res.json(); setUsers(data.users); setInvites(data.pendingInvites ?? []); } } finally { setLoading(false); } }, []); useEffect(() => { fetchTeam(); }, [fetchTeam]); const handleInvite = async () => { if (!inviteEmail) return; setInviting(true); try { const res = await fetch("/api/team", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: inviteEmail, role: inviteRole }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error); toast.success(`Invitation sent to ${inviteEmail}`); setInviteOpen(false); setInviteEmail(""); fetchTeam(); } catch (err) { toast.error(err instanceof Error ? err.message : "Failed to send invitation"); } finally { setInviting(false); } }; return (

Team

Manage who can access the CubeAdmin panel

{/* Active members */} {loading ? (
{[1, 2, 3].map(i => )}
) : (
Active Members ({users.length})
{users.map(user => (
{user.name?.slice(0, 2).toUpperCase()}

{user.name}

{user.email}

{ROLE_CONFIG[user.role ?? ""]?.label ?? user.role ?? "No role"}

Joined {formatDistanceToNow(new Date(user.createdAt), { addSuffix: true })}

))}
)}
{/* Pending invites */} {invites.length > 0 && (
Pending Invitations ({invites.length})
{invites.map(invite => (

{invite.email}

Expires {formatDistanceToNow(new Date(invite.expiresAt), { addSuffix: true })}

{ROLE_CONFIG[invite.role]?.label ?? invite.role}
))}
)} {/* Invite dialog */} Invite Team Member They'll receive an email with a link to create their account.
setInviteEmail(e.target.value)} placeholder="teammate@example.com" className="bg-zinc-800 border-zinc-700 text-white placeholder:text-zinc-500 focus:border-emerald-500/50" />
); }