瀏覽代碼

feat(web): layout & auto-update debug page

Paul Armstrong 4 年之前
父節點
當前提交
18db6daf0a
共有 4 個文件被更改,包括 108 次插入24 次删除
  1. 1 0
      web/snowpack.config.js
  2. 2 2
      web/src/App.jsx
  3. 89 8
      web/src/Debug.jsx
  4. 16 14
      web/src/components/Table.jsx

+ 1 - 0
web/snowpack.config.js

@@ -21,6 +21,7 @@ module.exports = {
       },
     ],
   ],
+  routes: [{ match: 'routes', src: '.*', dest: '/index.html' }],
   packageOptions: {
     sourcemap: false,
   },

+ 2 - 2
web/src/App.jsx

@@ -24,9 +24,9 @@ export default function App() {
     <div />
   ) : (
     <Config.Provider value={config}>
-      <div className="md:flex flex-col md:flex-row md:min-h-screen w-full bg-gray-100 dark:bg-gray-800 ">
+      <div className="flex md:min-h-screen w-full bg-gray-100 dark:bg-gray-800">
         <Sidebar />
-        <div className="p-4">
+        <div className="p-4 min-w-0">
           <Router>
             <CameraMap path="/cameras/:camera/editor" />
             <Camera path="/cameras/:camera" />

+ 89 - 8
web/src/Debug.jsx

@@ -1,16 +1,97 @@
 import { h } from 'preact';
-import { ApiHost } from './context';
-import { useContext, useEffect, useState } from 'preact/hooks';
+import Heading from './components/Heading';
+import Link from './components/Link';
+import { ApiHost, Config } from './context';
+import { Table, Tbody, Thead, Tr, Th, Td } from './components/Table';
+import { useCallback, useContext, useEffect, useState } from 'preact/hooks';
 
 export default function Debug() {
   const apiHost = useContext(ApiHost);
-  const [config, setConfig] = useState({});
+  const config = useContext(Config);
+  const [stats, setStats] = useState({});
+  const [timeoutId, setTimeoutId] = useState(null);
 
-  useEffect(async () => {
-    const response = await fetch(`${apiHost}/api/stats`);
-    const data = response.ok ? await response.json() : {};
-    setConfig(data);
+  const fetchStats = useCallback(async () => {
+    const statsResponse = await fetch(`${apiHost}/api/stats`);
+    const stats = statsResponse.ok ? await statsResponse.json() : {};
+    setStats(stats);
+    setTimeoutId(setTimeout(fetchStats, 1000));
+  }, [setStats]);
+
+  useEffect(() => {
+    fetchStats();
   }, []);
 
-  return <pre>{JSON.stringify(config, null, 2)}</pre>;
+  useEffect(() => {
+    return () => {
+      clearTimeout(timeoutId);
+    };
+  }, [timeoutId]);
+
+  const { detectors, detection_fps, service, ...cameras } = stats;
+  if (!service) {
+    return 'loading…';
+  }
+
+  const detectorNames = Object.keys(detectors);
+  const detectorDataKeys = Object.keys(detectors[detectorNames[0]]);
+
+  const cameraNames = Object.keys(cameras);
+  const cameraDataKeys = Object.keys(cameras[cameraNames[0]]);
+
+  return (
+    <div>
+      <Heading>
+        Debug <span className="text-sm">{service.version}</span>
+      </Heading>
+      <Table className="w-full">
+        <Thead>
+          <Tr>
+            <Th>detector</Th>
+            {detectorDataKeys.map((name) => (
+              <Th>{name.replace('_', ' ')}</Th>
+            ))}
+          </Tr>
+        </Thead>
+        <Tbody>
+          {detectorNames.map((detector, i) => (
+            <Tr index={i}>
+              <Td>{detector}</Td>
+              {detectorDataKeys.map((name) => (
+                <Td key={`${name}-${detector}`}>{detectors[detector][name]}</Td>
+              ))}
+            </Tr>
+          ))}
+        </Tbody>
+      </Table>
+
+      <Table className="w-full">
+        <Thead>
+          <Tr>
+            <Th>camera</Th>
+            {cameraDataKeys.map((name) => (
+              <Th>{name.replace('_', ' ')}</Th>
+            ))}
+          </Tr>
+        </Thead>
+        <Tbody>
+          {cameraNames.map((camera, i) => (
+            <Tr index={i}>
+              <Td>
+                <Link href={`/cameras/${camera}`}>{camera}</Link>
+              </Td>
+              {cameraDataKeys.map((name) => (
+                <Td key={`${name}-${camera}`}>{cameras[camera][name]}</Td>
+              ))}
+            </Tr>
+          ))}
+        </Tbody>
+      </Table>
+
+      <Heading size="sm">Config</Heading>
+      <pre className="font-mono overflow-y-scroll overflow-x-scroll max-h-96 rounded bg-white dark:bg-gray-900">
+        {JSON.stringify(config, null, 2)}
+      </pre>
+    </div>
+  );
 }

+ 16 - 14
web/src/components/Table.jsx

@@ -1,29 +1,31 @@
 import { h } from 'preact';
 
-export function Table({ children }) {
-  return <table className="table-auto border-collapse text-gray-900 dark:text-gray-200">{children}</table>;
+export function Table({ children, className }) {
+  return (
+    <table className={`table-auto border-collapse text-gray-900 dark:text-gray-200 ${className}`}>{children}</table>
+  );
 }
 
-export function Thead({ children }) {
-  return <thead className="">{children}</thead>;
+export function Thead({ children, className }) {
+  return <thead className={`${className}`}>{children}</thead>;
 }
 
-export function Tbody({ children }) {
-  return <tbody className="">{children}</tbody>;
+export function Tbody({ children, className }) {
+  return <tbody className={`${className}`}>{children}</tbody>;
 }
 
-export function Tfoot({ children }) {
-  return <tfoot className="">{children}</tfoot>;
+export function Tfoot({ children, className }) {
+  return <tfoot className={`${className}`}>{children}</tfoot>;
 }
 
-export function Tr({ children, index }) {
-  return <tr className={`${index % 2 ? 'bg-gray-200 ' : ''}`}>{children}</tr>;
+export function Tr({ children, className, index }) {
+  return <tr className={`${index % 2 ? 'bg-gray-200 ' : ''} ${className}`}>{children}</tr>;
 }
 
-export function Th({ children }) {
-  return <th className="border-b-2 border-gray-400 p-4 text-left">{children}</th>;
+export function Th({ children, className }) {
+  return <th className={`border-b-2 border-gray-400 p-4 text-left ${className}`}>{children}</th>;
 }
 
-export function Td({ children }) {
-  return <td className="p-4">{children}</td>;
+export function Td({ children, className }) {
+  return <td className={`p-4 ${className}`}>{children}</td>;
 }