diff --git a/frontend/app/manufacturer/Navbar/navbar.tsx b/frontend/app/manufacturer/Navbar/navbar.tsx deleted file mode 100644 index 556b5f2..0000000 --- a/frontend/app/manufacturer/Navbar/navbar.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import Link from "next/link"; -import { Anchor } from "lucide-react"; - -export const NAVLINKS = [ - { - title: "DashBoard", - href: "/manager/dashBoard", - icon: Anchor - }, - { - title: "Accounting", - href: "/manager/accounting", - icon: Anchor - }, - { - title: "StockCount", - href: "/manager/stockCount", - icon: Anchor - }, -]; - -export function Navbar() { - return ( - - ); -} \ No newline at end of file diff --git a/frontend/app/manufacturer/page.tsx b/frontend/app/manufacturer/page.tsx index 0c09b27..eaa315f 100644 --- a/frontend/app/manufacturer/page.tsx +++ b/frontend/app/manufacturer/page.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useState, useCallback } from "react"; +import mqtt from "mqtt"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Card } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; @@ -26,6 +27,7 @@ import { import { AlertCircle, BarChartIcon, + BellIcon, ClockIcon, DollarSignIcon, ShoppingBagIcon, @@ -34,6 +36,8 @@ import { TrendingUpIcon, TruckIcon, UsersIcon, + WifiIcon, + XIcon, } from "lucide-react"; // Interfaces @@ -86,6 +90,14 @@ interface ShipmentResponse { results: Shipment[]; } +// Interface for anomaly notifications +interface AnomalyNotification { + id: string; + message: string; + timestamp: Date; + read: boolean; +} + // Hardcoded data for fallback const testData: OverviewCard = { totalOrders: 0, @@ -182,6 +194,9 @@ export const payments: Payment[] = [ // API URL for fetching counts const API_URL = "http://127.0.0.1:8000/api"; +// MQTT Topics +const ANOMALY_TOPIC = "manufacturing/anomalies"; + const Dashboard: React.FC = () => { const [overviewData, setOverviewData] = useState(testData); const [analytics] = useState(analyticsData); @@ -194,6 +209,13 @@ const Dashboard: React.FC = () => { const [shipmentsError, setShipmentsError] = useState(null); const [allocateLoading, setAllocateLoading] = useState(false); const [allocateError, setAllocateError] = useState(null); + const [mqttConnected, setMqttConnected] = useState(false); + + // States for anomaly notifications + const [anomalyNotifications, setAnomalyNotifications] = useState([]); + const [showNotificationPanel, setShowNotificationPanel] = useState(false); + const [unreadCount, setUnreadCount] = useState(0); + // Define columns for the shipment data table const shipmentColumns = [ { @@ -245,18 +267,58 @@ const Dashboard: React.FC = () => { }, ]; + // Function to add a new anomaly notification + const addAnomalyNotification = useCallback(() => { + const newNotification: AnomalyNotification = { + id: Date.now().toString(), + message: "Anomaly Detected", // Fixed message as per requirement + timestamp: new Date(), + read: false, + }; + + setAnomalyNotifications((prev) => [newNotification, ...prev]); + setUnreadCount((prev) => prev + 1); + + // Auto-hide the toast notification after 5 seconds + setTimeout(() => { + setAnomalyNotifications((prev) => + prev.filter((notification) => notification.id !== newNotification.id) + ); + setUnreadCount((prev) => Math.max(0, prev - 1)); + }, 5000); + }, []); + + // Function to mark notifications as read + const markAllAsRead = useCallback(() => { + setAnomalyNotifications((prev) => + prev.map((notification) => ({ ...notification, read: true })) + ); + setUnreadCount(0); + }, []); + + // Function to dismiss a specific notification + const dismissNotification = useCallback((id: string) => { + setAnomalyNotifications((prev) => { + const notification = prev.find((n) => n.id === id); + if (notification && !notification.read) { + setUnreadCount((count) => Math.max(0, count - 1)); + } + return prev.filter((notification) => notification.id !== id); + }); + }, []); + // Get auth token with error handling - const getAuthToken = useCallback(() => { - const token = localStorage.getItem("access_token"); + const getAuthToken = useCallback(async () => { + let token = localStorage.getItem("access_token"); if (!token) { - refreshAccessToken(); + token = await refreshAccessToken(); } return token; }, []); - + const getRefreshToken = () => localStorage.getItem("refresh_token"); - - const refreshAccessToken = async (): Promise => { + + const refreshAccessToken = useCallback(async (): Promise => { const refreshToken = getRefreshToken(); if (!refreshToken) return null; @@ -284,11 +346,12 @@ const Dashboard: React.FC = () => { } return null; - }; - const fetchWithAuth = async (url: string, options: RequestInit = {}) => { + }, []); + + const fetchWithAuth = useCallback(async (url: string, options: RequestInit = {}) => { let token = await getAuthToken(); if (!token) throw new Error("Authentication token not found. Please log in again."); - + const response = await fetch(url, { ...options, headers: { @@ -297,11 +360,11 @@ const Dashboard: React.FC = () => { "Content-Type": "application/json", }, }); - + if (response.status === 401) { token = await refreshAccessToken(); if (!token) throw new Error("Authentication token not found. Please log in again."); - + return fetch(url, { ...options, headers: { @@ -311,10 +374,10 @@ const Dashboard: React.FC = () => { }, }); } - + return response; - }; - + }, [getAuthToken, refreshAccessToken]); + // Fetch shipments data with improved error handling const fetchShipments = useCallback(async () => { try { @@ -339,7 +402,7 @@ const Dashboard: React.FC = () => { } finally { setShipmentsLoading(false); } - }, [getAuthToken]); + }, [getAuthToken, fetchWithAuth]); // Handle allocate orders with improved error handling and loading state const handleAllocateOrders = async () => { @@ -356,21 +419,19 @@ const Dashboard: React.FC = () => { if (!response.ok) { const errorData = await response.json().catch(() => ({})); if (errorData.error) { - setAllocateError(errorData.error); // Set the specific error message + setAllocateError(errorData.error); } else { throw new Error( errorData.detail || `Server responded with status ${response.status}` ); } } else { - // After successful allocation, refresh the data await Promise.all([fetchShipments(), fetchCounts()]); - alert("Orders allocated successfully!"); } } catch (err) { console.error("Error allocating orders:", err); if (!allocateError) { - alert(`Failed to allocate orders: ${(err as Error).message}`); + setAllocateError(`Failed to allocate orders: ${(err as Error).message}`); } } finally { setAllocateLoading(false); @@ -381,7 +442,7 @@ const Dashboard: React.FC = () => { const fetchCounts = useCallback(async () => { try { setLoading(true); - const token = getAuthToken(); + const token = await getAuthToken(); const response = await fetchWithAuth(`${API_URL}/count`); @@ -394,7 +455,7 @@ const Dashboard: React.FC = () => { const countsData: CountsResponse = await response.json(); setOverviewData((prevData) => ({ - totalOrders: countsData.orders_placed, // Keep existing value since it's not in the API + totalOrders: countsData.orders_placed, numStores: countsData.retailers_available, deliveryAgents: countsData.employees_available, pendingOrders: countsData.pending_orders, @@ -411,26 +472,231 @@ const Dashboard: React.FC = () => { // Set up polling with cleanup useEffect(() => { - // Initial fetch fetchCounts(); fetchShipments(); - // Set up polling const countsIntervalId = setInterval(fetchCounts, 5000); const shipmentsIntervalId = setInterval(fetchShipments, 30000); - // Clean up intervals on unmount return () => { clearInterval(countsIntervalId); clearInterval(shipmentsIntervalId); }; }, [fetchCounts, fetchShipments]); + // MQTT connection setup with anomaly detection + // MQTT connection setup with anomaly detection and reconnection logic + useEffect(() => { + console.log("Attempting to connect to MQTT broker..."); + + // Updated connection to use mqtt.eclipseprojects.io with WebSocket + const client = mqtt.connect("ws://mqtt.eclipseprojects.io:80/mqtt", { + keepalive: 60, + clientId: `mqttjs_${Math.random().toString(16).substr(2, 8)}`, + connectTimeout: 30000, // 30 seconds timeout + reconnectPeriod: 5000, // Reconnect every 5 seconds + clean: true, + }); + + let reconnectTimer; + + client.on("connect", () => { + console.log("MQTT connected successfully"); + setMqttConnected(true); + clearTimeout(reconnectTimer); + + console.log("Subscribing to topic:", ANOMALY_TOPIC); + client.subscribe(ANOMALY_TOPIC, { qos: 1 }, (err) => { + if (err) { + console.error("Failed to subscribe to anomaly topic:", err); + } else { + console.log("Successfully subscribed to anomaly topic:", ANOMALY_TOPIC); + } + }); + }); + + client.on("message", (topic, message) => { + if (topic === ANOMALY_TOPIC) { + const payload = message.toString().trim().toLowerCase(); + console.log(`Received message on topic ${topic}: ${payload}`); + if (payload === "anomaly detected: no qr code detected for over 10 seconds!") { + console.log("Anomaly detected! Adding notification."); + addAnomalyNotification(); + } else { + console.log("Message does not match 'anomaly detected: no qr code detected for over 10 seconds!'. Ignoring."); + } + } + }); + + client.on("error", (err) => { + // Safely handle the error object + const errorMessage = err instanceof Error ? err.message : String(err); + console.error("MQTT connection error:", errorMessage); + setMqttConnected(false); + + clearTimeout(reconnectTimer); + reconnectTimer = setTimeout(() => { + console.log("Attempting to reconnect to MQTT..."); + client.reconnect(); + }, 5000); + }); + + client.on("close", () => { + console.log("MQTT connection closed"); + setMqttConnected(false); + + clearTimeout(reconnectTimer); + reconnectTimer = setTimeout(() => { + console.log("Attempting to reconnect to MQTT after connection closed..."); + client.reconnect(); + }, 5000); + }); + + client.on("offline", () => { + console.log("MQTT client is offline"); + setMqttConnected(false); + }); + + client.on("reconnect", () => { + console.log("Attempting to reconnect to MQTT broker..."); + }); + + return () => { + clearTimeout(reconnectTimer); + console.log("Cleaning up MQTT client"); + client.end(true); // Force close the connection + }; + }, [addAnomalyNotification]); + return (
-

- Dashboard -

+ + +
+

Dashboard

+
+ {/* MQTT Connection Indicator */} + {mqttConnected ? ( +
+ +
+ ) : ( +
+ +
+ )} + + {/* Notification Bell with Counter */} +
+ +
+
+
+ + {/* Notification Panel */} + {showNotificationPanel && ( +
+
+

Anomaly Alerts

+
+ {unreadCount > 0 && ( + + )} + +
+
+
+ {anomalyNotifications.length > 0 ? ( +
+ {anomalyNotifications.map((notification) => ( +
+
+

{notification.message}

+ +
+

+ {notification.timestamp.toLocaleTimeString()} +

+
+ ))} +
+ ) : ( +
+ No anomalies detected +
+ )} +
+
+ )} + + {/* Toast Notifications - Shows the latest anomaly notification */} + {anomalyNotifications.length > 0 && !showNotificationPanel && ( +
+ {anomalyNotifications.slice(0, 1).map((notification) => ( +
+ +
+

{notification.message}

+

+ {notification.timestamp.toLocaleTimeString()} +

+
+ +
+ ))} +
+ )} @@ -448,7 +714,6 @@ const Dashboard: React.FC = () => { - {/* Authentication Error */} {error && error.includes("Authentication") && (
@@ -456,9 +721,6 @@ const Dashboard: React.FC = () => {
)} - - - {/* Other Errors */} {error && !error.includes("Authentication") && (
@@ -466,14 +728,11 @@ const Dashboard: React.FC = () => {
)} - {/* Display Data */}
- {/* Top Section - Four Cards */}

- Total - Orders + Total Orders

{overviewData.totalOrders} @@ -520,19 +779,13 @@ const Dashboard: React.FC = () => {

- {/* Bottom Section - Chart and Data Table*/}
- {/* Chart Section */}

- Monthly - Sales + Monthly Sales

- + {
- {/* Data Table Section - Updated for Shipment Data */}

Order Details -

) : (
-

- No shipment data available. -

+

No shipment data available.

- {/* Analytics Tab */}
-

- Daily Orders -

-

- {analytics.dailyOrders} -

+

Daily Orders

+

{analytics.dailyOrders}

-

- Avg. Order Value -

-

- ${analytics.avgOrderValue.toFixed(2)} -

+

Avg. Order Value

+

${analytics.avgOrderValue.toFixed(2)}

-

- Returning Customers -

-

- {analytics.returningCustomers} -

+

Returning Customers

+

{analytics.returningCustomers}

-

- Conversion Rate -

-

- {analytics.conversionRate}% -

+

Conversion Rate

+

{analytics.conversionRate}%

- {/* Reports Tab */}
-

- Monthly Revenue -

-

- ${reports.monthlyRevenue.toLocaleString()} -

+

Monthly Revenue

+

${reports.monthlyRevenue.toLocaleString()}

-

- Monthly Expenses -

-

- ${reports.monthlyExpenses.toLocaleString()} -

+

Monthly Expenses

+

${reports.monthlyExpenses.toLocaleString()}

-

- Profit -

-

- ${reports.profit.toLocaleString()} -

+

Profit

+

${reports.profit.toLocaleString()}

-

- Customer Satisfaction -

-

- {reports.customerSatisfaction}% -

+

Customer Satisfaction

+

{reports.customerSatisfaction}%

- {/* Notifications Tab */}
-

- Recent Notifications -

+

Recent Notifications

{notif.length > 0 ? (
    {notif.map((note) => ( @@ -702,9 +912,7 @@ const Dashboard: React.FC = () => { ))}
) : ( -

- No notifications available. -

+

No notifications available.

)}
@@ -713,4 +921,4 @@ const Dashboard: React.FC = () => { ); }; -export default Dashboard; +export default Dashboard; \ No newline at end of file diff --git a/frontend/components/manufacturer/nav_bar.tsx b/frontend/components/manufacturer/nav_bar.tsx index a3e0064..6b19ae7 100644 --- a/frontend/components/manufacturer/nav_bar.tsx +++ b/frontend/components/manufacturer/nav_bar.tsx @@ -42,7 +42,7 @@ export function Navbar() { export function Logo() { return ( - +

Manufacturer

); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a18acde..695356e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -33,8 +33,9 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@types/mqtt": "^2.5.0", "@types/node": "20.17.17", - "@types/react": "^19", + "@types/react": "^19.0.12", "@types/react-dom": "^19", "concurrently": "^9.1.2", "electron": "^34.2.0", @@ -3131,6 +3132,16 @@ "@types/node": "*" } }, + "node_modules/@types/mqtt": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@types/mqtt/-/mqtt-2.5.0.tgz", + "integrity": "sha512-n+0/ErBin30j+UbhcHGK/STjHjh65k85WNR6NlUjRG0g9yctpF12pS+SOkwz0wmp+7momAo9Cyi4Wmvy8UsCQg==", + "deprecated": "This is a stub types definition for MQTT (https://github.com/mqttjs/MQTT.js). MQTT provides its own type definitions, so you don't need @types/mqtt installed!", + "dev": true, + "dependencies": { + "mqtt": "*" + } + }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -3166,11 +3177,10 @@ } }, "node_modules/@types/react": { - "version": "19.0.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", - "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "version": "19.0.12", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz", + "integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==", "devOptional": true, - "license": "MIT", "dependencies": { "csstype": "^3.0.2" } @@ -3185,6 +3195,22 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/readable-stream": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.18.tgz", + "integrity": "sha512-21jK/1j+Wg+7jVw1xnSwy/2Q1VgVjWuFssbYGTREPUBeZ+rqVFl2udq0IkxzPC0ZhOzVceUbyIACFZKLqKEBlA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -3203,6 +3229,15 @@ "license": "MIT", "optional": true }, + "node_modules/@types/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -3444,6 +3479,18 @@ "dev": true, "license": "ISC" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -4980,6 +5027,12 @@ "node": ">= 6" } }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "dev": true + }, "node_modules/compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", @@ -5014,6 +5067,21 @@ "dev": true, "license": "MIT" }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "dev": true, + "engines": [ + "node >= 6.0" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, "node_modules/concurrently": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz", @@ -6765,6 +6833,15 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/eventemitter2": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", @@ -6776,6 +6853,15 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", @@ -6895,6 +6981,19 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, "node_modules/fastq": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", @@ -7609,6 +7708,12 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "dev": true + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -8447,6 +8552,16 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9197,6 +9312,147 @@ "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz", "integrity": "sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==" }, + "node_modules/mqtt": { + "version": "5.10.4", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.4.tgz", + "integrity": "sha512-wN+SuhT2/ZaG6NPxca0N6YtRivnMxk6VflxQUEeqDH4erKdj+wPAGhHmcTLzvqfE4sJRxrEJ+XJxUc0No0E7eQ==", + "dev": true, + "dependencies": { + "@types/readable-stream": "^4.0.18", + "@types/ws": "^8.5.14", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.0", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.1", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "reinterval": "^1.1.0", + "rfdc": "^1.4.1", + "split2": "^4.2.0", + "worker-timers": "^7.1.8", + "ws": "^8.18.0" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "dev": true, + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt-packet/node_modules/bl": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.0.tgz", + "integrity": "sha512-ClDyJGQkc8ZtzdAAbAwBmhMSpwN/sC9HA8jxdYm6nVUbCfZbe2mgza4qh7AuEYyEPB/c4Kznf9s66bnsKMQDjw==", + "dev": true, + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/mqtt-packet/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/mqtt-packet/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/mqtt/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/mqtt/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -9483,6 +9739,16 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "dev": true, + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -10160,13 +10426,21 @@ "node": ">= 0.8" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/progress": { "version": "2.0.3", @@ -10710,6 +10984,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/reinterval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", + "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==", + "dev": true + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -11416,6 +11696,15 @@ "source-map": "^0.6.0" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -12314,6 +12603,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, "node_modules/typescript": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", @@ -12652,6 +12947,40 @@ "node": ">=0.10.0" } }, + "node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -12729,6 +13058,27 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, + "node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0e4d124..cf9e39f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,8 +37,9 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@types/mqtt": "^2.5.0", "@types/node": "20.17.17", - "@types/react": "^19", + "@types/react": "^19.0.12", "@types/react-dom": "^19", "concurrently": "^9.1.2", "electron": "^34.2.0",