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:
@@ -7,6 +7,7 @@ import RequestForm from './components/Requests/RequestForm';
|
|||||||
import ScheduledPage from './components/Scheduled/ScheduledPage';
|
import ScheduledPage from './components/Scheduled/ScheduledPage';
|
||||||
import NewRequestPage from './components/Requests/NewRequestPage';
|
import NewRequestPage from './components/Requests/NewRequestPage';
|
||||||
import HistoryPage from './components/History/HistoryPage';
|
import HistoryPage from './components/History/HistoryPage';
|
||||||
|
import SettingsPage from './components/Settings/SettingsPage';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||||
@@ -24,6 +25,8 @@ function App() {
|
|||||||
? 'Nueva Solicitud'
|
? 'Nueva Solicitud'
|
||||||
: activeView === 'history'
|
: activeView === 'history'
|
||||||
? 'Historial'
|
? 'Historial'
|
||||||
|
: activeView === 'settings'
|
||||||
|
? 'Configuración'
|
||||||
: 'Dashboard';
|
: 'Dashboard';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -42,6 +45,8 @@ function App() {
|
|||||||
<NewRequestPage />
|
<NewRequestPage />
|
||||||
) : activeView === 'history' ? (
|
) : activeView === 'history' ? (
|
||||||
<HistoryPage />
|
<HistoryPage />
|
||||||
|
) : activeView === 'settings' ? (
|
||||||
|
<SettingsPage />
|
||||||
) : (
|
) : (
|
||||||
<section className="content-wrapper">
|
<section className="content-wrapper">
|
||||||
<DashboardCards />
|
<DashboardCards />
|
||||||
|
|||||||
151
src/components/Settings/SettingsPage.js
Normal file
151
src/components/Settings/SettingsPage.js
Normal 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;
|
||||||
Reference in New Issue
Block a user