Some checks failed
continuous-integration/drone/push Build was killed
Integrated a modal to handle credit reloading with a simulated payment process. Updated logic to manage payment amount, successful payment handling, and credit updates. Improved UI with validations and placeholders for future payment gateway integration.
198 lines
6.8 KiB
JavaScript
198 lines
6.8 KiB
JavaScript
import React, { useEffect, useState } from 'react';
|
|
import './styles/globals.css';
|
|
import { Sidebar, Header } from './components/Layout';
|
|
import { DashboardCards } from './components/Dashboard';
|
|
import RequestTable from './components/Requests/RequestTable';
|
|
import RequestForm from './components/Requests/RequestForm';
|
|
import ScheduledPage from './components/Scheduled/ScheduledPage';
|
|
import NewRequestPage from './components/Requests/NewRequestPage';
|
|
import HistoryPage from './components/History/HistoryPage';
|
|
import SettingsPage from './components/Settings/SettingsPage';
|
|
import HelpPage from './components/Help/HelpPage';
|
|
import ProfilePage from './components/Profile/ProfilePage';
|
|
|
|
function App() {
|
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
const [activeView, setActiveView] = useState('dashboard'); // 'dashboard' | 'scheduled' | 'create' | 'history' | 'settings'
|
|
const [credits, setCredits] = useState(() => {
|
|
try {
|
|
const raw = localStorage.getItem('credits');
|
|
const n = raw != null ? parseInt(raw, 10) : 0;
|
|
return Number.isNaN(n) ? 0 : n;
|
|
} catch {
|
|
return 0;
|
|
}
|
|
});
|
|
|
|
useEffect(() => {
|
|
const onStorage = (e) => {
|
|
if (e.key === 'credits') {
|
|
const n = parseInt(e.newValue, 10);
|
|
if (!Number.isNaN(n)) setCredits(n);
|
|
}
|
|
};
|
|
window.addEventListener('storage', onStorage);
|
|
return () => window.removeEventListener('storage', onStorage);
|
|
}, []);
|
|
|
|
const handleToggleMobileMenu = () => setIsMobileMenuOpen(!isMobileMenuOpen);
|
|
const handleNavigate = (key) => {
|
|
setActiveView(key);
|
|
setIsMobileMenuOpen(false);
|
|
};
|
|
|
|
const [showPayment, setShowPayment] = useState(false);
|
|
|
|
const handleReloadCredits = () => {
|
|
// Abrir modal de pasarela de pago
|
|
setShowPayment(true);
|
|
};
|
|
|
|
const handlePaymentClose = () => setShowPayment(false);
|
|
|
|
const handlePaymentSuccess = (amount) => {
|
|
// Actualizar créditos después de un pago exitoso
|
|
const add = parseInt(amount, 10);
|
|
if (!Number.isNaN(add) && add > 0) {
|
|
const next = credits + add;
|
|
setCredits(next);
|
|
try { localStorage.setItem('credits', String(next)); } catch {}
|
|
}
|
|
setShowPayment(false);
|
|
};
|
|
|
|
const headerTitle = activeView === 'scheduled'
|
|
? 'Solicitudes Programadas'
|
|
: activeView === 'create'
|
|
? 'Nueva Solicitud'
|
|
: activeView === 'history'
|
|
? 'Historial'
|
|
: activeView === 'settings'
|
|
? 'Configuración'
|
|
: activeView === 'help'
|
|
? 'Ayuda'
|
|
: activeView === 'profile'
|
|
? 'Perfil'
|
|
: 'Dashboard';
|
|
|
|
const [paymentAmount, setPaymentAmount] = useState('');
|
|
const [isPaying, setIsPaying] = useState(false);
|
|
|
|
const proceedPayment = async () => {
|
|
const val = parseInt(paymentAmount, 10);
|
|
if (Number.isNaN(val) || val <= 0) {
|
|
alert('Ingrese un monto válido mayor a 0.');
|
|
return;
|
|
}
|
|
// Simulación de pasarela de pago
|
|
try {
|
|
setIsPaying(true);
|
|
await new Promise((res) => setTimeout(res, 1200));
|
|
handlePaymentSuccess(val);
|
|
} finally {
|
|
setIsPaying(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="container">
|
|
<Sidebar
|
|
isMobileMenuOpen={isMobileMenuOpen}
|
|
onToggleMobileMenu={handleToggleMobileMenu}
|
|
activeView={activeView}
|
|
onNavigate={handleNavigate}
|
|
/>
|
|
<main className="main-content">
|
|
<Header title={headerTitle} credits={credits} onReloadCredits={handleReloadCredits} />
|
|
{activeView === 'scheduled' ? (
|
|
<ScheduledPage />
|
|
) : activeView === 'create' ? (
|
|
<NewRequestPage />
|
|
) : activeView === 'history' ? (
|
|
<HistoryPage />
|
|
) : activeView === 'settings' ? (
|
|
<SettingsPage />
|
|
) : activeView === 'help' ? (
|
|
<HelpPage />
|
|
) : activeView === 'profile' ? (
|
|
<ProfilePage />
|
|
) : (
|
|
<section className="content-wrapper">
|
|
<DashboardCards />
|
|
<div className="content-grid">
|
|
<RequestForm />
|
|
<RequestTable />
|
|
</div>
|
|
</section>
|
|
)}
|
|
|
|
{showPayment && (
|
|
<div className="modal-overlay" onClick={handlePaymentClose}>
|
|
<div className="modal" onClick={(e) => e.stopPropagation()}>
|
|
<div className="modal-header">
|
|
<h3>
|
|
<i className="fas fa-credit-card"></i>
|
|
Recargar créditos
|
|
</h3>
|
|
<button className="btn btn-outline btn-sm" onClick={handlePaymentClose} disabled={isPaying}>
|
|
<i className="fas fa-times"></i> Cerrar
|
|
</button>
|
|
</div>
|
|
<div className="modal-body">
|
|
<div className="form-container">
|
|
<div className="form-row">
|
|
<div className="form-group" style={{ flex: 1 }}>
|
|
<label>
|
|
<i className="fas fa-coins" aria-hidden="true"></i> Monto a recargar (créditos)
|
|
</label>
|
|
<input
|
|
className="input"
|
|
type="number"
|
|
min={1}
|
|
step={1}
|
|
value={paymentAmount}
|
|
onChange={(e) => setPaymentAmount(e.target.value)}
|
|
placeholder="Ej: 10"
|
|
disabled={isPaying}
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Placeholder de pasarela de pago: reemplace con su integración real (Mercado Pago, Stripe, etc.) */}
|
|
<div className="card" style={{ marginTop: 12 }}>
|
|
<div className="form-header">
|
|
<h3>
|
|
<i className="fas fa-shield-alt" style={{ marginRight: 8 }}></i>
|
|
Pasarela de pago
|
|
</h3>
|
|
</div>
|
|
<div style={{ padding: 12 }}>
|
|
<p style={{ marginTop: 0, color: 'var(--gray)' }}>
|
|
Esta es una simulación. Conecte aquí su pasarela (iframe, checkout, etc.).
|
|
</p>
|
|
<button className="btn btn-primary" onClick={proceedPayment} disabled={isPaying}>
|
|
{isPaying ? (
|
|
<>
|
|
<i className="fas fa-spinner fa-spin"></i> Procesando...
|
|
</>
|
|
) : (
|
|
<>
|
|
<i className="fas fa-lock"></i> Pagar y recargar
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|