Skip to content

Commit

Permalink
Add a live feed implementation for host registration (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
dottorblaster authored Jan 20, 2022
1 parent 1b217a8 commit 82d3162
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 22 deletions.
68 changes: 68 additions & 0 deletions assets/js/components/Home/Home.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import { useSelector } from 'react-redux';

const Home = () => {
const liveFeed = useSelector((state) => state.liveFeed.entries);
const hosts = useSelector((state) => state.hostsList.hosts);
const hostsNumber = hosts.length;

return (
<div className="container px-4">
<div className="">
<div className="max-w-xs py-4 px-8 bg-white shadow rounded-lg my-2">
<div>
<h2 className="text-gray-600">Hosts</h2>
<p className="mt-2 text-gray-800 text-3xl font-semibold">
{hostsNumber}
</p>
</div>
</div>
</div>
<div className="flex flex-col mt-16">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Time
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Source
</th>
<th
scope="col"
className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
Message
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{liveFeed.map(({ time, source, message }) => (
<tr key={time} className="animate-fade">
<td className="px-6 py-4 whitespace-nowrap">
<div className="content-center">{time}</div>
</td>
<td className="px-6 py-4 whitespace-nowrap">{source}</td>
<td className="px-6 py-4 whitespace-nowrap">{message}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
);
};

export default Home;
3 changes: 3 additions & 0 deletions assets/js/components/Home/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Home from './Home';

export default Home;
6 changes: 2 additions & 4 deletions assets/js/components/HostsList.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React from 'react';
import { useSelector } from 'react-redux';

import {
EOS_LENS_FILLED,
} from 'eos-icons-react';
import { EOS_LENS_FILLED } from 'eos-icons-react';

const getHeartbeatIcon = ({ heartbeat }) => {
switch (heartbeat) {
Expand Down Expand Up @@ -55,7 +53,7 @@ const HostsList = () => {
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{hosts.map((host) => (
<tr key={host.id}>
<tr key={host.id} className="animate-fade">
<td className="px-6 py-4 whitespace-nowrap">
<div className="content-center">
{getHeartbeatIcon(host)}
Expand Down
8 changes: 2 additions & 6 deletions assets/js/components/Layout/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
EOS_CLUSTER_ROLE,
} from 'eos-icons-react';

import TrentoLogo from '../../../static/trento-logo-stacked.svg'
import TrentoLogo from '../../../static/trento-logo-stacked.svg';

const classNames = (...classes) => classes.filter(Boolean).join(' ');

Expand Down Expand Up @@ -93,11 +93,7 @@ const Layout = () => {
</div>
</Transition.Child>
<div className="flex-shrink-0 flex items-center px-4">
<img
className="h-8 w-auto"
src={TrentoLogo}
alt="Workflow"
/>
<img className="h-8 w-auto" src={TrentoLogo} alt="Workflow" />
</div>
<div className="mt-5 flex-1 h-0 overflow-y-auto">
<nav className="px-2 space-y-1">
Expand Down
18 changes: 12 additions & 6 deletions assets/js/state/hosts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ const initialState = {
hosts: [],
};


export const hostsListSlice = createSlice({
name: 'hostsList',
initialState,
Expand All @@ -19,23 +18,23 @@ export const hostsListSlice = createSlice({
updateHost: (state, action) => {
state.hosts = state.hosts.map((host) => {
if (host.id === action.payload.id) {
host = { ...host, ...action.payload }
host = { ...host, ...action.payload };
}
return host;
});
},
setHeartbeatPassing: (state, action) => {
state.hosts = state.hosts.map((host) => {
if (host.id === action.payload.id) {
host.heartbeat = "passing";
host.heartbeat = 'passing';
}
return host;
});
},
setHeartbeatCritical: (state, action) => {
state.hosts = state.hosts.map((host) => {
if (host.id === action.payload.id) {
host.heartbeat = "critical";
host.heartbeat = 'critical';
}
return host;
});
Expand All @@ -49,7 +48,14 @@ export const hostsListSlice = createSlice({
},
});

export const { setHosts, appendHost, updateHost, startLoading, stopLoading, setHeartbeatPassing, setHeartbeatCritical } =
hostsListSlice.actions;
export const {
setHosts,
appendHost,
updateHost,
startLoading,
stopLoading,
setHeartbeatPassing,
setHeartbeatCritical,
} = hostsListSlice.actions;

export default hostsListSlice.reducer;
6 changes: 4 additions & 2 deletions assets/js/state/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import createSagaMiddleware from 'redux-saga';
import { Socket } from 'phoenix';

import hostsListReducer from './hosts';
import liveFeedReducer from './liveFeed';

import rootSaga from './sagas';

Expand All @@ -11,6 +12,7 @@ const sagaMiddleware = createSagaMiddleware();
export const store = configureStore({
reducer: {
hostsList: hostsListReducer,
liveFeed: liveFeedReducer,
},
middleware: [sagaMiddleware],
});
Expand All @@ -27,8 +29,8 @@ const processChannelEvents = (store) => {
);

channel.on('host_details_updated', (payload) =>
store.dispatch({ type: 'HOST_DETAILS_UPDATED', payload })
);
store.dispatch({ type: 'HOST_DETAILS_UPDATED', payload })
);

channel.on('heartbeat_succeded', (payload) =>
store.dispatch({ type: 'HEARTBEAT_SUCCEDED', payload })
Expand Down
22 changes: 22 additions & 0 deletions assets/js/state/liveFeed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { createSlice } from '@reduxjs/toolkit';

const initialState = {
entries: [],
};

export const liveFeedSlice = createSlice({
name: 'liveFeed',
initialState,
reducers: {
appendEntryToLiveFeed: (state, { payload: { source, message } }) => {
const time = Date.now();
const newEntry = { time, source, message };
const entries = [newEntry, ...state.entries];
state.entries = entries;
},
},
});

export const { appendEntryToLiveFeed } = liveFeedSlice.actions;

export default liveFeedSlice.reducer;
27 changes: 24 additions & 3 deletions assets/js/state/sagas/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import { get } from 'axios';
import { put, all, call, takeEvery } from 'redux-saga/effects';

import { appendHost, setHosts, updateHost, startLoading, stopLoading, setHeartbeatPassing, setHeartbeatCritical }
from '../hosts';
import {
appendHost,
setHosts,
updateHost,
startLoading,
stopLoading,
setHeartbeatPassing,
setHeartbeatCritical,
} from '../hosts';
import { appendEntryToLiveFeed } from '../liveFeed';
import { watchNotifications } from './notifications';

const notify = ({ text, icon }) => ({
Expand All @@ -19,6 +27,12 @@ function* initialDataFetch() {

function* hostRegistered({ payload }) {
yield put(appendHost(payload));
yield put(
appendEntryToLiveFeed({
source: payload.hostname,
message: 'New host registered.',
})
);
yield put(
notify({
text: `A new host, ${payload.hostname}, has been discovered.`,
Expand Down Expand Up @@ -68,5 +82,12 @@ function* watchHeartbeatFailed() {
}

export default function* rootSaga() {
yield all([initialDataFetch(), watchHostRegistered(), watchHostDetailsUpdated(), watchHeartbeatSucceded(), watchHeartbeatFailed(), watchNotifications()]);
yield all([
initialDataFetch(),
watchHostRegistered(),
watchHostDetailsUpdated(),
watchHeartbeatSucceded(),
watchHeartbeatFailed(),
watchNotifications(),
]);
}
2 changes: 1 addition & 1 deletion assets/js/tronto.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { Provider } from 'react-redux';
import { store } from './state';

import Layout from './components/Layout';
import Home from './components/Home';
import HostsList from './components/HostsList';

const Home = () => <h1>HOME HERE</h1>;
const ClustersList = () => <h1>CLUSTERS LIST</h1>;

const App = () => {
Expand Down
9 changes: 9 additions & 0 deletions assets/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@ module.exports = {
content: ['./js/**/*.{js,jsx}', '../lib/*_web/**/*.*ex'],
theme: {
extend: {
animation: {
fade: 'fadeIn 0.5s ease-in-out',
},
keyframes: (theme) => ({
fadeIn: {
'0%': { opacity: '0%' },
'100%': { opacity: '100%' },
},
}),
colors: {
'pine-green': '#0C322C',
'jungle-green': {
Expand Down

0 comments on commit 82d3162

Please sign in to comment.