diff --git a/ui/src/App.tsx b/ui/src/App.tsx
index fcd8b00..b1f25f2 100644
--- a/ui/src/App.tsx
+++ b/ui/src/App.tsx
@@ -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() {
);
diff --git a/ui/src/features/radio/LogViewer.module.scss b/ui/src/features/radio/LogViewer.module.scss
new file mode 100644
index 0000000..b5f502a
--- /dev/null
+++ b/ui/src/features/radio/LogViewer.module.scss
@@ -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;
+ }
+}
diff --git a/ui/src/features/radio/LogViewer.tsx b/ui/src/features/radio/LogViewer.tsx
new file mode 100644
index 0000000..36c6138
--- /dev/null
+++ b/ui/src/features/radio/LogViewer.tsx
@@ -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 (
+
+
+
+
+
+
+ {!hidden &&
+
+ {loading ?
+ 'Caricamento' :
+ logs?.map((line, i) =>
{line}
)}
+
+ }
+
+
+ );
+}
diff --git a/ui/src/features/radio/Radio.tsx b/ui/src/features/radio/Radio.tsx
index a68a78e..62e710f 100644
--- a/ui/src/features/radio/Radio.tsx
+++ b/ui/src/features/radio/Radio.tsx
@@ -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);
diff --git a/ui/src/features/radio/radioAPI.ts b/ui/src/features/radio/radioAPI.ts
index e2b2efb..e2007b3 100644
--- a/ui/src/features/radio/radioAPI.ts
+++ b/ui/src/features/radio/radioAPI.ts
@@ -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;
+}
diff --git a/ui/src/features/radio/radioSlice.spec.ts b/ui/src/features/radio/radioSlice.spec.ts
index 810c354..d59cc1b 100644
--- a/ui/src/features/radio/radioSlice.spec.ts
+++ b/ui/src/features/radio/radioSlice.spec.ts
@@ -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', () => {
diff --git a/ui/src/features/radio/radioSlice.ts b/ui/src/features/radio/radioSlice.ts
index 05c942c..cd0e1f3 100644
--- a/ui/src/features/radio/radioSlice.ts
+++ b/ui/src/features/radio/radioSlice.ts
@@ -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,
+ 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 =