finished conenct modal

This commit is contained in:
PxlLoewe
2025-03-15 20:40:40 -07:00
parent 2ecc91adb9
commit cf61740698
8 changed files with 147 additions and 95 deletions

View File

@@ -5,6 +5,7 @@ import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter"; import { createAdapter } from "@socket.io/redis-adapter";
import { jwtMiddleware } from "modules/socketJWTmiddleware"; import { jwtMiddleware } from "modules/socketJWTmiddleware";
import { pubClient, subClient } from "modules/redis"; import { pubClient, subClient } from "modules/redis";
import { handle } from "socket-events/connect-dispatch";
const app = express(); const app = express();
const server = createServer(app); const server = createServer(app);
@@ -17,7 +18,7 @@ const io = new Server(server, {
io.use(jwtMiddleware); io.use(jwtMiddleware);
io.on("connection", (socket) => { io.on("connection", (socket) => {
socket.on("connect-dispatch", () => {}); socket.on("connect-dispatch", handle(socket, io));
}); });
server.listen(process.env.PORT, () => { server.listen(process.env.PORT, () => {
console.log(`Server running on port ${process.env.PORT}`); console.log(`Server running on port ${process.env.PORT}`);

View File

@@ -13,12 +13,12 @@
"@types/express": "^5.0.0", "@types/express": "^5.0.0",
"@types/node": "^22.13.5", "@types/node": "^22.13.5",
"@types/nodemailer": "^6.4.17", "@types/nodemailer": "^6.4.17",
"@types/socket.io-redis": "^3.0.0",
"concurrently": "^9.1.2", "concurrently": "^9.1.2",
"typescript": "latest" "typescript": "latest"
}, },
"dependencies": { "dependencies": {
"@react-email/components": "^0.0.33", "@react-email/components": "^0.0.33",
"@redis/json": "^1.0.7",
"@socket.io/redis-adapter": "^8.3.0", "@socket.io/redis-adapter": "^8.3.0",
"axios": "^1.7.9", "axios": "^1.7.9",
"cron": "^4.1.0", "cron": "^4.1.0",
@@ -28,7 +28,6 @@
"nodemailer": "^6.10.0", "nodemailer": "^6.10.0",
"react": "^19.0.0", "react": "^19.0.0",
"redis": "^4.7.0", "redis": "^4.7.0",
"socket.io": "^4.8.1", "socket.io": "^4.8.1"
"socket.io-redis": "^6.1.1"
} }
} }

View File

@@ -1,14 +1,34 @@
import { pubClient } from "modules/redis"; import { pubClient } from "modules/redis";
import { Server, Socket } from "socket.io"; import { Server, Socket } from "socket.io";
export const handle = (socket: Socket, io: Server) => async (jwt: string) => { export const handle =
const userId = socket.data.user.id; // User ID aus dem JWT-Token (socket: Socket, io: Server) =>
await pubClient.set(`dispatchers:${socket.id}`, userId); async ({
logoffTime,
selectedZone,
}: {
logoffTime: string;
selectedZone: string;
}) => {
const userId = socket.data.user.id; // User ID aus dem JWT-Token
await pubClient.json.set(`dispatchers:${socket.id}`, "$", {
logoffTime,
selectedZone,
userId,
});
socket.join("dispatchers"); // Dem Dispatcher-Raum beitreten socket.join("dispatchers"); // Dem Dispatcher-Raum beitreten
socket.on("disconnect", async () => { const keys = await pubClient.keys("dispatchers:*");
console.log("Disconnected from dispatch server"); const dispatchers = await Promise.all(
await pubClient.del(`dispatchers:${socket.id}`); keys.map(async (key) => {
}); return await pubClient.json.get(key);
}; }),
);
console.log(dispatchers);
socket.on("disconnect", async () => {
console.log("Disconnected from dispatch server");
await pubClient.json.del(`dispatchers:${socket.id}`);
});
};

View File

@@ -1,79 +1,96 @@
"use client"; "use client";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { connectionStore } from "../../_store/connectionStore"; import { connectionStore } from "../../_store/connectionStore";
import { useEffect } from "react"; import { useEffect, useRef, useState } from "react";
import { CheckCircledIcon } from "@radix-ui/react-icons"; import { useForm } from "react-hook-form";
export const ConnectBtn = () => { export const ConnectionBtn = () => {
const modalRef = useRef<HTMLDialogElement>(null);
const connection = connectionStore((state) => state);
const [form, setForm] = useState({
logoffTime: "",
selectedZone: "LST_01",
});
const session = useSession();
const uid = session.data?.user?.id;
if (!uid) return null;
return ( return (
<> <>
<button {!connection.isConnected ? (
className="btn btn-soft btn-info" <button
onClick={() => className="btn btn-soft btn-info"
( onClick={() => modalRef.current?.showModal()}
document.getElementById("my_modal_1") as HTMLDialogElement >
)?.showModal() Verbinden
} </button>
> ) : (
Verbinden <button
</button> className="btn btn-soft btn-success"
<dialog id="my_modal_1" className="modal"> onClick={() => modalRef.current?.showModal()}
>
Verbunden
</button>
)}
<dialog ref={modalRef} className="modal">
<div className="modal-box flex flex-col items-center justify-center"> <div className="modal-box flex flex-col items-center justify-center">
<h3 className="text-lg font-bold mb-5">Als Disponent anmelden</h3> {connection.isConnected ? (
<h3 className="text-lg font-bold mb-5">
Verbunden als{" "}
<span className="text-info">
&lt;{connection.selectedZone}&gt;
</span>
</h3>
) : (
<h3 className="text-lg font-bold mb-5">Als Disponent anmelden</h3>
)}
<fieldset className="fieldset w-full"> <fieldset className="fieldset w-full">
<label className="floating-label w-full text-base"> <label className="floating-label w-full text-base">
<span>Logoff Zeit (UTC+1)</span> <span>Logoff Zeit (UTC+1)</span>
<input type="time" className="input w-full" /> <input
onChange={(e) =>
setForm({
...form,
logoffTime: e.target.value,
})
}
value={form.logoffTime}
type="time"
className="input w-full"
/>
</label> </label>
<p className="fieldset-label"> {!connection.isConnected && (
Du kannst diese Zeit später noch anpassen. <p className="fieldset-label">
</p> Du kannst diese Zeit später noch anpassen.
</p>
)}
</fieldset> </fieldset>
<div className="modal-action flex justify-between w-full"> <div className="modal-action flex justify-between w-full">
<form method="dialog" className="w-full flex justify-between"> <form method="dialog" className="w-full flex justify-between">
<button className="btn btn-soft">Abbrechen</button> <button className="btn btn-soft">Abbrechen</button>
<button className="btn btn-soft btn-info">Verbinden</button> {connection.isConnected ? (
</form> <button
</div> className="btn btn-soft btn-error"
</div> type="submit"
</dialog> onSubmit={() => false}
</> onClick={() => {
); connection.disconnect();
}; }}
>
export const ConnectedBtn = () => { Verbindung Trennen
return ( </button>
<> ) : (
<button <button
className="btn btn-soft btn-success" type="submit"
onClick={() => onSubmit={() => false}
( onClick={() => {
document.getElementById("my_modal_1") as HTMLDialogElement connection.connect(uid, form.selectedZone, form.logoffTime);
)?.showModal() }}
} className="btn btn-soft btn-info"
> >
Verbunden Verbinden
</button> </button>
<dialog id="my_modal_1" className="modal"> )}
<div className="modal-box flex flex-col items-center justify-center">
<h3 className="text-lg font-bold mb-5">
Verbunden als <span className="text-info">&lt;LST_01&gt;</span>
</h3>
<fieldset className="fieldset w-full">
<label className="floating-label w-full text-base join">
<span>Logoff Zeit (UTC+1)</span>
<input type="time" className="input w-full" />
<button className="btn btn-soft btn-info join-item">
<CheckCircledIcon className="w-4 h-4" /> Save
</button>
</label>
</fieldset>
<div className="modal-action flex justify-between w-full">
<form method="dialog" className="w-full flex justify-between">
<button className="btn btn-soft">Abbrechen</button>
<button className="btn btn-soft btn-error">
Verbindung Trennen
</button>
</form> </form>
</div> </div>
</div> </div>
@@ -83,15 +100,9 @@ export const ConnectedBtn = () => {
}; };
export const Connection = () => { export const Connection = () => {
const session = useSession(); return (
const cStore = connectionStore((state) => state); <div>
const uid = session.data?.user?.id; <ConnectionBtn />
</div>
useEffect(() => { );
if (uid) {
cStore.connect(uid);
}
}, [uid]);
return <div>{cStore.isConnected ? <ConnectedBtn /> : <ConnectBtn />}</div>;
}; };

View File

@@ -3,17 +3,39 @@ import { socket } from "../(dispatch)/socket";
interface ConnectionStore { interface ConnectionStore {
isConnected: boolean; isConnected: boolean;
connect: (uid: string) => Promise<void>; selectedZone: string;
connect: (
uid: string,
selectedZone: string,
logoffTime: string,
) => Promise<void>;
disconnect: () => void;
} }
export const connectionStore = create<ConnectionStore>((set) => ({ export const connectionStore = create<ConnectionStore>((set) => ({
isConnected: false, isConnected: false,
connect: async (uid: string) => { selectedZone: "LST_01",
socket.auth = { uid }; connect: async (uid, selectedZone, logoffTime) =>
socket.connect(); new Promise((resolve) => {
socket.auth = { uid };
set({ selectedZone });
socket.connect();
socket.once("connect", () => {
socket.emit("connect-dispatch", {
logoffTime,
selectedZone,
});
resolve();
});
}),
disconnect: () => {
socket.disconnect();
}, },
})); }));
socket.on("connect", () => { socket.on("connect", () => {
connectionStore.setState({ isConnected: true }); connectionStore.setState({ isConnected: true });
}); });
socket.on("disconnect", () => {
connectionStore.setState({ isConnected: false });
});

View File

@@ -20,6 +20,7 @@
"postcss": "^8.5.1", "postcss": "^8.5.1",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"socket.io-client": "^4.8.1", "socket.io-client": "^4.8.1",
"tailwindcss": "^4.0.14", "tailwindcss": "^4.0.14",
"zustand": "^5.0.3" "zustand": "^5.0.3"

View File

@@ -23,15 +23,11 @@ services:
- postgres - postgres
redis: redis:
container_name: redis container_name: redis
image: docker.io/bitnami/redis:7.4 image: redis/redis-stack:latest
environment:
# ALLOW_EMPTY_PASSWORD is recommended only for development.
- ALLOW_EMPTY_PASSWORD=yes
- REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
ports: ports:
- "6379:6379" - "6379:6379"
volumes: volumes:
- "redis_data:/bitnami/redis/data" - "redis_data:/data"
moodle_database: moodle_database:
container_name: moodle_database container_name: moodle_database

2
package-lock.json generated
View File

@@ -30,6 +30,7 @@
"postcss": "^8.5.1", "postcss": "^8.5.1",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"socket.io-client": "^4.8.1", "socket.io-client": "^4.8.1",
"tailwindcss": "^4.0.14", "tailwindcss": "^4.0.14",
"zustand": "^5.0.3" "zustand": "^5.0.3"
@@ -48,6 +49,7 @@
"apps/dispatch-server": { "apps/dispatch-server": {
"dependencies": { "dependencies": {
"@react-email/components": "^0.0.33", "@react-email/components": "^0.0.33",
"@redis/json": "^1.0.7",
"@socket.io/redis-adapter": "^8.3.0", "@socket.io/redis-adapter": "^8.3.0",
"axios": "^1.7.9", "axios": "^1.7.9",
"cron": "^4.1.0", "cron": "^4.1.0",