Improve the frontend and fix TODOs

This commit is contained in:
Urtzi Alfaro
2025-10-24 13:05:04 +02:00
parent 07c33fa578
commit 61376b7a9f
100 changed files with 8284 additions and 3419 deletions

View File

@@ -4,8 +4,7 @@ import { Users, Plus, Search, Shield, Trash2, Crown, UserCheck } from 'lucide-re
import { Button, StatusCard, getStatusColor, StatsGrid, SearchAndFilter, type FilterConfig } from '../../../../components/ui';
import AddTeamMemberModal from '../../../../components/domain/team/AddTeamMemberModal';
import { PageHeader } from '../../../../components/layout';
import { useTeamMembers, useAddTeamMember, useRemoveTeamMember, useUpdateMemberRole, useTenantAccess } from '../../../../api/hooks/tenant';
import { useAllUsers } from '../../../../api/hooks/user';
import { useTeamMembers, useAddTeamMember, useAddTeamMemberWithUserCreation, useRemoveTeamMember, useUpdateMemberRole, useTenantAccess } from '../../../../api/hooks/tenant';
import { useAuthUser } from '../../../../stores/auth.store';
import { useCurrentTenant, useCurrentTenantAccess } from '../../../../stores/tenant.store';
import { useToast } from '../../../../hooks/ui/useToast';
@@ -26,15 +25,12 @@ const TeamPage: React.FC = () => {
currentUser?.id || '',
{ enabled: !!tenantId && !!currentUser?.id && !currentTenantAccess }
);
const { data: teamMembers = [], isLoading } = useTeamMembers(tenantId, false, { enabled: !!tenantId }); // Show all members including inactive
const { data: allUsers = [], error: allUsersError, isLoading: allUsersLoading } = useAllUsers({
retry: false, // Don't retry on permission errors
staleTime: 0 // Always fresh check for permissions
});
// Mutations
const addMemberMutation = useAddTeamMember();
const addMemberWithUserMutation = useAddTeamMemberWithUserCreation();
const removeMemberMutation = useRemoveTeamMember();
const updateRoleMutation = useUpdateMemberRole();
@@ -46,37 +42,35 @@ const TeamPage: React.FC = () => {
// Enhanced team members that includes owner information
// Note: Backend now enriches members with user info, so we just need to ensure owner is present
const enhancedTeamMembers = useMemo(() => {
const members = [...teamMembers];
// If tenant owner is not in the members list, add them
// If tenant owner is not in the members list, add them as a placeholder
if (currentTenant?.owner_id) {
const ownerInMembers = members.find(m => m.user_id === currentTenant.owner_id);
if (!ownerInMembers) {
// Find owner user data
const ownerUser = allUsers.find(u => u.id === currentTenant.owner_id);
if (ownerUser) {
// Add owner as a member
members.push({
id: `owner-${currentTenant.owner_id}`,
tenant_id: tenantId,
user_id: currentTenant.owner_id,
role: TENANT_ROLES.OWNER,
is_active: true,
joined_at: currentTenant.created_at,
user_email: ownerUser.email,
user_full_name: ownerUser.full_name,
user: ownerUser, // Add full user object for compatibility
} as any);
}
// Add owner as a member with basic info
// Note: The backend should ideally include the owner in the members list
members.push({
id: `owner-${currentTenant.owner_id}`,
tenant_id: tenantId,
user_id: currentTenant.owner_id,
role: TENANT_ROLES.OWNER,
is_active: true,
joined_at: currentTenant.created_at,
user_email: null, // Backend will enrich this
user_full_name: null, // Backend will enrich this
user: null,
} as any);
} else if (ownerInMembers.role !== TENANT_ROLES.OWNER) {
// Update existing member to owner role
ownerInMembers.role = TENANT_ROLES.OWNER;
}
}
return members;
}, [teamMembers, currentTenant, allUsers, tenantId]);
}, [teamMembers, currentTenant, tenantId]);
const roles = [
{ value: 'all', label: 'Todos los Roles', count: enhancedTeamMembers.length },
@@ -160,36 +154,54 @@ const TeamPage: React.FC = () => {
const getMemberActions = (member: any) => {
const actions = [];
// Role change actions (only for non-owners and if user can manage team)
// Primary action - View details (always available)
// This will be implemented in the future to show detailed member info modal
// For now, we can comment it out as there's no modal yet
// actions.push({
// label: 'Ver Detalles',
// icon: Eye,
// priority: 'primary' as const,
// onClick: () => {
// // TODO: Implement member details modal
// console.log('View member details:', member.user_id);
// },
// });
// Contextual role change actions (only for non-owners and if user can manage team)
if (canManageTeam && member.role !== TENANT_ROLES.OWNER) {
if (member.role !== TENANT_ROLES.ADMIN) {
// Promote/demote to most logical next role
if (member.role === TENANT_ROLES.VIEWER) {
// Viewer -> Member (promote)
actions.push({
label: 'Hacer Admin',
icon: Shield,
onClick: () => handleUpdateRole(member.user_id, TENANT_ROLES.ADMIN),
priority: 'secondary' as const,
});
}
if (member.role !== TENANT_ROLES.MEMBER) {
actions.push({
label: 'Hacer Miembro',
icon: Users,
label: 'Promover a Miembro',
icon: UserCheck,
onClick: () => handleUpdateRole(member.user_id, TENANT_ROLES.MEMBER),
priority: 'secondary' as const,
});
}
if (member.role !== TENANT_ROLES.VIEWER) {
actions.push({
label: 'Hacer Observador',
onClick: () => handleUpdateRole(member.user_id, TENANT_ROLES.VIEWER),
priority: 'tertiary' as const,
});
} else if (member.role === TENANT_ROLES.MEMBER) {
// Member -> Admin (promote) or Member -> Viewer (demote)
if (isOwner) {
actions.push({
label: 'Promover a Admin',
icon: Shield,
onClick: () => handleUpdateRole(member.user_id, TENANT_ROLES.ADMIN),
priority: 'secondary' as const,
});
}
} else if (member.role === TENANT_ROLES.ADMIN) {
// Admin -> Member (demote) - only owner can do this
if (isOwner) {
actions.push({
label: 'Cambiar a Miembro',
icon: Users,
onClick: () => handleUpdateRole(member.user_id, TENANT_ROLES.MEMBER),
priority: 'secondary' as const,
});
}
}
}
// Remove member action (only for owners)
// Remove member action (only for owners and non-owner members)
if (isOwner && member.role !== TENANT_ROLES.OWNER) {
actions.push({
label: 'Remover',
@@ -199,7 +211,7 @@ const TeamPage: React.FC = () => {
handleRemoveMember(member.user_id);
}
},
priority: 'tertiary' as const,
priority: 'secondary' as const,
destructive: true,
});
}
@@ -216,11 +228,6 @@ const TeamPage: React.FC = () => {
return matchesRole && matchesSearch;
});
// Available users for adding (exclude current members)
const availableUsers = allUsers.filter(u =>
!enhancedTeamMembers.some(m => m.user_id === u.id)
);
// Force reload tenant access if missing
React.useEffect(() => {
if (currentTenant?.id && !currentTenantAccess) {
@@ -241,10 +248,6 @@ const TeamPage: React.FC = () => {
directTenantAccess,
effectiveTenantAccess,
tenantAccess: effectiveTenantAccess?.role,
allUsers: allUsers.length,
allUsersError,
allUsersLoading,
availableUsers: availableUsers.length,
enhancedTeamMembers: enhancedTeamMembers.length
});
@@ -458,13 +461,31 @@ const TeamPage: React.FC = () => {
throw new Error(errorMessage);
}
await addMemberMutation.mutateAsync({
tenantId,
userId: userData.userId,
role: userData.role,
});
// Use appropriate mutation based on whether we're creating a user
if (userData.createUser) {
await addMemberWithUserMutation.mutateAsync({
tenantId,
memberData: {
create_user: true,
email: userData.email!,
full_name: userData.fullName!,
password: userData.password!,
phone: userData.phone,
role: userData.role,
language: 'es',
timezone: 'Europe/Madrid'
}
});
addToast('Usuario creado y agregado exitosamente', { type: 'success' });
} else {
await addMemberMutation.mutateAsync({
tenantId,
userId: userData.userId!,
role: userData.role,
});
addToast('Miembro agregado exitosamente', { type: 'success' });
}
addToast('Miembro agregado exitosamente', { type: 'success' });
setShowAddForm(false);
setSelectedUserToAdd('');
setSelectedRoleToAdd(TENANT_ROLES.MEMBER);
@@ -473,11 +494,14 @@ const TeamPage: React.FC = () => {
// Limit error already toasted above
throw error;
}
addToast('Error al agregar miembro', { type: 'error' });
addToast(
userData.createUser ? 'Error al crear usuario' : 'Error al agregar miembro',
{ type: 'error' }
);
throw error;
}
}}
availableUsers={availableUsers}
availableUsers={[]}
/>
</div>
);