diff --git a/.gitignore b/.gitignore
index d89992cb..395a7aa8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,4 @@
npm-debug.log*
.vscode/settings.json
+.env
diff --git a/package.json b/package.json
index 4db74509..e045f0f7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ucentral-client",
- "version": "4.1.0",
+ "version": "4.2.2",
"description": "",
"private": true,
"main": "index.tsx",
diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json
index 85680fd1..578b6cdb 100644
--- a/public/locales/de/translation.json
+++ b/public/locales/de/translation.json
@@ -533,6 +533,8 @@
"show_dev_releases": "Entwicklerversionen",
"status_explanation": "Verbindungsstatus von Geräten, die sich mit diesem Firmware-Server verbunden haben",
"unrecognized": "Unbekannte Firmware",
+ "recognized_firmware": "Bekannte Firmware",
+ "recognized_firmware_explanation": "Firmware-Revisionen, die derzeit von Geräten verwendet werden und von diesem Firmware-Server erkannt werden",
"unrecognized_firmware": "Unbekannte Firmware",
"unrecognized_firmware_explanation": "Firmware, die derzeit von Geräten verwendet wird und von diesem Firmware-Server nicht erkannt wird",
"up_to_date": "Aktuelle Geräte",
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index 01d8cb9a..98a4749a 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -540,6 +540,8 @@
"show_dev_releases": "Dev Releases",
"status_explanation": "Connection status of devices that have connected to this firmware server",
"unrecognized": "Unrecognized Firmware",
+ "recognized_firmware": "Recognized Firmware",
+ "recognized_firmware_explanation": "Firmware revisions currently in use by devices that are recognized by this firmware server",
"unrecognized_firmware": "Unrecognized Firmware",
"unrecognized_firmware_explanation": "Firmware that is currently used by devices and is not recognized by this firmware server",
"up_to_date": "Up To Date Devices",
diff --git a/public/locales/es/translation.json b/public/locales/es/translation.json
index fd724033..6c740d88 100644
--- a/public/locales/es/translation.json
+++ b/public/locales/es/translation.json
@@ -533,6 +533,8 @@
"show_dev_releases": "Lanzamientos de desarrollo",
"status_explanation": "Estado de conexión de los dispositivos que se han conectado a este servidor de firmware",
"unrecognized": "Firmware no reconocido",
+ "recognized_firmware": "Firmware reconocido",
+ "recognized_firmware_explanation": "Revisiones de firmware actualmente en uso por dispositivos que son reconocidas por este servidor de firmware",
"unrecognized_firmware": "Firmware no reconocido",
"unrecognized_firmware_explanation": "Firmware que utilizan actualmente los dispositivos y no es reconocido por este servidor de firmware",
"up_to_date": "Dispositivos actualizados",
diff --git a/public/locales/fr/translation.json b/public/locales/fr/translation.json
index daca5604..07ca3167 100644
--- a/public/locales/fr/translation.json
+++ b/public/locales/fr/translation.json
@@ -533,6 +533,8 @@
"show_dev_releases": "Versions de développement",
"status_explanation": "État de connexion des appareils qui se sont connectés à ce serveur de micrologiciel",
"unrecognized": "Micrologiciel non reconnu",
+ "recognized_firmware": "Micrologiciel reconnu",
+ "recognized_firmware_explanation": "Révisions de micrologiciel actuellement utilisées par les appareils et reconnues par ce serveur de firmware",
"unrecognized_firmware": "Micrologiciel non reconnu",
"unrecognized_firmware_explanation": "Firmware actuellement utilisé par les appareils et non reconnu par ce serveur de firmware",
"up_to_date": "Appareils à jour",
diff --git a/public/locales/pt/translation.json b/public/locales/pt/translation.json
index 33fb4692..7556f543 100644
--- a/public/locales/pt/translation.json
+++ b/public/locales/pt/translation.json
@@ -533,6 +533,8 @@
"show_dev_releases": "Lançamentos do desenvolvedor",
"status_explanation": "Status da conexão dos dispositivos que se conectaram a este servidor de firmware",
"unrecognized": "Firmware não reconhecido",
+ "recognized_firmware": "Firmware reconhecido",
+ "recognized_firmware_explanation": "Revisões de firmware atualmente em uso por dispositivos que são reconhecidas por este servidor de firmware",
"unrecognized_firmware": "Firmware não reconhecido",
"unrecognized_firmware_explanation": "Firmware que é usado atualmente por dispositivos e não é reconhecido por este servidor de firmware",
"up_to_date": "Dispositivos atualizados",
diff --git a/src/pages/Firmware/Dashboard/RecognizedFirmwareBarChart.tsx b/src/pages/Firmware/Dashboard/RecognizedFirmwareBarChart.tsx
new file mode 100644
index 00000000..2e7b83b4
--- /dev/null
+++ b/src/pages/Firmware/Dashboard/RecognizedFirmwareBarChart.tsx
@@ -0,0 +1,148 @@
+import * as React from 'react';
+import { useColorMode } from '@chakra-ui/react';
+import {
+ Chart as ChartJS,
+ CategoryScale,
+ LinearScale,
+ Title,
+ Tooltip,
+ Legend,
+ ChartData,
+ BarElement,
+ CoreChartOptions,
+ ElementChartOptions,
+ PluginChartOptions,
+ DatasetChartOptions,
+ ScaleChartOptions,
+ BarControllerChartOptions,
+} from 'chart.js';
+import { _DeepPartialObject } from 'chart.js/types/utils';
+import { Bar } from 'react-chartjs-2';
+import { useTranslation } from 'react-i18next';
+import GraphStatDisplay from 'components/Containers/GraphStatDisplay';
+import { COLORS } from 'constants/colors';
+import {
+ FirmwareDashboardRevision,
+} from 'hooks/Network/Firmware';
+
+ChartJS.register(
+ CategoryScale,
+ LinearScale,
+ BarElement,
+ Title,
+ Tooltip,
+ Legend,
+);
+
+const OPTIONS: (
+ colorMode: string,
+) => _DeepPartialObject<
+ CoreChartOptions<'bar'> &
+ ElementChartOptions<'bar'> &
+ PluginChartOptions<'bar'> &
+ DatasetChartOptions<'bar'> &
+ ScaleChartOptions<'bar'> &
+ BarControllerChartOptions
+> = (colorMode) => ({
+ responsive: true,
+ indexAxis: 'y',
+ interaction: {
+ mode: 'nearest',
+ axis: 'y',
+ intersect: false,
+ },
+ scales: {
+ xAxes: {
+ ticks: {
+ color: colorMode === 'dark' ? 'white' : undefined,
+ },
+ },
+ yAxes: {
+ ticks: {
+ color: colorMode === 'dark' ? 'white' : undefined,
+ callback(value) {
+ return (
+ this.getLabelForValue(value as number)
+ ?.split(' ')?.[0] ?? ''
+ );
+ },
+ },
+ },
+ },
+ plugins: {
+ legend: { display: false },
+ title: {
+ display: false,
+ },
+ tooltip: {
+ callbacks: {
+ label: (context) =>
+ `${context.label}: ${context.formattedValue} (${
+ Math.round(
+ // @ts-ignore
+ (context.raw /
+ context.dataset.data.reduce(
+ (acc, curr) => acc + curr,
+ 0,
+ )) *
+ 100 *
+ 100,
+ ) / 100
+ }%)`,
+ },
+ },
+ },
+});
+
+type Props = {
+ data: FirmwareDashboardRevision[];
+};
+const RecognizedFirmwareBarChart = ({ data }: Props) => {
+ const { t } = useTranslation();
+ const { colorMode } = useColorMode();
+
+ const parsedData: ChartData<'bar', number[], unknown> =
+ React.useMemo(() => {
+ const values: number[] = [];
+ const labels: string[] = [];
+
+ for (const { tag, value } of data.sort(
+ (a, b) => b.value - a.value,
+ )) {
+ values.push(value);
+ const split = tag.split(' / ');
+ if (split[1]) labels.push(split['1']);
+ else labels.push(tag === '' ? t('common.unknown') : tag);
+ }
+
+ return {
+ labels,
+ datasets: [
+ {
+ data: values,
+ backgroundColor: COLORS,
+ borderColor: COLORS,
+ borderWidth: 1,
+ },
+ ],
+ };
+ }, [data, colorMode]);
+
+ return (
+
+ }
+ />
+ );
+};
+
+export default RecognizedFirmwareBarChart;
diff --git a/src/pages/Firmware/Dashboard/index.tsx b/src/pages/Firmware/Dashboard/index.tsx
index eac23284..1cc70372 100644
--- a/src/pages/Firmware/Dashboard/index.tsx
+++ b/src/pages/Firmware/Dashboard/index.tsx
@@ -22,6 +22,7 @@ import DeviceTypesPieChart from './DeviceTypesPieChart';
import FirmwareDashboardEndpointDisplay from './EndpointsDisplay';
import FirmwareLatestPieChart from './LatestPieChart';
import OuisBarChart from './OuisBarChart';
+import RecognizedFirmwareBarChart from './RecognizedFirmwareBarChart';
import UnknownFirmwareBarChart from './UnknownFirmwareBarChart';
import UpToDateDevicesSimple from './UpToDateDevices';
import { RefreshButton } from 'components/Buttons/RefreshButton';
@@ -93,11 +94,12 @@ const FirmwareDashboard = () => {
+
-
+
)}
>