import Head from 'next/head'; import { useEffect, useState } from 'react'; const WS_PATH = '/api/device-events/ws'; interface ApiStatus { message: string; docs: string; } interface DeviceRecord { id: number; topic: string; payload: string; receivedAt: string; } export default function Home() { const [status, setStatus] = useState(null); const [error, setError] = useState(null); const [messages, setMessages] = useState([]); const [connected, setConnected] = useState(false); useEffect(() => { fetch('/api/status') .then((res) => res.json()) .then(setStatus) .catch((err) => setError(err.message)); }, []); useEffect(() => { fetch('/api/device-events') .then((res) => res.json()) .then((payload) => { const records = Array.isArray(payload) ? payload : payload?.records ?? []; setMessages(Array.isArray(records) ? records : []); setConnected(true); }) .catch((err) => { setError(err.message || 'Unable to load device events'); }); const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; const socket = new WebSocket(`${wsProtocol}://${window.location.host}${WS_PATH}`); socket.onopen = () => { setConnected(true); setError(null); }; socket.onmessage = (event) => { try { const payload = JSON.parse(event.data); if (payload.type === 'connected') { setConnected(true); return; } setMessages((prev) => [payload, ...prev].slice(0, 20)); } catch (err) { console.error('Failed to parse device-event payload', err); } }; socket.onerror = () => { setConnected(false); setError('Realtime stream disconnected'); }; return () => { socket.close(); }; }, []); return ( <> UlHub

UlHub

React + Next.js frontend with NestJS backend and live MQTT WebSocket data.

API Status

{error &&

{error}

} {status ? (

{status.message}

{status.docs}

) : (

Loading status...

)}

Device Events

Status: {connected ? 'Connected' : 'Connecting...'}

    {(Array.isArray(messages) ? messages : []).map((msg, index) => (
  • {msg.topic} {msg.receivedAt}
    {msg.payload}
  • ))}
); }