fix: personalización logo/color por contribuyente en vez de tenant

- Agrega getCustomizationContribuyente, uploadLogoContribuyente,
  updateColorContribuyente en contribuyente-facturapi.service.ts
- Agrega controllers per-contribuyente en facturacion.controller.ts
- Agrega rutas GET/POST/PUT /contribuyentes/:id/facturapi/customization|logo|color
- Modifica CustomizationSection para recibir contribuyenteId, usar endpoints
  per-contribuyente, y corrige useState mal aplicado a useEffect
- Backend y frontend buildeados y deployados
This commit is contained in:
Horux Dev
2026-05-22 18:20:09 +00:00
parent 46846200da
commit 5ba31b7291
5 changed files with 138 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
'use client';
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { Header } from '@/components/layouts/header';
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Button, Input, Label } from '@horux/shared-ui';
import { useTimbres } from '@/lib/hooks/use-facturacion';
@@ -9,21 +9,24 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useContribuyenteStore } from '@/stores/contribuyente-store';
import { Shield, Upload, Check, AlertCircle, Receipt, Palette, Image, Building2, FileText } from 'lucide-react';
function CustomizationSection() {
function CustomizationSection({ contribuyenteId }: { contribuyenteId: string }) {
const queryClient = useQueryClient();
const [logoUploading, setLogoUploading] = useState(false);
const [colorSaving, setColorSaving] = useState(false);
const [color, setColor] = useState('#75A4FF');
const [msg, setMsg] = useState<{ type: 'success' | 'error'; text: string } | null>(null);
const queryKey = ['facturapi-customization', contribuyenteId];
const { data: customization } = useQuery({
queryKey: ['facturapi-customization'],
queryFn: () => apiClient.get('/facturacion/customization').then(r => r.data),
queryKey,
queryFn: () => apiClient.get(`/contribuyentes/${contribuyenteId}/facturapi/customization`).then(r => r.data),
enabled: !!contribuyenteId,
});
useState(() => {
useEffect(() => {
if (customization?.color) setColor(`#${customization.color}`);
});
}, [customization?.color]);
const handleLogoUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
@@ -45,8 +48,8 @@ function CustomizationSection() {
reader.onload = async () => {
const base64 = (reader.result as string).split(',')[1];
try {
await apiClient.post('/facturacion/logo', { logo: base64 });
queryClient.invalidateQueries({ queryKey: ['facturapi-customization'] });
await apiClient.post(`/contribuyentes/${contribuyenteId}/facturapi/logo`, { logo: base64 });
queryClient.invalidateQueries({ queryKey });
setMsg({ type: 'success', text: 'Logo subido correctamente' });
} catch {
setMsg({ type: 'error', text: 'Error al subir logo' });
@@ -61,8 +64,8 @@ function CustomizationSection() {
setColorSaving(true);
setMsg(null);
try {
await apiClient.put('/facturacion/color', { color: color.replace('#', '') });
queryClient.invalidateQueries({ queryKey: ['facturapi-customization'] });
await apiClient.put(`/contribuyentes/${contribuyenteId}/facturapi/color`, { color: color.replace('#', '') });
queryClient.invalidateQueries({ queryKey });
setMsg({ type: 'success', text: 'Color actualizado' });
} catch {
setMsg({ type: 'error', text: 'Error al actualizar color' });
@@ -357,7 +360,7 @@ export default function CsdConfigPage() {
)}
{/* Personalización de factura */}
{selectedContribuyenteId && orgStatus?.configured && <CustomizationSection />}
{selectedContribuyenteId && orgStatus?.configured && <CustomizationSection contribuyenteId={selectedContribuyenteId} />}
{/* Timbres */}
{selectedContribuyenteId && <Card>