diff --git a/webapp/package-lock.json b/webapp/package-lock.json
index 842d3eb..8b044d5 100644
--- a/webapp/package-lock.json
+++ b/webapp/package-lock.json
@@ -15,7 +15,11 @@
"@tanstack/react-query": "^5.36.2",
"axios": "^1.7.4",
"base32-encode": "^2.0.0",
+ "chart.js": "^4.4.4",
+ "chartjs-adapter-date-fns": "^3.0.0",
+ "date-fns": "^3.6.0",
"react": "^18.2.0",
+ "react-chartjs-2": "^5.2.0",
"react-cookie": "^7.1.4",
"react-dom": "^18.2.0",
"react-hook-qrcode-svg": "^1.5.1",
@@ -1038,6 +1042,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@kurkle/color": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz",
+ "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==",
+ "license": "MIT"
+ },
"node_modules/@mantine/core": {
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.10.1.tgz",
@@ -1995,6 +2005,28 @@
"node": ">=4"
}
},
+ "node_modules/chart.js": {
+ "version": "4.4.4",
+ "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz",
+ "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==",
+ "license": "MIT",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
+ "node_modules/chartjs-adapter-date-fns": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz",
+ "integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "chart.js": ">=2.8.0",
+ "date-fns": ">=2.0.0"
+ }
+ },
"node_modules/check-error": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
@@ -2089,6 +2121,16 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
+ "node_modules/date-fns": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz",
+ "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
"node_modules/debug": {
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz",
@@ -3621,6 +3663,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-chartjs-2": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz",
+ "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "chart.js": "^4.1.1",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/react-cookie": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.1.4.tgz",
diff --git a/webapp/package.json b/webapp/package.json
index 44a2ab6..57f74a3 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -18,7 +18,11 @@
"@tanstack/react-query": "^5.36.2",
"axios": "^1.7.4",
"base32-encode": "^2.0.0",
+ "chart.js": "^4.4.4",
+ "chartjs-adapter-date-fns": "^3.0.0",
+ "date-fns": "^3.6.0",
"react": "^18.2.0",
+ "react-chartjs-2": "^5.2.0",
"react-cookie": "^7.1.4",
"react-dom": "^18.2.0",
"react-hook-qrcode-svg": "^1.5.1",
diff --git a/webapp/src/Routes/Home/Home.tsx b/webapp/src/Routes/Home/Home.tsx
index 27da565..df487a3 100644
--- a/webapp/src/Routes/Home/Home.tsx
+++ b/webapp/src/Routes/Home/Home.tsx
@@ -7,6 +7,7 @@ import { AppSettings } from '../../Constants/Constants';
import { useQuery } from '@tanstack/react-query';
import { UpgradeAlert } from './UpgradeAlert';
import { IconPaperBag } from '@tabler/icons-react';
+import { UserStats } from './UserStats';
export function Home() {
const {authInfo} = useAuthContext()
@@ -60,6 +61,7 @@ export function Home() {
}
+
);
}
\ No newline at end of file
diff --git a/webapp/src/Routes/Home/UserStats.tsx b/webapp/src/Routes/Home/UserStats.tsx
new file mode 100644
index 0000000..46953ce
--- /dev/null
+++ b/webapp/src/Routes/Home/UserStats.tsx
@@ -0,0 +1,54 @@
+import { Card } from "@mantine/core";
+import { useQuery } from "@tanstack/react-query";
+import { useAuthContext } from "../../Auth/Auth";
+import { AppSettings } from '../../Constants/Constants';
+import { Chart } from 'react-chartjs-2';
+import 'chartjs-adapter-date-fns';
+import { Chart as ChartJS, LineController, LineElement, PointElement, LinearScale, Title, CategoryScale, TimeScale } from 'chart.js';
+
+export function UserStats() {
+ ChartJS.register(LineController, LineElement, PointElement, LinearScale, Title, CategoryScale, TimeScale);
+
+ const {authInfo} = useAuthContext()
+ const { isPending, error, data } = useQuery({
+ queryKey: ['userstats'],
+ queryFn: () =>
+ fetch(AppSettings.url + '/stats/user', {
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer " + authInfo.token
+ },
+ }).then((res) => {
+ return res.json()
+ }
+
+ ),
+ enabled: authInfo.role === "admin",
+ })
+ const options = {
+ responsive: true,
+ plugins: {
+ legend: {
+ position: 'top' as const,
+ },
+ title: {
+ display: true,
+ text: 'VPN Received (in bytes)',
+ },
+ },
+ scales: {
+ x: {
+ type: 'time',
+ }
+ }
+ }
+
+ if (isPending) return ''
+ if (error) return 'cannot retrieve licensed users'
+
+ return (
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/webapp/yarn.lock b/webapp/yarn.lock
index 386f00a..203064f 100644
--- a/webapp/yarn.lock
+++ b/webapp/yarn.lock
@@ -343,6 +343,11 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
+"@kurkle/color@^0.3.0":
+ version "0.3.2"
+ resolved "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz"
+ integrity sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==
+
"@mantine/core@^7.9.2":
version "7.10.1"
resolved "https://registry.npmjs.org/@mantine/core/-/core-7.10.1.tgz"
@@ -802,6 +807,18 @@ chalk@^4.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
+chart.js@^4.1.1, chart.js@^4.4.4, chart.js@>=2.8.0:
+ version "4.4.4"
+ resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz"
+ integrity sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==
+ dependencies:
+ "@kurkle/color" "^0.3.0"
+
+chartjs-adapter-date-fns@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz"
+ integrity sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==
+
check-error@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz"
@@ -877,6 +894,11 @@ csstype@^3.0.2:
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+date-fns@^3.6.0, date-fns@>=2.0.0:
+ version "3.6.0"
+ resolved "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz"
+ integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==
+
debug@^4.1.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5:
version "4.3.5"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz"
@@ -1716,6 +1738,11 @@ queue-microtask@^1.2.2:
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+react-chartjs-2@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz"
+ integrity sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==
+
react-cookie@^7.1.4:
version "7.1.4"
resolved "https://registry.npmjs.org/react-cookie/-/react-cookie-7.1.4.tgz"