Skip to content

Commit 24ab013

Browse files
Merge pull request #17 from tusharpatel0504/feature/Dark-Themed-UI
feat(ui): implement dark theme UI and improve dark-mode styles
2 parents dd44590 + 6fa7877 commit 24ab013

7 files changed

Lines changed: 384 additions & 11 deletions

File tree

src/App.jsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import React from 'react';
3939
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
4040
import { ToastContainer } from 'react-toastify';
4141
import 'react-toastify/dist/ReactToastify.css';
42-
import { AppProvider } from './context/AppContext';
42+
import { AppProvider, useApp } from './context/AppContext';
4343
import { SocketProvider } from './context/SocketContext';
4444
import { NotificationProvider } from './context/NotificationContext';
4545
import Dashboard from './pages/Dashboard';
@@ -53,6 +53,7 @@ function App() {
5353
<NotificationProvider>
5454
<SocketProvider>
5555
<Router>
56+
<InnerApp />
5657
<div className="min-h-screen bg-gray-50">
5758
{/* Skip link for keyboard navigation */}
5859
<a href="#main-content" className="skip-link">
@@ -89,4 +90,35 @@ function App() {
8990
);
9091
}
9192

93+
function InnerApp() {
94+
const { state } = useApp();
95+
const toastTheme = state.theme === 'dark' ? 'dark' : 'light';
96+
97+
return (
98+
<div className="min-h-screen bg-gray-50">
99+
<Navigation />
100+
<main className="lg:ml-64">
101+
<Routes>
102+
<Route path="/" element={<Navigate to="/dashboard" replace />} />
103+
<Route path="/dashboard" element={<Dashboard />} />
104+
<Route path="/logs" element={<Logs />} />
105+
<Route path="/settings" element={<Settings />} />
106+
</Routes>
107+
</main>
108+
<ToastContainer
109+
position="top-right"
110+
autoClose={5000}
111+
hideProgressBar={false}
112+
newestOnTop
113+
closeOnClick
114+
rtl={false}
115+
pauseOnFocusLoss
116+
draggable
117+
pauseOnHover
118+
theme={toastTheme}
119+
/>
120+
</div>
121+
);
122+
}
123+
92124
export default App;

src/components/EnergyCharts.jsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ const EnergyCharts = () => {
5050
return (
5151
<div className="space-y-4 sm:space-y-6">
5252
{/* Real-time Energy Consumption */}
53+
<div className="chart-container p-6">
54+
<h3 className="text-xl font-bold text-gray-800 mb-4">Real-time Energy Overview</h3>
55+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6">
56+
<div className="bg-blue-50 p-4 rounded-lg stat-card">
57+
<p className="text-sm text-blue-600">Current Consumption</p>
58+
<p className="text-2xl font-bold text-blue-800">{state.energyConsumption} W</p>
59+
</div>
60+
<div className="bg-green-50 p-4 rounded-lg stat-card">
61+
<p className="text-sm text-green-600">Battery Level</p>
62+
<p className="text-2xl font-bold text-green-800">{state.batteryLevel}%</p>
63+
</div>
64+
<div className="bg-orange-50 p-4 rounded-lg stat-card">
65+
<p className="text-sm text-orange-600">Temperature</p>
66+
<p className="text-2xl font-bold text-orange-800">{state.temperature}°C</p>
67+
</div>
5368
<motion.div
5469
initial={{ opacity: 0, y: 20 }}
5570
animate={{ opacity: 1, y: 0 }}

src/components/StatusCard.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ const StatusCard = ({ title, value, subtitle, icon, color, onClick }) => {
8181
<motion.div
8282
whileHover={{ scale: 1.02 }}
8383
whileTap={{ scale: 0.98 }}
84+
className={`rounded-xl p-6 text-white shadow-lg cursor-pointer transition-all duration-300 ${color} status-card`}
8485
className={`rounded-xl p-4 sm:p-6 text-white shadow-lg cursor-pointer transition-all duration-300 interactive ${color}`}
8586
onClick={onClick}
8687
role="button"

src/context/AppContext.jsx

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { createContext, useContext, useReducer } from 'react';
1+
import React, { createContext, useContext, useReducer, useEffect } from 'react';
22

33
const AppContext = createContext();
44

@@ -12,6 +12,7 @@ const initialState = {
1212
schedules: [],
1313
isManualMode: false,
1414
currentTime: new Date().toLocaleTimeString(),
15+
theme: 'light',
1516
};
1617

1718
function appReducer(state, action) {
@@ -44,13 +45,52 @@ function appReducer(state, action) {
4445
return { ...state, isManualMode: !state.isManualMode };
4546
case 'UPDATE_TIME':
4647
return { ...state, currentTime: action.payload };
48+
case 'SET_THEME':
49+
return { ...state, theme: action.payload };
4750
default:
4851
return state;
4952
}
5053
}
5154

5255
export function AppProvider({ children }) {
53-
const [state, dispatch] = useReducer(appReducer, initialState);
56+
// Lazy initializer reads saved theme synchronously to avoid an initial flash
57+
const init = (initState) => {
58+
try {
59+
const saved = localStorage.getItem('themeChoice');
60+
if (saved) {
61+
return { ...initState, theme: saved };
62+
}
63+
} catch (e) {
64+
// ignore and fall back to defaults
65+
}
66+
return initState;
67+
};
68+
69+
const [state, dispatch] = useReducer(appReducer, initialState, init);
70+
71+
// Apply theme class to document root and persist choice
72+
useEffect(() => {
73+
const applyTheme = (themeChoice) => {
74+
let resolved = themeChoice;
75+
if (themeChoice === 'auto') {
76+
resolved = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
77+
}
78+
79+
if (resolved === 'dark') {
80+
document.documentElement.classList.add('dark');
81+
} else {
82+
document.documentElement.classList.remove('dark');
83+
}
84+
85+
try {
86+
localStorage.setItem('themeChoice', themeChoice);
87+
} catch (e) {
88+
// ignore
89+
}
90+
};
91+
92+
applyTheme(state.theme);
93+
}, [state.theme]);
5494

5595
return (
5696
<AppContext.Provider value={{ state, dispatch }}>

0 commit comments

Comments
 (0)