Add LogViewer to ui
This commit is contained in:
parent
8ad6721315
commit
79a2c8aa66
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import { RadioToggler } from './features/radio/Radio';
|
||||
import { LogViewer } from './features/radio/LogViewer';
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
|
@ -7,6 +8,7 @@ function App() {
|
|||
<div className="App">
|
||||
<header className="App-header">
|
||||
<RadioToggler />
|
||||
<LogViewer />
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
|
|
44
ui/src/features/radio/LogViewer.module.scss
Normal file
44
ui/src/features/radio/LogViewer.module.scss
Normal file
|
@ -0,0 +1,44 @@
|
|||
$button-color: #4CAF50;
|
||||
$button-background: white;
|
||||
$row-even-background: transparent;
|
||||
$row-odd-background: rgba(138, 230, 141, 0.7);
|
||||
|
||||
.button {
|
||||
transition-duration: 0.2s;
|
||||
border: 2px solid $button-color;
|
||||
border-radius: 4px;
|
||||
padding: 10px 24px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: $button-color; /* Green */
|
||||
color: $button-background;
|
||||
}
|
||||
|
||||
.logsContainer {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.buttonContainer {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.rowsContainer {
|
||||
height: 500px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.rows {
|
||||
padding: 10px;
|
||||
overflow-y: scroll;
|
||||
|
||||
.even {
|
||||
background-color: $row-even-background;
|
||||
}
|
||||
|
||||
.odd {
|
||||
background-color: $row-odd-background;
|
||||
}
|
||||
}
|
38
ui/src/features/radio/LogViewer.tsx
Normal file
38
ui/src/features/radio/LogViewer.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useAppSelector, useAppDispatch } from '../../app/hooks';
|
||||
import { selectLogsLoading, selectLogs, requestLogsThunk } from './radioSlice';
|
||||
import styles from './LogViewer.module.scss';
|
||||
|
||||
export function LogViewer() {
|
||||
const loading = useAppSelector(selectLogsLoading);
|
||||
const logs = useAppSelector(selectLogs);
|
||||
const [hidden, setHidden] = useState(true);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
return (
|
||||
<div className={styles.logsContainer}>
|
||||
<div className={styles.buttonContainer}>
|
||||
<button
|
||||
className={styles.button}
|
||||
onClick={() => dispatch(requestLogsThunk())}
|
||||
>Recupera stato</button>
|
||||
<button
|
||||
className={styles.button}
|
||||
onClick={() => setHidden(!hidden)}
|
||||
>{hidden ? 'Mostra' : 'Nascondi'}</button>
|
||||
</div>
|
||||
<div className={styles.rowsContainer}>
|
||||
{!hidden &&
|
||||
<div className={styles.rows}>
|
||||
{loading ?
|
||||
'Caricamento' :
|
||||
logs?.map((line, i) => <div
|
||||
className={(i % 2) ? styles.even : styles.odd}
|
||||
>{line}</div>)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
|
@ -5,7 +5,7 @@ import { useAppSelector, useAppDispatch } from '../../app/hooks';
|
|||
import { RadioState, ConnectionState } from '../../app/types';
|
||||
import {
|
||||
toggleRadioState,
|
||||
selectLoading,
|
||||
selectRadioLoading,
|
||||
selectRadio,
|
||||
selectConnection,
|
||||
} from './radioSlice';
|
||||
|
@ -26,7 +26,7 @@ const getWSUrl = async () => {
|
|||
};
|
||||
|
||||
export function RadioToggler() {
|
||||
const loading = useAppSelector(selectLoading);
|
||||
const loading = useAppSelector(selectRadioLoading);
|
||||
const radioState = useAppSelector(selectRadio);
|
||||
const connectionState = useAppSelector(selectConnection);
|
||||
|
||||
|
|
|
@ -46,3 +46,23 @@ export const requestToStop = async () => {
|
|||
return success;
|
||||
}
|
||||
|
||||
export const requestLogs = async () => {
|
||||
console.log("[REST] request logs");
|
||||
|
||||
try {
|
||||
const logsUrl = window.location.href + 'status';
|
||||
const resp = await fetch(logsUrl);
|
||||
|
||||
if (resp.ok) {
|
||||
console.log("[REST] Logs: success");
|
||||
const body = await resp.json();
|
||||
if (Array.isArray(body['lines'])) {
|
||||
return body['lines'];
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("[REST] Logs: failure: %s", err);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -1,46 +1,46 @@
|
|||
import { RadioState, ConnectionState } from '../../app/types';
|
||||
import reducer, {
|
||||
AppState,
|
||||
toggleLoading,
|
||||
toggleRadioLoading,
|
||||
setRadioStarted,
|
||||
setRadioStopped,
|
||||
toggleRadio,
|
||||
setConnected,
|
||||
setDisconnected,
|
||||
toggleConnection,
|
||||
selectLoading,
|
||||
selectRadioLoading,
|
||||
selectRadio,
|
||||
selectConnection,
|
||||
} from './radioSlice';
|
||||
|
||||
describe('radio reducer', () => {
|
||||
const initialState: AppState = {
|
||||
loading: false,
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.DISCONNECTED,
|
||||
};
|
||||
|
||||
const connectedStarted: AppState = {
|
||||
radio: RadioState.STARTED,
|
||||
loading: false,
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.CONNECTED,
|
||||
};
|
||||
|
||||
const connectedStopped: AppState = {
|
||||
radio: RadioState.STOPPED,
|
||||
loading: false,
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.CONNECTED,
|
||||
};
|
||||
|
||||
it('should handle initial state', () => {
|
||||
expect(reducer(undefined, { type: 'unknown' })).toEqual({
|
||||
loading: false,
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.DISCONNECTED,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle set loading', () => {
|
||||
const actual = reducer(initialState, toggleLoading());
|
||||
expect(actual.loading).toEqual(true);
|
||||
const actual = reducer(initialState, toggleRadioLoading());
|
||||
expect(actual.loading.radio).toEqual(true);
|
||||
});
|
||||
|
||||
it('should hande toggle radio started', () => {
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||
import { RootState, AppThunk } from '../../app/store';
|
||||
import { RadioState, ConnectionState } from '../../app/types';
|
||||
import { requestToStart, requestToStop } from './radioAPI';
|
||||
import { requestToStart, requestToStop, requestLogs } from './radioAPI';
|
||||
|
||||
export interface LoadingState {
|
||||
radio: boolean,
|
||||
logs: boolean,
|
||||
}
|
||||
|
||||
export interface AppState {
|
||||
radio?: RadioState,
|
||||
loading: boolean,
|
||||
logs?: Array<string>,
|
||||
loading: LoadingState,
|
||||
connection: ConnectionState
|
||||
}
|
||||
|
||||
const initialState: AppState = {
|
||||
radio: undefined,
|
||||
loading: false,
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.DISCONNECTED,
|
||||
};
|
||||
|
||||
|
@ -31,11 +37,19 @@ export const requestToStopThunk = createAsyncThunk(
|
|||
}
|
||||
);
|
||||
|
||||
export const requestLogsThunk = createAsyncThunk(
|
||||
'radio/restStatus',
|
||||
async () => {
|
||||
return await requestLogs();
|
||||
}
|
||||
);
|
||||
|
||||
export const radioSlice = createSlice({
|
||||
name: 'radio',
|
||||
initialState,
|
||||
reducers: {
|
||||
toggleLoading: (state) => { state.loading = false ? state.loading : true },
|
||||
toggleRadioLoading: (state) => { state.loading.radio = !state.loading.radio },
|
||||
toggleLogsLoading: (state) => { state.loading.logs = !state.loading.logs },
|
||||
setRadioStarted: (state) => { console.log("[Redux] STARTED"); state.radio = RadioState.STARTED },
|
||||
setRadioStopped: (state) => { console.log("[Redux] STOPPED"); state.radio = RadioState.STOPPED },
|
||||
unsetRadio: (state) => { state.radio = undefined },
|
||||
|
@ -59,32 +73,42 @@ export const radioSlice = createSlice({
|
|||
extraReducers: builder => {
|
||||
builder
|
||||
.addCase(requestToStartThunk.pending, (state) => {
|
||||
state.loading = true;
|
||||
state.loading.radio = true;
|
||||
return state;
|
||||
})
|
||||
.addCase(requestToStartThunk.fulfilled, (state, action) => {
|
||||
state.loading = false;
|
||||
state.loading.radio = false;
|
||||
if (action.payload) {
|
||||
state.radio = RadioState.STARTED;
|
||||
}
|
||||
return state;
|
||||
})
|
||||
.addCase(requestToStopThunk.pending, (state, _) => {
|
||||
state.loading = true;
|
||||
state.loading.radio = true;
|
||||
return state;
|
||||
})
|
||||
.addCase(requestToStopThunk.fulfilled, (state, action) => {
|
||||
state.loading = false;
|
||||
state.loading.radio = false;
|
||||
if (action.payload) {
|
||||
state.radio = RadioState.STOPPED;
|
||||
}
|
||||
return state;
|
||||
})
|
||||
.addCase(requestLogsThunk.pending, (state, _) => {
|
||||
state.loading.logs = true;
|
||||
return state;
|
||||
})
|
||||
.addCase(requestLogsThunk.fulfilled, (state, action) => {
|
||||
state.loading.logs = false;
|
||||
state.logs = action.payload;
|
||||
return state;
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
toggleLoading,
|
||||
toggleRadioLoading,
|
||||
toggleLogsLoading,
|
||||
setRadioStarted,
|
||||
setRadioStopped,
|
||||
unsetRadio,
|
||||
|
@ -94,8 +118,10 @@ export const {
|
|||
toggleConnection,
|
||||
} = radioSlice.actions;
|
||||
|
||||
export const selectLoading = (state: RootState) => state.globalState.loading;
|
||||
export const selectRadioLoading = (state: RootState) => state.globalState.loading.radio;
|
||||
export const selectLogsLoading = (state: RootState) => state.globalState.loading.logs;
|
||||
export const selectRadio = (state: RootState) => state.globalState.radio;
|
||||
export const selectLogs = (state: RootState) => state.globalState.logs;
|
||||
export const selectConnection = (state: RootState) => state.globalState.connection;
|
||||
|
||||
export const toggleRadioState =
|
||||
|
|
Loading…
Reference in New Issue
Block a user