diff --git a/apps/api/src/controllers/client-invitations.controller.ts b/apps/api/src/controllers/client-invitations.controller.ts index c1c8f96..aec8958 100644 --- a/apps/api/src/controllers/client-invitations.controller.ts +++ b/apps/api/src/controllers/client-invitations.controller.ts @@ -9,10 +9,10 @@ export async function createInvitation(req: Request, res: Response, next: NextFu return res.status(400).json({ message: 'El email es requerido' }); } - // Admin y Vendedor (platform_sales) pueden crear invitaciones - const isAdmin = await hasAnyPlatformRole(req.user!.userId, 'platform_admin', 'platform_sales'); + // Solo platform_admin puede crear invitaciones de cliente + const isAdmin = await hasAnyPlatformRole(req.user!.userId, 'platform_admin'); if (!isAdmin) { - return res.status(403).json({ message: 'Solo administradores o vendedores pueden crear invitaciones' }); + return res.status(403).json({ message: 'Solo administradores pueden crear invitaciones' }); } const invitation = await clientInvitationService.createInvitation({ @@ -70,7 +70,7 @@ export async function registerFromInvitation(req: Request, res: Response, next: export async function resendInvitation(req: Request, res: Response, next: NextFunction) { try { - const isAdmin = await hasAnyPlatformRole(req.user!.userId, 'platform_admin', 'platform_sales'); + const isAdmin = await hasAnyPlatformRole(req.user!.userId, 'platform_admin'); if (!isAdmin) { return res.status(403).json({ message: 'No autorizado' }); } @@ -88,7 +88,7 @@ export async function resendInvitation(req: Request, res: Response, next: NextFu export async function listInvitations(req: Request, res: Response, next: NextFunction) { try { - const isAdmin = await hasAnyPlatformRole(req.user!.userId, 'platform_admin', 'platform_sales'); + const isAdmin = await hasAnyPlatformRole(req.user!.userId, 'platform_admin'); if (!isAdmin) { return res.status(403).json({ message: 'No autorizado' }); } diff --git a/apps/api/src/controllers/tenants.controller.ts b/apps/api/src/controllers/tenants.controller.ts index 2f74e4f..71ee138 100644 --- a/apps/api/src/controllers/tenants.controller.ts +++ b/apps/api/src/controllers/tenants.controller.ts @@ -3,6 +3,7 @@ import { z } from 'zod'; import * as tenantsService from '../services/tenants.service.js'; import { AppError } from '../middlewares/error.middleware.js'; import { isGlobalAdmin } from '../utils/global-admin.js'; +import { hasAnyPlatformRole } from '../utils/platform-admin.js'; import { isOwnerSomewhere } from '../utils/memberships.js'; async function requireGlobalAdmin(req: Request): Promise { @@ -13,8 +14,10 @@ async function requireGlobalAdmin(req: Request): Promise { export async function getAllTenants(req: Request, res: Response, next: NextFunction) { try { - const isAdmin = await isGlobalAdmin(req.user!.tenantId, req.user!.role, req.user!.userId); - if (!isAdmin) { + // Admin global, TI y Vendedor pueden ver el listado completo de tenants. + // Vendedor lo necesita para enviar invitaciones de trial. + const canList = await hasAnyPlatformRole(req.user!.userId, 'platform_admin', 'platform_ti', 'platform_sales'); + if (!canList) { // Evita 403 en consola del frontend cuando componentes sin-gate hacen polling res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); return res.json([]); diff --git a/apps/web/app/(dashboard)/admin/staff/page.tsx b/apps/web/app/(dashboard)/admin/staff/page.tsx index d69dc35..db42d2a 100644 --- a/apps/web/app/(dashboard)/admin/staff/page.tsx +++ b/apps/web/app/(dashboard)/admin/staff/page.tsx @@ -12,7 +12,7 @@ const ROLE_META: Record