From 347609a18658f4e661d1bb0ef58e61ac0790dfa2 Mon Sep 17 00:00:00 2001 From: codex-bot Date: Tue, 21 Oct 2025 11:55:05 -0300 Subject: [PATCH] Remove tenant UI; restrict machine links to non-admin users; polish Users/Machines UX --- convex/machines.ts | 4 + src/components/admin/admin-users-manager.tsx | 136 +++++++----------- .../machines/admin-machines-overview.tsx | 1 + 3 files changed, 54 insertions(+), 87 deletions(-) diff --git a/convex/machines.ts b/convex/machines.ts index a5014a8..bb22df6 100644 --- a/convex/machines.ts +++ b/convex/machines.ts @@ -1088,6 +1088,10 @@ export const linkUser = mutation({ .withIndex("by_tenant_email", (q) => q.eq("tenantId", tenantId).eq("email", normalized)) .first() if (!user) throw new ConvexError("Usuário não encontrado") + const role = (user.role ?? "").toUpperCase() + if (role === 'ADMIN' || role === 'AGENT') { + throw new ConvexError('Usuários administrativos não podem ser vinculados a máquinas') + } const current = new Set>(machine.linkedUserIds ?? []) current.add(user._id) diff --git a/src/components/admin/admin-users-manager.tsx b/src/components/admin/admin-users-manager.tsx index 204df15..4aba10d 100644 --- a/src/components/admin/admin-users-manager.tsx +++ b/src/components/admin/admin-users-manager.tsx @@ -103,11 +103,7 @@ function machinePersonaBadgeVariant(persona: string | null | undefined) { return "outline" as const } -function formatTenantLabel(tenantId: string, defaultTenantId: string) { - if (!tenantId) return "Principal" - if (tenantId === defaultTenantId) return "Principal" - return tenantId -} +// Tenant removido da UI (sem exibição) function formatDate(dateIso: string) { const date = new Date(dateIso) @@ -242,7 +238,7 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d const [teamSearch, setTeamSearch] = useState("") const [teamRoleFilter, setTeamRoleFilter] = useState<"all" | RoleOption>("all") const [teamCompanyFilter, setTeamCompanyFilter] = useState("all") - const [teamTenantFilter, setTeamTenantFilter] = useState("all") + // Removido: filtro por espaço (tenant) const [teamSelection, setTeamSelection] = useState>(new Set()) const [isBulkDeletingTeam, setIsBulkDeletingTeam] = useState(false) const [bulkDeleteTeamOpen, setBulkDeleteTeamOpen] = useState(false) @@ -251,7 +247,7 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d const [usersSearch, setUsersSearch] = useState("") const [usersTypeFilter, setUsersTypeFilter] = useState<"all" | "people" | "machines">("people") const [usersCompanyFilter, setUsersCompanyFilter] = useState("all") - const [usersTenantFilter, setUsersTenantFilter] = useState("all") + // Removido: filtro por espaço (tenant) const [usersSelection, setUsersSelection] = useState>(new Set()) const [isBulkDeletingUsersCombined, setIsBulkDeletingUsersCombined] = useState(false) const [bulkDeleteUsersCombinedOpen, setBulkDeleteUsersCombinedOpen] = useState(false) @@ -307,19 +303,13 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d return map }, [machinesList]) - // Options of tenants present in dataset for filtering - const tenantOptions = useMemo(() => { - const set = new Set() - users.forEach((u) => u.tenantId && set.add(u.tenantId)) - const list = Array.from(set) - return list.length > 0 ? list : [defaultTenantId] - }, [users, defaultTenantId]) + // Tenant removido da UI const filteredTeamUsers = useMemo(() => { const term = teamSearch.trim().toLowerCase() return teamUsers.filter((user) => { if (teamCompanyFilter !== "all" && user.companyId !== teamCompanyFilter) return false - if (teamTenantFilter !== "all" && (user.tenantId ?? defaultTenantId) !== teamTenantFilter) return false + // filtro por espaço removido if (teamRoleFilter !== "all" && coerceRole(user.role) !== teamRoleFilter) return false if (!term) return true const haystack = [ @@ -332,7 +322,7 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d .toLowerCase() return haystack.includes(term) }) - }, [teamUsers, teamSearch, teamRoleFilter, teamCompanyFilter, teamTenantFilter, defaultTenantId]) + }, [teamUsers, teamSearch, teamRoleFilter, teamCompanyFilter]) // Removido: lista específica de Pessoas (uso substituído pelo unificado) @@ -348,7 +338,7 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d const term = usersSearch.trim().toLowerCase() return combinedBaseUsers.filter((user) => { if (usersCompanyFilter !== "all" && user.companyId !== usersCompanyFilter) return false - if (usersTenantFilter !== "all" && (user.tenantId ?? defaultTenantId) !== usersTenantFilter) return false + // filtro por espaço removido if (!term) return true const persona = (user.machinePersona ?? "").toLowerCase() const machineId = extractMachineId(user.email) ?? "" @@ -364,7 +354,7 @@ export function AdminUsersManager({ initialUsers, initialInvites, roleOptions, d .toLowerCase() return haystack.includes(term) }) - }, [combinedBaseUsers, usersSearch, usersCompanyFilter, usersTenantFilter, defaultTenantId]) + }, [combinedBaseUsers, usersSearch, usersCompanyFilter]) useEffect(() => { void (async () => { @@ -987,17 +977,7 @@ async function handleDeleteUser() { ))} - + {/* Filtro por espaço removido */} {(teamSearch.trim().length > 0 || teamRoleFilter !== "all") ? (