diff --git a/amqp_dashboard/frontend/.prettierrc.json b/amqp_dashboard/frontend/.prettierrc.json
new file mode 100644
index 0000000..544138b
--- /dev/null
+++ b/amqp_dashboard/frontend/.prettierrc.json
@@ -0,0 +1,3 @@
+{
+ "singleQuote": true
+}
diff --git a/amqp_dashboard/frontend/src/App.css b/amqp_dashboard/frontend/src/App.css
deleted file mode 100644
index 74b5e05..0000000
--- a/amqp_dashboard/frontend/src/App.css
+++ /dev/null
@@ -1,38 +0,0 @@
-.App {
- text-align: center;
-}
-
-.App-logo {
- height: 40vmin;
- pointer-events: none;
-}
-
-@media (prefers-reduced-motion: no-preference) {
- .App-logo {
- animation: App-logo-spin infinite 20s linear;
- }
-}
-
-.App-header {
- background-color: #282c34;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- font-size: calc(10px + 2vmin);
- color: white;
-}
-
-.App-link {
- color: #61dafb;
-}
-
-@keyframes App-logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
diff --git a/amqp_dashboard/frontend/src/App.test.tsx b/amqp_dashboard/frontend/src/App.test.tsx
deleted file mode 100644
index d76787e..0000000
--- a/amqp_dashboard/frontend/src/App.test.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from "react";
-import { render, screen } from "@testing-library/react";
-import App from "./App";
-
-test("renders learn react link", () => {
- render();
- const linkElement = screen.getByText(/learn react/i);
- expect(linkElement).toBeInTheDocument();
-});
diff --git a/amqp_dashboard/frontend/src/App.tsx b/amqp_dashboard/frontend/src/App.tsx
deleted file mode 100644
index 9cc63d9..0000000
--- a/amqp_dashboard/frontend/src/App.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from "react";
-import logo from "./logo.svg";
-import "./App.css";
-
-function App() {
- return (
-
- );
-}
-
-export default App;
diff --git a/amqp_dashboard/frontend/src/app.css b/amqp_dashboard/frontend/src/app.css
new file mode 100644
index 0000000..96fa3f8
--- /dev/null
+++ b/amqp_dashboard/frontend/src/app.css
@@ -0,0 +1,17 @@
+html {
+ font-family: arial, sans-serif;
+ background-color: #282c34;
+ color: white;
+}
+
+.app {
+ margin: 50px;
+}
+
+table,
+th,
+td {
+ border: 1px solid white;
+ border-collapse: collapse;
+ padding: 10px;
+}
diff --git a/amqp_dashboard/frontend/src/app.tsx b/amqp_dashboard/frontend/src/app.tsx
new file mode 100644
index 0000000..4d07d6a
--- /dev/null
+++ b/amqp_dashboard/frontend/src/app.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import DataPage from './components/data-page';
+import './app.css';
+
+const IS_PROD = process.env.NODE_ENV === 'production';
+
+const URL_PREFIX = IS_PROD ? '' : 'http://localhost:8080/';
+
+const App = () => {
+ return (
+
+
+
+
+ );
+};
+
+export default App;
diff --git a/amqp_dashboard/frontend/src/components/data-page.tsx b/amqp_dashboard/frontend/src/components/data-page.tsx
new file mode 100644
index 0000000..6d830da
--- /dev/null
+++ b/amqp_dashboard/frontend/src/components/data-page.tsx
@@ -0,0 +1,87 @@
+import React, { FC, useCallback, useEffect, useState } from 'react';
+import Table from './table';
+import type { Data } from '../types';
+
+const fetchData = async (prefix: string): Promise => {
+ const url = `${prefix}api/data`;
+ return fetch(url).then((res) => res.json());
+};
+
+type Props = {
+ prefix: string;
+};
+
+const DataPage: FC = ({ prefix }) => {
+ const [data, setData] = useState(null);
+
+ const refresh = useCallback(async () => {
+ const newData = await fetchData(prefix);
+ setData(newData);
+ }, [setData, prefix]);
+
+ useEffect(() => {
+ const interval = setInterval(refresh, 1000);
+
+ return () => clearInterval(interval);
+ }, [refresh]);
+
+ return (
+
+
+ Connections
+ {data ? (
+ [
+ connection.id,
+ connection.peerAddr,
+ connection.channels.length,
+ ])}
+ />
+ ) : (
+ Loading...
+ )}
+
+
+ Queues
+ {data ? (
+ [
+ queue.id,
+ queue.name,
+ queue.durable ? 'Yes' : 'No',
+ ])}
+ />
+ ) : (
+ Loading...
+ )}
+
+
+ Channels
+ {data ? (
+
+ connection.channels.map((channel) => ({
+ ...channel,
+ connectionId: connection.id,
+ }))
+ )
+ .flat()
+ .map((channel) => [
+ channel.id,
+ channel.connectionId,
+ channel.number,
+ ])}
+ />
+ ) : (
+ Loading...
+ )}
+
+
+ );
+};
+
+export default DataPage;
diff --git a/amqp_dashboard/frontend/src/components/table.tsx b/amqp_dashboard/frontend/src/components/table.tsx
new file mode 100644
index 0000000..66a7e7d
--- /dev/null
+++ b/amqp_dashboard/frontend/src/components/table.tsx
@@ -0,0 +1,31 @@
+import React, { FC } from 'react';
+
+type Cell = string | number;
+
+type Row = ReadonlyArray;
+
+type Props = {
+ headers: ReadonlyArray;
+ rows: ReadonlyArray;
+};
+
+const Table: FC = ({ headers, rows }) => {
+ return (
+
+
+ {headers.map((header) => (
+ | {header} |
+ ))}
+
+ {rows.map((row) => (
+
+ {row.map((cell) => (
+ | {cell} |
+ ))}
+
+ ))}
+
+ );
+};
+
+export default Table;
diff --git a/amqp_dashboard/frontend/src/index.css b/amqp_dashboard/frontend/src/index.css
index 4a1df4d..ec2585e 100644
--- a/amqp_dashboard/frontend/src/index.css
+++ b/amqp_dashboard/frontend/src/index.css
@@ -1,13 +1,13 @@
body {
margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
- "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
diff --git a/amqp_dashboard/frontend/src/index.tsx b/amqp_dashboard/frontend/src/index.tsx
index ad9cbbb..d6f53d1 100644
--- a/amqp_dashboard/frontend/src/index.tsx
+++ b/amqp_dashboard/frontend/src/index.tsx
@@ -1,17 +1,11 @@
-import React from "react";
-import ReactDOM from "react-dom";
-import "./index.css";
-import App from "./App";
-import reportWebVitals from "./reportWebVitals";
+import React from 'react';
+import ReactDOM from 'react-dom';
+import './index.css';
+import App from './app';
ReactDOM.render(
,
- document.getElementById("root")
+ document.getElementById('root')
);
-
-// If you want to start measuring performance in your app, pass a function
-// to log results (for example: reportWebVitals(console.log))
-// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
-reportWebVitals();
diff --git a/amqp_dashboard/frontend/src/logo.svg b/amqp_dashboard/frontend/src/logo.svg
deleted file mode 100644
index 9dfc1c0..0000000
--- a/amqp_dashboard/frontend/src/logo.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/amqp_dashboard/frontend/src/react-app-env.d.ts b/amqp_dashboard/frontend/src/react-app-env.d.ts
deleted file mode 100644
index 6431bc5..0000000
--- a/amqp_dashboard/frontend/src/react-app-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/amqp_dashboard/frontend/src/reportWebVitals.ts b/amqp_dashboard/frontend/src/reportWebVitals.ts
deleted file mode 100644
index 5fa3583..0000000
--- a/amqp_dashboard/frontend/src/reportWebVitals.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { ReportHandler } from "web-vitals";
-
-const reportWebVitals = (onPerfEntry?: ReportHandler) => {
- if (onPerfEntry && onPerfEntry instanceof Function) {
- import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
- getCLS(onPerfEntry);
- getFID(onPerfEntry);
- getFCP(onPerfEntry);
- getLCP(onPerfEntry);
- getTTFB(onPerfEntry);
- });
- }
-};
-
-export default reportWebVitals;
diff --git a/amqp_dashboard/frontend/src/setupTests.ts b/amqp_dashboard/frontend/src/setupTests.ts
deleted file mode 100644
index 1dd407a..0000000
--- a/amqp_dashboard/frontend/src/setupTests.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-// jest-dom adds custom jest matchers for asserting on DOM nodes.
-// allows you to do things like:
-// expect(element).toHaveTextContent(/react/i)
-// learn more: https://github.com/testing-library/jest-dom
-import "@testing-library/jest-dom";
diff --git a/amqp_dashboard/frontend/src/types.ts b/amqp_dashboard/frontend/src/types.ts
new file mode 100644
index 0000000..b052d40
--- /dev/null
+++ b/amqp_dashboard/frontend/src/types.ts
@@ -0,0 +1,21 @@
+export type Channel = {
+ id: string;
+ number: number;
+};
+
+export type Connection = {
+ id: string;
+ peerAddr: string;
+ channels: ReadonlyArray;
+};
+
+export type Queue = {
+ id: string;
+ name: string;
+ durable: boolean;
+};
+
+export type Data = {
+ connections: ReadonlyArray;
+ queues: ReadonlyArray;
+};
|