From c801b9a57eff9c16130bfe83a2607676c523dbde Mon Sep 17 00:00:00 2001 From: Sebastian Brenner Date: Wed, 7 Jan 2026 10:55:46 +0100 Subject: [PATCH] add member view to group --- data.sqlite | Bin 49152 -> 49152 bytes src/client/Store.ts | 5 ++ src/client/components/Group.tsx | 51 ++++++++++++++++-- .../components/authentication/SignIn.tsx | 4 +- src/client/serverApi.ts | 30 +++++++++++ src/index.ts | 16 +++++- src/server/db.ts | 8 +++ 7 files changed, 107 insertions(+), 7 deletions(-) diff --git a/data.sqlite b/data.sqlite index 944683265977e2d9d8b768ef5ca03cda8edffa62..5a52c339423ee450be0385208007e85ee4fb8ddd 100644 GIT binary patch delta 212 zcmZo@U~Xt&o**q~%)r3F1H|k=%mkt*>KH2;GwAjHAz&DfsG@m@EHc`h|(U3u}_a`s^4+d`bPYirB`A_q?^VahS@w#!9 zalhmYW&gCXVG=h>lOZenW-*>O$~>;5Pq { this.currentView = view; @@ -32,6 +33,10 @@ export class Store { this.group = group; } + setGroupMembers = (members: User[]) => { + this.groupMembers = members; + } + logout = () => { void cookieStore.delete('user'); void cookieStore.delete('group'); diff --git a/src/client/components/Group.tsx b/src/client/components/Group.tsx index 727bec7..b8a5e00 100644 --- a/src/client/components/Group.tsx +++ b/src/client/components/Group.tsx @@ -4,13 +4,26 @@ import Stack from '@mui/material/Stack'; import { useTheme } from '@mui/material/styles'; import Card from './Card'; import { useStore } from '../Store'; -import { CardHeader, IconButton } from '@mui/material'; +import { Avatar, CardHeader, IconButton, List, ListItem, ListItemAvatar, ListItemText, ListSubheader } from '@mui/material'; import LogoutIcon from '@mui/icons-material/Logout'; +import { useEffect, useState } from 'react'; +import { fetchGroupMembers } from '../serverApi'; +import MoreHorizIcon from '@mui/icons-material/MoreHoriz'; +import AccountCircleIcon from '@mui/icons-material/AccountCircle'; const Group = () => { const theme = useTheme(); const store = useStore(); const { group, logout } = store; + const [loading, setLoading] = useState(false); + + useEffect(() => { + setLoading(true); + void fetchGroupMembers(group?.id ?? 0).then(members => { + store.setGroupMembers(members); + setLoading(false); + }); + }, [group]); if (!group) { return (Keine Gruppe gefunden.); @@ -46,11 +59,39 @@ const Group = () => { }} > - {'Willkommen in der Gruppe!'} - - - {'Admin: ' + group.mail} + {`Aktuelle Phase: ${group.phase}`} + + Mitglieder + + } + dense={true} + > + {store.groupMembers.map((member) => ( + + + + } + > + + + + + + + ))} + diff --git a/src/client/components/authentication/SignIn.tsx b/src/client/components/authentication/SignIn.tsx index cfd828f..05de794 100644 --- a/src/client/components/authentication/SignIn.tsx +++ b/src/client/components/authentication/SignIn.tsx @@ -12,7 +12,7 @@ import { useTheme } from '@mui/material/styles'; import GroupAddIcon from '@mui/icons-material/GroupAdd'; import ForgotPassword from '../ForgotPassword'; import Card from '../Card'; -import { createUser, fetchGroupByCode, fetchUser } from '../../serverApi'; +import { addUserToGroup, createUser, fetchGroupByCode, fetchUser } from '../../serverApi'; import { observer } from 'mobx-react-lite'; import { useStore } from '../../Store'; @@ -64,6 +64,8 @@ const SignIn = observer(() => { if (!user) throw new Error('Error creating user'); } + await addUserToGroup(groupData.id, user.id); + await cookieStore.set('user', JSON.stringify(user)); await cookieStore.set('group', JSON.stringify(groupData)); setGroup(groupData); diff --git a/src/client/serverApi.ts b/src/client/serverApi.ts index 22b4215..939e826 100644 --- a/src/client/serverApi.ts +++ b/src/client/serverApi.ts @@ -58,4 +58,34 @@ export async function createGroup(group: Partial): Promise console.error('Failed to create group:', err); return null; } +} + +export async function fetchGroupMembers(groupId: number): Promise { + try { + const res = await fetch(`/api/group_members/${groupId}`); + if (!res.ok) return []; + const data: User[] = await res.json(); + return data; + } catch (err) { + console.error('Failed to fetch group members:', err); + return []; + } +} + +export async function addUserToGroup(groupId: number, userId: number): Promise<{ id: number | bigint; } | null> { + try { + const res = await fetch('/api/group_members', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ groupId, userId }), + }); + if (!res.ok) return null; + const data: { id: number | bigint; } = await res.json(); + return data; + } catch (err) { + console.error('Failed to add user to group:', err); + return null; + } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index da0c848..19b2dc1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,10 +60,24 @@ const server = serve({ console.log('Fetching members for group ID:', groupId, 'Result:', members); return new Response(JSON.stringify(members), { headers: { 'Content-Type': 'application/json' } }); }, + + '/api/group_members': { + async POST(req) { + try { + const { groupId, userId } = await req.json() as { groupId: number; userId: number }; + console.log('Received request to add user ID ', userId, 'to group ID:', groupId); + const response = await db.addUserToGroup(groupId, userId); + return new Response(JSON.stringify(response), { headers: { 'Content-Type': 'application/json' } }); + } catch (err) { + console.error('Error adding user to group:', err); + return new Response(JSON.stringify({ error: 'Failed to add user to group' }), { status: 500 }); + } + } + } }, development: process.env.NODE_ENV !== 'production' && { - // Enable browser hot reloading in development + // Enable browser hot reloading in development hmr: true, // Echo console logs from the browser to the server diff --git a/src/server/db.ts b/src/server/db.ts index 578151c..cb25037 100644 --- a/src/server/db.ts +++ b/src/server/db.ts @@ -157,6 +157,14 @@ class DB { return users; } + public async addUserToGroup(groupId: number, userId: number): Promise<{ id: number | bigint; } | null> { + const membership: { id: number | bigint;}[] = await this.instance` + INSERT OR IGNORE INTO membership (group_id, user_id) VALUES (${groupId}, ${userId}) + RETURNING * + `; + return membership[0] ?? null; + } + constructor(url: string = './data.sqlite') { this.instance = new SQL(url, {adapter: 'sqlite'}); this.prepareDB();