Add "Configuración" page and update navigation to support new view

Introduced the `SettingsPage` component for managing application preferences with localStorage persistence. Updated navigation and dynamic header title logic to include the new "settings" view. Adjusted `App.js` to handle the new view state and display `SettingsPage`.
This commit is contained in:
2025-11-20 20:18:44 -04:00
parent a6065bcdb5
commit d9a763191f
2 changed files with 156 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ 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';
function App() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
@@ -24,6 +25,8 @@ function App() {
? 'Nueva Solicitud'
: activeView === 'history'
? 'Historial'
: activeView === 'settings'
? 'Configuración'
: 'Dashboard';
return (
@@ -42,6 +45,8 @@ function App() {
<NewRequestPage />
) : activeView === 'history' ? (
<HistoryPage />
) : activeView === 'settings' ? (
<SettingsPage />
) : (
<section className="content-wrapper">
<DashboardCards />

View File

@@ -0,0 +1,151 @@
import React, { useEffect, useState } from 'react';
import '../../styles/components.css';
// Simple settings persisted in localStorage
const DEFAULT_SETTINGS = {
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC',
theme: 'light',
notifications: true,
apiBaseUrl: ''
};
const loadSettings = () => {
try {
const raw = localStorage.getItem('app_settings');
if (!raw) return DEFAULT_SETTINGS;
const parsed = JSON.parse(raw);
return { ...DEFAULT_SETTINGS, ...parsed };
} catch {
return DEFAULT_SETTINGS;
}
};
const saveSettings = (settings) => {
localStorage.setItem('app_settings', JSON.stringify(settings));
};
const SettingsPage = () => {
const [settings, setSettings] = useState(loadSettings);
const [saved, setSaved] = useState(false);
useEffect(() => {
if (!saved) return;
const t = setTimeout(() => setSaved(false), 2000);
return () => clearTimeout(t);
}, [saved]);
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setSettings((s) => ({
...s,
[name]: type === 'checkbox' ? checked : value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
saveSettings(settings);
setSaved(true);
};
const handleReset = () => {
setSettings(DEFAULT_SETTINGS);
saveSettings(DEFAULT_SETTINGS);
};
return (
<section className="content-wrapper">
<div className="card" style={{ marginBottom: 24 }}>
<div className="form-header">
<h3>
<i className="fas fa-cog"></i>
Configuración
</h3>
</div>
<p style={{ color: 'var(--gray)' }}>
Ajusta las preferencias de la aplicación. Estos valores se guardan en este navegador.
</p>
</div>
<div className="content-grid">
<div className="card">
<div className="form-header">
<h3>
<i className="fas fa-sliders-h"></i>
Preferencias
</h3>
</div>
<form onSubmit={handleSubmit}>
<div className="form-grid">
<div className="form-group">
<label htmlFor="timezone">Zona horaria</label>
<input
id="timezone"
name="timezone"
type="text"
placeholder="Ej: America/Bogota"
value={settings.timezone}
onChange={handleChange}
/>
</div>
<div className="form-group">
<label htmlFor="theme">Tema</label>
<select id="theme" name="theme" value={settings.theme} onChange={handleChange}>
<option value="light">Claro</option>
<option value="dark">Oscuro</option>
<option value="system">Sistema</option>
</select>
</div>
<div className="form-group">
<label className="checkbox">
<input
type="checkbox"
name="notifications"
checked={settings.notifications}
onChange={handleChange}
/>
Habilitar notificaciones
</label>
</div>
<div className="form-group" style={{ gridColumn: '1 / -1' }}>
<label htmlFor="apiBaseUrl">API Base URL</label>
<input
id="apiBaseUrl"
name="apiBaseUrl"
type="text"
placeholder="https://api.midominio.com"
value={settings.apiBaseUrl}
onChange={handleChange}
/>
<small style={{ color: 'var(--gray)' }}>
Base para las solicitudes HTTP; si se deja vacío, se usará la URL por defecto del sistema.
</small>
</div>
</div>
<div className="form-actions" style={{ marginTop: 16 }}>
<button type="submit" className="btn btn-primary">
<i className="fas fa-save" style={{ marginRight: 8 }}></i>
Guardar cambios
</button>
<button type="button" className="btn btn-outline" onClick={handleReset}>
Restablecer
</button>
{saved && (
<span style={{ color: 'var(--success)', marginLeft: 12 }}>
<i className="fas fa-check-circle" style={{ marginRight: 6 }}></i>
Guardado
</span>
)}
</div>
</form>
</div>
</div>
</section>
);
};
export default SettingsPage;