feat: add Settings page with hijri seasons CRUD
- Server: seasons CRUD routes + generic NocoDB helpers - Client: Settings page at /settings with inline add/edit/delete - Seasons stored in NocoDB Seasons table - Vite proxy: /api/seasons routed to Express server - Nav links added (desktop + mobile) - Locale keys for EN + AR - Seasons loaded non-blocking on app mount Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+19
-3
@@ -3,10 +3,12 @@ import { BrowserRouter as Router, Routes, Route, Link, useLocation } from 'react
|
||||
|
||||
const Dashboard = lazy(() => import('./components/Dashboard'));
|
||||
const Comparison = lazy(() => import('./components/Comparison'));
|
||||
const Settings = lazy(() => import('./components/Settings'));
|
||||
import LoadingSkeleton from './components/shared/LoadingSkeleton';
|
||||
import { fetchData, getCacheStatus, refreshData } from './services/dataService';
|
||||
import { fetchSeasons } from './services/seasonsService';
|
||||
import { useLanguage } from './contexts/LanguageContext';
|
||||
import type { MuseumRecord, CacheStatus, DataErrorType } from './types';
|
||||
import type { MuseumRecord, Season, CacheStatus, DataErrorType } from './types';
|
||||
import { DataError } from './types';
|
||||
import './App.css';
|
||||
|
||||
@@ -43,6 +45,7 @@ function App() {
|
||||
const [showDataLabels, setShowDataLabels] = useState<boolean>(false);
|
||||
const [includeVAT, setIncludeVAT] = useState<boolean>(true);
|
||||
const [dataSource, setDataSource] = useState<string>('museums');
|
||||
const [seasons, setSeasons] = useState<Season[]>([]);
|
||||
const [theme, setTheme] = useState<string>(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
return localStorage.getItem('hihala_theme') || 'light';
|
||||
@@ -97,8 +100,14 @@ function App() {
|
||||
}
|
||||
}, []);
|
||||
|
||||
const loadSeasons = useCallback(async () => {
|
||||
const s = await fetchSeasons();
|
||||
setSeasons(s);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
loadSeasons();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
@@ -237,8 +246,9 @@ function App() {
|
||||
<main>
|
||||
<Suspense fallback={<LoadingSkeleton />}>
|
||||
<Routes>
|
||||
<Route path="/" element={<Dashboard data={data} showDataLabels={showDataLabels} setShowDataLabels={setShowDataLabels} includeVAT={includeVAT} setIncludeVAT={setIncludeVAT} />} />
|
||||
<Route path="/comparison" element={<Comparison data={data} showDataLabels={showDataLabels} setShowDataLabels={setShowDataLabels} includeVAT={includeVAT} setIncludeVAT={setIncludeVAT} />} />
|
||||
<Route path="/" element={<Dashboard data={data} seasons={seasons} showDataLabels={showDataLabels} setShowDataLabels={setShowDataLabels} includeVAT={includeVAT} setIncludeVAT={setIncludeVAT} />} />
|
||||
<Route path="/comparison" element={<Comparison data={data} seasons={seasons} showDataLabels={showDataLabels} setShowDataLabels={setShowDataLabels} includeVAT={includeVAT} setIncludeVAT={setIncludeVAT} />} />
|
||||
<Route path="/settings" element={<Settings onSeasonsChange={loadSeasons} />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</main>
|
||||
@@ -262,6 +272,12 @@ function App() {
|
||||
</svg>
|
||||
<span>{t('nav.compare')}</span>
|
||||
</NavLink>
|
||||
<NavLink to="/settings" className="mobile-nav-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
|
||||
</svg>
|
||||
<span>{t('nav.settings')}</span>
|
||||
</NavLink>
|
||||
<button
|
||||
className="mobile-nav-item"
|
||||
onClick={switchLanguage}
|
||||
|
||||
Reference in New Issue
Block a user