From 1d5aa24ebdeb18db3c711fea3c99440a58a63cc8 Mon Sep 17 00:00:00 2001 From: PxlLoewe <72106766+PxlLoewe@users.noreply.github.com> Date: Sat, 3 May 2025 11:00:15 -0700 Subject: [PATCH] saving connection log in DB, added Dispo stats --- .../socket-events/connect-dispatch.ts | 45 +++-- apps/dispatch/app/_store/connectionStore.ts | 27 ++- .../app/dispatch/_components/left/Chat.tsx | 6 +- .../map/_components/MarkerCluster.tsx | 2 - .../_components/navbar/_components/Audio.tsx | 5 +- .../navbar/_components/Connection.tsx | 22 ++- .../_components/pannel/MissionForm.tsx | 5 +- .../app/(app)/_components/PilotDispoStats.tsx | 187 ++++++++++++++++++ .../(app)/_components/StatsClientWrapper.tsx | 25 --- .../{Header.tsx => StatsToggle.tsx} | 35 +++- apps/hub/app/(app)/_components/stats.tsx | 135 ++----------- apps/hub/app/(app)/page.tsx | 14 +- docker-compose.dev.yml | 12 +- grafana/grafana.db | Bin 1122304 -> 1122304 bytes 14 files changed, 313 insertions(+), 207 deletions(-) create mode 100644 apps/hub/app/(app)/_components/PilotDispoStats.tsx delete mode 100644 apps/hub/app/(app)/_components/StatsClientWrapper.tsx rename apps/hub/app/(app)/_components/{Header.tsx => StatsToggle.tsx} (54%) diff --git a/apps/dispatch-server/socket-events/connect-dispatch.ts b/apps/dispatch-server/socket-events/connect-dispatch.ts index fb40f9e6..f5f97878 100644 --- a/apps/dispatch-server/socket-events/connect-dispatch.ts +++ b/apps/dispatch-server/socket-events/connect-dispatch.ts @@ -1,4 +1,4 @@ -import { getPublicUser, prisma } from "@repo/db"; +import { getPublicUser, prisma, User } from "@repo/db"; import { pubClient } from "modules/redis"; import { Server, Socket } from "socket.io"; @@ -12,15 +12,40 @@ export const handleConnectDispatch = selectedZone: string; }) => { try { - const userId = socket.data.user.id; // User ID aus dem JWT-Token + const user: User = socket.data.user; // User ID aus dem JWT-Token console.log("User connected to dispatch server"); - const user = await prisma.user.findUnique({ + + if (!user) return Error("User not found"); + + if (!user.permissions?.includes("DISPO")) { + socket.emit( + "error", + "You do not have permission to connect to the dispatch server.", + ); + return; + } + + const existingConnection = await prisma.connectedDispatcher.findFirst({ where: { - id: userId, + userId: user.id, + logoutTime: null, }, }); - if (!user) return Error("User not found"); + if (existingConnection) { + await io + .to(`user:${user.id}`) + .emit("force-disconnect", "double-connection"); + await prisma.connectedDispatcher.updateMany({ + where: { + userId: user.id, + logoutTime: null, + }, + data: { + logoutTime: new Date().toISOString(), + }, + }); + } let parsedLogoffDate = null; if (logoffTime.length > 0) { @@ -48,20 +73,14 @@ export const handleConnectDispatch = publicUser: getPublicUser(user) as any, esimatedLogoutTime: parsedLogoffDate?.toISOString() || null, lastHeartbeat: new Date().toISOString(), - userId: userId, + userId: user.id, loginTime: new Date().toISOString(), }, }); socket.join("dispatchers"); // Dem Dispatcher-Raum beitreten - socket.join(`user:${userId}`); // Dem User-Raum beitreten + socket.join(`user:${user.id}`); // Dem User-Raum beitreten - /* const keys = await pubClient.keys("Dispatcher:*"); - await Promise.all( - keys.map(async (key) => { - return await pubClient.json.get(key); - }), - ); */ io.to("dispatchers").emit("dispatcher-update"); io.to("pilots").emit("dispatcher-update"); diff --git a/apps/dispatch/app/_store/connectionStore.ts b/apps/dispatch/app/_store/connectionStore.ts index 559e2d32..879ad0df 100644 --- a/apps/dispatch/app/_store/connectionStore.ts +++ b/apps/dispatch/app/_store/connectionStore.ts @@ -2,7 +2,8 @@ import { create } from "zustand"; import { socket } from "../dispatch/socket"; interface ConnectionStore { - isConnected: boolean; + status: "connected" | "disconnected" | "connecting" | "error"; + message: string; selectedZone: string; connect: ( uid: string, @@ -13,10 +14,12 @@ interface ConnectionStore { } export const useDispatchConnectionStore = create((set) => ({ - isConnected: false, + status: "disconnected", + message: "", selectedZone: "LST_01", connect: async (uid, selectedZone, logoffTime) => new Promise((resolve) => { + set({ status: "connecting", message: "" }); socket.auth = { uid }; set({ selectedZone }); socket.connect(); @@ -34,8 +37,24 @@ export const useDispatchConnectionStore = create((set) => ({ })); socket.on("connect", () => { - useDispatchConnectionStore.setState({ isConnected: true }); + useDispatchConnectionStore.setState({ status: "connected", message: "" }); }); + +socket.on("connect_error", (err) => { + useDispatchConnectionStore.setState({ + status: "error", + message: err.message, + }); +}); + socket.on("disconnect", () => { - useDispatchConnectionStore.setState({ isConnected: false }); + useDispatchConnectionStore.setState({ status: "disconnected", message: "" }); +}); + +socket.on("force-disconnect", (reason: string) => { + console.log("force-disconnect", reason); + useDispatchConnectionStore.setState({ + status: "disconnected", + message: reason, + }); }); diff --git a/apps/dispatch/app/dispatch/_components/left/Chat.tsx b/apps/dispatch/app/dispatch/_components/left/Chat.tsx index 0b01c367..e1fa097d 100644 --- a/apps/dispatch/app/dispatch/_components/left/Chat.tsx +++ b/apps/dispatch/app/dispatch/_components/left/Chat.tsx @@ -39,10 +39,10 @@ export const Chat = () => { const data = await getConenctedUsers(); if (data) { const filteredConnectedUser = data.filter((user) => { - /* return ( + return ( user.userId !== session.data?.user.id && !Object.keys(chats).includes(user.userId) - ); */ + ); return true; }); @@ -65,8 +65,6 @@ export const Chat = () => { }; }, [addTabValue, chats, session.data?.user.id]); - console.log("connectedUser", connectedUser); - return (
diff --git a/apps/dispatch/app/dispatch/_components/map/_components/MarkerCluster.tsx b/apps/dispatch/app/dispatch/_components/map/_components/MarkerCluster.tsx index b49385fd..aedba804 100644 --- a/apps/dispatch/app/dispatch/_components/map/_components/MarkerCluster.tsx +++ b/apps/dispatch/app/dispatch/_components/map/_components/MarkerCluster.tsx @@ -231,8 +231,6 @@ export const MarkerCluster = () => { const avgLng = allPos.reduce((sum, pos) => sum + pos[1]!, 0) / allPos.length; - console.log(allPos, { avgLat, avgLng }); - return { ...c, lat: avgLat, diff --git a/apps/dispatch/app/dispatch/_components/navbar/_components/Audio.tsx b/apps/dispatch/app/dispatch/_components/navbar/_components/Audio.tsx index 5c4c60d0..6aedc994 100644 --- a/apps/dispatch/app/dispatch/_components/navbar/_components/Audio.tsx +++ b/apps/dispatch/app/dispatch/_components/navbar/_components/Audio.tsx @@ -36,7 +36,7 @@ export const Audio = () => { useEffect(() => { const joinRoom = async () => { - if (!connection.isConnected) return; + if (connection.status != "connected") return; if (state === "connected") return; connect(selectedRoom); }; @@ -46,7 +46,8 @@ export const Audio = () => { return () => { disconnect(); }; - }, [connection.isConnected]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [connection.status]); return ( <> diff --git a/apps/dispatch/app/dispatch/_components/navbar/_components/Connection.tsx b/apps/dispatch/app/dispatch/_components/navbar/_components/Connection.tsx index be7fe6b2..762b30ae 100644 --- a/apps/dispatch/app/dispatch/_components/navbar/_components/Connection.tsx +++ b/apps/dispatch/app/dispatch/_components/navbar/_components/Connection.tsx @@ -14,15 +14,21 @@ export const ConnectionBtn = () => { const uid = session.data?.user?.id; if (!uid) return null; return ( - <> - {!connection.isConnected ? ( +
+ {connection.message.length > 0 && ( + {connection.message} + )} + + {connection.status === "disconnected" && ( - ) : ( + )} + + {connection.status == "connected" && ( - {connection.isConnected ? ( + {connection.status == "connected" ? (
- +
); }; diff --git a/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx b/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx index 6cc0b9ca..4f5eb046 100644 --- a/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx +++ b/apps/dispatch/app/dispatch/_components/pannel/MissionForm.tsx @@ -97,6 +97,7 @@ export const MissionForm = () => { }); }, []); + console.log(form.formState.errors); return ( {/* Koorinaten Section */} @@ -159,14 +160,14 @@ export const MissionForm = () => {

Rettungsmittel

setChecked(e.target.checked)} /> { - return isChecked ? : ; -}; - -export const PilotStats = (): JSX.Element => { +export const Stats = ({ stats }: { stats: "pilot" | "dispo" }) => { return ( -
-
-
- - - -
-
Einsätze geflogen
-
127
-
Du bist damit unter den top 5%!
+ <> + +
+ {stats === "dispo" && } + {stats === "pilot" && }
- -
-
- - - -
-
Pilot Login Zeit
-
35h 12m
-
Mehr als 58% aller anderen User!
-
- -
-
- -
-
Christoph 31
-
- War bisher dein Rettungsmittel der Wahl -
-
- 87 Stationen warten noch auf dich! -
-
-
- ); -}; - -export const DispoStats = (): JSX.Element => { - return ( -
-
-
- - - -
-
Einsätze disponiert
-
578
-
Du bist damit unter den top 9%!
-
- -
-
- - - -
-
Disponent Login Zeit
-
53h 12m
-
Mehr als 69% aller anderen User!
-
- -
-
- -
-
Christoph Berlin
-
Wurde von dir am meisten Disponiert
-
- 43 Stationen warten auf deine Einsätze! -
-
-
+ ); }; diff --git a/apps/hub/app/(app)/page.tsx b/apps/hub/app/(app)/page.tsx index 1557459a..c13328de 100644 --- a/apps/hub/app/(app)/page.tsx +++ b/apps/hub/app/(app)/page.tsx @@ -1,8 +1,8 @@ import Logbook from "./_components/Logbook"; -import { ArrowRight, NotebookText, Award, RocketIcon } from "lucide-react"; +import { ArrowRight, NotebookText } from "lucide-react"; import Link from "next/link"; import Events from "./_components/Events"; -import StatsClientWrapper from "./_components/StatsClientWrapper"; +import { Stats } from "./_components/Stats"; import { Badges } from "./_components/Badges"; /* @@ -15,10 +15,16 @@ Badges Aktive Events / Mandatory Events */ -export default function Home() { +export default async function Home({ + searchParams, +}: { + searchParams: { stats?: "pilot" | "dispo" }; +}) { + const { stats } = await searchParams; + const view = stats || "pilot"; return (
- +
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 40362051..c14f4172 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -19,16 +19,6 @@ services: - postgres volumes: - ./grafana:/var/lib/grafana - pgadmin: - image: dpage/pgadmin4:latest - container_name: pgadmin - environment: - PGADMIN_DEFAULT_EMAIL: dev@var.de - PGADMIN_DEFAULT_PASSWORD: dev - ports: - - "8080:80" - depends_on: - - postgres redis: container_name: redis image: redis/redis-stack:latest @@ -93,4 +83,4 @@ volumes: moodle_database: moodle_moodledata: redis_data: - driver: local + driver: local diff --git a/grafana/grafana.db b/grafana/grafana.db index a52d99cfdea9983d57ee39ec0225973e5d9e7c85..4a6dee7579ff0728e6d29e8e327bfe8437fda476 100644 GIT binary patch delta 235 zcmZoT;L>owWr8&0%!xA2j59YTeA8u*Smh-d9%P`wAPJgJ+A<=%wo&$(Ew;!_SvJPMYYUJI@$jB_Pon-;n zk)?d5x(3D}2F6w8$ L+u7U%e%%BBwE;pC delta 235 zcmZoT;L>owWr8$g%S0Jx#+Ho<-*nmJ0eBb2bA5gQ6t|%k+MG4l$Mtq4aIjH|ujKu_|PU__a;H zrOzSHDhN`x{hvO^a(-4h8D?9?=@0ceB-#(za{w{t_Cxku)&VR)jl5eK8JPvPvn=2` zvXsw6*VrJ$(89{l(8|ClYWhJRZb@;Fm;y{p&)Ce|($vh<+^n6=jT?x0fS4DE`GA;z KJDZ!pubTk>ghI3c