Files
SIG-CM/frontend/admin-panel/src/pages/Moderation/ModerationPage.tsx
2025-12-23 15:12:57 -03:00

130 lines
5.7 KiB
TypeScript

import { useState, useEffect } from 'react';
import api from '../../services/api';
import { Check, X, Printer, Globe, MessageSquare } from 'lucide-react';
interface Listing {
id: number;
title: string;
description: string;
price: number;
currency: string;
createdAt: string;
status: string;
printText: string;
isBold: boolean;
isFrame: boolean;
printDaysCount: number;
}
export default function ModerationPage() {
const [listings, setListings] = useState<Listing[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => { loadPending(); }, []);
const loadPending = async () => {
setLoading(true);
try {
// Este endpoint ya lo tenemos en el ListingsController que hicimos al inicio
const res = await api.get('/listings/pending');
setListings(res.data);
} catch (error) { console.error(error); }
finally { setLoading(false); }
};
const handleAction = async (id: number, action: 'Published' | 'Rejected') => {
try {
await api.put(`/listings/${id}/status`, JSON.stringify(action), {
headers: { 'Content-Type': 'application/json' }
});
setListings(listings.filter(l => l.id !== id));
} catch (e) { alert("Error al procesar el aviso"); }
};
if (loading) return <div className="p-10 text-center text-gray-500">Cargando avisos para revisar...</div>;
return (
<div className="max-w-6xl mx-auto">
<div className="mb-6">
<h2 className="text-2xl font-bold text-gray-800">Panel de Moderación</h2>
<p className="text-gray-500">Revisión de avisos entrantes para Web y Diario Papel</p>
</div>
{listings.length === 0 ? (
<div className="bg-white p-12 rounded-xl border-2 border-dashed text-center">
<Check className="mx-auto text-green-500 mb-4" size={48} />
<h3 className="text-lg font-bold text-gray-700">¡Bandeja vacía!</h3>
<p className="text-gray-500">No hay avisos pendientes de moderación en este momento.</p>
</div>
) : (
<div className="grid grid-cols-1 gap-6">
{listings.map(listing => (
<div key={listing.id} className="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden flex flex-col md:flex-row">
{/* Info del Aviso */}
<div className="flex-1 p-6">
<div className="flex justify-between items-start mb-4">
<div>
<span className="text-xs font-bold text-blue-600 bg-blue-50 px-2 py-1 rounded uppercase tracking-wider">
ID: #{listing.id}
</span>
<h3 className="text-xl font-bold text-gray-900 mt-1">{listing.title}</h3>
</div>
<div className="text-right">
<p className="text-lg font-black text-gray-900">{listing.currency} ${listing.price.toLocaleString()}</p>
<p className="text-xs text-gray-400">{new Date(listing.createdAt).toLocaleString()}</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Versión Web */}
<div className="bg-gray-50 p-4 rounded-lg border border-gray-100">
<div className="flex items-center gap-2 mb-2 text-gray-700 font-bold text-sm">
<Globe size={16} /> VERSIÓN WEB / MOBILE
</div>
<p className="text-sm text-gray-600 italic line-clamp-3">"{listing.description}"</p>
</div>
{/* Versión Impresa - CRÍTICO */}
<div className={`p-4 rounded-lg border-2 ${listing.isFrame ? 'border-black' : 'border-gray-200'} bg-white`}>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2 text-gray-700 font-bold text-sm">
<Printer size={16} /> VERSIÓN IMPRESA
</div>
<span className="text-[10px] bg-gray-100 px-2 py-0.5 rounded font-bold">
{listing.printDaysCount} DÍAS
</span>
</div>
<div className={`text-sm p-2 bg-gray-50 rounded border border-dashed border-gray-300 ${listing.isBold ? 'font-bold' : ''}`}>
{listing.printText || "Sin texto de impresión definido"}
</div>
</div>
</div>
</div>
{/* Acciones Laterales */}
<div className="bg-gray-50 border-t md:border-t-0 md:border-l border-gray-200 p-6 flex flex-row md:flex-col gap-3 justify-center min-w-[200px]">
<button
onClick={() => handleAction(listing.id, 'Published')}
className="flex-1 bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition shadow-sm"
>
<Check size={20} /> APROBAR
</button>
<button
onClick={() => handleAction(listing.id, 'Rejected')}
className="flex-1 bg-white hover:bg-red-50 text-red-600 border border-red-200 font-bold py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition"
>
<X size={20} /> RECHAZAR
</button>
<button className="p-3 text-gray-400 hover:text-gray-600 flex items-center justify-center gap-2 text-xs font-medium">
<MessageSquare size={16} /> Enviar Nota
</button>
</div>
</div>
))}
</div>
)}
</div>
);
}