"use client"; import { FormEvent, useEffect, useState } from "react"; import { api } from "@/lib/api"; import { Modal, RegistryTable, fieldClass } from "@/components/registry-table"; type Tenant = { id: string; name: string; slug: string; status: string; }; type Created = Tenant & { access: { email: string; password: string; path: string }; }; type TenantUser = { id: string; name: string; email: string; role: "ADMIN" | "MANAGER" | "OPERATOR"; password: string | null; }; const roleLabel = { ADMIN: "Administrador", MANAGER: "Manager", OPERATOR: "Operador", } as const; function slugFromName(value: string) { return value .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 30); } export default function PlatformPage() { const [tenants, setTenants] = useState([]); const [chatCounts, setChatCounts] = useState>({}); const [error, setError] = useState(""); const [open, setOpen] = useState(false); const [name, setName] = useState(""); const [slug, setSlug] = useState(""); const [created, setCreated] = useState(null); const [saving, setSaving] = useState(false); const [usersOf, setUsersOf] = useState(null); const [users, setUsers] = useState([]); const [usersError, setUsersError] = useState(""); const [issuing, setIssuing] = useState(null); async function load() { const [list, counts] = await Promise.all([ api("/api/platform/tenants"), api>("/api/chat/counts").catch(() => ({})), ]); setTenants(list); setChatCounts(counts); } useEffect(() => { load().catch((err: unknown) => setError(err instanceof Error ? err.message : "Erro")); }, []); function closeModal() { setOpen(false); setCreated(null); setName(""); setSlug(""); setError(""); } async function openUsers(tenant: Tenant) { setUsersError(""); setUsersOf(tenant); setUsers([]); try { const result = await api<{ users: TenantUser[] }>(`/api/platform/tenants/${tenant.id}/users`); setUsers(result.users); } catch (err) { setUsersError(err instanceof Error ? err.message : "Não foi possível listar os usuários."); } } function closeUsers() { setUsersOf(null); setUsers([]); setUsersError(""); setIssuing(null); } async function issuePassword(userId: string) { if (!usersOf) { return; } setUsersError(""); setIssuing(userId); try { const result = await api<{ id: string; password: string }>( `/api/platform/tenants/${usersOf.id}/users/${userId}/password`, { method: "POST" }, ); setUsers((current) => current.map((user) => (user.id === userId ? { ...user, password: result.password } : user)), ); } catch (err) { setUsersError(err instanceof Error ? err.message : "Não foi possível gerar a senha."); } finally { setIssuing(null); } } async function onCreate(event: FormEvent) { event.preventDefault(); setError(""); setSaving(true); try { const result = await api("/api/platform/tenants", { method: "POST", body: JSON.stringify({ name, slug }), }); setCreated(result); await load(); } catch (err) { setError(err instanceof Error ? err.message : "Não foi possível criar o cliente."); } finally { setSaving(false); } } return (

Admin

Clientes

Quem contrata a Sinka. Cada cliente tem um endereço próprio para o time entrar.

{error && !open ?

{error}

: null} setOpen(true)}> Adicionar } > {tenants.map((tenant) => ( {tenant.name} /{tenant.slug} {tenant.status === "ACTIVE" ? "Ativo" : "Suspenso"} ))} {open ? ( {created ? (

Cliente criado

Guarde o acesso do administrador:

Endereço:{" "} {created.access.path}

E-mail: {created.access.email}

Senha: {created.access.password}

) : (

Novo cliente

{error ?

{error}

: null} { setName(event.target.value); setSlug(slugFromName(event.target.value)); }} required /> setSlug(event.target.value.toLowerCase())} required />
)}
) : null} {usersOf ? (

Usuários · {usersOf.name}

Contas de {usersOf.name}. Se a senha estiver vazia, gere uma nova.

{usersError ?

{usersError}

: null}
{users.length === 0 ? ( ) : ( users.map((user) => ( )) )}
Nome Usuário Senha Papel
Nenhum usuário neste cliente.
{user.name} {user.email} {user.password || "-"} {roleLabel[user.role]}
) : null}
); }