Skip to content

Commit 33ae3ec

Browse files
committed
Add multi-view mode and UPS selection to kiosk mode
1 parent ff6af46 commit 33ae3ec

File tree

4 files changed

+512
-5
lines changed

4 files changed

+512
-5
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ All notable changes to the PowerPulse project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.10.0] - 2025-03-02
9+
10+
### Added
11+
- Kiosk Mode for small screens and display panels:
12+
- Created dedicated full-screen view optimized for small displays
13+
- Implemented modern, clean UI with dark theme for better visibility
14+
- Added auto-rotation between multiple UPS systems
15+
- Designed responsive layout that works well on various screen sizes
16+
- Made accessible without authentication for public displays
17+
- Added multi-view mode to display all UPS systems on a single screen
18+
- Implemented ability to select specific UPS systems to show in kiosk mode
19+
- Added toggle button to switch between single and multi-view modes
20+
- Created compact UPS cards for multi-view display
21+
- Added URL parameters to control view mode and UPS selection
22+
823
## [1.9.1] - 2025-03-02
924

1025
### Added

client/src/App.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import Dashboard from './pages/Dashboard';
1010
import UPSSystemsPage from './pages/UPSSystemsPage';
1111
import NUTServersPage from './pages/NUTServersPage';
1212
import SettingsPage from './pages/SettingsPage';
13+
import KioskMode from './pages/KioskMode';
1314
import Login from './pages/Login';
1415
import Register from './pages/Register';
1516
import SetupPage from './pages/SetupPage';
@@ -67,6 +68,9 @@ const App = () => {
6768
<Route path="/login" element={!user ? <Login /> : <Navigate to="/" replace />} />
6869
<Route path="/register" element={!user ? <Register /> : <Navigate to="/" replace />} />
6970

71+
{/* Kiosk mode route - accessible without authentication */}
72+
<Route path="/kiosk" element={<KioskMode />} />
73+
7074
<Route element={<ProtectedRoute />}>
7175
<Route element={<Layout />}>
7276
<Route path="/" element={<Dashboard />} />

client/src/pages/Dashboard.jsx

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useEffect, useReducer } from 'react';
22
import axios from 'axios';
3-
import { FiBattery, FiAlertCircle, FiClock, FiThermometer, FiPercent, FiZap, FiActivity, FiRefreshCw, FiCalendar, FiFilter } from 'react-icons/fi';
3+
import { FiBattery, FiAlertCircle, FiClock, FiThermometer, FiPercent, FiZap, FiActivity, FiRefreshCw, FiCalendar, FiFilter, FiGrid, FiList } from 'react-icons/fi';
44
import { useSettings } from '../context/SettingsContext';
55
import { useBatteryHistory } from '../hooks/useBatteryHistory';
66
import { Line } from 'react-chartjs-2';
@@ -37,7 +37,8 @@ const Dashboard = () => {
3737
error: null,
3838
selectedUpsId: null,
3939
lastUpdated: new Date(),
40-
forceUpdate: 0 // Used to force re-renders
40+
forceUpdate: 0, // Used to force re-renders
41+
kioskUpsIds: [] // Track which UPS systems to show in kiosk mode
4142
};
4243

4344
const dashboardReducer = (state, action) => {
@@ -55,14 +56,41 @@ const Dashboard = () => {
5556
return { ...state, error: action.payload };
5657
case 'SET_SELECTED_UPS_ID':
5758
return { ...state, selectedUpsId: action.payload };
59+
case 'TOGGLE_KIOSK_UPS':
60+
const upsId = action.payload;
61+
const kioskUpsIds = [...state.kioskUpsIds];
62+
63+
if (kioskUpsIds.includes(upsId)) {
64+
// Remove UPS from kiosk selection
65+
return {
66+
...state,
67+
kioskUpsIds: kioskUpsIds.filter(id => id !== upsId)
68+
};
69+
} else {
70+
// Add UPS to kiosk selection
71+
return {
72+
...state,
73+
kioskUpsIds: [...kioskUpsIds, upsId]
74+
};
75+
}
76+
case 'SELECT_ALL_KIOSK_UPS':
77+
return {
78+
...state,
79+
kioskUpsIds: state.upsSystems.map(ups => ups.id)
80+
};
81+
case 'CLEAR_KIOSK_UPS':
82+
return {
83+
...state,
84+
kioskUpsIds: []
85+
};
5886
default:
5987
return state;
6088
}
6189
};
6290

