diff options
-rw-r--r-- | modern/src/CachingController.js | 13 | ||||
-rw-r--r-- | modern/src/Navigation.js | 4 | ||||
-rw-r--r-- | modern/src/reports/ScheduledPage.js | 91 | ||||
-rw-r--r-- | modern/src/reports/components/ReportsMenu.js | 30 | ||||
-rw-r--r-- | modern/src/resources/l10n/en.json | 1 | ||||
-rw-r--r-- | modern/src/store/calendars.js | 16 | ||||
-rw-r--r-- | modern/src/store/index.js | 3 |
7 files changed, 143 insertions, 15 deletions
diff --git a/modern/src/CachingController.js b/modern/src/CachingController.js index 1c8fb43a..55a4e020 100644 --- a/modern/src/CachingController.js +++ b/modern/src/CachingController.js @@ -1,7 +1,7 @@ import { useDispatch, useSelector, connect } from 'react-redux'; import { - geofencesActions, groupsActions, driversActions, maintenancesActions, + geofencesActions, groupsActions, driversActions, maintenancesActions, calendarsActions, } from './store'; import { useEffectAsync } from './reactHelper'; @@ -53,6 +53,17 @@ const CachingController = () => { } }, [authenticated]); + useEffectAsync(async () => { + if (authenticated) { + const response = await fetch('/api/calendars'); + if (response.ok) { + dispatch(calendarsActions.update(await response.json())); + } else { + throw Error(await response.text()); + } + } + }, [authenticated]); + return null; }; diff --git a/modern/src/Navigation.js b/modern/src/Navigation.js index d08fc12b..1e3a37b2 100644 --- a/modern/src/Navigation.js +++ b/modern/src/Navigation.js @@ -48,6 +48,7 @@ import CommandSendPage from './settings/CommandSendPage'; import App from './App'; import ChangeServerPage from './other/ChangeServerPage'; import DevicesPage from './settings/DevicesPage'; +import ScheduledPage from './reports/ScheduledPage'; const Navigation = () => { const navigate = useNavigate(); @@ -141,10 +142,11 @@ const Navigation = () => { <Route path="chart" element={<ChartReportPage />} /> <Route path="event" element={<EventReportPage />} /> <Route path="route" element={<RouteReportPage />} /> - <Route path="statistics" element={<StatisticsPage />} /> <Route path="stop" element={<StopReportPage />} /> <Route path="summary" element={<SummaryReportPage />} /> <Route path="trip" element={<TripReportPage />} /> + <Route path="scheduled" element={<ScheduledPage />} /> + <Route path="statistics" element={<StatisticsPage />} /> </Route> </Route> </Routes> diff --git a/modern/src/reports/ScheduledPage.js b/modern/src/reports/ScheduledPage.js new file mode 100644 index 00000000..831588b0 --- /dev/null +++ b/modern/src/reports/ScheduledPage.js @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; +import { + Table, TableRow, TableCell, TableHead, TableBody, IconButton, +} from '@mui/material'; +import makeStyles from '@mui/styles/makeStyles'; +import DeleteIcon from '@mui/icons-material/Delete'; +import { useEffectAsync } from '../reactHelper'; +import { useTranslation } from '../common/components/LocalizationProvider'; +import PageLayout from '../common/components/PageLayout'; +import ReportsMenu from './components/ReportsMenu'; +import TableShimmer from '../common/components/TableShimmer'; + +const useStyles = makeStyles((theme) => ({ + columnAction: { + width: '1%', + paddingRight: theme.spacing(1), + }, +})); + +const ScheduledPage = () => { + const classes = useStyles(); + const t = useTranslation(); + + const calendars = useSelector((state) => state.calendars.items); + + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(false); + + useEffectAsync(async () => { + setLoading(true); + try { + const response = await fetch('/api/reports'); + if (response.ok) { + setItems(await response.json()); + } else { + throw Error(await response.text()); + } + } finally { + setLoading(false); + } + }, []); + + const formatType = (type) => { + switch (type) { + case 'events': + return t('reportEvents'); + case 'route': + return t('reportRoute'); + case 'summary': + return t('reportSummary'); + case 'trips': + return t('reportTrips'); + case 'stops': + return t('reportStops'); + default: + return type; + } + }; + + return ( + <PageLayout menu={<ReportsMenu />} breadcrumbs={['settingsTitle', 'sharedDrivers']}> + <Table> + <TableHead> + <TableRow> + <TableCell>{t('sharedType')}</TableCell> + <TableCell>{t('sharedDescription')}</TableCell> + <TableCell>{t('sharedCalendar')}</TableCell> + <TableCell className={classes.columnAction} /> + </TableRow> + </TableHead> + <TableBody> + {!loading ? items.map((item) => ( + <TableRow key={item.id}> + <TableCell>{formatType(item.type)}</TableCell> + <TableCell>{item.description}</TableCell> + <TableCell>{calendars[item.calendarId].name}</TableCell> + <TableCell className={classes.columnAction} padding="none"> + <IconButton size="small" onClick={() => {}}> + <DeleteIcon fontSize="small" /> + </IconButton> + </TableCell> + </TableRow> + )) : (<TableShimmer columns={4} endAction />)} + </TableBody> + </Table> + </PageLayout> + ); +}; + +export default ScheduledPage; diff --git a/modern/src/reports/components/ReportsMenu.js b/modern/src/reports/components/ReportsMenu.js index 8fa7df2d..3616f828 100644 --- a/modern/src/reports/components/ReportsMenu.js +++ b/modern/src/reports/components/ReportsMenu.js @@ -10,6 +10,7 @@ import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted'; import TrendingUpIcon from '@mui/icons-material/TrendingUp'; import BarChartIcon from '@mui/icons-material/BarChart'; import RouteIcon from '@mui/icons-material/Route'; +import EventRepeatIcon from '@mui/icons-material/EventRepeat'; import { Link, useLocation } from 'react-router-dom'; import { useTranslation } from '../../common/components/LocalizationProvider'; import { useAdministrator } from '../../common/util/permissions'; @@ -74,19 +75,22 @@ const ReportsMenu = () => { icon={<RouteIcon />} /> </List> - {admin && ( - <> - <Divider /> - <List> - <MenuItem - title={t('statisticsTitle')} - link="/reports/statistics" - icon={<BarChartIcon />} - selected={location.pathname === '/reports/statistics'} - /> - </List> - </> - )} + <Divider /> + <List> + <MenuItem + title={t('reportScheduled')} + link="/reports/scheduled" + icon={<EventRepeatIcon />} + /> + {admin && ( + <MenuItem + title={t('statisticsTitle')} + link="/reports/statistics" + icon={<BarChartIcon />} + selected={location.pathname === '/reports/statistics'} + /> + )} + </List> </> ); }; diff --git a/modern/src/resources/l10n/en.json b/modern/src/resources/l10n/en.json index f68f0083..d93ad5bf 100644 --- a/modern/src/resources/l10n/en.json +++ b/modern/src/resources/l10n/en.json @@ -220,6 +220,7 @@ "settingsAppVersion": "App Version", "settingsConnection": "Connection", "reportTitle": "Reports", + "reportScheduled": "Scheduled Reports", "reportDevice": "Device", "reportGroup": "Group", "reportFrom": "From", diff --git a/modern/src/store/calendars.js b/modern/src/store/calendars.js new file mode 100644 index 00000000..924288e6 --- /dev/null +++ b/modern/src/store/calendars.js @@ -0,0 +1,16 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const { reducer, actions } = createSlice({ + name: 'calendars', + initialState: { + items: {}, + }, + reducers: { + update(state, action) { + action.payload.forEach((item) => state.items[item.id] = item); + }, + }, +}); + +export { actions as calendarsActions }; +export { reducer as calendarsReducer }; diff --git a/modern/src/store/index.js b/modern/src/store/index.js index 0cc32d38..ea685ff3 100644 --- a/modern/src/store/index.js +++ b/modern/src/store/index.js @@ -8,6 +8,7 @@ import { geofencesReducer as geofences } from './geofences'; import { groupsReducer as groups } from './groups'; import { driversReducer as drivers } from './drivers'; import { maintenancesReducer as maintenances } from './maintenances'; +import { calendarsReducer as calendars } from './calendars'; import { reportsReducer as reports } from './reports'; import throttleMiddleware from './throttleMiddleware'; @@ -20,6 +21,7 @@ const reducer = combineReducers({ groups, drivers, maintenances, + calendars, reports, }); @@ -31,6 +33,7 @@ export { geofencesActions } from './geofences'; export { groupsActions } from './groups'; export { driversActions } from './drivers'; export { maintenancesActions } from './maintenances'; +export { calendarsActions } from './calendars'; export { reportsActions } from './reports'; export default configureStore({ |