import { Button } from '@/components/ui/button'; import { Field, FieldLabel } from '@/components/ui/field'; import { Input } from '@/components/ui/input'; import { ResponsiveDialog, ResponsiveDialogClose, ResponsiveDialogContent, ResponsiveDialogDescription, ResponsiveDialogFooter, ResponsiveDialogHeader, ResponsiveDialogTitle, } from '@/components/ui/responsive-dialog'; import { Separator } from '@/components/ui/separator'; import { UserAvatar } from '@/components/user-avatar'; import { apiClient } from '@/lib/api-client'; import type { ComplexUsersOverview } from '@repo/api-contract'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { AlertCircle, Loader2, Mail, RotateCw, Trash2, UserPlus, UserX, Users } from 'lucide-react'; import { useEffect, useState } from 'react'; type ComplexUsersSectionProps = { complexId: string | null; canManageUsers: boolean; }; function formatDate(value: string) { return new Intl.DateTimeFormat('es-AR', { dateStyle: 'medium', timeStyle: 'short', }).format(new Date(value)); } function UsersSkeleton() { return (

Usuarios

Cargando usuarios...

); } export function ComplexUsersSection({ complexId, canManageUsers }: ComplexUsersSectionProps) { const queryClient = useQueryClient(); const [email, setEmail] = useState(''); const [errorMessage, setErrorMessage] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [invitationToCancel, setInvitationToCancel] = useState< ComplexUsersOverview['invitations'][number] | null >(null); useEffect(() => { setEmail(''); setErrorMessage(null); setSuccessMessage(null); setInvitationToCancel(null); }, [complexId]); const usersQuery = useQuery({ queryKey: ['complex-users', complexId], enabled: Boolean(complexId), queryFn: () => apiClient.complexes.listUsers(complexId!), }); const inviteMutation = useMutation({ mutationFn: (payload: { email: string }) => apiClient.complexes.inviteUser(complexId!, payload), onSuccess: async () => { setEmail(''); setErrorMessage(null); setSuccessMessage('Invitación enviada correctamente.'); await queryClient.invalidateQueries({ queryKey: ['complex-users', complexId] }); }, onError: (error: Error) => { setErrorMessage(error.message || 'No pudimos enviar la invitación.'); setSuccessMessage(null); }, }); const revokeMutation = useMutation({ mutationFn: (userId: string) => apiClient.complexes.revokeUser(complexId!, { userId }), onSuccess: async () => { setErrorMessage(null); setSuccessMessage('El usuario fue desvinculado del complejo.'); await queryClient.invalidateQueries({ queryKey: ['complex-users', complexId] }); }, onError: (error: Error) => { setErrorMessage(error.message || 'No pudimos revocar el permiso.'); setSuccessMessage(null); }, }); const resendInvitationMutation = useMutation({ mutationFn: (invitationId: string) => apiClient.complexes.resendInvitation(complexId!, invitationId), onSuccess: async () => { setErrorMessage(null); setSuccessMessage('La invitación se reenvió correctamente.'); await queryClient.invalidateQueries({ queryKey: ['complex-users', complexId] }); }, onError: (error: Error) => { setErrorMessage(error.message || 'No pudimos reenviar la invitación.'); setSuccessMessage(null); }, }); const cancelInvitationMutation = useMutation({ mutationFn: (invitationId: string) => apiClient.complexes.cancelInvitation(complexId!, invitationId), onSuccess: async () => { setErrorMessage(null); setSuccessMessage('La invitación fue cancelada.'); setInvitationToCancel(null); await queryClient.invalidateQueries({ queryKey: ['complex-users', complexId] }); }, onError: (error: Error) => { setErrorMessage(error.message || 'No pudimos cancelar la invitación.'); setSuccessMessage(null); }, }); const handleInvite = async (event: React.FormEvent) => { event.preventDefault(); setErrorMessage(null); setSuccessMessage(null); const trimmedEmail = email.trim(); if (!trimmedEmail) { setErrorMessage('Ingresá un email.'); return; } inviteMutation.mutate({ email: trimmedEmail }); }; if (!complexId) { return (

Usuarios

Seleccioná un complejo para ver sus usuarios.

); } if (usersQuery.isLoading) { return ; } if (usersQuery.isError || !usersQuery.data) { return (

Usuarios

No se pudieron cargar los usuarios del complejo.

); } const data: ComplexUsersOverview = usersQuery.data; return (

Usuarios

{canManageUsers ? (

Invitar un usuario

Email setEmail(event.target.value)} placeholder="empleado@correo.com" />

La invitación se envía por email y el usuario quedará vinculado como EMPLOYEE al aceptar.

) : (
Solo un ADMIN puede invitar o desvincular usuarios.
)} {successMessage && (

{successMessage}

)} {errorMessage && (

{errorMessage}

)}

Miembros

{data.members.map((member) => (

{member.fullName}

{member.email}

Rol: {member.role} · Desde {formatDate(member.createdAt)}

{canManageUsers && member.role === 'EMPLOYEE' && ( )}
))}

Invitaciones pendientes

{data.invitations.length === 0 ? (

No hay invitaciones pendientes.

) : (
{data.invitations.map((invitation) => (

{invitation.email}

Vence {formatDate(invitation.expiresAt)}

Pendiente {canManageUsers && (
)}
))}
)}
{ if (!open) { setInvitationToCancel(null); } }} > Cancelar invitación Esta acción va a invalidar el link de invitación y la persona ya no podrá aceptar la invitación.

{invitationToCancel ? `Se cancelará la invitación enviada a ${invitationToCancel.email}.` : 'Se cancelará la invitación seleccionada.'}

Si querés volver a invitarlo, vas a poder generar una nueva invitación después.

); }