add member view to group

This commit is contained in:
2026-01-07 10:55:46 +01:00
parent 624e62822c
commit c801b9a57e
7 changed files with 107 additions and 7 deletions

Binary file not shown.

View File

@@ -15,6 +15,7 @@ export class Store {
currentView: View = 'signIn'; currentView: View = 'signIn';
user: User | null = null; user: User | null = null;
group: Group | null = null; group: Group | null = null;
groupMembers: User[] = [];
setCurrentView = (view: View) => { setCurrentView = (view: View) => {
this.currentView = view; this.currentView = view;
@@ -32,6 +33,10 @@ export class Store {
this.group = group; this.group = group;
} }
setGroupMembers = (members: User[]) => {
this.groupMembers = members;
}
logout = () => { logout = () => {
void cookieStore.delete('user'); void cookieStore.delete('user');
void cookieStore.delete('group'); void cookieStore.delete('group');

View File

@@ -4,13 +4,26 @@ import Stack from '@mui/material/Stack';
import { useTheme } from '@mui/material/styles'; import { useTheme } from '@mui/material/styles';
import Card from './Card'; import Card from './Card';
import { useStore } from '../Store'; 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 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 Group = () => {
const theme = useTheme(); const theme = useTheme();
const store = useStore(); const store = useStore();
const { group, logout } = store; 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) { if (!group) {
return (<Typography>Keine Gruppe gefunden.</Typography>); return (<Typography>Keine Gruppe gefunden.</Typography>);
@@ -46,11 +59,39 @@ const Group = () => {
}} }}
> >
<Typography> <Typography>
{'Willkommen in der Gruppe!'} {`Aktuelle Phase: ${group.phase}`}
</Typography>
<Typography>
{'Admin: ' + group.mail}
</Typography> </Typography>
<List
sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
component="nav"
aria-labelledby="nested-list-subheader"
subheader={
<ListSubheader component="div" id="nested-list-subheader">
Mitglieder
</ListSubheader>
}
dense={true}
>
{store.groupMembers.map((member) => (
<ListItem
key={member.id}
secondaryAction={
<IconButton edge="end" aria-label="delete">
<MoreHorizIcon />
</IconButton>
}
>
<ListItemAvatar>
<Avatar>
<AccountCircleIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary={member.mail}
secondary={member.mail === group.mail ? 'Admin' : 'Mitglied'}
/>
</ListItem>))}
</List>
</Box> </Box>
</Card> </Card>
</Stack> </Stack>

View File

@@ -12,7 +12,7 @@ import { useTheme } from '@mui/material/styles';
import GroupAddIcon from '@mui/icons-material/GroupAdd'; import GroupAddIcon from '@mui/icons-material/GroupAdd';
import ForgotPassword from '../ForgotPassword'; import ForgotPassword from '../ForgotPassword';
import Card from '../Card'; import Card from '../Card';
import { createUser, fetchGroupByCode, fetchUser } from '../../serverApi'; import { addUserToGroup, createUser, fetchGroupByCode, fetchUser } from '../../serverApi';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import { useStore } from '../../Store'; import { useStore } from '../../Store';
@@ -64,6 +64,8 @@ const SignIn = observer(() => {
if (!user) throw new Error('Error creating user'); if (!user) throw new Error('Error creating user');
} }
await addUserToGroup(groupData.id, user.id);
await cookieStore.set('user', JSON.stringify(user)); await cookieStore.set('user', JSON.stringify(user));
await cookieStore.set('group', JSON.stringify(groupData)); await cookieStore.set('group', JSON.stringify(groupData));
setGroup(groupData); setGroup(groupData);

View File

@@ -59,3 +59,33 @@ export async function createGroup(group: Partial<Group>): Promise<Group | null>
return null; return null;
} }
} }
export async function fetchGroupMembers(groupId: number): Promise<User[]> {
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;
}
}

View File

@@ -60,6 +60,20 @@ const server = serve({
console.log('Fetching members for group ID:', groupId, 'Result:', members); console.log('Fetching members for group ID:', groupId, 'Result:', members);
return new Response(JSON.stringify(members), { headers: { 'Content-Type': 'application/json' } }); 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' && { development: process.env.NODE_ENV !== 'production' && {

View File

@@ -157,6 +157,14 @@ class DB {
return users; 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') { constructor(url: string = './data.sqlite') {
this.instance = new SQL(url, {adapter: 'sqlite'}); this.instance = new SQL(url, {adapter: 'sqlite'});
this.prepareDB(); this.prepareDB();