diff --git a/Frontend/src/pages/AdminPage.tsx b/Frontend/src/pages/AdminPage.tsx index cc154be..1a913b2 100644 --- a/Frontend/src/pages/AdminPage.tsx +++ b/Frontend/src/pages/AdminPage.tsx @@ -1,11 +1,13 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { AdminService } from '../services/admin.service'; +import { AdsV2Service } from '../services/ads.v2.service'; import ModerationModal from '../components/ModerationModal'; import UserModal from '../components/UserModal'; import { parseUTCDate, getImageUrl } from '../utils/app.utils'; -import { STATUS_CONFIG } from '../constants/adStatuses'; +import { STATUS_CONFIG, AD_STATUSES } from '../constants/adStatuses'; import AdDetailsModal from '../components/AdDetailsModal'; import { Link } from 'react-router-dom'; +import ConfirmationModal from '../components/ConfirmationModal'; type TabType = 'stats' | 'ads' | 'moderation' | 'transactions' | 'users' | 'audit'; @@ -21,6 +23,69 @@ export default function AdminPage() { const [selectedAd, setSelectedAd] = useState(null); const [selectedUser, setSelectedUser] = useState(null); + const [modalConfig, setModalConfig] = useState<{ + isOpen: boolean; + title: string; + message: string; + adId: number | null; + newStatus: number | null; + isDanger: boolean; + }>({ + isOpen: false, + title: '', + message: '', + adId: null, + newStatus: null, + isDanger: false + }); + + const initiateStatusChange = (adId: number, newStatus: number) => { + let title = "Cambiar Estado"; + let message = "¿Estás seguro de realizar esta acción?"; + let isDanger = false; + + if (newStatus === AD_STATUSES.DELETED) { + title = "¿Eliminar Aviso?"; + message = "Esta acción eliminará el aviso permanentemente. No se puede deshacer.\n\n¿Estás seguro de continuar?"; + isDanger = true; + } else if (newStatus === AD_STATUSES.PAUSED) { + title = "Pausar Publicación"; + message = "Al pausar el aviso dejará de ser visible en los listados.\n\nPodrás reactivarlo cuando quieras."; + } else if (newStatus === AD_STATUSES.SOLD) { + title = "¡Felicitaciones!"; + message = "Al marcar como VENDIDO el aviso mostrará la etiqueta \"Vendido\" al público.\n\n¿Confirmas que ya vendiste el vehículo?"; + } else if (newStatus === AD_STATUSES.RESERVED) { + title = "Reservar Vehículo"; + message = "Se indicará a los interesados que el vehículo está reservado.\n\n¿Deseas continuar?"; + } else if (newStatus === AD_STATUSES.ACTIVE) { + title = "Reactivar Aviso"; + message = "El aviso volverá a estar visible para todos y recibirás consultas nuevamente."; + } + + setModalConfig({ + isOpen: true, + title, + message, + adId, + newStatus, + isDanger, + }); + }; + + // Acción real al confirmar en el modal + const confirmStatusChange = async () => { + const { adId, newStatus } = modalConfig; + if (!adId || !newStatus) return; + + try { + setModalConfig({ ...modalConfig, isOpen: false }); + await AdsV2Service.changeStatus(adId, newStatus); + loadData(); + } catch (error) { + alert("Error al actualizar estado"); + } + }; + // Estados para filtros de Usuarios const [userSearch, setUserSearch] = useState(''); const [userPage, setUserPage] = useState(1); @@ -292,7 +357,7 @@ export default function AdminPage() { {/* Lista de Avisos (Escritorio / Tabla) */} -
+
@@ -303,10 +368,9 @@ export default function AdminPage() { - {data.ads.map((ad: any) => { - const statusConfig = STATUS_CONFIG[ad.statusID] || { label: 'Desc.', bg: 'bg-gray-500', color: 'text-white' }; + {data.ads.map((ad: any, index: number) => { return ( - +
@@ -330,9 +394,12 @@ export default function AdminPage() {
- - {statusConfig.label} - +
+ initiateStatusChange(ad.adID, newStatus)} + /> +
@@ -367,11 +434,10 @@ export default function AdminPage() { {/* Lista de Avisos (Móvil / Cards) */}
- {data.ads.map((ad: any) => { - const statusConfig = STATUS_CONFIG[ad.statusID] || { label: 'Desc.', bg: 'bg-gray-500', color: 'text-white' }; + {data.ads.map((ad: any, index: number) => { return ( -
-
+
+
@@ -382,9 +448,12 @@ export default function AdminPage() { )}
- - {statusConfig.label} - +
+ initiateStatusChange(ad.adID, newStatus)} + /> +
@@ -965,6 +1034,21 @@ export default function AdminPage() { onUpdate={loadData} /> )} + + {/* MODAL DE CONFIRMACIÓN */} + setModalConfig({ ...modalConfig, isOpen: false })} + isDanger={modalConfig.isDanger} + confirmText={ + modalConfig.newStatus === AD_STATUSES.SOLD + ? '¡Sí, vendido!' + : 'Confirmar' + } + />
); } @@ -986,4 +1070,87 @@ function DashboardMiniCard({ label, value, icon, color = 'blue' }: { label: stri
); +} + +// DROPDOWN DE ESTADO +function StatusDropdown({ + currentStatus, + onChange, +}: { + currentStatus: number; + onChange: (val: number) => void; +}) { + const [isOpen, setIsOpen] = useState(false); + const wrapperRef = useRef(null); + + // Fallback seguro si currentStatus no tiene config + const currentConfig = STATUS_CONFIG[currentStatus] || { + label: "Desconocido", + color: "text-gray-400", + bg: "bg-gray-500/10", + border: "border-gray-500/20", + icon: "❓", + }; + + const ALLOWED_STATUSES = [ + AD_STATUSES.ACTIVE, + AD_STATUSES.PAUSED, + AD_STATUSES.RESERVED, + AD_STATUSES.SOLD, + AD_STATUSES.DELETED, + ]; + + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if ( + wrapperRef.current && + !wrapperRef.current.contains(event.target as Node) + ) { + setIsOpen(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + + return ( +
+ + + {isOpen && ( +
+ {ALLOWED_STATUSES.map((statusId) => { + const config = STATUS_CONFIG[statusId]; + + if (!config) return null; + + return ( + + ); + })} +
+ )} +
+ ); } \ No newline at end of file