import { useState, useEffect } from 'react'; import { X, Save, Calendar, Type, AlignLeft, AlignCenter, AlignRight, AlignJustify, Bold, Square as FrameIcon } from 'lucide-react'; import { useDebounce } from '../../hooks/useDebounce'; import api from '../../services/api'; import clsx from 'clsx'; import { processCategories, type FlatCategory } from '../../utils/categoryTreeUtils'; import { productService } from '../../services/productService'; import type { Product } from '../../types/Product'; interface AdEditorModalProps { isOpen: boolean; onClose: () => void; onConfirm: (listingId: number, price: number, description: string) => void; clientId: number | null; // El aviso se vinculará a este cliente productId: number; // Necesario para el precio base } interface PricingResult { totalPrice: number; wordCount: number; details: string; } export default function AdEditorModal({ isOpen, onClose, onConfirm, clientId, productId }: AdEditorModalProps) { const [flatCategories, setFlatCategories] = useState([]); const [operations, setOperations] = useState([]); const [product, setProduct] = useState(null); const [loading, setLoading] = useState(false); const [calculating, setCalculating] = useState(false); // Form State const [categoryId, setCategoryId] = useState(''); const [operationId, setOperationId] = useState(''); const [text, setText] = useState(''); const debouncedText = useDebounce(text, 500); const [days, setDays] = useState(3); const [startDate, setStartDate] = useState(new Date(Date.now() + 86400000).toISOString().split('T')[0]); // Mañana default // Styles const [styles, setStyles] = useState({ isBold: false, isFrame: false, fontSize: 'normal', alignment: 'left' }); const [pricing, setPricing] = useState({ totalPrice: 0, wordCount: 0, details: '' }); // Carga inicial de datos useEffect(() => { if (isOpen) { // Reset state on open setText(''); setDays(3); setPricing({ totalPrice: 0, wordCount: 0, details: '' }); const loadData = async () => { try { const [catRes, opRes, prodData] = await Promise.all([ api.get('/categories'), api.get('/operations'), productService.getById(productId) ]); const allCategories = processCategories(catRes.data); let filteredCategories = allCategories; if (prodData.categoryId) { const rootCategory = allCategories.find(c => c.id === prodData.categoryId); if (rootCategory) { // Filtrar para mostrar solo la categoría raíz y sus descendientes filteredCategories = allCategories.filter(c => c.id === rootCategory.id || c.path.startsWith(rootCategory.path + ' > ') ); } } setFlatCategories(filteredCategories); setOperations(opRes.data); setProduct(prodData); // Ajustar días iniciales según la duración del producto if (prodData.priceDurationDays > 1) { setDays(prodData.priceDurationDays); } else { setDays(3); } // Si hay una sola opción elegible, seleccionarla automáticamente const selectable = filteredCategories.filter(c => c.isSelectable); if (selectable.length === 1) { setCategoryId(selectable[0].id.toString()); } } catch (e) { console.error("Error cargando configuración", e); } }; loadData(); } }, [isOpen]); // Calculadora de Precio en Tiempo Real useEffect(() => { if (!categoryId) { setPricing({ totalPrice: 0, wordCount: 0, details: '' }); return; } const calculate = async () => { setCalculating(true); try { const res = await api.post('/pricing/calculate', { categoryId: parseInt(categoryId), productId: productId, text: debouncedText, days: days, isBold: styles.isBold, isFrame: styles.isFrame, startDate: startDate }); setPricing(res.data); } catch (e) { console.error(e); } finally { setCalculating(false); } }; calculate(); }, [debouncedText, categoryId, days, styles, startDate, productId]); const handleSave = async () => { if (!categoryId || !operationId || !text) return alert("Complete los campos obligatorios"); if (!clientId) return alert("Error interno: Cliente no identificado"); setLoading(true); try { // Creamos el aviso en estado 'Draft' (Borrador) o 'PendingPayment' const payload = { categoryId: parseInt(categoryId), operationId: parseInt(operationId), title: text.substring(0, 30) + '...', description: text, price: 0, // Precio del bien (opcional, no lo pedimos en este form simple) adFee: pricing.totalPrice, // Costo del aviso status: 'Draft', // Importante: Nace como borrador hasta que se paga la Orden origin: 'Mostrador', clientId: clientId, // Datos técnicos de impresión printText: text, printStartDate: startDate, printDaysCount: days, isBold: styles.isBold, isFrame: styles.isFrame, printFontSize: styles.fontSize, printAlignment: styles.alignment }; const res = await api.post('/listings', payload); // Devolvemos el ID y el Precio al Carrito onConfirm(res.data.id, pricing.totalPrice, `Aviso: ${text.substring(0, 20)}... (${days} días)`); onClose(); } catch (error) { console.error(error); alert("Error al guardar el borrador del aviso"); } finally { setLoading(false); } }; if (!isOpen) return null; return (
{/* Header */}

Redacción de Aviso

Configuración Técnica e Impresión

{/* Body */}
{/* Clasificación */}
{/* Editor de Texto */}