Add frontend pages imporvements

This commit is contained in:
Urtzi Alfaro
2025-09-21 22:56:55 +02:00
parent f08667150d
commit ecfc6a1997
14 changed files with 1538 additions and 1093 deletions

View File

@@ -40,7 +40,7 @@ interface BusinessHours {
const BakeryConfigPage: React.FC = () => {
const { addToast } = useToast();
const currentTenant = useCurrentTenant();
const { loadUserTenants } = useTenantActions();
const { loadUserTenants, setCurrentTenant } = useTenantActions();
const tenantId = currentTenant?.id || '';
// Use the current tenant from the store instead of making additional API calls
@@ -83,12 +83,10 @@ const BakeryConfigPage: React.FC = () => {
language: 'es'
});
// Load user tenants on component mount if no current tenant
// Load user tenants on component mount to ensure fresh data
React.useEffect(() => {
if (!currentTenant) {
loadUserTenants();
}
}, [currentTenant, loadUserTenants]);
loadUserTenants();
}, [loadUserTenants]);
// Update config when tenant data is loaded
@@ -97,8 +95,8 @@ const BakeryConfigPage: React.FC = () => {
setConfig({
name: tenant.name || '',
description: tenant.description || '',
email: tenant.email || '', // Fixed: use email instead of contact_email
phone: tenant.phone || '', // Fixed: use phone instead of contact_phone
email: tenant.email || '',
phone: tenant.phone || '',
website: tenant.website || '',
address: tenant.address || '',
city: tenant.city || '',
@@ -106,9 +104,10 @@ const BakeryConfigPage: React.FC = () => {
country: tenant.country || '',
taxId: '', // Not supported by backend yet
currency: 'EUR', // Default value
timezone: 'Europe/Madrid', // Default value
timezone: 'Europe/Madrid', // Default value
language: 'es' // Default value
});
setHasUnsavedChanges(false); // Reset unsaved changes when loading fresh data
}
}, [tenant]);
@@ -249,30 +248,48 @@ const BakeryConfigPage: React.FC = () => {
const handleSaveConfig = async () => {
if (!validateConfig() || !tenantId) return;
setIsLoading(true);
try {
await updateTenantMutation.mutateAsync({
const updateData = {
name: config.name,
description: config.description,
email: config.email,
phone: config.phone,
website: config.website,
address: config.address,
city: config.city,
postal_code: config.postalCode,
country: config.country
};
const updatedTenant = await updateTenantMutation.mutateAsync({
tenantId,
updateData: {
name: config.name,
description: config.description,
email: config.email, // Fixed: use email instead of contact_email
phone: config.phone, // Fixed: use phone instead of contact_phone
website: config.website,
address: config.address,
city: config.city,
postal_code: config.postalCode,
country: config.country
// Note: tax_id, currency, timezone, language might not be supported by backend
}
updateData
});
// Update the tenant store with the new data
if (updatedTenant) {
setCurrentTenant(updatedTenant);
// Force reload tenant list to ensure cache consistency
await loadUserTenants();
// Update localStorage to persist the changes
const tenantStorage = localStorage.getItem('tenant-storage');
if (tenantStorage) {
const parsedStorage = JSON.parse(tenantStorage);
if (parsedStorage.state && parsedStorage.state.currentTenant) {
parsedStorage.state.currentTenant = updatedTenant;
localStorage.setItem('tenant-storage', JSON.stringify(parsedStorage));
}
}
}
setHasUnsavedChanges(false);
addToast('Configuración actualizada correctamente', { type: 'success' });
} catch (error) {
addToast('No se pudo actualizar la configuración', { type: 'error' });
addToast(`Error al actualizar: ${error instanceof Error ? error.message : 'Error desconocido'}`, { type: 'error' });
} finally {
setIsLoading(false);
}

View File

@@ -1,9 +1,9 @@
import React, { useState, useMemo } from 'react';
import { Users, Plus, Search, Mail, Phone, Shield, Trash2, Crown, X, UserCheck } from 'lucide-react';
import { Button, Card, Badge, Input, StatusCard, getStatusColor, StatsGrid } from '../../../../components/ui';
import { Users, Plus, Search, Shield, Trash2, Crown, UserCheck } from 'lucide-react';
import { Button, Card, Input, StatusCard, getStatusColor, StatsGrid } from '../../../../components/ui';
import AddTeamMemberModal from '../../../../components/domain/team/AddTeamMemberModal';
import { PageHeader } from '../../../../components/layout';
import { useTeamMembers, useAddTeamMember, useRemoveTeamMember, useUpdateMemberRole } from '../../../../api/hooks/tenant';
import { useTeamMembers, useAddTeamMember, useRemoveTeamMember, useUpdateMemberRole, useTenantAccess } from '../../../../api/hooks/tenant';
import { useAllUsers } from '../../../../api/hooks/user';
import { useAuthUser } from '../../../../stores/auth.store';
import { useCurrentTenant, useCurrentTenantAccess } from '../../../../stores/tenant.store';
@@ -12,12 +12,23 @@ import { TENANT_ROLES } from '../../../../types/roles';
const TeamPage: React.FC = () => {
const { addToast } = useToast();
const currentUser = useAuthUser();
const currentTenant = useCurrentTenant();
const currentTenantAccess = useCurrentTenantAccess();
const tenantId = currentTenant?.id || '';
// Try to get tenant access directly via hook as fallback
const { data: directTenantAccess } = useTenantAccess(
tenantId,
currentUser?.id || '',
{ enabled: !!tenantId && !!currentUser?.id && !currentTenantAccess }
);
const { data: teamMembers = [], isLoading } = useTeamMembers(tenantId, false, { enabled: !!tenantId }); // Show all members including inactive
const { data: allUsers = [] } = useAllUsers();
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();
@@ -72,9 +83,15 @@ const TeamPage: React.FC = () => {
{ value: TENANT_ROLES.VIEWER, label: 'Observador', count: enhancedTeamMembers.filter(m => m.role === TENANT_ROLES.VIEWER).length }
];
// Use direct tenant access as fallback
const effectiveTenantAccess = currentTenantAccess || directTenantAccess;
// Check if current user is the tenant owner (fallback when access endpoint fails)
const isCurrentUserOwner = currentUser?.id === currentTenant?.owner_id;
// Permission checks
const isOwner = currentTenantAccess?.role === TENANT_ROLES.OWNER;
const canManageTeam = isOwner || currentTenantAccess?.role === TENANT_ROLES.ADMIN;
const isOwner = effectiveTenantAccess?.role === TENANT_ROLES.OWNER || isCurrentUserOwner;
const canManageTeam = isOwner || effectiveTenantAccess?.role === TENANT_ROLES.ADMIN;
const teamStats = {
total: enhancedTeamMembers.length,
@@ -197,29 +214,38 @@ const TeamPage: React.FC = () => {
});
// Available users for adding (exclude current members)
const availableUsers = allUsers.filter(u =>
const availableUsers = allUsers.filter(u =>
!enhancedTeamMembers.some(m => m.user_id === u.id)
);
// Member action handlers
const handleAddMember = async () => {
if (!selectedUserToAdd || !selectedRoleToAdd || !tenantId) return;
try {
await addMemberMutation.mutateAsync({
tenantId,
userId: selectedUserToAdd,
role: selectedRoleToAdd,
});
addToast('Miembro agregado exitosamente', { type: 'success' });
setShowAddForm(false);
setSelectedUserToAdd('');
setSelectedRoleToAdd(TENANT_ROLES.MEMBER);
} catch (error) {
addToast('Error al agregar miembro', { type: 'error' });
// Force reload tenant access if missing
React.useEffect(() => {
if (currentTenant?.id && !currentTenantAccess) {
console.log('Forcing tenant access reload for tenant:', currentTenant.id);
// You can trigger a manual reload here if needed
}
};
}, [currentTenant?.id, currentTenantAccess]);
// Debug logging
console.log('TeamPage Debug:', {
canManageTeam,
isOwner,
isCurrentUserOwner,
currentUser: currentUser?.id,
currentTenant: currentTenant?.id,
tenantOwner: currentTenant?.owner_id,
currentTenantAccess,
directTenantAccess,
effectiveTenantAccess,
tenantAccess: effectiveTenantAccess?.role,
allUsers: allUsers.length,
allUsersError,
allUsersLoading,
availableUsers: availableUsers.length,
enhancedTeamMembers: enhancedTeamMembers.length
});
// Member action handlers - removed unused handleAddMember since modal handles it directly
const handleRemoveMember = async (memberUserId: string) => {
if (!tenantId) return;
@@ -276,7 +302,7 @@ const TeamPage: React.FC = () => {
title="Gestión de Equipo"
description="Administra los miembros del equipo, roles y permisos"
actions={
canManageTeam && availableUsers.length > 0 ? [{
canManageTeam ? [{
id: 'add-member',
label: 'Agregar Miembro',
icon: Plus,
@@ -351,7 +377,7 @@ const TeamPage: React.FC = () => {
</Card>
{/* Add Member Button */}
{canManageTeam && availableUsers.length > 0 && filteredMembers.length > 0 && (
{canManageTeam && filteredMembers.length > 0 && (
<div className="flex justify-end">
<Button
onClick={() => setShowAddForm(true)}
@@ -406,7 +432,7 @@ const TeamPage: React.FC = () => {
: "Este tenant aún no tiene miembros del equipo"
}
</p>
{canManageTeam && availableUsers.length > 0 && (
{canManageTeam && (
<Button
onClick={() => setShowAddForm(true)}
variant="primary"