6391
// Use reducer instead of multiple useState calls
6492
const [state, dispatch] = useReducer(dashboardReducer, initialState);
65-
const { upsSystems, loading, error, selectedUpsId, lastUpdated, forceUpdate } = state;
93+
const { upsSystems, loading, error, selectedUpsId, lastUpdated, forceUpdate, kioskUpsIds } = state;
6694

6795
// Get the selected UPS from the current state
6896
const selectedUps = upsSystems.find(ups => ups.id === selectedUpsId) || null;
@@ -206,7 +234,29 @@ const Dashboard = () => {
206234
return (
207235
<div className="space-y-6">
208236
<div className="flex justify-between items-center">
209-
<h1 className="text-2xl font-semibold text-gray-900 dark:text-white">UPS Monitoring Dashboard</h1>
237+
<div className="flex items-center">
238+
<h1 className="text-2xl font-semibold text-gray-900 dark:text-white">UPS Monitoring Dashboard</h1>
239+
<div className="ml-4 flex space-x-2">
240+
<a
241+
href={`/kiosk${kioskUpsIds.length > 0 ? `?ups=${kioskUpsIds.join(',')}` : ''}`}
242+
target="_blank"
243+
rel="noopener noreferrer"
244+
className="inline-flex items-center px-3 py-1 border border-transparent text-sm leading-4 font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
245+
>
246+
<FiList className="mr-1 h-4 w-4" />
247+
Kiosk Mode
248+
</a>
249+
<a
250+
href={`/kiosk?multi=true${kioskUpsIds.length > 0 ? `&ups=${kioskUpsIds.join(',')}` : ''}`}
251+
target="_blank"
252+
rel="noopener noreferrer"
253+
className="inline-flex items-center px-3 py-1 border border-transparent text-sm leading-4 font-medium rounded-md shadow-sm text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
254+
>
255+
<FiGrid className="mr-1 h-4 w-4" />
256+
Multi-View
257+
</a>
258+
</div>
259+
</div>
210260
<div className="text-sm text-gray-500 dark:text-gray-400 flex items-center">
211261
<FiClock className="mr-1" />
212262
Last updated: {lastUpdated.toLocaleTimeString()}
@@ -221,12 +271,41 @@ const Dashboard = () => {
221271
<div className="md:col-span-1">
222272
<div className="bg-white dark:bg-gray-800 shadow rounded-lg overflow-hidden">
223273
<div className="px-4 py-5 sm:px-6 border-b dark:border-gray-700">
224-
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">UPS Systems</h3>
274+
<div className="flex justify-between items-center">
275+
<h3 className="text-lg leading-6 font-medium text-gray-900 dark:text-white">UPS Systems</h3>
276+
<div className="flex items-center space-x-2">
277+
<button
278+
onClick={() => dispatch({ type: 'SELECT_ALL_KIOSK_UPS' })}
279+
className="text-xs text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300"
280+
>
281+
Select All
282+
</button>
283+
<span className="text-gray-300 dark:text-gray-600">|</span>
284+
<button
285+
onClick={() => dispatch({ type: 'CLEAR_KIOSK_UPS' })}
286+
className="text-xs text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300"
287+
>
288+
Clear
289+
</button>
290+
</div>
291+
</div>
292+
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
293+
Select UPS systems to show in kiosk mode
294+
</p>
225295
</div>
226296
<ul className="divide-y divide-gray-200 dark:divide-gray-700">
227297
{upsSystems.map((ups, index) => (
228298
<li key={ups.id}>
229299
<div className="flex items-center">
300+
<div className="flex-shrink-0 pl-4 pr-2">
301+
<input
302+
type="checkbox"
303+
checked={kioskUpsIds.includes(ups.id)}
304+
onChange={() => dispatch({ type: 'TOGGLE_KIOSK_UPS', payload: ups.id })}
305+
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded"
306+
onClick={(e) => e.stopPropagation()}
307+
/>
308+
</div>
230309
<button
231310
onClick={() => handleUpsSelect(ups)}
232311
className={`w-full px-4 py-4 flex items-center hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-150 ${

0 commit comments

Comments
 (0)