From 4825a57a54cb772de9ee4d76c60a6b6afcd0a8dc Mon Sep 17 00:00:00 2001 From: Michess Date: Tue, 14 Apr 2026 12:35:09 +0200 Subject: [PATCH] feat: add real-time notification system and fix admin login bug - Fix login: role field was missing from session (admin page inaccessible) - Add onlineUsers map in socket/state.ts to track connected users - Join personal user:${id} socket room on connect for targeted notifications - Emit friendRequestReceived/friendRequestAccepted socket events from friends controller - Add sendGameInvite socket event handler on server - New NotificationContext: global socket provider for registered users - New NotificationPanel: bell icon with unread badge and dropdown in header - Friends page: inline game invite input per friend with sendGameInvite Co-Authored-By: Claude Sonnet 4.6 --- client/src/app/friends/page.tsx | 43 ++++++++-- client/src/components/Header.tsx | 2 + client/src/components/NotificationPanel.tsx | 80 ++++++++++++++++++ client/src/context/ContextProvider.tsx | 7 +- client/src/context/NotificationContext.tsx | 87 ++++++++++++++++++++ server/src/controllers/auth.controller.ts | 3 +- server/src/controllers/friends.controller.ts | 18 ++++ server/src/socket/index.ts | 24 +++++- server/src/socket/state.ts | 2 + types/index.d.ts | 12 +++ 10 files changed, 267 insertions(+), 11 deletions(-) create mode 100644 client/src/components/NotificationPanel.tsx create mode 100644 client/src/context/NotificationContext.tsx create mode 100644 server/src/socket/state.ts diff --git a/client/src/app/friends/page.tsx b/client/src/app/friends/page.tsx index 90c6ce6..e8a2216 100644 --- a/client/src/app/friends/page.tsx +++ b/client/src/app/friends/page.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; import { API_URL } from "@/config"; +import { useNotifications } from "@/context/NotificationContext"; import { useSession } from "@/context/session"; interface Friend { @@ -29,10 +30,13 @@ interface FriendRequest { export default function FriendsPage() { const { user } = useSession(); const router = useRouter(); + const { sendGameInvite } = useNotifications(); const [friends, setFriends] = useState([]); const [requests, setRequests] = useState<{ incoming: FriendRequest[]; outgoing: FriendRequest[] }>({ incoming: [], outgoing: [] }); const [addUsername, setAddUsername] = useState(""); const [addMsg, setAddMsg] = useState(""); + const [inviteCodes, setInviteCodes] = useState>({}); + const [inviteSent, setInviteSent] = useState>({}); useEffect(() => { if (user === null) router.push("/"); @@ -146,14 +150,37 @@ export default function FriendsPage() {

Meine Freunde ({friends.length})

{friends.length === 0 &&

Noch keine Freunde. Füge jemanden hinzu!

} {friends.map((f) => ( -
- - {f.friend_name} - - {f.wins}W / {f.losses}L / {f.draws}D - +
+
+ + {f.friend_name} + + {f.wins}W / {f.losses}L / {f.draws}D + +
+
+ setInviteCodes((prev) => ({ ...prev, [f.friend_id]: e.target.value }))} + /> + +
))}
diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx index 494c6ce..c9423db 100644 --- a/client/src/components/Header.tsx +++ b/client/src/components/Header.tsx @@ -2,6 +2,7 @@ import { IconUser, IconShield } from "@tabler/icons-react"; import Link from "next/link"; +import NotificationPanel from "./NotificationPanel"; import ThemeToggle from "./ThemeToggle"; import { useSession } from "@/context/session"; @@ -35,6 +36,7 @@ export default function Header() { Admin )} + {user?.id && typeof user.id === "number" && }