Add i18 support

This commit is contained in:
Urtzi Alfaro
2025-09-22 11:04:03 +02:00
parent ecfc6a1997
commit ee36c45d25
28 changed files with 2307 additions and 565 deletions

View File

@@ -0,0 +1,283 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { PageHeader } from '../../../../components/layout';
import { Button } from '../../../../components/ui/Button';
import { Card, CardHeader, CardBody } from '../../../../components/ui/Card';
import { Badge } from '../../../../components/ui/Badge';
import { Tooltip } from '../../../../components/ui/Tooltip';
import { useTenant } from '../../../../stores/tenant.store';
import { useAuthUser } from '../../../../stores/auth.store';
import {
Plus,
Building2,
Settings,
Users,
Calendar,
MapPin,
Phone,
Mail,
Globe,
MoreHorizontal,
ArrowRight,
Crown,
Shield,
Eye
} from 'lucide-react';
const OrganizationsPage: React.FC = () => {
const navigate = useNavigate();
const user = useAuthUser();
const { currentTenant, availableTenants, switchTenant } = useTenant();
const [isLoading, setIsLoading] = useState(false);
const handleAddNewOrganization = () => {
navigate('/app/onboarding?new=true');
};
const handleSwitchToTenant = async (tenantId: string) => {
if (tenantId === currentTenant?.id) return;
setIsLoading(true);
await switchTenant(tenantId);
setIsLoading(false);
};
const handleManageTenant = (tenantId: string) => {
// Navigate to tenant settings
navigate(`/app/database/bakery-config`);
};
const handleManageTeam = (tenantId: string) => {
// Navigate to team management
navigate(`/app/database/team`);
};
const getRoleIcon = (ownerId: string) => {
if (user?.id === ownerId) {
return <Crown className="w-4 h-4 text-[var(--color-warning)]" />;
}
return <Shield className="w-4 h-4 text-[var(--color-primary)]" />;
};
const getRoleLabel = (ownerId: string) => {
if (user?.id === ownerId) {
return 'Propietario';
}
return 'Miembro';
};
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('es-ES', {
year: 'numeric',
month: 'long',
day: 'numeric'
});
};
return (
<div className="space-y-6 p-4 sm:p-6">
<PageHeader
title="Mis Organizaciones"
description="Gestiona tus panaderías y negocios"
actions={
<Button
onClick={handleAddNewOrganization}
variant="primary"
size="lg"
className="flex items-center gap-2"
>
<Plus className="w-4 h-4" />
Nueva Organización
</Button>
}
/>
{/* Statistics */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<Card>
<CardBody className="text-center">
<Building2 className="w-8 h-8 text-[var(--color-primary)] mx-auto mb-2" />
<div className="text-2xl font-bold text-[var(--text-primary)]">
{availableTenants?.length || 0}
</div>
<div className="text-sm text-[var(--text-secondary)]">Organizaciones Totales</div>
</CardBody>
</Card>
<Card>
<CardBody className="text-center">
<Crown className="w-8 h-8 text-[var(--color-warning)] mx-auto mb-2" />
<div className="text-2xl font-bold text-[var(--text-primary)]">
{availableTenants?.filter(t => t.owner_id === user?.id).length || 0}
</div>
<div className="text-sm text-[var(--text-secondary)]">Propietario</div>
</CardBody>
</Card>
<Card>
<CardBody className="text-center">
<Shield className="w-8 h-8 text-[var(--color-primary)] mx-auto mb-2" />
<div className="text-2xl font-bold text-[var(--text-primary)]">
{availableTenants?.filter(t => t.owner_id !== user?.id).length || 0}
</div>
<div className="text-sm text-[var(--text-secondary)]">Miembro</div>
</CardBody>
</Card>
</div>
{/* Organizations List */}
<div className="space-y-4">
<h3 className="text-lg font-semibold text-[var(--text-primary)]">Tus Organizaciones</h3>
{availableTenants && availableTenants.length > 0 ? (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{availableTenants.map((tenant) => (
<Card
key={tenant.id}
className={`transition-all duration-200 hover:shadow-lg ${
currentTenant?.id === tenant.id
? 'ring-2 ring-[var(--color-primary)]/20 bg-[var(--color-primary)]/5'
: 'hover:border-[var(--color-primary)]/30'
}`}
>
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-4">
<div className="flex items-start gap-3 flex-1 min-w-0">
<div className="w-12 h-12 bg-[var(--color-primary)]/10 rounded-lg flex items-center justify-center flex-shrink-0">
<Building2 className="w-6 h-6 text-[var(--color-primary)]" />
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<h4 className="font-semibold text-[var(--text-primary)] truncate">
{tenant.name}
</h4>
{currentTenant?.id === tenant.id && (
<Badge variant="primary" size="sm">Activa</Badge>
)}
</div>
<div className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
{getRoleIcon(tenant.owner_id)}
<span>{getRoleLabel(tenant.owner_id)}</span>
</div>
</div>
</div>
<div className="flex gap-1">
<Tooltip content="Configurar organización">
<Button
variant="ghost"
size="sm"
onClick={() => handleManageTenant(tenant.id)}
className="w-8 h-8 p-0"
>
<Settings className="w-4 h-4" />
</Button>
</Tooltip>
{user?.id === tenant.owner_id && (
<Tooltip content="Gestionar equipo">
<Button
variant="ghost"
size="sm"
onClick={() => handleManageTeam(tenant.id)}
className="w-8 h-8 p-0"
>
<Users className="w-4 h-4" />
</Button>
</Tooltip>
)}
</div>
</CardHeader>
<CardBody className="pt-0">
{/* Organization details */}
<div className="space-y-2 mb-4">
{tenant.business_type && (
<div className="text-sm text-[var(--text-secondary)]">
<Badge variant="outline" size="sm">{tenant.business_type}</Badge>
</div>
)}
{tenant.address && (
<div className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<MapPin className="w-4 h-4 flex-shrink-0" />
<span className="truncate">{tenant.address}</span>
</div>
)}
{tenant.city && (
<div className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<MapPin className="w-4 h-4 flex-shrink-0" />
<span>{tenant.city}</span>
</div>
)}
{tenant.phone && (
<div className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<Phone className="w-4 h-4 flex-shrink-0" />
<span>{tenant.phone}</span>
</div>
)}
<div className="flex items-center gap-2 text-sm text-[var(--text-secondary)]">
<Calendar className="w-4 h-4 flex-shrink-0" />
<span>Creada el {formatDate(tenant.created_at)}</span>
</div>
</div>
{/* Actions */}
<div className="flex gap-2">
{currentTenant?.id !== tenant.id ? (
<Button
onClick={() => handleSwitchToTenant(tenant.id)}
variant="primary"
size="sm"
disabled={isLoading}
className="flex items-center gap-2"
>
<ArrowRight className="w-4 h-4" />
Cambiar a esta organización
</Button>
) : (
<Button
onClick={() => navigate('/app/dashboard')}
variant="outline"
size="sm"
className="flex items-center gap-2"
>
<Eye className="w-4 h-4" />
Ver dashboard
</Button>
)}
</div>
</CardBody>
</Card>
))}
</div>
) : (
<Card>
<CardBody className="text-center py-12">
<Building2 className="w-16 h-16 text-[var(--text-tertiary)] mx-auto mb-4" />
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
No tienes organizaciones
</h3>
<p className="text-[var(--text-secondary)] mb-6">
Crea tu primera organización para comenzar a usar Bakery IA
</p>
<Button
onClick={handleAddNewOrganization}
variant="primary"
size="lg"
className="flex items-center gap-2"
>
<Plus className="w-4 h-4" />
Crear Primera Organización
</Button>
</CardBody>
</Card>
)}
</div>
</div>
);
};
export default OrganizationsPage;