fix: TypeScript build errors in frontend
- Fix CFDI type errors (ivaTraslado, tipoCambio, id types) - Fix sidebar navigation type errors (Role vs literal tuples) - Fix user invite type errors (UserInvite['role']) - Fix login page PlatformRole type cast
This commit is contained in:
@@ -7,7 +7,7 @@ import Image from 'next/image';
|
|||||||
import { Button, Input, Label, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@horux/shared-ui';
|
import { Button, Input, Label, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@horux/shared-ui';
|
||||||
import { login } from '@/lib/api/auth';
|
import { login } from '@/lib/api/auth';
|
||||||
import { useAuthStore } from '@/stores/auth-store';
|
import { useAuthStore } from '@/stores/auth-store';
|
||||||
import { isGlobalAdminRfc } from '@horux/shared';
|
import { isGlobalAdminRfc, type PlatformRole } from '@horux/shared';
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -32,7 +32,7 @@ export default function LoginPage() {
|
|||||||
const userRole = response.user?.role;
|
const userRole = response.user?.role;
|
||||||
// Admin global aterriza directo en `/clientes` — su home natural es la
|
// Admin global aterriza directo en `/clientes` — su home natural es la
|
||||||
// gestión de tenants, no el dashboard operativo del despacho.
|
// gestión de tenants, no el dashboard operativo del despacho.
|
||||||
const platformRoles = (response.user as { platformRoles?: string[] }).platformRoles;
|
const platformRoles = (response.user as { platformRoles?: PlatformRole[] }).platformRoles;
|
||||||
const isGlobalAdmin = isGlobalAdminRfc(response.user?.tenantRfc, userRole, platformRoles);
|
const isGlobalAdmin = isGlobalAdminRfc(response.user?.tenantRfc, userRole, platformRoles);
|
||||||
if (isGlobalAdmin) {
|
if (isGlobalAdmin) {
|
||||||
router.push('/clientes');
|
router.push('/clientes');
|
||||||
|
|||||||
@@ -328,10 +328,11 @@ export default function CfdiPage() {
|
|||||||
const [cancelSubstitution, setCancelSubstitution] = useState('');
|
const [cancelSubstitution, setCancelSubstitution] = useState('');
|
||||||
const [cancelling, setCancelling] = useState(false);
|
const [cancelling, setCancelling] = useState(false);
|
||||||
|
|
||||||
const handleViewCfdi = async (id: string) => {
|
const handleViewCfdi = async (id: string | number) => {
|
||||||
setLoadingCfdi(id);
|
const idStr = String(id);
|
||||||
|
setLoadingCfdi(idStr);
|
||||||
try {
|
try {
|
||||||
const cfdi = await getCfdiById(id);
|
const cfdi = await getCfdiById(idStr);
|
||||||
setViewingCfdi(cfdi);
|
setViewingCfdi(cfdi);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading CFDI:', error);
|
console.error('Error loading CFDI:', error);
|
||||||
@@ -738,10 +739,11 @@ export default function CfdiPage() {
|
|||||||
setUploadProgress(prev => ({ ...prev, status: 'idle' }));
|
setUploadProgress(prev => ({ ...prev, status: 'idle' }));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (id: string) => {
|
const handleDelete = async (id: string | number) => {
|
||||||
|
const idStr = String(id);
|
||||||
if (confirm('¿Eliminar este CFDI?')) {
|
if (confirm('¿Eliminar este CFDI?')) {
|
||||||
try {
|
try {
|
||||||
await deleteCfdi.mutateAsync(id);
|
await deleteCfdi.mutateAsync(idStr);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error deleting CFDI:', error);
|
console.error('Error deleting CFDI:', error);
|
||||||
}
|
}
|
||||||
@@ -776,9 +778,9 @@ export default function CfdiPage() {
|
|||||||
const calculateTotal = () => {
|
const calculateTotal = () => {
|
||||||
const subtotal = formData.subtotal || 0;
|
const subtotal = formData.subtotal || 0;
|
||||||
const descuento = formData.descuento || 0;
|
const descuento = formData.descuento || 0;
|
||||||
const iva = formData.ivaTrasladoTraslado || 0;
|
const iva = formData.ivaTraslado || 0;
|
||||||
const isrRetencion = formData.isrRetencion || 0;
|
const isrRetencion = formData.isrRetencion || 0;
|
||||||
const ivaRetencion = formData.ivaTrasladoRetencion || 0;
|
const ivaRetencion = formData.ivaRetencion || 0;
|
||||||
return subtotal - descuento + iva - isrRetencion - ivaRetencion;
|
return subtotal - descuento + iva - isrRetencion - ivaRetencion;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1642,10 +1644,10 @@ export default function CfdiPage() {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
onClick={() => handleViewCfdi(cfdi.id)}
|
onClick={() => handleViewCfdi(cfdi.id)}
|
||||||
disabled={loadingCfdi === cfdi.id}
|
disabled={loadingCfdi === String(cfdi.id)}
|
||||||
title="Ver factura"
|
title="Ver factura"
|
||||||
>
|
>
|
||||||
{loadingCfdi === cfdi.id ? (
|
{loadingCfdi === String(cfdi.id) ? (
|
||||||
<Loader2 className="h-4 w-4 animate-spin" />
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
<Eye className="h-4 w-4" />
|
<Eye className="h-4 w-4" />
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ function RegimenesActivosSection() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activos && catalogo) {
|
if (activos && catalogo) {
|
||||||
const ids = new Set(activos.map(a => catalogo.find(c => c.clave === a.clave)?.id).filter(Boolean) as number[]);
|
const ids = new Set(activos.map((a: { clave: string }) => catalogo.find((c: { clave: string }) => c.clave === a.clave)?.id).filter(Boolean) as number[]);
|
||||||
setSelected(ids);
|
setSelected(ids);
|
||||||
}
|
}
|
||||||
}, [activos, catalogo]);
|
}, [activos, catalogo]);
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { cn } from '@horux/shared-ui';
|
import { cn } from '@horux/shared-ui';
|
||||||
import { isDespachoTenant } from '@horux/shared';
|
import { isDespachoTenant } from '@horux/shared';
|
||||||
import type { Role } from '@horux/shared';
|
import type { Role, UserInvite } from '@horux/shared';
|
||||||
|
|
||||||
// ── Horux360 legacy roles ─────────────────────────────────────────────────────
|
// ── Horux360 legacy roles ─────────────────────────────────────────────────────
|
||||||
const legacyRoleLabels: Record<string, { label: string; icon: React.ElementType; color: string }> = {
|
const legacyRoleLabels: Record<string, { label: string; icon: React.ElementType; color: string }> = {
|
||||||
@@ -25,7 +25,7 @@ const legacyRoleLabels: Record<string, { label: string; icon: React.ElementType;
|
|||||||
visor: { label: 'Visor', icon: Eye, color: 'text-muted-foreground' },
|
visor: { label: 'Visor', icon: Eye, color: 'text-muted-foreground' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const legacyInviteRoles: { value: string; label: string }[] = [
|
const legacyInviteRoles: { value: string; label: string; description?: string }[] = [
|
||||||
{ value: 'contador', label: 'Contador' },
|
{ value: 'contador', label: 'Contador' },
|
||||||
{ value: 'visor', label: 'Visor' },
|
{ value: 'visor', label: 'Visor' },
|
||||||
{ value: 'auxiliar', label: 'Auxiliar' },
|
{ value: 'auxiliar', label: 'Auxiliar' },
|
||||||
@@ -39,7 +39,7 @@ const despachoRoleLabels: Record<string, { label: string; icon: React.ElementTyp
|
|||||||
cliente: { label: 'Cliente', icon: Building2, color: 'text-muted-foreground' },
|
cliente: { label: 'Cliente', icon: Building2, color: 'text-muted-foreground' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const despachoInviteRoles: { value: string; label: string; description: string }[] = [
|
const despachoInviteRoles: { value: string; label: string; description?: string }[] = [
|
||||||
{
|
{
|
||||||
value: 'supervisor',
|
value: 'supervisor',
|
||||||
label: 'Supervisor',
|
label: 'Supervisor',
|
||||||
@@ -83,10 +83,10 @@ export default function UsuariosPage() {
|
|||||||
const isAdmin = currentUser?.role === 'owner' || currentUser?.role === 'cfo';
|
const isAdmin = currentUser?.role === 'owner' || currentUser?.role === 'cfo';
|
||||||
|
|
||||||
const [showInvite, setShowInvite] = useState(false);
|
const [showInvite, setShowInvite] = useState(false);
|
||||||
const [inviteForm, setInviteForm] = useState<{ email: string; nombre: string; role: Role; supervisorUserId?: string }>({
|
const [inviteForm, setInviteForm] = useState<{ email: string; nombre: string; role: UserInvite['role']; supervisorUserId?: string }>({
|
||||||
email: '',
|
email: '',
|
||||||
nombre: '',
|
nombre: '',
|
||||||
role: defaultInviteRole as Role,
|
role: defaultInviteRole as UserInvite['role'],
|
||||||
});
|
});
|
||||||
const [selectedRfcIds, setSelectedRfcIds] = useState<string[]>([]);
|
const [selectedRfcIds, setSelectedRfcIds] = useState<string[]>([]);
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ export default function UsuariosPage() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
setShowInvite(false);
|
setShowInvite(false);
|
||||||
setInviteForm({ email: '', nombre: '', role: defaultInviteRole as Role, supervisorUserId: undefined });
|
setInviteForm({ email: '', nombre: '', role: defaultInviteRole as UserInvite['role'], supervisorUserId: undefined });
|
||||||
setSelectedRfcIds([]);
|
setSelectedRfcIds([]);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
alert(error.response?.data?.message || 'Error al invitar usuario');
|
alert(error.response?.data?.message || 'Error al invitar usuario');
|
||||||
@@ -263,7 +263,7 @@ export default function UsuariosPage() {
|
|||||||
<Label htmlFor="role">Rol</Label>
|
<Label htmlFor="role">Rol</Label>
|
||||||
<Select
|
<Select
|
||||||
value={inviteForm.role}
|
value={inviteForm.role}
|
||||||
onValueChange={(v) => { setInviteForm({ ...inviteForm, role: v as Role, supervisorUserId: undefined }); if (v !== 'cliente') setSelectedRfcIds([]); }}
|
onValueChange={(v) => { setInviteForm({ ...inviteForm, role: v as UserInvite['role'], supervisorUserId: undefined }); if (v !== 'cliente') setSelectedRfcIds([]); }}
|
||||||
>
|
>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
<SelectValue />
|
<SelectValue />
|
||||||
|
|||||||
@@ -177,8 +177,8 @@ export const CfdiInvoice = forwardRef<HTMLDivElement, CfdiInvoiceProps>(
|
|||||||
<div className="bg-gray-50 rounded-lg p-3 text-center">
|
<div className="bg-gray-50 rounded-lg p-3 text-center">
|
||||||
<p className="text-xs text-gray-500 uppercase tracking-wide">Moneda</p>
|
<p className="text-xs text-gray-500 uppercase tracking-wide">Moneda</p>
|
||||||
<p className="text-sm font-semibold text-gray-800 mt-1">{cfdi.moneda || 'MXN'}</p>
|
<p className="text-sm font-semibold text-gray-800 mt-1">{cfdi.moneda || 'MXN'}</p>
|
||||||
{cfdi.typeCambio && cfdi.typeCambio !== 1 && (
|
{cfdi.tipoCambio && cfdi.tipoCambio !== 1 && (
|
||||||
<p className="text-xs text-gray-500">TC: {cfdi.typeCambio}</p>
|
<p className="text-xs text-gray-500">TC: {cfdi.tipoCambio}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-50 rounded-lg p-3 text-center">
|
<div className="bg-gray-50 rounded-lg p-3 text-center">
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ export function CfdiViewerModal({ cfdi, open, onClose }: CfdiViewerModalProps) {
|
|||||||
let xml = xmlContent;
|
let xml = xmlContent;
|
||||||
|
|
||||||
if (!xml) {
|
if (!xml) {
|
||||||
xml = await getCfdiXml(cfdi.id);
|
xml = await getCfdiXml(String(cfdi.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!xml) {
|
if (!xml) {
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ import { useAuthStore } from '@/stores/auth-store';
|
|||||||
import { logout } from '@/lib/api/auth';
|
import { logout } from '@/lib/api/auth';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { hasFeature, isGlobalAdminRfc, type Plan } from '@horux/shared';
|
import { hasFeature, isGlobalAdminRfc, type Plan, type Role } from '@horux/shared';
|
||||||
|
|
||||||
const navigation = [
|
const navigation: { name: string; href: string; icon: React.ElementType; feature?: string; roles?: Role[] }[] = [
|
||||||
{ name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard, roles: ['owner', 'contador'] },
|
{ name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard, roles: ['owner', 'contador'] },
|
||||||
{ name: 'CFDI', href: '/cfdi', icon: FileText },
|
{ name: 'CFDI', href: '/cfdi', icon: FileText },
|
||||||
{ name: 'Impuestos', href: '/impuestos', icon: Calculator },
|
{ name: 'Impuestos', href: '/impuestos', icon: Calculator },
|
||||||
@@ -35,7 +35,7 @@ const navigation = [
|
|||||||
{ name: 'Facturación', href: '/facturacion', icon: Send, roles: ['owner', 'contador'] },
|
{ name: 'Facturación', href: '/facturacion', icon: Send, roles: ['owner', 'contador'] },
|
||||||
{ name: 'Usuarios', href: '/usuarios', icon: Users, roles: ['owner'] },
|
{ name: 'Usuarios', href: '/usuarios', icon: Users, roles: ['owner'] },
|
||||||
{ name: 'Configuración', href: '/configuracion', icon: Settings, roles: ['owner'] },
|
{ name: 'Configuración', href: '/configuracion', icon: Settings, roles: ['owner'] },
|
||||||
] as const;
|
];
|
||||||
|
|
||||||
const adminNavigation = [
|
const adminNavigation = [
|
||||||
{ name: 'Clientes', href: '/clientes', icon: Building2 },
|
{ name: 'Clientes', href: '/clientes', icon: Building2 },
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ import {
|
|||||||
import { useAuthStore } from '@/stores/auth-store';
|
import { useAuthStore } from '@/stores/auth-store';
|
||||||
import { logout } from '@/lib/api/auth';
|
import { logout } from '@/lib/api/auth';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { hasFeature, isGlobalAdminRfc, type Plan } from '@horux/shared';
|
import { hasFeature, isGlobalAdminRfc, type Plan, type Role } from '@horux/shared';
|
||||||
|
|
||||||
const navigation = [
|
const navigation: { name: string; href: string; icon: React.ElementType; feature?: string; roles?: Role[] }[] = [
|
||||||
{ name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard, roles: ['owner', 'contador'] },
|
{ name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard, roles: ['owner', 'contador'] },
|
||||||
{ name: 'CFDI', href: '/cfdi', icon: FileText },
|
{ name: 'CFDI', href: '/cfdi', icon: FileText },
|
||||||
{ name: 'Impuestos', href: '/impuestos', icon: Calculator },
|
{ name: 'Impuestos', href: '/impuestos', icon: Calculator },
|
||||||
@@ -34,7 +34,7 @@ const navigation = [
|
|||||||
{ name: 'Facturación', href: '/facturacion', icon: Send, roles: ['owner', 'contador'] },
|
{ name: 'Facturación', href: '/facturacion', icon: Send, roles: ['owner', 'contador'] },
|
||||||
{ name: 'Usuarios', href: '/usuarios', icon: Users, roles: ['owner'] },
|
{ name: 'Usuarios', href: '/usuarios', icon: Users, roles: ['owner'] },
|
||||||
{ name: 'Config', href: '/configuracion', icon: Settings, roles: ['owner'] },
|
{ name: 'Config', href: '/configuracion', icon: Settings, roles: ['owner'] },
|
||||||
] as const;
|
];
|
||||||
|
|
||||||
const adminNavigation = [
|
const adminNavigation = [
|
||||||
{ name: 'Clientes', href: '/clientes', icon: Building2 },
|
{ name: 'Clientes', href: '/clientes', icon: Building2 },
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ import { useAuthStore } from '@/stores/auth-store';
|
|||||||
import { logout } from '@/lib/api/auth';
|
import { logout } from '@/lib/api/auth';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { hasFeature, isGlobalAdminRfc, type Plan } from '@horux/shared';
|
import { hasFeature, isGlobalAdminRfc, type Plan, type Role } from '@horux/shared';
|
||||||
|
|
||||||
const navigation = [
|
const navigation: { name: string; href: string; icon: React.ElementType; feature?: string; roles?: Role[] }[] = [
|
||||||
{ name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard, roles: ['owner', 'contador'] },
|
{ name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard, roles: ['owner', 'contador'] },
|
||||||
{ name: 'CFDI', href: '/cfdi', icon: FileText },
|
{ name: 'CFDI', href: '/cfdi', icon: FileText },
|
||||||
{ name: 'Impuestos', href: '/impuestos', icon: Calculator },
|
{ name: 'Impuestos', href: '/impuestos', icon: Calculator },
|
||||||
@@ -35,7 +35,7 @@ const navigation = [
|
|||||||
{ name: 'Facturación', href: '/facturacion', icon: Send, roles: ['owner', 'contador'] },
|
{ name: 'Facturación', href: '/facturacion', icon: Send, roles: ['owner', 'contador'] },
|
||||||
{ name: 'Usuarios', href: '/usuarios', icon: Users, roles: ['owner'] },
|
{ name: 'Usuarios', href: '/usuarios', icon: Users, roles: ['owner'] },
|
||||||
{ name: 'Config', href: '/configuracion', icon: Settings, roles: ['owner'] },
|
{ name: 'Config', href: '/configuracion', icon: Settings, roles: ['owner'] },
|
||||||
] as const;
|
];
|
||||||
|
|
||||||
const adminNavigation = [
|
const adminNavigation = [
|
||||||
{ name: 'Clientes', href: '/clientes', icon: Building2 },
|
{ name: 'Clientes', href: '/clientes', icon: Building2 },
|
||||||
|
|||||||
Reference in New Issue
Block a user